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..f4fe17774 100644 --- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -2,16 +2,485 @@ 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 { + /* + Implants + */ + val advanced_regen = ImplantDefinition(0) + + val targeting = ImplantDefinition(1) + + val audio_amplifier = ImplantDefinition(2) + + val darklight_vision = ImplantDefinition(3) + + val melee_booster = ImplantDefinition(4) + + val personal_shield = ImplantDefinition(5) + + val range_magnifier = ImplantDefinition(6) + + val second_wind = ImplantDefinition(7) + + val silent_run = ImplantDefinition(8) + + val surge = ImplantDefinition(9) + + /* + Equipment (locker_container, kits, ammunition, weapons) + */ + import net.psforever.packet.game.objectcreate.ObjectClass + val locker_container = new EquipmentDefinition(456) { + Name = "locker container" + Size = EquipmentSize.Inventory + Packet = new LockerContainerConverter() + } + + val medkit = KitDefinition(Kits.medkit) + + val super_medkit = KitDefinition(Kits.super_medkit) + + val super_armorkit = KitDefinition(Kits.super_armorkit) + + val super_staminakit = KitDefinition(Kits.super_staminakit) //super stimpak + + val melee_ammo = AmmoBoxDefinition(Ammo.melee_ammo) + + val frag_grenade_ammo = AmmoBoxDefinition(Ammo.frag_grenade_ammo) + + val plasma_grenade_ammo = AmmoBoxDefinition(Ammo.plasma_grenade_ammo) + + val jammer_grenade_ammo = AmmoBoxDefinition(Ammo.jammer_grenade_ammo) + + val bullet_9mm = AmmoBoxDefinition(Ammo.bullet_9mm) + + val bullet_9mm_AP = AmmoBoxDefinition(Ammo.bullet_9mm_AP) + + val shotgun_shell = AmmoBoxDefinition(Ammo.shotgun_shell) + + val shotgun_shell_AP = AmmoBoxDefinition(Ammo.shotgun_shell_AP) + + val energy_cell = AmmoBoxDefinition(Ammo.energy_cell) + + val anniversary_ammo = AmmoBoxDefinition(Ammo.anniversary_ammo) //10mm multi-phase + + val ancient_ammo_combo = AmmoBoxDefinition(Ammo.ancient_ammo_combo) + + val maelstrom_ammo = AmmoBoxDefinition(Ammo.maelstrom_ammo) + + val phoenix_missile = AmmoBoxDefinition(Ammo.phoenix_missile) //decimator missile + + val striker_missile_ammo = AmmoBoxDefinition(Ammo.striker_missile_ammo) + + val hunter_seeker_missile = AmmoBoxDefinition(Ammo.hunter_seeker_missile) //phoenix missile + + val lancer_cartridge = AmmoBoxDefinition(Ammo.lancer_cartridge) + + val rocket = AmmoBoxDefinition(Ammo.rocket) + + val frag_cartridge = AmmoBoxDefinition(Ammo.frag_cartridge) + + val plasma_cartridge = AmmoBoxDefinition(Ammo.plasma_cartridge) + + val jammer_cartridge = AmmoBoxDefinition(Ammo.jammer_cartridge) + + val bolt = AmmoBoxDefinition(Ammo.bolt) + + val oicw_ammo = AmmoBoxDefinition(Ammo.oicw_ammo) //scorpion missile + + val flamethrower_ammo = AmmoBoxDefinition(Ammo.flamethrower_ammo) + + val health_canister = AmmoBoxDefinition(Ammo.health_canister) + + val armor_canister = AmmoBoxDefinition(Ammo.armor_canister) + + val upgrade_canister = AmmoBoxDefinition(Ammo.upgrade_canister) + + val trek_ammo = AmmoBoxDefinition(Ammo.trek_ammo) + // + val bullet_35mm = AmmoBoxDefinition(Ammo.bullet_35mm) //liberator nosegun + + val ancient_ammo_vehicle = AmmoBoxDefinition(Ammo.ancient_ammo_vehicle) + // + val aphelion_laser_ammo = AmmoBoxDefinition(Ammo.aphelion_laser_ammo) + + val aphelion_immolation_cannon_ammo = AmmoBoxDefinition(Ammo.aphelion_immolation_cannon_ammo) + + val aphelion_plasma_rocket_ammo = AmmoBoxDefinition(Ammo.aphelion_plasma_rocket_ammo) + + val aphelion_ppa_ammo = AmmoBoxDefinition(Ammo.aphelion_ppa_ammo) + + val aphelion_starfire_ammo = AmmoBoxDefinition(Ammo.aphelion_starfire_ammo) + + val skyguard_flak_cannon_ammo = AmmoBoxDefinition(Ammo.skyguard_flak_cannon_ammo) + + val firebird_missile = AmmoBoxDefinition(ObjectClass.firebird_missile) + + val flux_cannon_thresher_battery = AmmoBoxDefinition(Ammo.flux_cannon_thresher_battery) + + val fluxpod_ammo = AmmoBoxDefinition(Ammo.fluxpod_ammo) + + val hellfire_ammo = AmmoBoxDefinition(Ammo.hellfire_ammo) + + val liberator_bomb = AmmoBoxDefinition(Ammo.liberator_bomb) + + val bullet_25mm = AmmoBoxDefinition(Ammo.bullet_25mm) //liberator tailgun + + val bullet_75mm = AmmoBoxDefinition(Ammo.bullet_75mm) //lightning shell + + val heavy_grenade_mortar = AmmoBoxDefinition(Ammo.heavy_grenade_mortar) //marauder and gal gunship + + val pulse_battery = AmmoBoxDefinition(Ammo.pulse_battery) + + val heavy_rail_beam_battery = AmmoBoxDefinition(Ammo.heavy_rail_beam_battery) + + val reaver_rocket = AmmoBoxDefinition(Ammo.reaver_rocket) + + val bullet_20mm = AmmoBoxDefinition(Ammo.bullet_20mm) //reaver nosegun + + val bullet_12mm = AmmoBoxDefinition(Ammo.bullet_12mm) //common + + val wasp_rocket_ammo = AmmoBoxDefinition(Ammo.wasp_rocket_ammo) + + val wasp_gun_ammo = AmmoBoxDefinition(Ammo.wasp_gun_ammo) //wasp nosegun + + val bullet_15mm = AmmoBoxDefinition(Ammo.bullet_15mm) + + val colossus_100mm_cannon_ammo = AmmoBoxDefinition(Ammo.colossus_100mm_cannon_ammo) + + val colossus_burster_ammo = AmmoBoxDefinition(Ammo.colossus_burster_ammo) + + val colossus_cluster_bomb_ammo = AmmoBoxDefinition(Ammo.colossus_cluster_bomb_ammo) //colossus mortar launcher shells + + val colossus_chaingun_ammo = AmmoBoxDefinition(Ammo.colossus_chaingun_ammo) + + val colossus_tank_cannon_ammo = AmmoBoxDefinition(Ammo.colossus_tank_cannon_ammo) + + val bullet_105mm = AmmoBoxDefinition(Ammo.bullet_105mm) //prowler 100mm cannon shell + + val gauss_cannon_ammo = AmmoBoxDefinition(Ammo.gauss_cannon_ammo) + + val peregrine_dual_machine_gun_ammo = AmmoBoxDefinition(Ammo.peregrine_dual_machine_gun_ammo) + + val peregrine_mechhammer_ammo = AmmoBoxDefinition(Ammo.peregrine_mechhammer_ammo) + + val peregrine_particle_cannon_ammo = AmmoBoxDefinition(Ammo.peregrine_particle_cannon_ammo) + + val peregrine_rocket_pod_ammo = AmmoBoxDefinition(Ammo.peregrine_rocket_pod_ammo) + + val peregrine_sparrow_ammo = AmmoBoxDefinition(Ammo.peregrine_sparrow_ammo) + + val bullet_150mm = AmmoBoxDefinition(Ammo.bullet_150mm) + init_ammo() + + val chainblade = ToolDefinition(ObjectClass.chainblade) + + val magcutter = ToolDefinition(ObjectClass.magcutter) + + val forceblade = ToolDefinition(ObjectClass.forceblade) + + val katana = ToolDefinition(ObjectClass.katana) + + val frag_grenade = ToolDefinition(ObjectClass.frag_grenade) + + val plasma_grenade = ToolDefinition(ObjectClass.plasma_grenade) + + val jammer_grenade = ToolDefinition(ObjectClass.jammer_grenade) + + val repeater = ToolDefinition(ObjectClass.repeater) + + val isp = ToolDefinition(ObjectClass.isp) //mag-scatter + + val beamer = ToolDefinition(ObjectClass.beamer) + + val ilc9 = ToolDefinition(ObjectClass.ilc9) //amp + + val suppressor = ToolDefinition(ObjectClass.suppressor) + + val punisher = ToolDefinition(ObjectClass.punisher) + + val flechette = ToolDefinition(ObjectClass.flechette) //sweeper + + val cycler = ToolDefinition(ObjectClass.cycler) + + val gauss = ToolDefinition(ObjectClass.gauss) + + val pulsar = ToolDefinition(ObjectClass.pulsar) + + val anniversary_guna = ToolDefinition(ObjectClass.anniversary_guna) //tr stinger + + val anniversary_gun = ToolDefinition(ObjectClass.anniversary_gun) //nc spear + + val anniversary_gunb = ToolDefinition(ObjectClass.anniversary_gunb) //vs eraser + + val spiker = ToolDefinition(ObjectClass.spiker) + + val mini_chaingun = ToolDefinition(ObjectClass.mini_chaingun) + + val r_shotgun = ToolDefinition(ObjectClass.r_shotgun) //jackhammer + + val lasher = ToolDefinition(ObjectClass.lasher) + + val maelstrom = ToolDefinition(ObjectClass.maelstrom) + + val phoenix = ToolDefinition(ObjectClass.phoenix) //decimator + + val striker = ToolDefinition(ObjectClass.striker) + + val hunterseeker = ToolDefinition(ObjectClass.hunterseeker) //phoenix + + val lancer = ToolDefinition(ObjectClass.lancer) + + val rocklet = ToolDefinition(ObjectClass.rocklet) + + val thumper = ToolDefinition(ObjectClass.thumper) + + val radiator = ToolDefinition(ObjectClass.radiator) + + val heavy_sniper = ToolDefinition(ObjectClass.heavy_sniper) //hsr + + val bolt_driver = ToolDefinition(ObjectClass.bolt_driver) + + val oicw = ToolDefinition(ObjectClass.oicw) //scorpion + + val flamethrower = ToolDefinition(ObjectClass.flamethrower) + + val trhev_dualcycler = ToolDefinition(ObjectClass.trhev_dualcycler) //TODO + + val trhev_pounder = ToolDefinition(ObjectClass.trhev_pounder) //TODO + + val trhev_burster = ToolDefinition(ObjectClass.trhev_burster) //TODO + + val nchev_scattercannon = ToolDefinition(ObjectClass.nchev_scattercannon) //TODO + + val nchev_falcon = ToolDefinition(ObjectClass.nchev_falcon) //TODO + + val nchev_sparrow = ToolDefinition(ObjectClass.nchev_sparrow) //TODO + + val vshev_quasar = ToolDefinition(ObjectClass.vshev_quasar) //TODO + + val vshev_comet = ToolDefinition(ObjectClass.vshev_comet) //TODO + + val vshev_starfire = ToolDefinition(ObjectClass.vshev_starfire) //TODO + + val medicalapplicator = ToolDefinition(ObjectClass.medicalapplicator) + + val nano_dispenser = ToolDefinition(ObjectClass.nano_dispenser) + + val bank = ToolDefinition(ObjectClass.bank) + + val remote_electronics_kit = SimpleItemDefinition(SItem.remote_electronics_kit) + + val trek = ToolDefinition(ObjectClass.trek) + + val flail_targeting_laser = SimpleItemDefinition(SItem.flail_targeting_laser) + + val command_detonater = SimpleItemDefinition(SItem.command_detonater) + + val ace = ConstructionItemDefinition(CItem.Unit.ace) + + val advanced_ace = ConstructionItemDefinition(CItem.Unit.advanced_ace) + + val fury_weapon_systema = ToolDefinition(ObjectClass.fury_weapon_systema) + + val quadassault_weapon_system = ToolDefinition(ObjectClass.quadassault_weapon_system) + + val scythe = ToolDefinition(ObjectClass.scythe) //TODO resolve ammo slot/pool discrepancy + + val chaingun_p = ToolDefinition(ObjectClass.chaingun_p) + + val skyguard_weapon_system = ToolDefinition(ObjectClass.skyguard_weapon_system) + + val grenade_launcher_marauder = ToolDefinition(ObjectClass.grenade_launcher_marauder) + + val advanced_missile_launcher_t = ToolDefinition(ObjectClass.advanced_missile_launcher_t) + + val flux_cannon_thresher = ToolDefinition(ObjectClass.flux_cannon_thresher) + + val mediumtransport_weapon_systemA = ToolDefinition(ObjectClass.mediumtransport_weapon_systemA) + + val mediumtransport_weapon_systemB = ToolDefinition(ObjectClass.mediumtransport_weapon_systemB) + + val battlewagon_weapon_systema = ToolDefinition(ObjectClass.battlewagon_weapon_systema) + + val battlewagon_weapon_systemb = ToolDefinition(ObjectClass.battlewagon_weapon_systemb) + + val battlewagon_weapon_systemc = ToolDefinition(ObjectClass.battlewagon_weapon_systemc) + + val battlewagon_weapon_systemd = ToolDefinition(ObjectClass.battlewagon_weapon_systemd) + + val thunderer_weapon_systema = ToolDefinition(ObjectClass.thunderer_weapon_systema) + + val thunderer_weapon_systemb = ToolDefinition(ObjectClass.thunderer_weapon_systemb) + + val aurora_weapon_systema = ToolDefinition(ObjectClass.aurora_weapon_systema) + + val aurora_weapon_systemb = ToolDefinition(ObjectClass.aurora_weapon_systemb) + + val apc_weapon_systema = ToolDefinition(ObjectClass.apc_weapon_systema) + + val apc_weapon_systemb = ToolDefinition(ObjectClass.apc_weapon_systemb) + + val apc_ballgun_r = ToolDefinition(ObjectClass.apc_ballgun_r) + + val apc_ballgun_l = ToolDefinition(ObjectClass.apc_ballgun_l) + + val apc_weapon_systemc_tr = ToolDefinition(ObjectClass.apc_weapon_systemc_tr) + + val apc_weapon_systemd_tr = ToolDefinition(ObjectClass.apc_weapon_systemd_tr) + + val apc_weapon_systemc_nc = ToolDefinition(ObjectClass.apc_weapon_systemc_nc) + + val apc_weapon_systemd_nc = ToolDefinition(ObjectClass.apc_weapon_systemd_nc) + + val apc_weapon_systemc_vs = ToolDefinition(ObjectClass.apc_weapon_systemc_vs) + + val apc_weapon_systemd_vs = ToolDefinition(ObjectClass.apc_weapon_systemd_vs) + + val lightning_weapon_system = ToolDefinition(ObjectClass.lightning_weapon_system) + + val prowler_weapon_systemA = ToolDefinition(ObjectClass.prowler_weapon_systemA) + + val prowler_weapon_systemB = ToolDefinition(ObjectClass.prowler_weapon_systemB) + + val vanguard_weapon_system = ToolDefinition(ObjectClass.vanguard_weapon_system) + + val particle_beam_magrider = ToolDefinition(ObjectClass.particle_beam_magrider) + + val heavy_rail_beam_magrider = ToolDefinition(ObjectClass.heavy_rail_beam_magrider) + + val flail_weapon = ToolDefinition(ObjectClass.flail_weapon) + + val rotarychaingun_mosquito = ToolDefinition(ObjectClass.rotarychaingun_mosquito) + + val lightgunship_weapon_system = ToolDefinition(ObjectClass.lightgunship_weapon_system) + + val wasp_weapon_system = ToolDefinition(ObjectClass.wasp_weapon_system) + + val liberator_weapon_system = ToolDefinition(ObjectClass.liberator_weapon_system) + + val liberator_bomb_bay = ToolDefinition(ObjectClass.liberator_bomb_bay) + + val liberator_25mm_cannon = ToolDefinition(ObjectClass.liberator_25mm_cannon) + + val vulture_nose_weapon_system = ToolDefinition(ObjectClass.vulture_nose_weapon_system) + + val vulture_bomb_bay = ToolDefinition(ObjectClass.vulture_bomb_bay) + + val vulture_tail_cannon = ToolDefinition(ObjectClass.vulture_tail_cannon) + + val cannon_dropship_20mm = ToolDefinition(ObjectClass.cannon_dropship_20mm) + + val dropship_rear_turret = ToolDefinition(ObjectClass.dropship_rear_turret) + + val galaxy_gunship_cannon = ToolDefinition(ObjectClass.galaxy_gunship_cannon) + + val galaxy_gunship_tailgun = ToolDefinition(ObjectClass.galaxy_gunship_tailgun) + + val galaxy_gunship_gun = ToolDefinition(ObjectClass.galaxy_gunship_gun) + init_tools() + + /* + Vehicles + */ + val fury = VehicleDefinition(ObjectClass.fury) + + val quadassault = VehicleDefinition(ObjectClass.quadassault) + + val quadstealth = VehicleDefinition(ObjectClass.quadstealth) + + val two_man_assault_buggy = VehicleDefinition(ObjectClass.two_man_assault_buggy) + + val skyguard = VehicleDefinition(ObjectClass.skyguard) + + val threemanheavybuggy = VehicleDefinition(ObjectClass.threemanheavybuggy) + + val twomanheavybuggy = VehicleDefinition(ObjectClass.twomanheavybuggy) + + val twomanhoverbuggy = VehicleDefinition(ObjectClass.twomanhoverbuggy) + + val mediumtransport = VehicleDefinition(ObjectClass.mediumtransport) + + val battlewagon = VehicleDefinition(ObjectClass.battlewagon) + + val thunderer = VehicleDefinition(ObjectClass.thunderer) + + val aurora = VehicleDefinition(ObjectClass.aurora) + + val apc_tr = VehicleDefinition(ObjectClass.apc_tr) + + val apc_nc = VehicleDefinition(ObjectClass.apc_nc) + + val apc_vs = VehicleDefinition(ObjectClass.apc_vs) + + val lightning = VehicleDefinition(ObjectClass.lightning) + + val prowler = VehicleDefinition(ObjectClass.prowler) + + val vanguard = VehicleDefinition(ObjectClass.vanguard) + + val magrider = VehicleDefinition(ObjectClass.magrider) + + val ant = VehicleDefinition(ObjectClass.ant) + + val ams = VehicleDefinition(ObjectClass.ams) + + val router = VehicleDefinition(ObjectClass.router) + + val switchblade = VehicleDefinition(ObjectClass.switchblade) + + val flail = VehicleDefinition(ObjectClass.flail) + + val mosquito = VehicleDefinition(ObjectClass.mosquito) + + val lightgunship = VehicleDefinition(ObjectClass.lightgunship) + + val wasp = VehicleDefinition(ObjectClass.wasp) + + val liberator = VehicleDefinition(ObjectClass.liberator) + + val vulture = VehicleDefinition(ObjectClass.vulture) + + val dropship = VehicleDefinition(ObjectClass.dropship) + + val galaxy_gunship = VehicleDefinition(ObjectClass.galaxy_gunship) + + val lodestar = VehicleDefinition(ObjectClass.lodestar) + + val phantasm = VehicleDefinition(ObjectClass.phantasm) + init_vehicles() + + /* + Miscellaneous + */ + 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 + + val door = new DoorDefinition + /** * Given a faction, provide the standard assault melee weapon. * @param faction the faction @@ -163,6 +632,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 are 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 are 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 are 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 +692,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 +717,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 +730,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` */ @@ -273,1048 +769,1607 @@ object GlobalDefinitions { } } - /* - Implants - */ - val - advanced_regen = ImplantDefinition(0) + /** + * Initialize `AmmoBoxDefinition` globals. + */ + private def init_ammo() : Unit = { + bullet_9mm.Capacity = 50 + bullet_9mm.Tile = InventoryTile.Tile33 - val - targeting = ImplantDefinition(1) + bullet_9mm_AP.Capacity = 50 + bullet_9mm_AP.Tile = InventoryTile.Tile33 - val - audio_amplifier = ImplantDefinition(2) + shotgun_shell.Capacity = 32 + shotgun_shell.Tile = InventoryTile.Tile33 - val - darklight_vision = ImplantDefinition(3) + shotgun_shell_AP.Capacity = 32 + shotgun_shell_AP.Tile = InventoryTile.Tile33 - val - melee_booster = ImplantDefinition(4) + energy_cell.Capacity = 50 + energy_cell.Tile = InventoryTile.Tile33 - val - personal_shield = ImplantDefinition(5) + anniversary_ammo.Capacity = 30 + anniversary_ammo.Tile = InventoryTile.Tile33 - val - range_magnifier = ImplantDefinition(6) + ancient_ammo_combo.Capacity = 30 + ancient_ammo_combo.Tile = InventoryTile.Tile33 - val - second_wind = ImplantDefinition(7) + maelstrom_ammo.Capacity = 50 + maelstrom_ammo.Tile = InventoryTile.Tile33 - val - silent_run = ImplantDefinition(8) + striker_missile_ammo.Capacity = 15 + striker_missile_ammo.Tile = InventoryTile.Tile44 - val - surge = ImplantDefinition(9) + hunter_seeker_missile.Capacity = 9 + hunter_seeker_missile.Tile = InventoryTile.Tile44 - /* - Equipment (locker_container, kits, ammunition, weapons) - */ - import net.psforever.packet.game.objectcreate.ObjectClass - val - locker_container = new EquipmentDefinition(456) { - Name = "locker container" - Size = EquipmentSize.Inventory - Packet = new LockerContainerConverter() + lancer_cartridge.Capacity = 18 + lancer_cartridge.Tile = InventoryTile.Tile44 + + rocket.Capacity = 15 + rocket.Tile = InventoryTile.Tile33 + + frag_cartridge.Capacity = 12 + frag_cartridge.Tile = InventoryTile.Tile33 + + plasma_cartridge.Capacity = 12 + plasma_cartridge.Tile = InventoryTile.Tile33 + + jammer_cartridge.Capacity = 12 + jammer_cartridge.Tile = InventoryTile.Tile33 + + bolt.Capacity = 10 + bolt.Tile = InventoryTile.Tile33 + + oicw_ammo.Capacity = 10 + oicw_ammo.Tile = InventoryTile.Tile44 + + flamethrower_ammo.Capacity = 100 + flamethrower_ammo.Tile = InventoryTile.Tile44 + + health_canister.Capacity = 100 + health_canister.Tile = InventoryTile.Tile33 + + armor_canister.Capacity = 100 + armor_canister.Tile = InventoryTile.Tile33 + + upgrade_canister.Capacity = 100 + upgrade_canister.Tile = InventoryTile.Tile33 + + bullet_35mm.Capacity = 100 + bullet_35mm.Tile = InventoryTile.Tile44 + + aphelion_laser_ammo.Capacity = 165 + aphelion_laser_ammo.Tile = InventoryTile.Tile44 + + aphelion_immolation_cannon_ammo.Capacity = 100 + aphelion_immolation_cannon_ammo.Tile = InventoryTile.Tile55 + + aphelion_plasma_rocket_ammo.Capacity = 195 + aphelion_plasma_rocket_ammo.Tile = InventoryTile.Tile55 + + aphelion_ppa_ammo.Capacity = 110 + aphelion_ppa_ammo.Tile = InventoryTile.Tile44 + + aphelion_starfire_ammo.Capacity = 132 + aphelion_starfire_ammo.Tile = InventoryTile.Tile44 + + skyguard_flak_cannon_ammo.Capacity = 200 + skyguard_flak_cannon_ammo.Tile = InventoryTile.Tile44 + + firebird_missile.Capacity = 50 + firebird_missile.Tile = InventoryTile.Tile44 + + flux_cannon_thresher_battery.Capacity = 150 + flux_cannon_thresher_battery.Tile = InventoryTile.Tile44 + + fluxpod_ammo.Capacity = 80 + fluxpod_ammo.Tile = InventoryTile.Tile44 + + hellfire_ammo.Capacity = 24 + hellfire_ammo.Tile = InventoryTile.Tile44 + + liberator_bomb.Capacity = 20 + liberator_bomb.Tile = InventoryTile.Tile44 + + bullet_25mm.Capacity = 150 + bullet_25mm.Tile = InventoryTile.Tile44 + + bullet_75mm.Capacity = 100 + bullet_75mm.Tile = InventoryTile.Tile44 + + heavy_grenade_mortar.Capacity = 100 + heavy_grenade_mortar.Tile = InventoryTile.Tile44 + + pulse_battery.Capacity = 100 + pulse_battery.Tile = InventoryTile.Tile44 + + heavy_rail_beam_battery.Capacity = 100 + heavy_rail_beam_battery.Tile = InventoryTile.Tile44 + + reaver_rocket.Capacity = 12 + reaver_rocket.Tile = InventoryTile.Tile44 + + bullet_20mm.Capacity = 200 + bullet_20mm.Tile = InventoryTile.Tile44 + + bullet_12mm.Capacity = 200 + bullet_12mm.Tile = InventoryTile.Tile44 + + wasp_rocket_ammo.Capacity = 6 + wasp_rocket_ammo.Tile = InventoryTile.Tile44 + + wasp_gun_ammo.Capacity = 150 + wasp_gun_ammo.Tile = InventoryTile.Tile44 + + bullet_15mm.Capacity = 360 + bullet_15mm.Tile = InventoryTile.Tile44 + + colossus_100mm_cannon_ammo.Capacity = 90 + colossus_100mm_cannon_ammo.Tile = InventoryTile.Tile55 + + colossus_burster_ammo.Capacity = 235 + colossus_burster_ammo.Tile = InventoryTile.Tile44 + + colossus_cluster_bomb_ammo.Capacity = 150 + colossus_cluster_bomb_ammo.Tile = InventoryTile.Tile55 + + colossus_chaingun_ammo.Capacity = 600 + colossus_chaingun_ammo.Tile = InventoryTile.Tile44 + + colossus_tank_cannon_ammo.Capacity = 110 + colossus_tank_cannon_ammo.Tile = InventoryTile.Tile44 + + bullet_105mm.Capacity = 100 + bullet_105mm.Tile = InventoryTile.Tile44 + + gauss_cannon_ammo.Capacity = 15 + gauss_cannon_ammo.Tile = InventoryTile.Tile44 + + peregrine_dual_machine_gun_ammo.Capacity = 240 + peregrine_dual_machine_gun_ammo.Tile = InventoryTile.Tile44 + + peregrine_mechhammer_ammo.Capacity = 30 + peregrine_mechhammer_ammo.Tile = InventoryTile.Tile44 + + peregrine_particle_cannon_ammo.Capacity = 40 + peregrine_particle_cannon_ammo.Tile = InventoryTile.Tile55 + + peregrine_rocket_pod_ammo.Capacity = 275 + peregrine_rocket_pod_ammo.Tile = InventoryTile.Tile55 + + peregrine_sparrow_ammo.Capacity = 150 + peregrine_sparrow_ammo.Tile = InventoryTile.Tile44 + + bullet_150mm.Capacity = 50 + bullet_150mm.Tile = InventoryTile.Tile44 } - val - medkit = KitDefinition(Kits.medkit) - - val - super_medkit = KitDefinition(Kits.super_medkit) - - val - super_armorkit = KitDefinition(Kits.super_armorkit) - - val - super_staminakit = KitDefinition(Kits.super_staminakit) //super stimpak - - val - melee_ammo = AmmoBoxDefinition(Ammo.melee_ammo) - - val - frag_grenade_ammo = AmmoBoxDefinition(Ammo.frag_grenade_ammo) - - val - plasma_grenade_ammo = AmmoBoxDefinition(Ammo.plasma_grenade_ammo) - - val - jammer_grenade_ammo = AmmoBoxDefinition(Ammo.jammer_grenade_ammo) - - val - bullet_9mm = AmmoBoxDefinition(Ammo.bullet_9mm) - bullet_9mm.Capacity = 50 - bullet_9mm.Tile = InventoryTile.Tile33 - - val - bullet_9mm_AP = AmmoBoxDefinition(Ammo.bullet_9mm_AP) - bullet_9mm_AP.Capacity = 50 - bullet_9mm_AP.Tile = InventoryTile.Tile33 - - val - shotgun_shell = AmmoBoxDefinition(Ammo.shotgun_shell) - shotgun_shell.Capacity = 32 - shotgun_shell.Tile = InventoryTile.Tile33 - - val - shotgun_shell_AP = AmmoBoxDefinition(Ammo.shotgun_shell_AP) - shotgun_shell_AP.Capacity = 32 - shotgun_shell_AP.Tile = InventoryTile.Tile33 - - val - energy_cell = AmmoBoxDefinition(Ammo.energy_cell) - energy_cell.Capacity = 50 - energy_cell.Tile = InventoryTile.Tile33 - - val - anniversary_ammo = AmmoBoxDefinition(Ammo.anniversary_ammo) //10mm multi-phase - anniversary_ammo.Capacity = 30 - anniversary_ammo.Tile = InventoryTile.Tile33 - - val - ancient_ammo_combo = AmmoBoxDefinition(Ammo.ancient_ammo_combo) - ancient_ammo_combo.Capacity = 30 - ancient_ammo_combo.Tile = InventoryTile.Tile33 - - val - maelstrom_ammo = AmmoBoxDefinition(Ammo.maelstrom_ammo) - maelstrom_ammo.Capacity = 50 - maelstrom_ammo.Tile = InventoryTile.Tile33 - - val - phoenix_missile = AmmoBoxDefinition(Ammo.phoenix_missile) //decimator missile - - val - striker_missile_ammo = AmmoBoxDefinition(Ammo.striker_missile_ammo) - striker_missile_ammo.Capacity = 15 - striker_missile_ammo.Tile = InventoryTile.Tile44 - - val - hunter_seeker_missile = AmmoBoxDefinition(Ammo.hunter_seeker_missile) //phoenix missile - hunter_seeker_missile.Capacity = 9 - hunter_seeker_missile.Tile = InventoryTile.Tile44 - - val - lancer_cartridge = AmmoBoxDefinition(Ammo.lancer_cartridge) - lancer_cartridge.Capacity = 18 - lancer_cartridge.Tile = InventoryTile.Tile44 - - val - rocket = AmmoBoxDefinition(Ammo.rocket) - rocket.Capacity = 15 - rocket.Tile = InventoryTile.Tile33 - - val - frag_cartridge = AmmoBoxDefinition(Ammo.frag_cartridge) - frag_cartridge.Capacity = 12 - frag_cartridge.Tile = InventoryTile.Tile33 - - val - plasma_cartridge = AmmoBoxDefinition(Ammo.plasma_cartridge) - plasma_cartridge.Capacity = 12 - plasma_cartridge.Tile = InventoryTile.Tile33 - - val - jammer_cartridge = AmmoBoxDefinition(Ammo.jammer_cartridge) - jammer_cartridge.Capacity = 12 - jammer_cartridge.Tile = InventoryTile.Tile33 - - val - bolt = AmmoBoxDefinition(Ammo.bolt) - bolt.Capacity = 10 - bolt.Tile = InventoryTile.Tile33 - - val - oicw_ammo = AmmoBoxDefinition(Ammo.oicw_ammo) //scorpion missile - oicw_ammo.Capacity = 10 - oicw_ammo.Tile = InventoryTile.Tile44 - - val - flamethrower_ammo = AmmoBoxDefinition(Ammo.flamethrower_ammo) - flamethrower_ammo.Capacity = 100 - flamethrower_ammo.Tile = InventoryTile.Tile44 - - val - health_canister = AmmoBoxDefinition(Ammo.health_canister) - health_canister.Capacity = 100 - health_canister.Tile = InventoryTile.Tile33 - - val - armor_canister = AmmoBoxDefinition(Ammo.armor_canister) - armor_canister.Capacity = 100 - armor_canister.Tile = InventoryTile.Tile33 - - val - upgrade_canister = AmmoBoxDefinition(Ammo.upgrade_canister) - upgrade_canister.Capacity = 100 - upgrade_canister.Tile = InventoryTile.Tile33 - - val - trek_ammo = AmmoBoxDefinition(Ammo.trek_ammo) -// - val - bullet_35mm = AmmoBoxDefinition(Ammo.bullet_35mm) //liberator nosegun - bullet_35mm.Capacity = 100 - bullet_35mm.Tile = InventoryTile.Tile44 - - val - aphelion_laser_ammo = AmmoBoxDefinition(Ammo.aphelion_laser_ammo) - aphelion_laser_ammo.Capacity = 165 - aphelion_laser_ammo.Tile = InventoryTile.Tile44 - - val - aphelion_immolation_cannon_ammo = AmmoBoxDefinition(Ammo.aphelion_immolation_cannon_ammo) - aphelion_immolation_cannon_ammo.Capacity = 100 - aphelion_immolation_cannon_ammo.Tile = InventoryTile.Tile55 - - val - aphelion_plasma_rocket_ammo = AmmoBoxDefinition(Ammo.aphelion_plasma_rocket_ammo) - aphelion_plasma_rocket_ammo.Capacity = 195 - aphelion_plasma_rocket_ammo.Tile = InventoryTile.Tile55 - - val - aphelion_ppa_ammo = AmmoBoxDefinition(Ammo.aphelion_ppa_ammo) - aphelion_ppa_ammo.Capacity = 110 - aphelion_ppa_ammo.Tile = InventoryTile.Tile44 - - val - aphelion_starfire_ammo = AmmoBoxDefinition(Ammo.aphelion_starfire_ammo) - aphelion_starfire_ammo.Capacity = 132 - aphelion_starfire_ammo.Tile = InventoryTile.Tile44 - - val - skyguard_flak_cannon_ammo = AmmoBoxDefinition(Ammo.skyguard_flak_cannon_ammo) - skyguard_flak_cannon_ammo.Capacity = 200 - skyguard_flak_cannon_ammo.Tile = InventoryTile.Tile44 - - val - flux_cannon_thresher_battery = AmmoBoxDefinition(Ammo.flux_cannon_thresher_battery) - flux_cannon_thresher_battery.Capacity = 150 - flux_cannon_thresher_battery.Tile = InventoryTile.Tile44 - - val - fluxpod_ammo = AmmoBoxDefinition(Ammo.fluxpod_ammo) - fluxpod_ammo.Capacity = 80 - fluxpod_ammo.Tile = InventoryTile.Tile44 - - val - hellfire_ammo = AmmoBoxDefinition(Ammo.hellfire_ammo) - hellfire_ammo.Capacity = 24 - hellfire_ammo.Tile = InventoryTile.Tile44 - - val - liberator_bomb = AmmoBoxDefinition(Ammo.liberator_bomb) - liberator_bomb.Capacity = 20 - liberator_bomb.Tile = InventoryTile.Tile44 - - val - bullet_25mm = AmmoBoxDefinition(Ammo.bullet_25mm) //liberator tailgun - bullet_25mm.Capacity = 150 - bullet_25mm.Tile = InventoryTile.Tile44 - - val - bullet_75mm = AmmoBoxDefinition(Ammo.bullet_75mm) //lightning shell - bullet_75mm.Capacity = 100 - bullet_75mm.Tile = InventoryTile.Tile44 - - val - heavy_grenade_mortar = AmmoBoxDefinition(Ammo.heavy_grenade_mortar) //marauder and gal gunship - heavy_grenade_mortar.Capacity = 100 - heavy_grenade_mortar.Tile = InventoryTile.Tile44 - - val - pulse_battery = AmmoBoxDefinition(Ammo.pulse_battery) - pulse_battery.Capacity = 100 - pulse_battery.Tile = InventoryTile.Tile44 - - val - heavy_rail_beam_battery = AmmoBoxDefinition(Ammo.heavy_rail_beam_battery) - heavy_rail_beam_battery.Capacity = 100 - heavy_rail_beam_battery.Tile = InventoryTile.Tile44 - - val - reaver_rocket = AmmoBoxDefinition(Ammo.reaver_rocket) - reaver_rocket.Capacity = 12 - reaver_rocket.Tile = InventoryTile.Tile44 - - val - bullet_20mm = AmmoBoxDefinition(Ammo.bullet_20mm) //reaver nosegun - bullet_20mm.Capacity = 200 - bullet_20mm.Tile = InventoryTile.Tile44 - - val - bullet_12mm = AmmoBoxDefinition(Ammo.bullet_12mm) //common - bullet_12mm.Capacity = 200 - bullet_12mm.Tile = InventoryTile.Tile44 - - val - wasp_rocket_ammo = AmmoBoxDefinition(Ammo.wasp_rocket_ammo) - wasp_rocket_ammo.Capacity = 6 - wasp_rocket_ammo.Tile = InventoryTile.Tile44 - - val - wasp_gun_ammo = AmmoBoxDefinition(Ammo.wasp_gun_ammo) //wasp nosegun - wasp_gun_ammo.Capacity = 150 - wasp_gun_ammo.Tile = InventoryTile.Tile44 - - val - bullet_15mm = AmmoBoxDefinition(Ammo.bullet_15mm) - bullet_15mm.Capacity = 360 - bullet_15mm.Tile = InventoryTile.Tile44 - - val - colossus_100mm_cannon_ammo = AmmoBoxDefinition(Ammo.colossus_100mm_cannon_ammo) - colossus_100mm_cannon_ammo.Capacity = 90 - colossus_100mm_cannon_ammo.Tile = InventoryTile.Tile55 - - val - colossus_burster_ammo = AmmoBoxDefinition(Ammo.colossus_burster_ammo) - colossus_burster_ammo.Capacity = 235 - colossus_burster_ammo.Tile = InventoryTile.Tile44 - - val - colossus_cluster_bomb_ammo = AmmoBoxDefinition(Ammo.colossus_cluster_bomb_ammo) //colossus mortar launcher shells - colossus_cluster_bomb_ammo.Capacity = 150 - colossus_cluster_bomb_ammo.Tile = InventoryTile.Tile55 - - val - colossus_chaingun_ammo = AmmoBoxDefinition(Ammo.colossus_chaingun_ammo) - colossus_chaingun_ammo.Capacity = 600 - colossus_chaingun_ammo.Tile = InventoryTile.Tile44 - - val - colossus_tank_cannon_ammo = AmmoBoxDefinition(Ammo.colossus_tank_cannon_ammo) - colossus_tank_cannon_ammo.Capacity = 110 - colossus_tank_cannon_ammo.Tile = InventoryTile.Tile44 - - val - bullet_105mm = AmmoBoxDefinition(Ammo.bullet_105mm) //prowler 100mm cannon shell - bullet_105mm.Capacity = 100 - bullet_105mm.Tile = InventoryTile.Tile44 - - val - gauss_cannon_ammo = AmmoBoxDefinition(Ammo.gauss_cannon_ammo) - gauss_cannon_ammo.Capacity = 15 - gauss_cannon_ammo.Tile = InventoryTile.Tile44 - - val - peregrine_dual_machine_gun_ammo = AmmoBoxDefinition(Ammo.peregrine_dual_machine_gun_ammo) - peregrine_dual_machine_gun_ammo.Capacity = 240 - peregrine_dual_machine_gun_ammo.Tile = InventoryTile.Tile44 - - val - peregrine_mechhammer_ammo = AmmoBoxDefinition(Ammo.peregrine_mechhammer_ammo) - peregrine_mechhammer_ammo.Capacity = 30 - peregrine_mechhammer_ammo.Tile = InventoryTile.Tile44 - - val - peregrine_particle_cannon_ammo = AmmoBoxDefinition(Ammo.peregrine_particle_cannon_ammo) - peregrine_particle_cannon_ammo.Capacity = 40 - peregrine_particle_cannon_ammo.Tile = InventoryTile.Tile55 - - val - peregrine_rocket_pod_ammo = AmmoBoxDefinition(Ammo.peregrine_rocket_pod_ammo) - peregrine_rocket_pod_ammo.Capacity = 275 - peregrine_rocket_pod_ammo.Tile = InventoryTile.Tile55 - - val - peregrine_sparrow_ammo = AmmoBoxDefinition(Ammo.peregrine_sparrow_ammo) - peregrine_sparrow_ammo.Capacity = 150 - peregrine_sparrow_ammo.Tile = InventoryTile.Tile44 - - val - bullet_150mm = AmmoBoxDefinition(Ammo.bullet_150mm) - bullet_150mm.Capacity = 50 - bullet_150mm.Tile = InventoryTile.Tile44 - - val - chainblade = ToolDefinition(ObjectClass.chainblade) - chainblade.Size = EquipmentSize.Melee - chainblade.AmmoTypes += Ammo.melee_ammo - chainblade.FireModes += new FireModeDefinition - chainblade.FireModes.head.AmmoTypeIndices += 0 - chainblade.FireModes.head.AmmoSlotIndex = 0 - chainblade.FireModes.head.Magazine = 1 - chainblade.FireModes += new FireModeDefinition - chainblade.FireModes(1).AmmoTypeIndices += 0 - chainblade.FireModes(1).AmmoSlotIndex = 0 - chainblade.FireModes(1).Magazine = 1 - - val - magcutter = ToolDefinition(ObjectClass.magcutter) - magcutter.Size = EquipmentSize.Melee - magcutter.AmmoTypes += Ammo.melee_ammo - magcutter.FireModes += new FireModeDefinition - magcutter.FireModes.head.AmmoTypeIndices += 0 - magcutter.FireModes.head.AmmoSlotIndex = 0 - magcutter.FireModes.head.Magazine = 1 - magcutter.FireModes += new FireModeDefinition - magcutter.FireModes(1).AmmoTypeIndices += 0 - magcutter.FireModes(1).AmmoSlotIndex = 0 - magcutter.FireModes(1).Magazine = 1 - - val - forceblade = ToolDefinition(ObjectClass.forceblade) - forceblade.Size = EquipmentSize.Melee - forceblade.AmmoTypes += Ammo.melee_ammo - forceblade.FireModes += new FireModeDefinition - forceblade.FireModes.head.AmmoTypeIndices += 0 - forceblade.FireModes.head.AmmoSlotIndex = 0 - forceblade.FireModes.head.Magazine = 1 - forceblade.FireModes.head.Chamber = 0 - forceblade.FireModes += new FireModeDefinition - forceblade.FireModes(1).AmmoTypeIndices += 0 - forceblade.FireModes(1).AmmoSlotIndex = 0 - forceblade.FireModes(1).Magazine = 1 - forceblade.FireModes(1).Chamber = 0 - - val - katana = ToolDefinition(ObjectClass.katana) - katana.Size = EquipmentSize.Melee - katana.AmmoTypes += Ammo.melee_ammo - katana.FireModes += new FireModeDefinition - katana.FireModes.head.AmmoTypeIndices += 0 - katana.FireModes.head.AmmoSlotIndex = 0 - katana.FireModes.head.Magazine = 1 - katana.FireModes.head.Chamber = 0 - katana.FireModes += new FireModeDefinition - katana.FireModes(1).AmmoTypeIndices += 0 - katana.FireModes(1).AmmoSlotIndex = 0 - katana.FireModes(1).Magazine = 1 - katana.FireModes(1).Chamber = 0 - - val - frag_grenade = ToolDefinition(ObjectClass.frag_grenade) - frag_grenade.Size = EquipmentSize.Pistol - frag_grenade.AmmoTypes += Ammo.frag_grenade_ammo - frag_grenade.FireModes += new FireModeDefinition - frag_grenade.FireModes.head.AmmoTypeIndices += 0 - frag_grenade.FireModes.head.AmmoSlotIndex = 0 - frag_grenade.FireModes.head.Magazine = 3 - frag_grenade.FireModes += new FireModeDefinition - frag_grenade.FireModes(1).AmmoTypeIndices += 0 - frag_grenade.FireModes(1).AmmoSlotIndex = 0 - frag_grenade.FireModes(1).Magazine = 3 - frag_grenade.Tile = InventoryTile.Tile22 - - val - plasma_grenade = ToolDefinition(ObjectClass.plasma_grenade) - plasma_grenade.Size = EquipmentSize.Pistol - plasma_grenade.AmmoTypes += Ammo.plasma_grenade_ammo - plasma_grenade.FireModes += new FireModeDefinition - plasma_grenade.FireModes.head.AmmoTypeIndices += 0 - plasma_grenade.FireModes.head.AmmoSlotIndex = 0 - plasma_grenade.FireModes.head.Magazine = 3 - plasma_grenade.FireModes += new FireModeDefinition - plasma_grenade.FireModes(1).AmmoTypeIndices += 0 - plasma_grenade.FireModes(1).AmmoSlotIndex = 0 - plasma_grenade.FireModes(1).Magazine = 3 - plasma_grenade.Tile = InventoryTile.Tile22 - - val - jammer_grenade = ToolDefinition(ObjectClass.jammer_grenade) - jammer_grenade.Size = EquipmentSize.Pistol - jammer_grenade.AmmoTypes += Ammo.jammer_grenade_ammo - jammer_grenade.FireModes += new FireModeDefinition - jammer_grenade.FireModes.head.AmmoTypeIndices += 0 - jammer_grenade.FireModes.head.AmmoSlotIndex = 0 - jammer_grenade.FireModes.head.Magazine = 3 - jammer_grenade.FireModes += new FireModeDefinition - jammer_grenade.FireModes(1).AmmoTypeIndices += 0 - jammer_grenade.FireModes(1).AmmoSlotIndex = 0 - jammer_grenade.FireModes(1).Magazine = 3 - jammer_grenade.Tile = InventoryTile.Tile22 - - val - repeater = ToolDefinition(ObjectClass.repeater) - repeater.Size = EquipmentSize.Pistol - repeater.AmmoTypes += Ammo.bullet_9mm - repeater.AmmoTypes += Ammo.bullet_9mm_AP - repeater.FireModes += new FireModeDefinition - repeater.FireModes.head.AmmoTypeIndices += 0 - repeater.FireModes.head.AmmoTypeIndices += 1 - repeater.FireModes.head.AmmoSlotIndex = 0 - repeater.FireModes.head.Magazine = 20 - repeater.Tile = InventoryTile.Tile33 - - val - isp = ToolDefinition(ObjectClass.isp) //mag-scatter - isp.Size = EquipmentSize.Pistol - isp.AmmoTypes += Ammo.shotgun_shell - isp.AmmoTypes += Ammo.shotgun_shell_AP - isp.FireModes += new FireModeDefinition - isp.FireModes.head.AmmoTypeIndices += 0 - isp.FireModes.head.AmmoTypeIndices += 1 - isp.FireModes.head.AmmoSlotIndex = 0 - isp.FireModes.head.Magazine = 8 - isp.Tile = InventoryTile.Tile33 - - val - beamer = ToolDefinition(ObjectClass.beamer) - beamer.Size = EquipmentSize.Pistol - beamer.AmmoTypes += Ammo.energy_cell - beamer.FireModes += new FireModeDefinition - beamer.FireModes.head.AmmoTypeIndices += 0 - beamer.FireModes.head.AmmoSlotIndex = 0 - beamer.FireModes.head.Magazine = 16 - beamer.FireModes += new FireModeDefinition - beamer.FireModes(1).AmmoTypeIndices += 0 - beamer.FireModes(1).AmmoSlotIndex = 0 - beamer.FireModes(1).Magazine = 16 - beamer.Tile = InventoryTile.Tile33 - - val - ilc9 = ToolDefinition(ObjectClass.ilc9) //amp - ilc9.Size = EquipmentSize.Pistol - ilc9.AmmoTypes += Ammo.bullet_9mm - ilc9.AmmoTypes += Ammo.bullet_9mm_AP - ilc9.FireModes += new FireModeDefinition - ilc9.FireModes.head.AmmoTypeIndices += 0 - ilc9.FireModes.head.AmmoTypeIndices += 1 - ilc9.FireModes.head.AmmoSlotIndex = 0 - ilc9.FireModes.head.Magazine = 30 - ilc9.Tile = InventoryTile.Tile33 - - val - suppressor = ToolDefinition(ObjectClass.suppressor) - suppressor.Size = EquipmentSize.Rifle - suppressor.AmmoTypes += Ammo.bullet_9mm - suppressor.AmmoTypes += Ammo.bullet_9mm_AP - suppressor.FireModes += new FireModeDefinition - suppressor.FireModes.head.AmmoTypeIndices += 0 - suppressor.FireModes.head.AmmoTypeIndices += 1 - suppressor.FireModes.head.AmmoSlotIndex = 0 - suppressor.FireModes.head.Magazine = 25 - suppressor.Tile = InventoryTile.Tile63 - - 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.FireModes += new FireModeDefinition - punisher.FireModes.head.AmmoTypeIndices += 0 - punisher.FireModes.head.AmmoTypeIndices += 1 - punisher.FireModes.head.AmmoSlotIndex = 0 - punisher.FireModes.head.Magazine = 30 - punisher.FireModes += new FireModeDefinition - punisher.FireModes(1).AmmoTypeIndices += 2 - punisher.FireModes(1).AmmoTypeIndices += 3 - punisher.FireModes(1).AmmoTypeIndices += 4 - punisher.FireModes(1).AmmoTypeIndices += 5 - punisher.FireModes(1).AmmoSlotIndex = 1 - punisher.FireModes(1).Magazine = 1 - punisher.Tile = InventoryTile.Tile63 - - val - flechette = ToolDefinition(ObjectClass.flechette) //sweeper - flechette.Size = EquipmentSize.Rifle - flechette.AmmoTypes += Ammo.shotgun_shell - flechette.AmmoTypes += Ammo.shotgun_shell_AP - flechette.FireModes += new FireModeDefinition - flechette.FireModes.head.AmmoTypeIndices += 0 - flechette.FireModes.head.AmmoTypeIndices += 1 - flechette.FireModes.head.AmmoSlotIndex = 0 - flechette.FireModes.head.Magazine = 12 //12 shells * 8 pellets = 96 - flechette.Tile = InventoryTile.Tile63 - - val - cycler = ToolDefinition(ObjectClass.cycler) - cycler.Size = EquipmentSize.Rifle - cycler.AmmoTypes += Ammo.bullet_9mm - cycler.AmmoTypes += Ammo.bullet_9mm_AP - cycler.FireModes += new FireModeDefinition - cycler.FireModes.head.AmmoTypeIndices += 0 - cycler.FireModes.head.AmmoTypeIndices += 1 - cycler.FireModes.head.AmmoSlotIndex = 0 - cycler.FireModes.head.Magazine = 50 - cycler.Tile = InventoryTile.Tile63 - - val - gauss = ToolDefinition(ObjectClass.gauss) - gauss.Size = EquipmentSize.Rifle - gauss.AmmoTypes += Ammo.bullet_9mm - gauss.AmmoTypes += Ammo.bullet_9mm_AP - gauss.FireModes += new FireModeDefinition - gauss.FireModes.head.AmmoTypeIndices += 0 - gauss.FireModes.head.AmmoTypeIndices += 1 - gauss.FireModes.head.AmmoSlotIndex = 0 - gauss.FireModes.head.Magazine = 30 - gauss.Tile = InventoryTile.Tile63 - - val - pulsar = ToolDefinition(ObjectClass.pulsar) - pulsar.Size = EquipmentSize.Rifle - pulsar.AmmoTypes += Ammo.energy_cell - pulsar.FireModes += new FireModeDefinition - pulsar.FireModes.head.AmmoTypeIndices += 0 - pulsar.FireModes.head.AmmoSlotIndex = 0 - pulsar.FireModes.head.Magazine = 40 - pulsar.FireModes += new FireModeDefinition - pulsar.FireModes(1).AmmoTypeIndices += 0 - pulsar.FireModes(1).AmmoSlotIndex = 0 - pulsar.FireModes(1).Magazine = 40 - pulsar.Tile = InventoryTile.Tile63 - - val - anniversary_guna = ToolDefinition(ObjectClass.anniversary_guna) //tr stinger - anniversary_guna.Size = EquipmentSize.Pistol - anniversary_guna.AmmoTypes += Ammo.anniversary_ammo - anniversary_guna.FireModes += new FireModeDefinition - anniversary_guna.FireModes.head.AmmoTypeIndices += 0 - anniversary_guna.FireModes.head.AmmoSlotIndex = 0 - anniversary_guna.FireModes.head.Magazine = 6 - anniversary_guna.FireModes += new FireModeDefinition - anniversary_guna.FireModes(1).AmmoTypeIndices += 0 - anniversary_guna.FireModes(1).AmmoSlotIndex = 0 - anniversary_guna.FireModes(1).Magazine = 6 - anniversary_guna.FireModes(1).Chamber = 6 - anniversary_guna.Tile = InventoryTile.Tile33 - - val - anniversary_gun = ToolDefinition(ObjectClass.anniversary_gun) //nc spear - anniversary_gun.Size = EquipmentSize.Pistol - anniversary_gun.AmmoTypes += Ammo.anniversary_ammo - anniversary_gun.FireModes += new FireModeDefinition - anniversary_gun.FireModes.head.AmmoTypeIndices += 0 - anniversary_gun.FireModes.head.AmmoSlotIndex = 0 - anniversary_gun.FireModes.head.Magazine = 6 - anniversary_gun.FireModes += new FireModeDefinition - anniversary_gun.FireModes(1).AmmoTypeIndices += 0 - anniversary_gun.FireModes(1).AmmoSlotIndex = 0 - anniversary_gun.FireModes(1).Magazine = 6 - anniversary_gun.FireModes(1).Chamber = 6 - anniversary_gun.Tile = InventoryTile.Tile33 - - val - anniversary_gunb = ToolDefinition(ObjectClass.anniversary_gunb) //vs eraser - anniversary_gunb.Size = EquipmentSize.Pistol - anniversary_gunb.AmmoTypes += Ammo.anniversary_ammo - anniversary_gunb.FireModes += new FireModeDefinition - anniversary_gunb.FireModes.head.AmmoTypeIndices += 0 - anniversary_gunb.FireModes.head.AmmoSlotIndex = 0 - anniversary_gunb.FireModes.head.Magazine = 6 - anniversary_gunb.FireModes += new FireModeDefinition - anniversary_gunb.FireModes(1).AmmoTypeIndices += 0 - anniversary_gunb.FireModes(1).AmmoSlotIndex = 0 - anniversary_gunb.FireModes(1).Magazine = 6 - anniversary_gunb.FireModes(1).Chamber = 6 - anniversary_gunb.Tile = InventoryTile.Tile33 - - val - spiker = ToolDefinition(ObjectClass.spiker) - spiker.Size = EquipmentSize.Pistol - spiker.AmmoTypes += Ammo.ancient_ammo_combo - spiker.FireModes += new FireModeDefinition - spiker.FireModes.head.AmmoTypeIndices += 0 - spiker.FireModes.head.AmmoSlotIndex = 0 - spiker.FireModes.head.Magazine = 25 - spiker.Tile = InventoryTile.Tile33 - - 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.FireModes += new FireModeDefinition - mini_chaingun.FireModes.head.AmmoTypeIndices += 0 - mini_chaingun.FireModes.head.AmmoTypeIndices += 1 - mini_chaingun.FireModes.head.AmmoSlotIndex = 0 - mini_chaingun.FireModes.head.Magazine = 100 - mini_chaingun.Tile = InventoryTile.Tile93 - - 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.FireModes += new FireModeDefinition - r_shotgun.FireModes.head.AmmoTypeIndices += 0 - r_shotgun.FireModes.head.AmmoTypeIndices += 1 - r_shotgun.FireModes.head.AmmoSlotIndex = 0 - r_shotgun.FireModes.head.Magazine = 16 //16 shells * 8 pellets = 128 - r_shotgun.FireModes += new FireModeDefinition - r_shotgun.FireModes(1).AmmoTypeIndices += 0 - r_shotgun.FireModes(1).AmmoTypeIndices += 1 - r_shotgun.FireModes(1).AmmoSlotIndex = 0 - r_shotgun.FireModes(1).Magazine = 16 //16 shells * 8 pellets = 128 - r_shotgun.FireModes(1).Chamber = 3 - r_shotgun.Tile = InventoryTile.Tile93 - - val - lasher = ToolDefinition(ObjectClass.lasher) - lasher.Size = EquipmentSize.Rifle - lasher.AmmoTypes += Ammo.energy_cell - lasher.FireModes += new FireModeDefinition - lasher.FireModes.head.AmmoTypeIndices += 0 - lasher.FireModes.head.AmmoSlotIndex = 0 - lasher.FireModes.head.Magazine = 35 - lasher.FireModes += new FireModeDefinition - lasher.FireModes(1).AmmoTypeIndices += 0 - lasher.FireModes(1).AmmoSlotIndex = 0 - lasher.FireModes(1).Magazine = 35 - lasher.Tile = InventoryTile.Tile93 - - val - maelstrom = ToolDefinition(ObjectClass.maelstrom) - maelstrom.Size = EquipmentSize.Rifle - maelstrom.AmmoTypes += Ammo.maelstrom_ammo - maelstrom.FireModes += new FireModeDefinition - maelstrom.FireModes.head.AmmoTypeIndices += 0 - maelstrom.FireModes.head.AmmoSlotIndex = 0 - maelstrom.FireModes.head.Magazine = 150 - maelstrom.FireModes += new FireModeDefinition - maelstrom.FireModes(1).AmmoTypeIndices += 0 - maelstrom.FireModes(1).AmmoSlotIndex = 0 - maelstrom.FireModes(1).Magazine = 150 - maelstrom.FireModes += new FireModeDefinition - maelstrom.FireModes(2).AmmoTypeIndices += 0 - maelstrom.FireModes(2).AmmoSlotIndex = 0 - maelstrom.FireModes(2).Magazine = 150 - maelstrom.Tile = InventoryTile.Tile93 - - val - phoenix = ToolDefinition(ObjectClass.phoenix) //decimator - phoenix.Size = EquipmentSize.Rifle - phoenix.AmmoTypes += Ammo.phoenix_missile - phoenix.FireModes += new FireModeDefinition - phoenix.FireModes.head.AmmoTypeIndices += 0 - phoenix.FireModes.head.AmmoSlotIndex = 0 - phoenix.FireModes.head.Magazine = 3 - phoenix.FireModes += new FireModeDefinition - phoenix.FireModes(1).AmmoTypeIndices += 0 - phoenix.FireModes(1).AmmoSlotIndex = 0 - phoenix.FireModes(1).Magazine = 3 - phoenix.Tile = InventoryTile.Tile93 - - val - striker = ToolDefinition(ObjectClass.striker) - striker.Size = EquipmentSize.Rifle - striker.AmmoTypes += Ammo.striker_missile_ammo - striker.FireModes += new FireModeDefinition - striker.FireModes.head.AmmoTypeIndices += 0 - striker.FireModes.head.AmmoSlotIndex = 0 - striker.FireModes.head.Magazine = 5 - striker.FireModes += new FireModeDefinition - striker.FireModes(1).AmmoTypeIndices += 0 - striker.FireModes(1).AmmoSlotIndex = 0 - striker.FireModes(1).Magazine = 5 - striker.Tile = InventoryTile.Tile93 - - val - hunterseeker = ToolDefinition(ObjectClass.hunterseeker) //phoenix - hunterseeker.Size = EquipmentSize.Rifle - hunterseeker.AmmoTypes += Ammo.hunter_seeker_missile - hunterseeker.FireModes += new FireModeDefinition - hunterseeker.FireModes.head.AmmoTypeIndices += 0 - hunterseeker.FireModes.head.AmmoSlotIndex = 0 - hunterseeker.FireModes.head.Magazine = 1 - hunterseeker.FireModes += new FireModeDefinition - hunterseeker.FireModes(1).AmmoTypeIndices += 0 - hunterseeker.FireModes(1).AmmoSlotIndex = 0 - hunterseeker.FireModes(1).Magazine = 1 - hunterseeker.Tile = InventoryTile.Tile93 - - val - lancer = ToolDefinition(ObjectClass.lancer) - lancer.Size = EquipmentSize.Rifle - lancer.AmmoTypes += Ammo.lancer_cartridge - lancer.FireModes += new FireModeDefinition - lancer.FireModes.head.AmmoTypeIndices += 0 - lancer.FireModes.head.AmmoSlotIndex = 0 - lancer.FireModes.head.Magazine = 6 - lancer.Tile = InventoryTile.Tile93 - - val - rocklet = ToolDefinition(ObjectClass.rocklet) - rocklet.Size = EquipmentSize.Rifle - rocklet.AmmoTypes += Ammo.rocket - rocklet.AmmoTypes += Ammo.frag_cartridge - rocklet.FireModes += new FireModeDefinition - rocklet.FireModes.head.AmmoTypeIndices += 0 - rocklet.FireModes.head.AmmoTypeIndices += 1 - rocklet.FireModes.head.AmmoSlotIndex = 0 - rocklet.FireModes.head.Magazine = 6 - rocklet.FireModes += new FireModeDefinition - rocklet.FireModes(1).AmmoTypeIndices += 0 - rocklet.FireModes(1).AmmoTypeIndices += 1 - rocklet.FireModes(1).AmmoSlotIndex = 0 - rocklet.FireModes(1).Magazine = 6 - rocklet.FireModes(1).Chamber = 6 - rocklet.Tile = InventoryTile.Tile63 - - 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.FireModes += new FireModeDefinition - thumper.FireModes.head.AmmoTypeIndices += 0 - thumper.FireModes.head.AmmoTypeIndices += 1 - thumper.FireModes.head.AmmoTypeIndices += 2 - thumper.FireModes.head.AmmoSlotIndex = 0 - thumper.FireModes.head.Magazine = 6 - thumper.FireModes += new FireModeDefinition - thumper.FireModes(1).AmmoTypeIndices += 0 - thumper.FireModes(1).AmmoTypeIndices += 1 - thumper.FireModes(1).AmmoTypeIndices += 2 - thumper.FireModes(1).AmmoSlotIndex = 0 - thumper.FireModes(1).Magazine = 6 - thumper.Tile = InventoryTile.Tile63 - - val - radiator = ToolDefinition(ObjectClass.radiator) - radiator.Size = EquipmentSize.Rifle - radiator.AmmoTypes += Ammo.ancient_ammo_combo - radiator.FireModes += new FireModeDefinition - radiator.FireModes.head.AmmoTypeIndices += 0 - radiator.FireModes.head.AmmoSlotIndex = 0 - radiator.FireModes.head.Magazine = 25 - radiator.FireModes += new FireModeDefinition - radiator.FireModes(1).AmmoTypeIndices += 0 - radiator.FireModes(1).AmmoSlotIndex = 0 - radiator.FireModes(1).Magazine = 25 - radiator.Tile = InventoryTile.Tile63 - - val - heavy_sniper = ToolDefinition(ObjectClass.heavy_sniper) //hsr - heavy_sniper.Size = EquipmentSize.Rifle - heavy_sniper.AmmoTypes += Ammo.bolt - heavy_sniper.FireModes += new FireModeDefinition - heavy_sniper.FireModes.head.AmmoTypeIndices += 0 - heavy_sniper.FireModes.head.AmmoSlotIndex = 0 - heavy_sniper.FireModes.head.Magazine = 10 - heavy_sniper.Tile = InventoryTile.Tile93 - - val - bolt_driver = ToolDefinition(ObjectClass.bolt_driver) - bolt_driver.Size = EquipmentSize.Rifle - bolt_driver.AmmoTypes += Ammo.bolt - bolt_driver.FireModes += new FireModeDefinition - bolt_driver.FireModes.head.AmmoTypeIndices += 0 - bolt_driver.FireModes.head.AmmoSlotIndex = 0 - bolt_driver.FireModes.head.Magazine = 1 - bolt_driver.Tile = InventoryTile.Tile93 - - val - oicw = ToolDefinition(ObjectClass.oicw) //scorpion - oicw.Size = EquipmentSize.Rifle - oicw.AmmoTypes += Ammo.oicw_ammo - oicw.FireModes += new FireModeDefinition - oicw.FireModes.head.AmmoTypeIndices += 0 - oicw.FireModes.head.AmmoSlotIndex = 0 - oicw.FireModes.head.Magazine = 1 - oicw.FireModes += new FireModeDefinition - oicw.FireModes(1).AmmoTypeIndices += 0 - oicw.FireModes(1).AmmoSlotIndex = 0 - oicw.FireModes(1).Magazine = 1 - oicw.Tile = InventoryTile.Tile93 - - val - flamethrower = ToolDefinition(ObjectClass.flamethrower) - flamethrower.Size = EquipmentSize.Rifle - flamethrower.AmmoTypes += Ammo.flamethrower_ammo - flamethrower.FireModes += new FireModeDefinition - flamethrower.FireModes.head.AmmoTypeIndices += 0 - flamethrower.FireModes.head.AmmoSlotIndex = 0 - flamethrower.FireModes.head.Magazine = 100 - flamethrower.FireModes.head.Chamber = 5 - flamethrower.FireModes += new FireModeDefinition - flamethrower.FireModes(1).AmmoTypeIndices += 0 - flamethrower.FireModes(1).AmmoSlotIndex = 0 - flamethrower.FireModes(1).Magazine = 100 - flamethrower.FireModes(1).Chamber = 50 - flamethrower.Tile = InventoryTile.Tile63 - - val - medicalapplicator = ToolDefinition(ObjectClass.medicalapplicator) - medicalapplicator.Size = EquipmentSize.Pistol - medicalapplicator.AmmoTypes += Ammo.health_canister - medicalapplicator.FireModes += new FireModeDefinition - medicalapplicator.FireModes.head.AmmoTypeIndices += 0 - medicalapplicator.FireModes.head.AmmoSlotIndex = 0 - medicalapplicator.FireModes.head.Magazine = 100 - medicalapplicator.FireModes += new FireModeDefinition - medicalapplicator.FireModes(1).AmmoTypeIndices += 0 - medicalapplicator.FireModes(1).AmmoSlotIndex = 0 - medicalapplicator.FireModes(1).Magazine = 100 - medicalapplicator.Tile = InventoryTile.Tile33 - - 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.FireModes += new FireModeDefinition - nano_dispenser.FireModes.head.AmmoTypeIndices += 0 - nano_dispenser.FireModes.head.AmmoTypeIndices += 1 - nano_dispenser.FireModes.head.AmmoSlotIndex = 0 - nano_dispenser.FireModes.head.Magazine = 100 - nano_dispenser.Tile = InventoryTile.Tile63 - - val - bank = ToolDefinition(ObjectClass.bank) - bank.Size = EquipmentSize.Pistol - bank.AmmoTypes += Ammo.armor_canister - bank.FireModes += new FireModeDefinition - bank.FireModes.head.AmmoTypeIndices += 0 - bank.FireModes.head.AmmoSlotIndex = 0 - bank.FireModes.head.Magazine = 100 - bank.FireModes += new FireModeDefinition - bank.FireModes(1).AmmoTypeIndices += 0 - bank.FireModes(1).AmmoSlotIndex = 0 - bank.FireModes(1).Magazine = 100 - bank.Tile = InventoryTile.Tile33 - - val - remote_electronics_kit = SimpleItemDefinition(SItem.remote_electronics_kit) - remote_electronics_kit.Packet = new REKConverter - remote_electronics_kit.Tile = InventoryTile.Tile33 - - val - trek = ToolDefinition(ObjectClass.trek) - trek.Size = EquipmentSize.Pistol - trek.AmmoTypes += Ammo.trek_ammo - trek.FireModes += new FireModeDefinition - trek.FireModes.head.AmmoTypeIndices += 0 - trek.FireModes.head.AmmoSlotIndex = 0 - trek.FireModes.head.Magazine = 4 - trek.FireModes += new FireModeDefinition - trek.FireModes(1).AmmoTypeIndices += 0 - trek.FireModes(1).AmmoSlotIndex = 0 - trek.FireModes(1).Magazine = 0 - trek.Tile = InventoryTile.Tile33 - - val - flail_targeting_laser = SimpleItemDefinition(SItem.flail_targeting_laser) - flail_targeting_laser.Packet = new CommandDetonaterConverter - flail_targeting_laser.Tile = InventoryTile.Tile33 - - val - command_detonater = SimpleItemDefinition(SItem.command_detonater) - command_detonater.Packet = new CommandDetonaterConverter - command_detonater.Tile = InventoryTile.Tile33 - - val - ace = ConstructionItemDefinition(CItem.Unit.ace) - ace.Modes += DeployedItem.boomer - ace.Modes += DeployedItem.he_mine - ace.Modes += DeployedItem.jammer_mine - ace.Modes += DeployedItem.spitfire_turret - ace.Modes += DeployedItem.spitfire_cloaked - ace.Modes += DeployedItem.spitfire_aa - ace.Modes += DeployedItem.motionalarmsensor - ace.Modes += DeployedItem.sensor_shield - ace.Tile = InventoryTile.Tile33 - - val - advanced_ace = ConstructionItemDefinition(CItem.Unit.advanced_ace) - advanced_ace.Modes += DeployedItem.tank_traps - advanced_ace.Modes += DeployedItem.portable_manned_turret - advanced_ace.Modes += DeployedItem.deployable_shield_generator - advanced_ace.Tile = InventoryTile.Tile63 - - 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.FireModes += new FireModeDefinition - fury_weapon_systema.FireModes.head.AmmoTypeIndices += 0 - fury_weapon_systema.FireModes.head.AmmoSlotIndex = 0 - fury_weapon_systema.FireModes.head.Magazine = 2 - - 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.FireModes += new FireModeDefinition - quadassault_weapon_system.FireModes.head.AmmoTypeIndices += 0 - quadassault_weapon_system.FireModes.head.AmmoSlotIndex = 0 - quadassault_weapon_system.FireModes.head.Magazine = 100 - - val - chaingun_p = ToolDefinition(ObjectClass.chaingun_p) - chaingun_p.Size = EquipmentSize.VehicleWeapon - chaingun_p.AmmoTypes += Ammo.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 - fury = VehicleDefinition(ObjectClass.fury) - fury.Seats += 0 -> new SeatDefinition() - fury.Seats(0).Bailable = true - fury.Seats(0).ControlledWeapon = 1 - fury.MountPoints += 1 -> 0 - fury.MountPoints += 2 -> 0 - fury.Weapons += 1 -> fury_weapon_systema - fury.TrunkSize = InventoryTile(11, 11) - fury.TrunkOffset = 30 - - val - quadassault = VehicleDefinition(ObjectClass.quadassault) - quadassault.Seats += 0 -> new SeatDefinition() - quadassault.Seats(0).Bailable = true - quadassault.Seats(0).ControlledWeapon = 1 - quadassault.MountPoints += 1 -> 0 - quadassault.MountPoints += 2 -> 0 - quadassault.Weapons += 1 -> quadassault_weapon_system - quadassault.TrunkSize = InventoryTile(11, 11) - quadassault.TrunkOffset = 30 - - val - quadstealth = VehicleDefinition(ObjectClass.quadstealth) - quadstealth.CanCloak = true - quadstealth.Seats += 0 -> new SeatDefinition() - quadstealth.Seats(0).Bailable = true - quadstealth.MountPoints += 1 -> 0 - quadstealth.MountPoints += 2 -> 0 - quadstealth.CanCloak = true - quadstealth.TrunkSize = InventoryTile(11, 11) - quadstealth.TrunkOffset = 30 - - val - two_man_assault_buggy = VehicleDefinition(ObjectClass.two_man_assault_buggy) - two_man_assault_buggy.Seats += 0 -> new SeatDefinition() - two_man_assault_buggy.Seats(0).Bailable = true - 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.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.TrunkOffset = 30 - - val - phantasm = VehicleDefinition(ObjectClass.phantasm) - phantasm.CanCloak = true - phantasm.Seats += 0 -> new SeatDefinition() - phantasm.Seats += 1 -> new SeatDefinition() - phantasm.Seats(1).Bailable = true - phantasm.Seats += 2 -> new SeatDefinition() - phantasm.Seats(2).Bailable = true - phantasm.Seats += 3 -> new SeatDefinition() - 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 - - val - order_terminal = new OrderTerminalDefinition - val - cert_terminal = new CertTerminalDefinition - - val - lock_external = new IFFLockDefinition - val - door = new DoorDefinition + /** + * Initialize `ToolDefinition` globals. + */ + private def init_tools() : Unit = { + chainblade.Size = EquipmentSize.Melee + chainblade.AmmoTypes += melee_ammo + chainblade.FireModes += new FireModeDefinition + chainblade.FireModes.head.AmmoTypeIndices += 0 + chainblade.FireModes.head.AmmoSlotIndex = 0 + chainblade.FireModes.head.Magazine = 1 + chainblade.FireModes += new FireModeDefinition + chainblade.FireModes(1).AmmoTypeIndices += 0 + chainblade.FireModes(1).AmmoSlotIndex = 0 + chainblade.FireModes(1).Magazine = 1 + + magcutter.Size = EquipmentSize.Melee + magcutter.AmmoTypes += melee_ammo + magcutter.FireModes += new FireModeDefinition + magcutter.FireModes.head.AmmoTypeIndices += 0 + magcutter.FireModes.head.AmmoSlotIndex = 0 + magcutter.FireModes.head.Magazine = 1 + magcutter.FireModes += new FireModeDefinition + magcutter.FireModes(1).AmmoTypeIndices += 0 + magcutter.FireModes(1).AmmoSlotIndex = 0 + magcutter.FireModes(1).Magazine = 1 + + forceblade.Size = EquipmentSize.Melee + forceblade.AmmoTypes += melee_ammo + forceblade.FireModes += new FireModeDefinition + forceblade.FireModes.head.AmmoTypeIndices += 0 + forceblade.FireModes.head.AmmoSlotIndex = 0 + forceblade.FireModes.head.Magazine = 1 + forceblade.FireModes.head.Chamber = 0 + forceblade.FireModes += new FireModeDefinition + forceblade.FireModes(1).AmmoTypeIndices += 0 + forceblade.FireModes(1).AmmoSlotIndex = 0 + forceblade.FireModes(1).Magazine = 1 + forceblade.FireModes(1).Chamber = 0 + + katana.Size = EquipmentSize.Melee + katana.AmmoTypes += melee_ammo + katana.FireModes += new FireModeDefinition + katana.FireModes.head.AmmoTypeIndices += 0 + katana.FireModes.head.AmmoSlotIndex = 0 + katana.FireModes.head.Magazine = 1 + katana.FireModes.head.Chamber = 0 + katana.FireModes += new FireModeDefinition + katana.FireModes(1).AmmoTypeIndices += 0 + katana.FireModes(1).AmmoSlotIndex = 0 + katana.FireModes(1).Magazine = 1 + katana.FireModes(1).Chamber = 0 + + frag_grenade.Size = EquipmentSize.Pistol + frag_grenade.AmmoTypes += frag_grenade_ammo + frag_grenade.FireModes += new FireModeDefinition + frag_grenade.FireModes.head.AmmoTypeIndices += 0 + frag_grenade.FireModes.head.AmmoSlotIndex = 0 + frag_grenade.FireModes.head.Magazine = 3 + frag_grenade.FireModes += new FireModeDefinition + frag_grenade.FireModes(1).AmmoTypeIndices += 0 + frag_grenade.FireModes(1).AmmoSlotIndex = 0 + frag_grenade.FireModes(1).Magazine = 3 + frag_grenade.Tile = InventoryTile.Tile22 + + plasma_grenade.Size = EquipmentSize.Pistol + plasma_grenade.AmmoTypes += plasma_grenade_ammo + plasma_grenade.FireModes += new FireModeDefinition + plasma_grenade.FireModes.head.AmmoTypeIndices += 0 + plasma_grenade.FireModes.head.AmmoSlotIndex = 0 + plasma_grenade.FireModes.head.Magazine = 3 + plasma_grenade.FireModes += new FireModeDefinition + plasma_grenade.FireModes(1).AmmoTypeIndices += 0 + plasma_grenade.FireModes(1).AmmoSlotIndex = 0 + plasma_grenade.FireModes(1).Magazine = 3 + plasma_grenade.Tile = InventoryTile.Tile22 + + jammer_grenade.Size = EquipmentSize.Pistol + jammer_grenade.AmmoTypes += jammer_grenade_ammo + jammer_grenade.FireModes += new FireModeDefinition + jammer_grenade.FireModes.head.AmmoTypeIndices += 0 + jammer_grenade.FireModes.head.AmmoSlotIndex = 0 + jammer_grenade.FireModes.head.Magazine = 3 + jammer_grenade.FireModes += new FireModeDefinition + jammer_grenade.FireModes(1).AmmoTypeIndices += 0 + jammer_grenade.FireModes(1).AmmoSlotIndex = 0 + jammer_grenade.FireModes(1).Magazine = 3 + jammer_grenade.Tile = InventoryTile.Tile22 + + repeater.Size = EquipmentSize.Pistol + repeater.AmmoTypes += bullet_9mm + repeater.AmmoTypes += bullet_9mm_AP + repeater.FireModes += new FireModeDefinition + repeater.FireModes.head.AmmoTypeIndices += 0 + repeater.FireModes.head.AmmoTypeIndices += 1 + repeater.FireModes.head.AmmoSlotIndex = 0 + repeater.FireModes.head.Magazine = 20 + repeater.Tile = InventoryTile.Tile33 + + isp.Size = EquipmentSize.Pistol + isp.AmmoTypes += shotgun_shell + isp.AmmoTypes += shotgun_shell_AP + isp.FireModes += new FireModeDefinition + isp.FireModes.head.AmmoTypeIndices += 0 + isp.FireModes.head.AmmoTypeIndices += 1 + isp.FireModes.head.AmmoSlotIndex = 0 + isp.FireModes.head.Magazine = 8 + isp.Tile = InventoryTile.Tile33 + + beamer.Size = EquipmentSize.Pistol + beamer.AmmoTypes += energy_cell + beamer.FireModes += new FireModeDefinition + beamer.FireModes.head.AmmoTypeIndices += 0 + beamer.FireModes.head.AmmoSlotIndex = 0 + beamer.FireModes.head.Magazine = 16 + beamer.FireModes += new FireModeDefinition + beamer.FireModes(1).AmmoTypeIndices += 0 + beamer.FireModes(1).AmmoSlotIndex = 0 + beamer.FireModes(1).Magazine = 16 + beamer.Tile = InventoryTile.Tile33 + + ilc9.Size = EquipmentSize.Pistol + ilc9.AmmoTypes += bullet_9mm + ilc9.AmmoTypes += bullet_9mm_AP + ilc9.FireModes += new FireModeDefinition + ilc9.FireModes.head.AmmoTypeIndices += 0 + ilc9.FireModes.head.AmmoTypeIndices += 1 + ilc9.FireModes.head.AmmoSlotIndex = 0 + ilc9.FireModes.head.Magazine = 30 + ilc9.Tile = InventoryTile.Tile33 + + suppressor.Size = EquipmentSize.Rifle + suppressor.AmmoTypes += bullet_9mm + suppressor.AmmoTypes += bullet_9mm_AP + suppressor.FireModes += new FireModeDefinition + suppressor.FireModes.head.AmmoTypeIndices += 0 + suppressor.FireModes.head.AmmoTypeIndices += 1 + suppressor.FireModes.head.AmmoSlotIndex = 0 + suppressor.FireModes.head.Magazine = 25 + suppressor.Tile = InventoryTile.Tile63 + + punisher.Size = EquipmentSize.Rifle + 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 + punisher.FireModes.head.AmmoSlotIndex = 0 + punisher.FireModes.head.Magazine = 30 + punisher.FireModes += new FireModeDefinition + punisher.FireModes(1).AmmoTypeIndices += 2 + punisher.FireModes(1).AmmoTypeIndices += 3 + punisher.FireModes(1).AmmoTypeIndices += 4 + punisher.FireModes(1).AmmoTypeIndices += 5 + punisher.FireModes(1).AmmoSlotIndex = 1 + punisher.FireModes(1).Magazine = 1 + punisher.Tile = InventoryTile.Tile63 + + flechette.Size = EquipmentSize.Rifle + flechette.AmmoTypes += shotgun_shell + flechette.AmmoTypes += shotgun_shell_AP + flechette.FireModes += new FireModeDefinition + flechette.FireModes.head.AmmoTypeIndices += 0 + flechette.FireModes.head.AmmoTypeIndices += 1 + flechette.FireModes.head.AmmoSlotIndex = 0 + flechette.FireModes.head.Magazine = 12 //12 shells * 8 pellets = 96 + flechette.Tile = InventoryTile.Tile63 + + cycler.Size = EquipmentSize.Rifle + cycler.AmmoTypes += bullet_9mm + cycler.AmmoTypes += bullet_9mm_AP + cycler.FireModes += new FireModeDefinition + cycler.FireModes.head.AmmoTypeIndices += 0 + cycler.FireModes.head.AmmoTypeIndices += 1 + cycler.FireModes.head.AmmoSlotIndex = 0 + cycler.FireModes.head.Magazine = 50 + cycler.Tile = InventoryTile.Tile63 + + gauss.Size = EquipmentSize.Rifle + gauss.AmmoTypes += bullet_9mm + gauss.AmmoTypes += bullet_9mm_AP + gauss.FireModes += new FireModeDefinition + gauss.FireModes.head.AmmoTypeIndices += 0 + gauss.FireModes.head.AmmoTypeIndices += 1 + gauss.FireModes.head.AmmoSlotIndex = 0 + gauss.FireModes.head.Magazine = 30 + gauss.Tile = InventoryTile.Tile63 + + pulsar.Size = EquipmentSize.Rifle + pulsar.AmmoTypes += energy_cell + pulsar.FireModes += new FireModeDefinition + pulsar.FireModes.head.AmmoTypeIndices += 0 + pulsar.FireModes.head.AmmoSlotIndex = 0 + pulsar.FireModes.head.Magazine = 40 + pulsar.FireModes += new FireModeDefinition + pulsar.FireModes(1).AmmoTypeIndices += 0 + pulsar.FireModes(1).AmmoSlotIndex = 0 + pulsar.FireModes(1).Magazine = 40 + pulsar.Tile = InventoryTile.Tile63 + + anniversary_guna.Size = EquipmentSize.Pistol + anniversary_guna.AmmoTypes += anniversary_ammo + anniversary_guna.FireModes += new FireModeDefinition + anniversary_guna.FireModes.head.AmmoTypeIndices += 0 + anniversary_guna.FireModes.head.AmmoSlotIndex = 0 + anniversary_guna.FireModes.head.Magazine = 6 + anniversary_guna.FireModes += new FireModeDefinition + anniversary_guna.FireModes(1).AmmoTypeIndices += 0 + anniversary_guna.FireModes(1).AmmoSlotIndex = 0 + anniversary_guna.FireModes(1).Magazine = 6 + anniversary_guna.FireModes(1).Chamber = 6 + anniversary_guna.Tile = InventoryTile.Tile33 + + anniversary_gun.Size = EquipmentSize.Pistol + anniversary_gun.AmmoTypes += anniversary_ammo + anniversary_gun.FireModes += new FireModeDefinition + anniversary_gun.FireModes.head.AmmoTypeIndices += 0 + anniversary_gun.FireModes.head.AmmoSlotIndex = 0 + anniversary_gun.FireModes.head.Magazine = 6 + anniversary_gun.FireModes += new FireModeDefinition + anniversary_gun.FireModes(1).AmmoTypeIndices += 0 + anniversary_gun.FireModes(1).AmmoSlotIndex = 0 + anniversary_gun.FireModes(1).Magazine = 6 + anniversary_gun.FireModes(1).Chamber = 6 + anniversary_gun.Tile = InventoryTile.Tile33 + + anniversary_gunb.Size = EquipmentSize.Pistol + anniversary_gunb.AmmoTypes += anniversary_ammo + anniversary_gunb.FireModes += new FireModeDefinition + anniversary_gunb.FireModes.head.AmmoTypeIndices += 0 + anniversary_gunb.FireModes.head.AmmoSlotIndex = 0 + anniversary_gunb.FireModes.head.Magazine = 6 + anniversary_gunb.FireModes += new FireModeDefinition + anniversary_gunb.FireModes(1).AmmoTypeIndices += 0 + anniversary_gunb.FireModes(1).AmmoSlotIndex = 0 + anniversary_gunb.FireModes(1).Magazine = 6 + anniversary_gunb.FireModes(1).Chamber = 6 + anniversary_gunb.Tile = InventoryTile.Tile33 + + spiker.Size = EquipmentSize.Pistol + spiker.AmmoTypes += ancient_ammo_combo + spiker.FireModes += new FireModeDefinition + spiker.FireModes.head.AmmoTypeIndices += 0 + spiker.FireModes.head.AmmoSlotIndex = 0 + spiker.FireModes.head.Magazine = 25 + spiker.Tile = InventoryTile.Tile33 + + mini_chaingun.Size = EquipmentSize.Rifle + 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 + mini_chaingun.FireModes.head.AmmoSlotIndex = 0 + mini_chaingun.FireModes.head.Magazine = 100 + mini_chaingun.Tile = InventoryTile.Tile93 + + r_shotgun.Size = EquipmentSize.Rifle + 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 + r_shotgun.FireModes.head.AmmoSlotIndex = 0 + r_shotgun.FireModes.head.Magazine = 16 //16 shells * 8 pellets = 128 + r_shotgun.FireModes += new FireModeDefinition + r_shotgun.FireModes(1).AmmoTypeIndices += 0 + r_shotgun.FireModes(1).AmmoTypeIndices += 1 + r_shotgun.FireModes(1).AmmoSlotIndex = 0 + r_shotgun.FireModes(1).Magazine = 16 //16 shells * 8 pellets = 128 + r_shotgun.FireModes(1).Chamber = 3 + r_shotgun.Tile = InventoryTile.Tile93 + + lasher.Size = EquipmentSize.Rifle + lasher.AmmoTypes += energy_cell + lasher.FireModes += new FireModeDefinition + lasher.FireModes.head.AmmoTypeIndices += 0 + lasher.FireModes.head.AmmoSlotIndex = 0 + lasher.FireModes.head.Magazine = 35 + lasher.FireModes += new FireModeDefinition + lasher.FireModes(1).AmmoTypeIndices += 0 + lasher.FireModes(1).AmmoSlotIndex = 0 + lasher.FireModes(1).Magazine = 35 + lasher.Tile = InventoryTile.Tile93 + + maelstrom.Size = EquipmentSize.Rifle + maelstrom.AmmoTypes += maelstrom_ammo + maelstrom.FireModes += new FireModeDefinition + maelstrom.FireModes.head.AmmoTypeIndices += 0 + maelstrom.FireModes.head.AmmoSlotIndex = 0 + maelstrom.FireModes.head.Magazine = 150 + maelstrom.FireModes += new FireModeDefinition + maelstrom.FireModes(1).AmmoTypeIndices += 0 + maelstrom.FireModes(1).AmmoSlotIndex = 0 + maelstrom.FireModes(1).Magazine = 150 + maelstrom.FireModes += new FireModeDefinition + maelstrom.FireModes(2).AmmoTypeIndices += 0 + maelstrom.FireModes(2).AmmoSlotIndex = 0 + maelstrom.FireModes(2).Magazine = 150 + maelstrom.Tile = InventoryTile.Tile93 + + phoenix.Size = EquipmentSize.Rifle + phoenix.AmmoTypes += phoenix_missile + phoenix.FireModes += new FireModeDefinition + phoenix.FireModes.head.AmmoTypeIndices += 0 + phoenix.FireModes.head.AmmoSlotIndex = 0 + phoenix.FireModes.head.Magazine = 3 + phoenix.FireModes += new FireModeDefinition + phoenix.FireModes(1).AmmoTypeIndices += 0 + phoenix.FireModes(1).AmmoSlotIndex = 0 + phoenix.FireModes(1).Magazine = 3 + phoenix.Tile = InventoryTile.Tile93 + + striker.Size = EquipmentSize.Rifle + striker.AmmoTypes += striker_missile_ammo + striker.FireModes += new FireModeDefinition + striker.FireModes.head.AmmoTypeIndices += 0 + striker.FireModes.head.AmmoSlotIndex = 0 + striker.FireModes.head.Magazine = 5 + striker.FireModes += new FireModeDefinition + striker.FireModes(1).AmmoTypeIndices += 0 + striker.FireModes(1).AmmoSlotIndex = 0 + striker.FireModes(1).Magazine = 5 + striker.Tile = InventoryTile.Tile93 + + hunterseeker.Size = EquipmentSize.Rifle + hunterseeker.AmmoTypes += hunter_seeker_missile + hunterseeker.FireModes += new FireModeDefinition + hunterseeker.FireModes.head.AmmoTypeIndices += 0 + hunterseeker.FireModes.head.AmmoSlotIndex = 0 + hunterseeker.FireModes.head.Magazine = 1 + hunterseeker.FireModes += new FireModeDefinition + hunterseeker.FireModes(1).AmmoTypeIndices += 0 + hunterseeker.FireModes(1).AmmoSlotIndex = 0 + hunterseeker.FireModes(1).Magazine = 1 + hunterseeker.Tile = InventoryTile.Tile93 + + lancer.Size = EquipmentSize.Rifle + lancer.AmmoTypes += lancer_cartridge + lancer.FireModes += new FireModeDefinition + lancer.FireModes.head.AmmoTypeIndices += 0 + lancer.FireModes.head.AmmoSlotIndex = 0 + lancer.FireModes.head.Magazine = 6 + lancer.Tile = InventoryTile.Tile93 + + rocklet.Size = EquipmentSize.Rifle + rocklet.AmmoTypes += rocket + rocklet.AmmoTypes += frag_cartridge + rocklet.FireModes += new FireModeDefinition + rocklet.FireModes.head.AmmoTypeIndices += 0 + rocklet.FireModes.head.AmmoTypeIndices += 1 + rocklet.FireModes.head.AmmoSlotIndex = 0 + rocklet.FireModes.head.Magazine = 6 + rocklet.FireModes += new FireModeDefinition + rocklet.FireModes(1).AmmoTypeIndices += 0 + rocklet.FireModes(1).AmmoTypeIndices += 1 + rocklet.FireModes(1).AmmoSlotIndex = 0 + rocklet.FireModes(1).Magazine = 6 + rocklet.FireModes(1).Chamber = 6 + rocklet.Tile = InventoryTile.Tile63 + + thumper.Size = EquipmentSize.Rifle + 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 + thumper.FireModes.head.AmmoTypeIndices += 2 + thumper.FireModes.head.AmmoSlotIndex = 0 + thumper.FireModes.head.Magazine = 6 + thumper.FireModes += new FireModeDefinition + thumper.FireModes(1).AmmoTypeIndices += 0 + thumper.FireModes(1).AmmoTypeIndices += 1 + thumper.FireModes(1).AmmoTypeIndices += 2 + thumper.FireModes(1).AmmoSlotIndex = 0 + thumper.FireModes(1).Magazine = 6 + thumper.Tile = InventoryTile.Tile63 + + radiator.Size = EquipmentSize.Rifle + radiator.AmmoTypes += ancient_ammo_combo + radiator.FireModes += new FireModeDefinition + radiator.FireModes.head.AmmoTypeIndices += 0 + radiator.FireModes.head.AmmoSlotIndex = 0 + radiator.FireModes.head.Magazine = 25 + radiator.FireModes += new FireModeDefinition + radiator.FireModes(1).AmmoTypeIndices += 0 + radiator.FireModes(1).AmmoSlotIndex = 0 + radiator.FireModes(1).Magazine = 25 + radiator.Tile = InventoryTile.Tile63 + + heavy_sniper.Size = EquipmentSize.Rifle + heavy_sniper.AmmoTypes += bolt + heavy_sniper.FireModes += new FireModeDefinition + heavy_sniper.FireModes.head.AmmoTypeIndices += 0 + heavy_sniper.FireModes.head.AmmoSlotIndex = 0 + heavy_sniper.FireModes.head.Magazine = 10 + heavy_sniper.Tile = InventoryTile.Tile93 + + bolt_driver.Size = EquipmentSize.Rifle + bolt_driver.AmmoTypes += bolt + bolt_driver.FireModes += new FireModeDefinition + bolt_driver.FireModes.head.AmmoTypeIndices += 0 + bolt_driver.FireModes.head.AmmoSlotIndex = 0 + bolt_driver.FireModes.head.Magazine = 1 + bolt_driver.Tile = InventoryTile.Tile93 + + oicw.Size = EquipmentSize.Rifle + oicw.AmmoTypes += oicw_ammo + oicw.FireModes += new FireModeDefinition + oicw.FireModes.head.AmmoTypeIndices += 0 + oicw.FireModes.head.AmmoSlotIndex = 0 + oicw.FireModes.head.Magazine = 1 + oicw.FireModes += new FireModeDefinition + oicw.FireModes(1).AmmoTypeIndices += 0 + oicw.FireModes(1).AmmoSlotIndex = 0 + oicw.FireModes(1).Magazine = 1 + oicw.Tile = InventoryTile.Tile93 + + flamethrower.Size = EquipmentSize.Rifle + flamethrower.AmmoTypes += flamethrower_ammo + flamethrower.FireModes += new FireModeDefinition + flamethrower.FireModes.head.AmmoTypeIndices += 0 + flamethrower.FireModes.head.AmmoSlotIndex = 0 + flamethrower.FireModes.head.Magazine = 100 + flamethrower.FireModes.head.Chamber = 5 + flamethrower.FireModes += new FireModeDefinition + flamethrower.FireModes(1).AmmoTypeIndices += 0 + flamethrower.FireModes(1).AmmoSlotIndex = 0 + flamethrower.FireModes(1).Magazine = 100 + flamethrower.FireModes(1).Chamber = 50 + flamethrower.Tile = InventoryTile.Tile63 + + medicalapplicator.Size = EquipmentSize.Pistol + medicalapplicator.AmmoTypes += health_canister + medicalapplicator.FireModes += new FireModeDefinition + medicalapplicator.FireModes.head.AmmoTypeIndices += 0 + medicalapplicator.FireModes.head.AmmoSlotIndex = 0 + medicalapplicator.FireModes.head.Magazine = 100 + medicalapplicator.FireModes += new FireModeDefinition + medicalapplicator.FireModes(1).AmmoTypeIndices += 0 + medicalapplicator.FireModes(1).AmmoSlotIndex = 0 + medicalapplicator.FireModes(1).Magazine = 100 + medicalapplicator.Tile = InventoryTile.Tile33 + + nano_dispenser.Size = EquipmentSize.Rifle + 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 + nano_dispenser.FireModes.head.AmmoSlotIndex = 0 + nano_dispenser.FireModes.head.Magazine = 100 + nano_dispenser.Tile = InventoryTile.Tile63 + + bank.Size = EquipmentSize.Pistol + bank.AmmoTypes += armor_canister + bank.FireModes += new FireModeDefinition + bank.FireModes.head.AmmoTypeIndices += 0 + bank.FireModes.head.AmmoSlotIndex = 0 + bank.FireModes.head.Magazine = 100 + bank.FireModes += new FireModeDefinition + bank.FireModes(1).AmmoTypeIndices += 0 + bank.FireModes(1).AmmoSlotIndex = 0 + bank.FireModes(1).Magazine = 100 + bank.Tile = InventoryTile.Tile33 + + remote_electronics_kit.Packet = new REKConverter + remote_electronics_kit.Tile = InventoryTile.Tile33 + + trek.Size = EquipmentSize.Pistol + trek.AmmoTypes += trek_ammo + trek.FireModes += new FireModeDefinition + trek.FireModes.head.AmmoTypeIndices += 0 + trek.FireModes.head.AmmoSlotIndex = 0 + trek.FireModes.head.Magazine = 4 + trek.FireModes += new FireModeDefinition + trek.FireModes(1).AmmoTypeIndices += 0 + trek.FireModes(1).AmmoSlotIndex = 0 + trek.FireModes(1).Magazine = 0 + trek.Tile = InventoryTile.Tile33 + + flail_targeting_laser.Packet = new CommandDetonaterConverter + flail_targeting_laser.Tile = InventoryTile.Tile33 + + command_detonater.Packet = new CommandDetonaterConverter + command_detonater.Tile = InventoryTile.Tile33 + + ace.Modes += DeployedItem.boomer + ace.Modes += DeployedItem.he_mine + ace.Modes += DeployedItem.jammer_mine + ace.Modes += DeployedItem.spitfire_turret + ace.Modes += DeployedItem.spitfire_cloaked + ace.Modes += DeployedItem.spitfire_aa + ace.Modes += DeployedItem.motionalarmsensor + ace.Modes += DeployedItem.sensor_shield + ace.Tile = InventoryTile.Tile33 + + advanced_ace.Modes += DeployedItem.tank_traps + advanced_ace.Modes += DeployedItem.portable_manned_turret + advanced_ace.Modes += DeployedItem.deployable_shield_generator + advanced_ace.Tile = InventoryTile.Tile63 + + fury_weapon_systema.Size = EquipmentSize.VehicleWeapon + 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 + fury_weapon_systema.FireModes.head.Magazine = 2 + + quadassault_weapon_system.Size = EquipmentSize.VehicleWeapon + 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 = 150 + + 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 + + chaingun_p.Size = EquipmentSize.VehicleWeapon + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + battlewagon_weapon_systemb.Size = EquipmentSize.VehicleWeapon + battlewagon_weapon_systemb.AmmoTypes += bullet_15mm + battlewagon_weapon_systemb.FireModes += new FireModeDefinition + battlewagon_weapon_systemb.FireModes.head.AmmoTypeIndices += 0 + battlewagon_weapon_systemb.FireModes.head.AmmoSlotIndex = 0 + battlewagon_weapon_systemb.FireModes.head.Magazine = 235 + + battlewagon_weapon_systemc.Size = EquipmentSize.VehicleWeapon + battlewagon_weapon_systemc.AmmoTypes += bullet_15mm + battlewagon_weapon_systemc.FireModes += new FireModeDefinition + battlewagon_weapon_systemc.FireModes.head.AmmoTypeIndices += 0 + battlewagon_weapon_systemc.FireModes.head.AmmoSlotIndex = 0 + battlewagon_weapon_systemc.FireModes.head.Magazine = 235 + + battlewagon_weapon_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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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? + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + } + + /** + * Initialize `VehicleDefinition` globals. + */ + private def init_vehicles() : Unit = { + 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.TrunkSize = InventoryTile.Tile1111 + fury.TrunkOffset = 30 + + 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.TrunkSize = InventoryTile.Tile1111 + quadassault.TrunkOffset = 30 + + 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.TrunkSize = InventoryTile.Tile1111 + quadstealth.TrunkOffset = 30 + + two_man_assault_buggy.Seats += 0 -> new SeatDefinition() + two_man_assault_buggy.Seats(0).Bailable = true + 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.TrunkSize = InventoryTile.Tile1111 + two_man_assault_buggy.TrunkOffset = 30 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + val utilityConverter = new UtilityVehicleConverter + ant.Seats += 0 -> new SeatDefinition() + ant.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + ant.MountPoints += 1 -> 0 + ant.MountPoints += 2 -> 0 + ant.Packet = utilityConverter + + ams.Seats += 0 -> new SeatDefinition() + ams.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax + ams.MountPoints += 1 -> 0 + ams.MountPoints += 2 -> 0 + ams.Packet = utilityConverter + + val variantConverter = new VariantVehicleConverter + router.Seats += 0 -> new SeatDefinition() + router.MountPoints += 1 -> 0 + router.TrunkSize = InventoryTile.Tile1511 + router.TrunkOffset = 30 + router.Packet = variantConverter + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + lodestar.Seats += 0 -> new SeatDefinition() + lodestar.MountPoints += 1 -> 0 + lodestar.TrunkSize = InventoryTile.Tile1612 + lodestar.TrunkOffset = 30 + lodestar.Packet = variantConverter + + phantasm.CanCloak = true + phantasm.Seats += 0 -> new SeatDefinition() + phantasm.Seats += 1 -> new SeatDefinition() + phantasm.Seats(1).Bailable = true + phantasm.Seats += 2 -> new SeatDefinition() + phantasm.Seats(2).Bailable = true + phantasm.Seats += 3 -> new SeatDefinition() + phantasm.Seats(3).Bailable = true + phantasm.Seats += 4 -> new SeatDefinition() + phantasm.Seats(4).Bailable = true + 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 + } } 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/GUIDTaskTest.scala b/common/src/test/scala/objects/GUIDTaskTest.scala deleted file mode 100644 index 1ea187dee..000000000 --- a/common/src/test/scala/objects/GUIDTaskTest.scala +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright (c) 2017 PSForever -package objects - -import java.util.logging.LogManager - -import akka.actor.{ActorRef, ActorSystem, Props} -import akka.testkit.TestProbe -import net.psforever.objects._ -import net.psforever.objects.entity.IdentifiableEntity -import net.psforever.objects.guid.actor.{NumberPoolActor, UniqueNumberSystem} -import net.psforever.objects.guid.selector.RandomSelector -import net.psforever.objects.guid.source.LimitedNumberSource -import net.psforever.objects.guid.{GUIDTask, NumberPoolHub, Task, TaskResolver} -import net.psforever.types.{CharacterGender, PlanetSideEmpire} - -class GUIDTaskRegister1Test extends ActorTest() { - "RegisterObjectTask" in { - val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup - val obj = new GUIDTaskTest.TestObject - - assert(!obj.HasGUID) - taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterObjectTask(obj)(uns))) - probe.expectMsg(scala.util.Success) - assert(obj.HasGUID) - } -} - -class GUIDTaskRegister2Test extends ActorTest() { - "RegisterEquipment -> RegisterObjectTask" in { - val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup - val obj = AmmoBox(GlobalDefinitions.energy_cell) - - assert(!obj.HasGUID) - taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterEquipment(obj)(uns))) - probe.expectMsg(scala.util.Success) - assert(obj.HasGUID) - } -} - -class GUIDTaskRegister3Test extends ActorTest() { - "RegisterEquipment -> RegisterTool" in { - val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup - val obj = Tool(GlobalDefinitions.beamer) - obj.AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.energy_cell) - - assert(!obj.HasGUID) - assert(!obj.AmmoSlots.head.Box.HasGUID) - taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterEquipment(obj)(uns))) - probe.expectMsg(scala.util.Success) - assert(obj.HasGUID) - assert(obj.AmmoSlots.head.Box.HasGUID) - } -} - -class GUIDTaskRegister4Test extends ActorTest() { - "RegisterVehicle" in { - val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup - val obj = Vehicle(GlobalDefinitions.fury) - val obj_wep = obj.WeaponControlledFromSeat(0).get - val obj_wep_ammo = (obj.WeaponControlledFromSeat(0).get.asInstanceOf[Tool].AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.hellfire_ammo)).get - obj.Trunk += 30 -> AmmoBox(GlobalDefinitions.hellfire_ammo) - val obj_trunk_ammo = obj.Trunk.Items(0).obj - - assert(!obj.HasGUID) - assert(!obj_wep.HasGUID) - assert(!obj_wep_ammo.HasGUID) - assert(!obj_trunk_ammo.HasGUID) - taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterVehicle(obj)(uns))) - probe.expectMsg(scala.util.Success) - assert(obj.HasGUID) - assert(obj_wep.HasGUID) - assert(obj_wep_ammo.HasGUID) - assert(obj_trunk_ammo.HasGUID) - } -} - -class GUIDTaskRegister5Test extends ActorTest() { - "RegisterAvatar" in { - val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup - val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) - val obj_wep = Tool(GlobalDefinitions.beamer) - obj.Slot(0).Equipment = obj_wep - val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell) - obj_wep.AmmoSlots.head.Box = obj_wep_ammo - val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell) - obj.Slot(6).Equipment = obj_inv_ammo - val obj_locker = obj.Slot(5).Equipment.get - val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell) - obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo - - assert(!obj.HasGUID) - assert(!obj_wep.HasGUID) - assert(!obj_wep_ammo.HasGUID) - assert(!obj_inv_ammo.HasGUID) - assert(!obj_locker.HasGUID) - assert(!obj_locker_ammo.HasGUID) - taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterAvatar(obj)(uns))) - probe.expectMsg(scala.util.Success) - assert(obj.HasGUID) - assert(obj_wep.HasGUID) - assert(obj_wep_ammo.HasGUID) - assert(obj_inv_ammo.HasGUID) - assert(obj_locker.HasGUID) - assert(obj_locker_ammo.HasGUID) - } -} - -class GUIDTaskUnregister1Test extends ActorTest() { - "UnregisterObjectTask" in { - val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup - val obj = new GUIDTaskTest.TestObject - guid.register(obj, "dynamic") - - assert(obj.HasGUID) - taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterObjectTask(obj)(uns))) - probe.expectMsg(scala.util.Success) - assert(!obj.HasGUID) - } -} - -class GUIDTaskUnregister2Test extends ActorTest() { - "UnregisterEquipment -> UnregisterObjectTask" in { - val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup - val obj = AmmoBox(GlobalDefinitions.energy_cell) - guid.register(obj, "dynamic") - - assert(obj.HasGUID) - taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterEquipment(obj)(uns))) - probe.expectMsg(scala.util.Success) - assert(!obj.HasGUID) - } -} - -class GUIDTaskUnregister3Test extends ActorTest() { - "UnregisterEquipment -> UnregisterTool" in { - val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup - val obj = Tool(GlobalDefinitions.beamer) - obj.AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.energy_cell) - guid.register(obj, "dynamic") - guid.register(obj.AmmoSlots.head.Box, "dynamic") - - assert(obj.HasGUID) - assert(obj.AmmoSlots.head.Box.HasGUID) - taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterEquipment(obj)(uns))) - probe.expectMsg(scala.util.Success) - assert(!obj.HasGUID) - assert(!obj.AmmoSlots.head.Box.HasGUID) - } -} - -class GUIDTaskUnregister4Test extends ActorTest() { - "RegisterVehicle" in { - val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup - val obj = Vehicle(GlobalDefinitions.fury) - val obj_wep = obj.WeaponControlledFromSeat(0).get - val obj_wep_ammo = (obj.WeaponControlledFromSeat(0).get.asInstanceOf[Tool].AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.hellfire_ammo)).get - obj.Trunk += 30 -> AmmoBox(GlobalDefinitions.hellfire_ammo) - val obj_trunk_ammo = obj.Trunk.Items(0).obj - guid.register(obj, "dynamic") - guid.register(obj_wep, "dynamic") - guid.register(obj_wep_ammo, "dynamic") - guid.register(obj_trunk_ammo, "dynamic") - - assert(obj.HasGUID) - assert(obj_wep.HasGUID) - assert(obj_wep_ammo.HasGUID) - assert(obj_trunk_ammo.HasGUID) - taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterVehicle(obj)(uns))) - probe.expectMsg(scala.util.Success) - assert(!obj.HasGUID) - assert(!obj_wep.HasGUID) - assert(!obj_wep_ammo.HasGUID) - assert(!obj_trunk_ammo.HasGUID) - } -} - -class GUIDTaskUnregister5Test extends ActorTest() { - "UnregisterAvatar" in { - val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup - val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) - val obj_wep = Tool(GlobalDefinitions.beamer) - obj.Slot(0).Equipment = obj_wep - val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell) - obj_wep.AmmoSlots.head.Box = obj_wep_ammo - val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell) - obj.Slot(6).Equipment = obj_inv_ammo - val obj_locker = obj.Slot(5).Equipment.get - val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell) - obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo - guid.register(obj, "dynamic") - guid.register(obj_wep, "dynamic") - guid.register(obj_wep_ammo, "dynamic") - guid.register(obj_inv_ammo, "dynamic") - guid.register(obj_locker, "dynamic") - guid.register(obj_locker_ammo, "dynamic") - - assert(obj.HasGUID) - assert(obj_wep.HasGUID) - assert(obj_wep_ammo.HasGUID) - assert(obj_inv_ammo.HasGUID) - assert(obj_locker.HasGUID) - assert(obj_locker_ammo.HasGUID) - taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterAvatar(obj)(uns))) - probe.expectMsg(scala.util.Success) - assert(!obj.HasGUID) - assert(!obj_wep.HasGUID) - assert(!obj_wep_ammo.HasGUID) - assert(!obj_inv_ammo.HasGUID) - assert(!obj_locker.HasGUID) - assert(!obj_locker_ammo.HasGUID) - } -} - -object GUIDTaskTest { - class TestObject extends IdentifiableEntity - - class RegisterTestTask(probe : ActorRef) extends Task { - def Execute(resolver : ActorRef) : Unit = { - probe ! scala.util.Success - resolver ! scala.util.Success(this) - } - } - - def CommonTestSetup(implicit system : ActorSystem) : (NumberPoolHub, ActorRef, ActorRef, TestProbe) = { - import akka.actor.Props - import akka.routing.RandomPool - import akka.testkit.TestProbe - - val guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(110)) - guid.AddPool("dynamic", (1 to 100).toList).Selector = new RandomSelector //TODO name is hardcoded for now - val uns = system.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, GUIDTaskTest.AllocateNumberPoolActors(guid))), "uns") - val taskResolver = system.actorOf(RandomPool(15).props(Props[TaskResolver]), "resolver") - LogManager.getLogManager.reset() //suppresses any internal loggers created by the above elements - (guid, uns, taskResolver, TestProbe()) - } - - /** - * @see `UniqueNumberSystem.AllocateNumberPoolActors(NumberPoolHub)(implicit ActorContext)` - */ - def AllocateNumberPoolActors(poolSource : NumberPoolHub)(implicit system : ActorSystem) : Map[String, ActorRef] = { - poolSource.Pools.map({ case ((pname, pool)) => - pname -> system.actorOf(Props(classOf[NumberPoolActor], pool), pname) - }).toMap - } -} 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/guidtask/GUIDTaskRegister1Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskRegister1Test.scala new file mode 100644 index 000000000..803a60241 --- /dev/null +++ b/common/src/test/scala/objects/guidtask/GUIDTaskRegister1Test.scala @@ -0,0 +1,52 @@ +// Copyright (c) 2017 PSForever +package objects.guidtask + +import akka.actor.{Actor, ActorSystem, Props} +import net.psforever.objects.guid.{GUIDTask, TaskResolver} +import org.specs2.mutable.Specification + +import scala.concurrent.Await +import akka.pattern.ask +import akka.util.Timeout +import scala.concurrent.duration._ + +class GUIDTaskRegister1Test extends Specification { + "RegisterObjectTask" should { + "register (1)" in { + val system = ActorSystem("sys") + val test = system.actorOf(Props(classOf[GUIDTaskRegister1TestActor], system), "test") + + implicit val timeout = Timeout(5 seconds) + val future = test ? "test" + val result = Await.result(future, timeout.duration).asInstanceOf[String] + result mustEqual "success" + } + } +} + +private class GUIDTaskRegister1TestActor(implicit system : ActorSystem) extends Actor { + def receive : Receive = { + case "test" => + val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup + val obj = new GUIDTaskTest.TestObject + + assert(!obj.HasGUID) + taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterObjectTask(obj)(uns))) + probe.expectMsg(scala.util.Success) + assert(obj.HasGUID) + sender ! "success" + case _ => ; + } +} + +//class GUIDTaskRegister1Test extends ActorTest() { +// "RegisterObjectTask" in { +// val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup +// val obj = new GUIDTaskTest.TestObject +// +// assert(!obj.HasGUID) +// taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterObjectTask(obj)(uns))) +// probe.expectMsg(scala.util.Success) +// assert(obj.HasGUID) +// } +//} diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskRegister2Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskRegister2Test.scala new file mode 100644 index 000000000..e6ffb6ebc --- /dev/null +++ b/common/src/test/scala/objects/guidtask/GUIDTaskRegister2Test.scala @@ -0,0 +1,18 @@ +// Copyright (c) 2017 PSForever +package objects.guidtask + +import net.psforever.objects._ +import net.psforever.objects.guid.{GUIDTask, TaskResolver} +import objects.ActorTest + +class GUIDTaskRegister2Test extends ActorTest() { + "RegisterEquipment -> RegisterObjectTask" in { + val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup + val obj = AmmoBox(GlobalDefinitions.energy_cell) + + assert(!obj.HasGUID) + taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterEquipment(obj)(uns))) + probe.expectMsg(scala.util.Success) + assert(obj.HasGUID) + } +} diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskRegister3Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskRegister3Test.scala new file mode 100644 index 000000000..58b3ce484 --- /dev/null +++ b/common/src/test/scala/objects/guidtask/GUIDTaskRegister3Test.scala @@ -0,0 +1,21 @@ +// Copyright (c) 2017 PSForever +package objects.guidtask + +import net.psforever.objects._ +import net.psforever.objects.guid.{GUIDTask, TaskResolver} +import objects.ActorTest + +class GUIDTaskRegister3Test extends ActorTest() { + "RegisterEquipment -> RegisterTool" in { + val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup + val obj = Tool(GlobalDefinitions.beamer) + obj.AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.energy_cell) + + assert(!obj.HasGUID) + assert(!obj.AmmoSlots.head.Box.HasGUID) + taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterEquipment(obj)(uns))) + probe.expectMsg(scala.util.Success) + assert(obj.HasGUID) + assert(obj.AmmoSlots.head.Box.HasGUID) + } +} diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskRegister4Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskRegister4Test.scala new file mode 100644 index 000000000..1eab3dbf5 --- /dev/null +++ b/common/src/test/scala/objects/guidtask/GUIDTaskRegister4Test.scala @@ -0,0 +1,28 @@ +// Copyright (c) 2017 PSForever +package objects.guidtask + +import net.psforever.objects._ +import net.psforever.objects.guid.{GUIDTask, TaskResolver} +import objects.ActorTest + +class GUIDTaskRegister4Test extends ActorTest() { + "RegisterVehicle" in { + val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup + val obj = Vehicle(GlobalDefinitions.fury) + val obj_wep = obj.WeaponControlledFromSeat(0).get + val obj_wep_ammo = (obj.WeaponControlledFromSeat(0).get.asInstanceOf[Tool].AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.hellfire_ammo)).get + obj.Trunk += 30 -> AmmoBox(GlobalDefinitions.hellfire_ammo) + val obj_trunk_ammo = obj.Trunk.Items(0).obj + + assert(!obj.HasGUID) + assert(!obj_wep.HasGUID) + assert(!obj_wep_ammo.HasGUID) + assert(!obj_trunk_ammo.HasGUID) + taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterVehicle(obj)(uns))) + probe.expectMsg(scala.util.Success) + assert(obj.HasGUID) + assert(obj_wep.HasGUID) + assert(obj_wep_ammo.HasGUID) + assert(obj_trunk_ammo.HasGUID) + } +} diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskRegister5Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskRegister5Test.scala new file mode 100644 index 000000000..a96b55dd2 --- /dev/null +++ b/common/src/test/scala/objects/guidtask/GUIDTaskRegister5Test.scala @@ -0,0 +1,39 @@ +// Copyright (c) 2017 PSForever +package objects.guidtask + + +import net.psforever.objects._ +import net.psforever.objects.guid.{GUIDTask, TaskResolver} +import net.psforever.types.{CharacterGender, PlanetSideEmpire} +import objects.ActorTest + +class GUIDTaskRegister5Test extends ActorTest() { + "RegisterAvatar" in { + val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup + val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val obj_wep = Tool(GlobalDefinitions.beamer) + obj.Slot(0).Equipment = obj_wep + val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell) + obj_wep.AmmoSlots.head.Box = obj_wep_ammo + val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell) + obj.Slot(6).Equipment = obj_inv_ammo + val obj_locker = obj.Slot(5).Equipment.get + val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell) + obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo + + assert(!obj.HasGUID) + assert(!obj_wep.HasGUID) + assert(!obj_wep_ammo.HasGUID) + assert(!obj_inv_ammo.HasGUID) + assert(!obj_locker.HasGUID) + assert(!obj_locker_ammo.HasGUID) + taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.RegisterAvatar(obj)(uns))) + probe.expectMsg(scala.util.Success) + assert(obj.HasGUID) + assert(obj_wep.HasGUID) + assert(obj_wep_ammo.HasGUID) + assert(obj_inv_ammo.HasGUID) + assert(obj_locker.HasGUID) + assert(obj_locker_ammo.HasGUID) + } +} diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskTest.scala b/common/src/test/scala/objects/guidtask/GUIDTaskTest.scala new file mode 100644 index 000000000..f67ce2722 --- /dev/null +++ b/common/src/test/scala/objects/guidtask/GUIDTaskTest.scala @@ -0,0 +1,45 @@ +// Copyright (c) 2017 PSForever +package objects.guidtask + +import java.util.logging.LogManager + +import akka.actor.{ActorRef, ActorSystem, Props} +import akka.testkit.TestProbe +import net.psforever.objects.entity.IdentifiableEntity +import net.psforever.objects.guid.actor.{NumberPoolActor, UniqueNumberSystem} +import net.psforever.objects.guid.selector.RandomSelector +import net.psforever.objects.guid.source.LimitedNumberSource +import net.psforever.objects.guid.{NumberPoolHub, Task, TaskResolver} + +object GUIDTaskTest { + class TestObject extends IdentifiableEntity + + class RegisterTestTask(probe : ActorRef) extends Task { + def Execute(resolver : ActorRef) : Unit = { + probe ! scala.util.Success + resolver ! scala.util.Success(this) + } + } + + def CommonTestSetup(implicit system : ActorSystem) : (NumberPoolHub, ActorRef, ActorRef, TestProbe) = { + import akka.actor.Props + import akka.routing.RandomPool + import akka.testkit.TestProbe + + val guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(110)) + guid.AddPool("dynamic", (1 to 100).toList).Selector = new RandomSelector //TODO name is hardcoded for now + val uns = system.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, GUIDTaskTest.AllocateNumberPoolActors(guid))), "uns") + val taskResolver = system.actorOf(RandomPool(15).props(Props[TaskResolver]), "resolver") + LogManager.getLogManager.reset() //suppresses any internal loggers created by the above elements + (guid, uns, taskResolver, TestProbe()) + } + + /** + * @see `UniqueNumberSystem.AllocateNumberPoolActors(NumberPoolHub)(implicit ActorContext)` + */ + def AllocateNumberPoolActors(poolSource : NumberPoolHub)(implicit system : ActorSystem) : Map[String, ActorRef] = { + poolSource.Pools.map({ case ((pname, pool)) => + pname -> system.actorOf(Props(classOf[NumberPoolActor], pool), pname) + }).toMap + } +} diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskUnregister1Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister1Test.scala new file mode 100644 index 000000000..5de6f2e37 --- /dev/null +++ b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister1Test.scala @@ -0,0 +1,18 @@ +// Copyright (c) 2017 PSForever +package objects.guidtask + +import net.psforever.objects.guid.{GUIDTask, TaskResolver} +import objects.ActorTest + +class GUIDTaskUnregister1Test extends ActorTest() { + "UnregisterObjectTask" in { + val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup + val obj = new GUIDTaskTest.TestObject + guid.register(obj, "dynamic") + + assert(obj.HasGUID) + taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterObjectTask(obj)(uns))) + probe.expectMsg(scala.util.Success) + assert(!obj.HasGUID) + } +} diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskUnregister2Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister2Test.scala new file mode 100644 index 000000000..71dcc480b --- /dev/null +++ b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister2Test.scala @@ -0,0 +1,19 @@ +// Copyright (c) 2017 PSForever +package objects.guidtask + +import net.psforever.objects._ +import net.psforever.objects.guid.{GUIDTask, TaskResolver} +import objects.ActorTest + +class GUIDTaskUnregister2Test extends ActorTest() { + "UnregisterEquipment -> UnregisterObjectTask" in { + val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup + val obj = AmmoBox(GlobalDefinitions.energy_cell) + guid.register(obj, "dynamic") + + assert(obj.HasGUID) + taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterEquipment(obj)(uns))) + probe.expectMsg(scala.util.Success) + assert(!obj.HasGUID) + } +} diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskUnregister3Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister3Test.scala new file mode 100644 index 000000000..3f0fd9011 --- /dev/null +++ b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister3Test.scala @@ -0,0 +1,23 @@ +// Copyright (c) 2017 PSForever +package objects.guidtask + +import net.psforever.objects._ +import net.psforever.objects.guid.{GUIDTask, TaskResolver} +import objects.ActorTest + +class GUIDTaskUnregister3Test extends ActorTest() { + "UnregisterEquipment -> UnregisterTool" in { + val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup + val obj = Tool(GlobalDefinitions.beamer) + obj.AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.energy_cell) + guid.register(obj, "dynamic") + guid.register(obj.AmmoSlots.head.Box, "dynamic") + + assert(obj.HasGUID) + assert(obj.AmmoSlots.head.Box.HasGUID) + taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterEquipment(obj)(uns))) + probe.expectMsg(scala.util.Success) + assert(!obj.HasGUID) + assert(!obj.AmmoSlots.head.Box.HasGUID) + } +} diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskUnregister4Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister4Test.scala new file mode 100644 index 000000000..e52eeab79 --- /dev/null +++ b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister4Test.scala @@ -0,0 +1,32 @@ +// Copyright (c) 2017 PSForever +package objects.guidtask + +import net.psforever.objects._ +import net.psforever.objects.guid.{GUIDTask, TaskResolver} +import objects.ActorTest + +class GUIDTaskUnregister4Test extends ActorTest() { + "RegisterVehicle" in { + val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup + val obj = Vehicle(GlobalDefinitions.fury) + val obj_wep = obj.WeaponControlledFromSeat(0).get + val obj_wep_ammo = (obj.WeaponControlledFromSeat(0).get.asInstanceOf[Tool].AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.hellfire_ammo)).get + obj.Trunk += 30 -> AmmoBox(GlobalDefinitions.hellfire_ammo) + val obj_trunk_ammo = obj.Trunk.Items(0).obj + guid.register(obj, "dynamic") + guid.register(obj_wep, "dynamic") + guid.register(obj_wep_ammo, "dynamic") + guid.register(obj_trunk_ammo, "dynamic") + + assert(obj.HasGUID) + assert(obj_wep.HasGUID) + assert(obj_wep_ammo.HasGUID) + assert(obj_trunk_ammo.HasGUID) + taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterVehicle(obj)(uns))) + probe.expectMsg(scala.util.Success) + assert(!obj.HasGUID) + assert(!obj_wep.HasGUID) + assert(!obj_wep_ammo.HasGUID) + assert(!obj_trunk_ammo.HasGUID) + } +} diff --git a/common/src/test/scala/objects/guidtask/GUIDTaskUnregister5Test.scala b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister5Test.scala new file mode 100644 index 000000000..b4d28ab47 --- /dev/null +++ b/common/src/test/scala/objects/guidtask/GUIDTaskUnregister5Test.scala @@ -0,0 +1,44 @@ +// Copyright (c) 2017 PSForever +package objects.guidtask + +import net.psforever.objects._ +import net.psforever.objects.guid.{GUIDTask, TaskResolver} +import net.psforever.types.{CharacterGender, PlanetSideEmpire} +import objects.ActorTest + +class GUIDTaskUnregister5Test extends ActorTest() { + "UnregisterAvatar" in { + val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup + val obj = Player("test", PlanetSideEmpire.TR, CharacterGender.Male, 0, 0) + val obj_wep = Tool(GlobalDefinitions.beamer) + obj.Slot(0).Equipment = obj_wep + val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell) + obj_wep.AmmoSlots.head.Box = obj_wep_ammo + val obj_inv_ammo = AmmoBox(GlobalDefinitions.energy_cell) + obj.Slot(6).Equipment = obj_inv_ammo + val obj_locker = obj.Slot(5).Equipment.get + val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell) + obj_locker.asInstanceOf[LockerContainer].Inventory += 0 -> obj_locker_ammo + guid.register(obj, "dynamic") + guid.register(obj_wep, "dynamic") + guid.register(obj_wep_ammo, "dynamic") + guid.register(obj_inv_ammo, "dynamic") + guid.register(obj_locker, "dynamic") + guid.register(obj_locker_ammo, "dynamic") + + assert(obj.HasGUID) + assert(obj_wep.HasGUID) + assert(obj_wep_ammo.HasGUID) + assert(obj_inv_ammo.HasGUID) + assert(obj_locker.HasGUID) + assert(obj_locker_ammo.HasGUID) + taskResolver ! TaskResolver.GiveTask(new GUIDTaskTest.RegisterTestTask(probe.ref), List(GUIDTask.UnregisterAvatar(obj)(uns))) + probe.expectMsg(scala.util.Success) + assert(!obj.HasGUID) + assert(!obj_wep.HasGUID) + assert(!obj_wep_ammo.HasGUID) + assert(!obj_inv_ammo.HasGUID) + assert(!obj_locker.HasGUID) + assert(!obj_locker_ammo.HasGUID) + } +} 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..5c650542e 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) => @@ -1370,6 +1441,10 @@ class WorldSessionActor extends Actor with MDCContextAware { //TODO optimize this later log.info(s"DismountVehicleMsg: $msg") if(player.GUID == player_guid) { + //common warning for this section + def dismountWarning(msg : String) : Unit = { + log.warn(s"$msg; a vehicle may not know that a player is no longer sitting it in") + } //normally disembarking from a seat player.VehicleSeated match { case Some(vehicle_guid) => @@ -1377,23 +1452,27 @@ class WorldSessionActor extends Actor with MDCContextAware { case Some(obj : Vehicle) => obj.Seats.find(seat => seat.Occupant.contains(player)) match { case Some(seat) => - val vel = obj.Velocity.getOrElse(new Vector3(0f, 0f, 0f)) - val total_vel : Int = math.abs(vel.x * vel.y * vel.z).toInt - if(seat.Bailable || obj.Velocity.isEmpty || total_vel == 0) { + val vel = obj.Velocity.getOrElse(Vector3(0f, 0f, 0f)) + val has_vel : Int = math.abs(vel.x * vel.y * vel.z).toInt + if(seat.Bailable || obj.Velocity.isEmpty || has_vel == 0) { //ugh, float comparison seat.Occupant = None - 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") + dismountWarning(s"DismountVehicleMsg: can not find where player $player_guid is seated in vehicle $vehicle_guid") } case _ => - log.warn(s"DismountVehicleMsg: can not find vehicle $vehicle_guid") + dismountWarning(s"DismountVehicleMsg: can not find vehicle $vehicle_guid") } case None => - log.warn(s"DismountVehicleMsg: player $player_guid not considered seated in a vehicle") + dismountWarning(s"DismountVehicleMsg: player $player_guid not considered seated in a vehicle") } + //should be safe + player.VehicleSeated = None + sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, unk1, unk2))) + vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DismountVehicle(player_guid, unk1, unk2)) } else { //kicking someone else out of a seat; need to own that seat @@ -1409,6 +1488,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 +1815,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 +1837,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 +2033,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..366f79da7 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 */ @@ -59,7 +61,9 @@ class DeconstructionActor extends Actor { case Some(tplayer) => seat.Occupant = None tplayer.VehicleSeated = None - context.parent ! VehicleServiceMessage(zone_id, VehicleAction.KickPassenger(tplayer.GUID, 4, false)) + if(tplayer.HasGUID) { + context.parent ! VehicleServiceMessage(zone_id, VehicleAction.KickPassenger(tplayer.GUID, 4, false)) + } case None => ; } }) @@ -171,11 +175,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 _ => ; }