From 575f5fd7fc11b93effde30bd049c82df8cd4cf3e Mon Sep 17 00:00:00 2001 From: Subsonic154 <43527795+Subsonic154@users.noreply.github.com> Date: Tue, 7 Apr 2026 22:29:09 -0400 Subject: [PATCH] Initial implementation of VR Training (#1355) --- .../overrides/game_objectstz.adb.lst | 135 +++ src/main/resources/zonemaps/map14.json | 184 ++++ src/main/resources/zonemaps/map15.json | 977 ++++++++++++++++++ .../actors/session/AvatarActor.scala | 12 +- .../actors/session/SessionActor.scala | 5 +- .../session/support/GeneralOperations.scala | 6 + .../actors/session/support/SessionData.scala | 28 +- .../WeaponAndProjectileOperations.scala | 6 +- .../session/support/ZoningOperations.scala | 111 +- .../psforever/objects/GlobalDefinitions.scala | 24 +- .../scala/net/psforever/objects/Players.scala | 2 + .../global/GlobalDefinitionsBuilding.scala | 6 +- .../serverobject/PlanetSideServerObject.scala | 2 + .../VirtualTrainingTeleporter.scala | 42 + .../VirtualTrainingTeleporterDefinition.scala | 11 + .../terminals/tabs/EquipmentPage.scala | 2 +- .../terminals/tabs/VehiclePage.scala | 2 +- .../net/psforever/objects/zones/MapInfo.scala | 12 +- .../net/psforever/objects/zones/Zone.scala | 8 +- .../game/objectcreate/ObjectClass.scala | 2 + .../properties/PropertyOverrideManager.scala | 6 +- .../net/psforever/util/PointOfInterest.scala | 6 +- .../scala/net/psforever/zones/Zones.scala | 18 +- 23 files changed, 1580 insertions(+), 27 deletions(-) create mode 100644 server/src/main/resources/overrides/game_objectstz.adb.lst create mode 100644 src/main/resources/zonemaps/map14.json create mode 100755 src/main/resources/zonemaps/map15.json create mode 100644 src/main/scala/net/psforever/objects/serverobject/structures/VirtualTrainingTeleporter.scala create mode 100644 src/main/scala/net/psforever/objects/serverobject/structures/VirtualTrainingTeleporterDefinition.scala diff --git a/server/src/main/resources/overrides/game_objectstz.adb.lst b/server/src/main/resources/overrides/game_objectstz.adb.lst new file mode 100644 index 000000000..30c10d930 --- /dev/null +++ b/server/src/main/resources/overrides/game_objectstz.adb.lst @@ -0,0 +1,135 @@ +add_property ace firemode1_change_ammo_cert standard_assault +add_property ace firemode2_change_ammo_cert standard_assault +add_property ace firemode3_change_ammo_cert standard_assault +add_property ace requirement_certification0 standard_assault +add_property ace_deployable requirement_certification0 standard_assault +add_property advanced_ace requirement_certification0 standard_assault +add_property advanced_ace firemode0_requirement_certification0 standard_assault +add_property advanced_ace firemode1_requirement_certification0 standard_assault +add_property advanced_ace firemode2_requirement_certification0 standard_assault +add_property anniversary_gun requirement_certification0 standard_assault +add_property anniversary_guna requirement_certification0 standard_assault +add_property anniversary_gunb requirement_certification0 standard_assault +add_property applicator requirement_certification0 standard_assault +add_property bank requirement_certification0 standard_assault +add_property bolt_driver requirement_certification0 standard_assault +add_property boomer_trigger requirement_certification0 standard_assault +add_property cycler requirement_certification0 standard_assault +add_property cycler_v2 requirement_certification0 standard_assault +add_property cycler_v3 requirement_certification0 standard_assault +add_property cycler_v4 requirement_certification0 standard_assault +add_property dynomite allowed true +add_property flamethrower requirement_certification0 standard_assault +add_property flechette requirement_certification0 standard_assault +add_property gauss requirement_certification0 standard_assault +add_property heavy_sniper requirement_certification0 standard_assault +add_property hunterseeker requirement_certification0 standard_assault +add_property lancer requirement_certification0 standard_assault +add_property lasher requirement_certification0 standard_assault +add_property lite_armor requirement_certification0 standard_armor +add_property maelstrom purchase_module none +add_property maelstrom requirement_certification0 standard_assault +add_property med_armor requirement_certification0 standard_armor +add_property mini_chaingun requirement_certification0 standard_assault +add_property nano_dispenser requirement_certification0 standard_assault +add_property nchev_antiaircraft requirement_certification0 standard_armor +add_property nchev_antipersonnel requirement_certification0 standard_armor +add_property nchev_antivehicular requirement_certification0 standard_armor +add_property oicw requirement_certification0 standard_assault +add_property pellet_gun allowed true +add_property pellet_gun_ammo allowed true +add_property phoenix requirement_certification0 standard_assault +add_property pulsar requirement_certification0 standard_assault +add_property punisher requirement_certification0 standard_assault +add_property r_shotgun requirement_certification0 standard_assault +add_property radiator purchase_module none +add_property radiator requirement_certification0 standard_assault +add_property rocklet requirement_certification0 standard_assault +add_property six_shooter allowed true +add_property six_shooter_ammo allowed true +add_property spiker purchase_module none +add_property spiker requirement_certification0 standard_assault +add_property stealth_armor requirement_certification0 standard_armor +add_property striker requirement_certification0 standard_assault +add_property thumper requirement_certification0 standard_assault +add_property trek requirement_certification0 standard_assault +add_property trhev_antiaircraft requirement_certification0 standard_armor +add_property trhev_antipersonnel requirement_certification0 standard_armor +add_property trhev_antivehicular requirement_certification0 standard_armor +add_property vshev_antiaircraft requirement_certification0 standard_armor +add_property vshev_antipersonnel requirement_certification0 standard_armor +add_property vshev_antivehicular requirement_certification0 standard_armor +add_property winchester allowed true +add_property winchester_ammo allowed true +add_property order_terminal forsale_dynomite ordertype_weapon +add_property order_terminal forsale_pellet_gun ordertype_weapon +add_property order_terminal forsale_pellet_gun_ammo +add_property order_terminal forsale_six_shooter ordertype_weapon +add_property order_terminal forsale_six_shooter_ammo +add_property order_terminal forsale_winchester ordertype_weapon +add_property order_terminal forsale_winchester_ammo +add_property portable_order_terminal forsale_dynomite ordertype_weapon +add_property portable_order_terminal forsale_pellet_gun ordertype_weapon +add_property portable_order_terminal forsale_pellet_gun_ammo +add_property portable_order_terminal forsale_six_shooter ordertype_weapon +add_property portable_order_terminal forsale_six_shooter_ammo +add_property portable_order_terminal forsale_winchester ordertype_weapon +add_property portable_order_terminal forsale_winchester_ammo +add_property ams requirement_certification0 vehicles +add_property apc requirement_certification0 vehicles +add_property aphelion_flight requirement_award0 false +add_property aphelion_flight requirement_certification0 vehicles +add_property aphelion_gunner requirement_certification0 vehicles +add_property aphelion_laser requirement_certification0 vehicles +add_property aphelion_starfire requirement_certification0 vehicles +add_property aurora requirement_certification0 vehicles +add_property bfr_terminal requirement_certification0 vehicles +add_property colossus_burster requirement_certification0 vehicles +add_property colossus_chaingun requirement_certification0 vehicles +add_property colossus_flight requirement_award0 false +add_property colossus_flight requirement_certification0 vehicles +add_property colossus_gunner requirement_certification0 vehicles +add_property delivererv requirement_certification0 vehicles +add_property dropship requirement_certification0 vehicles +add_property flail purchase_module none +add_property flail requirement_certification0 vehicles +add_property fury requirement_certification0 vehicles +add_property galaxy_gunship requirement_certification0 vehicles +add_property liberator purchase_tech_plant false +add_property liberator requirement_certification0 vehicles +add_property lightgunship purchase_tech_plant false +add_property lightgunship requirement_certification0 vehicles +add_property lightning requirement_certification0 vehicles +add_property lodestar requirement_certification0 vehicles +add_property magrider purchase_tech_plant false +add_property magrider requirement_certification0 vehicles +add_property mediumtransport requirement_certification0 vehicles +add_property mosquito requirement_certification0 vehicles +add_property peregrine_flight requirement_award0 false +add_property peregrine_flight requirement_certification0 vehicles +add_property peregrine_gunner requirement_certification0 vehicles +add_property peregrine_mechhammer requirement_certification0 vehicles +add_property peregrine_sparrow requirement_certification0 vehicles +add_property phantasm purchase_tech_plant false +add_property phantasm requirement_certification0 vehicles +add_property prowler purchase_tech_plant false +add_property prowler requirement_certification0 vehicles +add_property quadassault requirement_certification0 vehicles +add_property quadstealth requirement_certification0 vehicles +add_property router requirement_certification0 vehicles +add_property skyguard purchase_tech_plant false +add_property skyguard requirement_certification0 vehicles +add_property switchblade purchase_module none +add_property switchblade requirement_certification0 vehicles +add_property threemanheavybuggy requirement_certification0 vehicles +add_property thunderer requirement_certification0 vehicles +add_property two_man_assault_buggy requirement_certification0 vehicles +add_property twomanheavybuggy requirement_certification0 vehicles +add_property twomanhoverbuggy requirement_certification0 vehicles +add_property vanguard purchase_tech_plant false +add_property vanguard requirement_certification0 vehicles +add_property vulture purchase_tech_plant false +add_property vulture requirement_award0 false +add_property vulture requirement_certification0 vehicles +add_property wasp purchase_tech_plant false +add_property wasp requirement_certification0 vehicles \ No newline at end of file diff --git a/src/main/resources/zonemaps/map14.json b/src/main/resources/zonemaps/map14.json new file mode 100644 index 000000000..b04b32264 --- /dev/null +++ b/src/main/resources/zonemaps/map14.json @@ -0,0 +1,184 @@ +[ + { + "Id": 0, + "ObjectName": "vr_training", + "ObjectType": "vr_training", + "Owner": null, + "AbsX": 500.0, + "AbsY": 530.0, + "AbsZ": 13.37697, + "Yaw": 360.0, + "GUID": 1, + "MapID": 1, + "IsChildObject": false + }, + { + "Id": 9, + "ObjectName": "order_terminal", + "ObjectType": "order_terminal", + "Owner": 0, + "AbsX": 483.33, + "AbsY": 527.669, + "AbsZ": 14.9649706, + "Yaw": 90.0, + "GUID": 65, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 10, + "ObjectName": "order_terminal", + "ObjectType": "order_terminal", + "Owner": 0, + "AbsX": 483.347, + "AbsY": 529.654, + "AbsZ": 14.9649706, + "Yaw": 90.0, + "GUID": 66, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 6, + "ObjectName": "spawn_pad", + "ObjectType": "spawn_pad", + "Owner": 0, + "AbsX": 500.0, + "AbsY": 530.0, + "AbsZ": 13.37697, + "Yaw": 180.0, + "GUID": 833, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 7, + "ObjectName": "order_terminal", + "ObjectType": "order_terminal", + "Owner": 0, + "AbsX": 489.495, + "AbsY": 540.36, + "AbsZ": 14.9649706, + "Yaw": 150.0, + "GUID": 82, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 11, + "ObjectName": "order_terminal", + "ObjectType": "order_terminal", + "Owner": 0, + "AbsX": 489.512, + "AbsY": 516.936, + "AbsZ": 14.9649706, + "Yaw": 30.0, + "GUID": 81, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 12, + "ObjectName": "order_terminal", + "ObjectType": "order_terminal", + "Owner": 0, + "AbsX": 491.222, + "AbsY": 515.929, + "AbsZ": 14.9649706, + "Yaw": 30.0, + "GUID": 97, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 8, + "ObjectName": "order_terminal", + "ObjectType": "order_terminal", + "Owner": 0, + "AbsX": 491.201, + "AbsY": 541.382, + "AbsZ": 14.9649706, + "Yaw": 150.0, + "GUID": 98, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 2, + "ObjectName": "order_terminal", + "ObjectType": "order_terminal", + "Owner": 0, + "AbsX": 509.539, + "AbsY": 515.922, + "AbsZ": 14.9649706, + "Yaw": 330.0, + "GUID": 113, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 13, + "ObjectName": "order_terminal", + "ObjectType": "order_terminal", + "Owner": 0, + "AbsX": 509.518, + "AbsY": 541.376, + "AbsZ": 14.9649706, + "Yaw": 210.0, + "GUID": 114, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 1, + "ObjectName": "order_terminal", + "ObjectType": "order_terminal", + "Owner": 0, + "AbsX": 511.245, + "AbsY": 516.945, + "AbsZ": 14.9649706, + "Yaw": 330.0, + "GUID": 129, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 5, + "ObjectName": "order_terminal", + "ObjectType": "order_terminal", + "Owner": 0, + "AbsX": 511.229, + "AbsY": 540.368, + "AbsZ": 14.9649706, + "Yaw": 210.0, + "GUID": 130, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 4, + "ObjectName": "order_terminal", + "ObjectType": "order_terminal", + "Owner": 0, + "AbsX": 517.393, + "AbsY": 527.651, + "AbsZ": 14.9649706, + "Yaw": 270.0, + "GUID": 145, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 3, + "ObjectName": "order_terminal", + "ObjectType": "order_terminal", + "Owner": 0, + "AbsX": 517.411, + "AbsY": 529.636, + "AbsZ": 14.9649706, + "Yaw": 270.0, + "GUID": 146, + "MapID": null, + "IsChildObject": true + } +] \ No newline at end of file diff --git a/src/main/resources/zonemaps/map15.json b/src/main/resources/zonemaps/map15.json new file mode 100755 index 000000000..b25d81349 --- /dev/null +++ b/src/main/resources/zonemaps/map15.json @@ -0,0 +1,977 @@ +[ + { + "Id": 72, + "ObjectName": "repair_silo_11115", + "ObjectType": "repair_silo", + "Owner": null, + "AbsX": 2469.91, + "AbsY": 1845.11, + "AbsZ": 23.33, + "Yaw": 245.0, + "GUID": 1, + "MapID": 11115, + "IsChildObject": false + }, + { + "Id": 27, + "ObjectName": "vt_air_vehicle", + "ObjectType": "vt_air_vehicle", + "Owner": null, + "AbsX": 1296.0, + "AbsY": 1778.0, + "AbsZ": 14.6012754, + "Yaw": 150.0, + "GUID": 17, + "MapID": 10, + "IsChildObject": false + }, + { + "Id": 30, + "ObjectName": "vt_air_vehicle", + "ObjectType": "vt_air_vehicle", + "Owner": null, + "AbsX": 2422.0, + "AbsY": 1190.0, + "AbsZ": 15.4802084, + "Yaw": 138.0, + "GUID": 19, + "MapID": 11, + "IsChildObject": false + }, + { + "Id": 33, + "ObjectName": "vt_air_vehicle", + "ObjectType": "vt_air_vehicle", + "Owner": null, + "AbsX": 2788.0, + "AbsY": 2582.0, + "AbsZ": 19.1222878, + "Yaw": 360.0, + "GUID": 21, + "MapID": 12, + "IsChildObject": false + }, + { + "Id": 24, + "ObjectName": "vt_dropship", + "ObjectType": "vt_dropship", + "Owner": null, + "AbsX": 1716.0, + "AbsY": 1860.0, + "AbsZ": 32.4491119, + "Yaw": 360.0, + "GUID": 29, + "MapID": 9, + "IsChildObject": false + }, + { + "Id": 66, + "ObjectName": "vt_spawn_11112", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 1309.08, + "AbsY": 2221.91, + "AbsZ": 15.18, + "Yaw": 0.0, + "GUID": 33, + "MapID": 11112, + "IsChildObject": false + }, + { + "Id": 36, + "ObjectName": "vt_spawn_11091", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 1441.54, + "AbsY": 1487.18, + "AbsZ": 17.76, + "Yaw": 0.0, + "GUID": 35, + "MapID": 11091, + "IsChildObject": false + }, + { + "Id": 38, + "ObjectName": "vt_spawn_11092", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 1447.23, + "AbsY": 1900.92, + "AbsZ": 12.31, + "Yaw": 338.0, + "GUID": 37, + "MapID": 11092, + "IsChildObject": false + }, + { + "Id": 64, + "ObjectName": "vt_spawn_11111", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 1481.31, + "AbsY": 2427.31, + "AbsZ": 16.62, + "Yaw": 0.0, + "GUID": 39, + "MapID": 11111, + "IsChildObject": false + }, + { + "Id": 68, + "ObjectName": "vt_spawn_11113", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 1526.13, + "AbsY": 981.66, + "AbsZ": 20.92, + "Yaw": 0.0, + "GUID": 41, + "MapID": 11113, + "IsChildObject": false + }, + { + "Id": 58, + "ObjectName": "vt_spawn_11106", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 1792.6, + "AbsY": 1748.62, + "AbsZ": 12.65, + "Yaw": 0.0, + "GUID": 43, + "MapID": 11106, + "IsChildObject": false + }, + { + "Id": 62, + "ObjectName": "vt_spawn_11109", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 1828.01, + "AbsY": 2390.93, + "AbsZ": 14.77, + "Yaw": 324.0, + "GUID": 45, + "MapID": 11109, + "IsChildObject": false + }, + { + "Id": 70, + "ObjectName": "vt_spawn_11114", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 1846.59, + "AbsY": 807.64, + "AbsZ": 13.76, + "Yaw": 346.0, + "GUID": 47, + "MapID": 11114, + "IsChildObject": false + }, + { + "Id": 46, + "ObjectName": "vt_spawn_11100", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 1898.58, + "AbsY": 3223.86, + "AbsZ": 14.37, + "Yaw": 0.0, + "GUID": 49, + "MapID": 11100, + "IsChildObject": false + }, + { + "Id": 48, + "ObjectName": "vt_spawn_11101", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 2038.42, + "AbsY": 3039.22, + "AbsZ": 24.41, + "Yaw": 0.0, + "GUID": 51, + "MapID": 11101, + "IsChildObject": false + }, + { + "Id": 60, + "ObjectName": "vt_spawn_11107", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 2126.87, + "AbsY": 2026.58, + "AbsZ": 14.55, + "Yaw": 0.0, + "GUID": 53, + "MapID": 11107, + "IsChildObject": false + }, + { + "Id": 56, + "ObjectName": "vt_spawn_11105", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 2237.12, + "AbsY": 1436.94, + "AbsZ": 20.63, + "Yaw": 313.0, + "GUID": 55, + "MapID": 11105, + "IsChildObject": false + }, + { + "Id": 40, + "ObjectName": "vt_spawn_11093", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 2456.26, + "AbsY": 1983.63, + "AbsZ": 13.75, + "Yaw": 43.0, + "GUID": 57, + "MapID": 11093, + "IsChildObject": false + }, + { + "Id": 42, + "ObjectName": "vt_spawn_11094", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 2456.92, + "AbsY": 2849.11, + "AbsZ": 20.43, + "Yaw": 0.0, + "GUID": 59, + "MapID": 11094, + "IsChildObject": false + }, + { + "Id": 44, + "ObjectName": "vt_spawn_11095", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 2492.26, + "AbsY": 2303.02, + "AbsZ": 12.81, + "Yaw": 22.0, + "GUID": 61, + "MapID": 11095, + "IsChildObject": false + }, + { + "Id": 54, + "ObjectName": "vt_spawn_11104", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 2643.76, + "AbsY": 1396.84, + "AbsZ": 15.43, + "Yaw": 0.0, + "GUID": 63, + "MapID": 11104, + "IsChildObject": false + }, + { + "Id": 50, + "ObjectName": "vt_spawn_11102", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 2916.48, + "AbsY": 2589.26, + "AbsZ": 14.75, + "Yaw": 0.0, + "GUID": 65, + "MapID": 11102, + "IsChildObject": false + }, + { + "Id": 52, + "ObjectName": "vt_spawn_11103", + "ObjectType": "vt_spawn", + "Owner": null, + "AbsX": 2963.03, + "AbsY": 1881.18, + "AbsZ": 17.4, + "Yaw": 353.0, + "GUID": 67, + "MapID": 11103, + "IsChildObject": false + }, + { + "Id": 18, + "ObjectName": "vt_vehicle", + "ObjectType": "vt_vehicle", + "Owner": null, + "AbsX": 1230.0, + "AbsY": 2266.0, + "AbsZ": 19.3969536, + "Yaw": 360.0, + "GUID": 105, + "MapID": 7, + "IsChildObject": false + }, + { + "Id": 21, + "ObjectName": "vt_vehicle", + "ObjectType": "vt_vehicle", + "Owner": null, + "AbsX": 1464.0, + "AbsY": 858.0, + "AbsZ": 18.7295151, + "Yaw": 55.0, + "GUID": 107, + "MapID": 8, + "IsChildObject": false + }, + { + "Id": 0, + "ObjectName": "vt_vehicle", + "ObjectType": "vt_vehicle", + "Owner": null, + "AbsX": 1728.0, + "AbsY": 3180.0, + "AbsZ": 17.1666622, + "Yaw": 360.0, + "GUID": 109, + "MapID": 1, + "IsChildObject": false + }, + { + "Id": 3, + "ObjectName": "xx06_width_13_slope_0.06_offset_-2_blend_50", + "ObjectType": "vt_vehicle", + "Owner": null, + "AbsX": 1888.0, + "AbsY": 3008.0, + "AbsZ": 19.0838337, + "Yaw": 36.0, + "GUID": 111, + "MapID": 2, + "IsChildObject": false + }, + { + "Id": 9, + "ObjectName": "vt_vehicle", + "ObjectType": "vt_vehicle", + "Owner": null, + "AbsX": 2186.0, + "AbsY": 3154.0, + "AbsZ": 16.35365, + "Yaw": 233.0, + "GUID": 113, + "MapID": 4, + "IsChildObject": false + }, + { + "Id": 15, + "ObjectName": "vt_vehicle", + "ObjectType": "vt_vehicle", + "Owner": null, + "AbsX": 2436.0, + "AbsY": 1872.0, + "AbsZ": 23.4730072, + "Yaw": 95.0, + "GUID": 115, + "MapID": 6, + "IsChildObject": false + }, + { + "Id": 6, + "ObjectName": "xx08_width_13_slope_0.06_offset_-2_blend_50", + "ObjectType": "vt_vehicle", + "Owner": null, + "AbsX": 2440.0, + "AbsY": 2974.0, + "AbsZ": 19.660635, + "Yaw": 232.0, + "GUID": 117, + "MapID": 3, + "IsChildObject": false + }, + { + "Id": 12, + "ObjectName": "vt_vehicle", + "ObjectType": "vt_vehicle", + "Owner": null, + "AbsX": 2732.0, + "AbsY": 1450.0, + "AbsZ": 13.8541822, + "Yaw": 102.0, + "GUID": 119, + "MapID": 5, + "IsChildObject": false + }, + { + "Id": 28, + "ObjectName": "air_vehicle_terminal", + "ObjectType": "air_vehicle_terminal", + "Owner": 27, + "AbsX": 1308.55444, + "AbsY": 1770.761, + "AbsZ": 17.2882748, + "Yaw": 120.0, + "GUID": 137, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 31, + "ObjectName": "air_vehicle_terminal", + "ObjectType": "air_vehicle_terminal", + "Owner": 30, + "AbsX": 2432.775, + "AbsY": 1180.309, + "AbsZ": 18.1672077, + "Yaw": 132.0, + "GUID": 139, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 34, + "ObjectName": "air_vehicle_terminal", + "ObjectType": "air_vehicle_terminal", + "Owner": 33, + "AbsX": 2773.508, + "AbsY": 2581.992, + "AbsZ": 21.809288, + "Yaw": 270.0, + "GUID": 141, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 75, + "ObjectName": "bfr_door", + "ObjectType": "bfr_door", + "Owner": 15, + "AbsX": 2402.71436, + "AbsY": 1928.01074, + "AbsZ": 20.6460171, + "Yaw": 50.0, + "GUID": 149, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 74, + "ObjectName": "bfr_terminal", + "ObjectType": "bfr_terminal", + "Owner": 15, + "AbsX": 2394.414, + "AbsY": 1931.7948, + "AbsZ": 17.5699425, + "Yaw": 5.0, + "GUID": 153, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 26, + "ObjectName": "dropship_pad_doors", + "ObjectType": "dropship_pad_doors", + "Owner": 24, + "AbsX": 1712.411, + "AbsY": 1859.98, + "AbsZ": 28.4641113, + "Yaw": 90.0, + "GUID": 157, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 25, + "ObjectName": "dropship_vehicle_terminal", + "ObjectType": "dropship_vehicle_terminal", + "Owner": 24, + "AbsX": 1692.53, + "AbsY": 1859.88, + "AbsZ": 35.317112, + "Yaw": 270.0, + "GUID": 161, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 20, + "ObjectName": "mb_pad_creation", + "ObjectType": "mb_pad_creation", + "Owner": 18, + "AbsX": 1230.147, + "AbsY": 2266.027, + "AbsZ": 17.9259529, + "Yaw": 180.0, + "GUID": 165, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 29, + "ObjectName": "mb_pad_creation", + "ObjectType": "mb_pad_creation", + "Owner": 27, + "AbsX": 1295.85925, + "AbsY": 1778.05017, + "AbsZ": 13.1302757, + "Yaw": 30.0, + "GUID": 167, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 23, + "ObjectName": "mb_pad_creation", + "ObjectType": "mb_pad_creation", + "Owner": 21, + "AbsX": 1464.06226, + "AbsY": 858.1359, + "AbsZ": 17.2585144, + "Yaw": 125.0, + "GUID": 169, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 2, + "ObjectName": "mb_pad_creation", + "ObjectType": "mb_pad_creation", + "Owner": 0, + "AbsX": 1728.147, + "AbsY": 3180.027, + "AbsZ": 15.6956625, + "Yaw": 180.0, + "GUID": 171, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 5, + "ObjectName": "mb_pad_creation", + "ObjectType": "mb_pad_creation", + "Owner": 3, + "AbsX": 1888.103, + "AbsY": 3008.10815, + "AbsZ": 17.612833, + "Yaw": 144.0, + "GUID": 173, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 11, + "ObjectName": "mb_pad_creation", + "ObjectType": "mb_pad_creation", + "Owner": 9, + "AbsX": 2185.933, + "AbsY": 3153.86646, + "AbsZ": 14.8826494, + "Yaw": 307.0, + "GUID": 175, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 32, + "ObjectName": "mb_pad_creation", + "ObjectType": "mb_pad_creation", + "Owner": 30, + "AbsX": 2421.8728, + "AbsY": 1190.07825, + "AbsZ": 14.0092087, + "Yaw": 42.0, + "GUID": 177, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 17, + "ObjectName": "mb_pad_creation", + "ObjectType": "mb_pad_creation", + "Owner": 15, + "AbsX": 2435.96021, + "AbsY": 1872.144, + "AbsZ": 22.0020065, + "Yaw": 85.0, + "GUID": 179, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 8, + "ObjectName": "mb_pad_creation", + "ObjectType": "mb_pad_creation", + "Owner": 6, + "AbsX": 2439.93066, + "AbsY": 2973.86743, + "AbsZ": 18.1896343, + "Yaw": 308.0, + "GUID": 181, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 14, + "ObjectName": "mb_pad_creation", + "ObjectType": "mb_pad_creation", + "Owner": 12, + "AbsX": 2731.943, + "AbsY": 1450.13818, + "AbsZ": 12.3831825, + "Yaw": 78.0, + "GUID": 183, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 35, + "ObjectName": "mb_pad_creation", + "ObjectType": "mb_pad_creation", + "Owner": 33, + "AbsX": 2788.147, + "AbsY": 2582.027, + "AbsZ": 17.6512871, + "Yaw": 180.0, + "GUID": 185, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 67, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 66, + "AbsX": 1309.08, + "AbsY": 2221.80981, + "AbsZ": 16.3210011, + "Yaw": 180.0, + "GUID": 209, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 37, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 36, + "AbsX": 1441.54, + "AbsY": 1487.08008, + "AbsZ": 18.901001, + "Yaw": 180.0, + "GUID": 211, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 39, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 38, + "AbsX": 1447.1925, + "AbsY": 1900.82727, + "AbsZ": 13.451, + "Yaw": 202.0, + "GUID": 213, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 65, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 64, + "AbsX": 1481.31, + "AbsY": 2427.21, + "AbsZ": 17.7610016, + "Yaw": 180.0, + "GUID": 215, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 69, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 68, + "AbsX": 1526.13, + "AbsY": 981.56, + "AbsZ": 22.061, + "Yaw": 180.0, + "GUID": 217, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 59, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 58, + "AbsX": 1792.6, + "AbsY": 1748.52, + "AbsZ": 13.7909994, + "Yaw": 180.0, + "GUID": 219, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 63, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 62, + "AbsX": 1827.95117, + "AbsY": 2390.849, + "AbsZ": 15.911, + "Yaw": 216.0, + "GUID": 221, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 71, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 70, + "AbsX": 1846.5658, + "AbsY": 807.542969, + "AbsZ": 14.901, + "Yaw": 194.0, + "GUID": 223, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 47, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 46, + "AbsX": 1898.58, + "AbsY": 3223.76, + "AbsZ": 15.511, + "Yaw": 180.0, + "GUID": 225, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 49, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 48, + "AbsX": 2038.42, + "AbsY": 3039.11987, + "AbsZ": 25.551, + "Yaw": 180.0, + "GUID": 227, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 61, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 60, + "AbsX": 2126.87, + "AbsY": 2026.48, + "AbsZ": 15.691, + "Yaw": 180.0, + "GUID": 229, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 57, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 56, + "AbsX": 2237.04688, + "AbsY": 1436.8717, + "AbsZ": 21.771, + "Yaw": 227.0, + "GUID": 231, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 41, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 40, + "AbsX": 2456.32812, + "AbsY": 1983.55688, + "AbsZ": 14.891, + "Yaw": 137.0, + "GUID": 233, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 43, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 42, + "AbsX": 2456.92, + "AbsY": 2849.01, + "AbsZ": 21.5710011, + "Yaw": 180.0, + "GUID": 235, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 45, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 44, + "AbsX": 2492.29736, + "AbsY": 2302.92725, + "AbsZ": 13.951, + "Yaw": 158.0, + "GUID": 237, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 55, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 54, + "AbsX": 2643.76, + "AbsY": 1396.74, + "AbsZ": 16.5710011, + "Yaw": 180.0, + "GUID": 239, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 51, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 50, + "AbsX": 2916.48, + "AbsY": 2589.16, + "AbsZ": 15.891, + "Yaw": 180.0, + "GUID": 241, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 53, + "ObjectName": "spawn_zone", + "ObjectType": "spawn_zone", + "Owner": 52, + "AbsX": 2963.01782, + "AbsY": 1881.08081, + "AbsZ": 18.541, + "Yaw": 187.0, + "GUID": 243, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 19, + "ObjectName": "vehicle_terminal", + "ObjectType": "vehicle_terminal", + "Owner": 18, + "AbsX": 1215.508, + "AbsY": 2265.992, + "AbsZ": 22.0839539, + "Yaw": 270.0, + "GUID": 281, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 22, + "ObjectName": "vehicle_terminal", + "ObjectType": "vehicle_terminal", + "Owner": 21, + "AbsX": 1455.69434, + "AbsY": 846.124268, + "AbsZ": 21.4165154, + "Yaw": 215.0, + "GUID": 283, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 1, + "ObjectName": "vehicle_terminal", + "ObjectType": "vehicle_terminal", + "Owner": 0, + "AbsX": 1713.508, + "AbsY": 3179.992, + "AbsZ": 19.8536625, + "Yaw": 270.0, + "GUID": 285, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 4, + "ObjectName": "vehicle_terminal", + "ObjectType": "vehicle_terminal", + "Owner": 3, + "AbsX": 1876.2804, + "AbsY": 2999.47534, + "AbsZ": 21.770834, + "Yaw": 234.0, + "GUID": 287, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 10, + "ObjectName": "vehicle_terminal", + "ObjectType": "vehicle_terminal", + "Owner": 9, + "AbsX": 2194.715, + "AbsY": 3165.57861, + "AbsZ": 19.04065, + "Yaw": 37.0, + "GUID": 289, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 16, + "ObjectName": "vehicle_terminal", + "ObjectType": "vehicle_terminal", + "Owner": 15, + "AbsX": 2437.271, + "AbsY": 1857.56384, + "AbsZ": 26.1600075, + "Yaw": 175.0, + "GUID": 291, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 7, + "ObjectName": "vehicle_terminal", + "ObjectType": "vehicle_terminal", + "Owner": 6, + "AbsX": 2448.91577, + "AbsY": 2985.4248, + "AbsZ": 22.3476353, + "Yaw": 38.0, + "GUID": 293, + "MapID": null, + "IsChildObject": true + }, + { + "Id": 13, + "ObjectName": "vehicle_terminal", + "ObjectType": "vehicle_terminal", + "Owner": 12, + "AbsX": 2735.021, + "AbsY": 1435.82629, + "AbsZ": 16.5411816, + "Yaw": 168.0, + "GUID": 295, + "MapID": null, + "IsChildObject": true + } +] \ No newline at end of file diff --git a/src/main/scala/net/psforever/actors/session/AvatarActor.scala b/src/main/scala/net/psforever/actors/session/AvatarActor.scala index 02d8d5ca1..32da5a6de 100644 --- a/src/main/scala/net/psforever/actors/session/AvatarActor.scala +++ b/src/main/scala/net/psforever/actors/session/AvatarActor.scala @@ -1559,6 +1559,8 @@ class AvatarActor( .foreach { case (item, name) => Avatar.purchaseCooldowns.get(item) match { + case Some(cooldown) if session.get.player.IsInVRZone => () + //don't update purchase timers for players in VR training zones case Some(cooldown) => //only send for items with cooldowns updateTheTimes = true @@ -2248,7 +2250,15 @@ class AvatarActor( sessionActor ! SessionActor.SendResponse( CharacterInfoMessage( unk = 15, - PlanetSideZoneID(zoneNum), + PlanetSideZoneID( + //there is a client bug that causes the NC/VS versions of VR shooting/driving zones to have no background image + //remap them to the TR versions as a workaround + //the VR combat zones have no background image at all, so they are not accounted for + zoneNum match { + case 17 | 20 => 14 + case 18 | 21 => 15 + case _ => zoneNum + }), avatar.id, pguid, finished = false, diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala index 21093f14f..aaec38df4 100644 --- a/src/main/scala/net/psforever/actors/session/SessionActor.scala +++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala @@ -12,7 +12,7 @@ import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal} import net.psforever.objects.zones.Zone import net.psforever.packet.PlanetSideGamePacket -import net.psforever.packet.game.{AIDamage, ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarGrenadeStateMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BeginZoningMessage, BindPlayerMessage, BugReportMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, ChildObjectStateMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DeployRequestMessage, DismountVehicleCargoMsg, DismountVehicleMsg, DisplayedAwardMessage, DropItemMessage, DroppodLaunchRequestMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FavoritesRequest, FrameVehicleStateMessage, FriendsRequest, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, HitMessage, InvalidTerrainMessage, ItemTransactionMessage, LashMessage, LongRangeProjectileInfoMessage, LootItemMessage, MountVehicleCargoMsg, MountVehicleMsg, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitMembershipRequest, OutfitMembershipResponse, OutfitRequest, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, ProjectileStateMessage, ProximityTerminalUseMessage, ReleaseAvatarRequestMessage, ReloadMessage, RequestDestroyMessage, SetChatFilterMessage, SpawnRequestMessage, SplashHitMessage, SquadDefinitionActionMessage, SquadMembershipRequest, SquadWaypointRequest, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UplinkRequest, UseItemMessage, VehicleStateMessage, VehicleSubStateMessage, VoiceHostInfo, VoiceHostRequest, WarpgateRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage, ZipLineMessage} +import net.psforever.packet.game.{AIDamage, ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarGrenadeStateMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BeginZoningMessage, BindPlayerMessage, BugReportMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, ChildObjectStateMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DeployRequestMessage, DismountVehicleCargoMsg, DismountVehicleMsg, DisplayedAwardMessage, DropItemMessage, DroppodLaunchRequestMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FavoritesRequest, FrameVehicleStateMessage, FriendsRequest, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, HitMessage, InvalidTerrainMessage, ItemTransactionMessage, LashMessage, LongRangeProjectileInfoMessage, LootItemMessage, MountVehicleCargoMsg, MountVehicleMsg, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitMembershipRequest, OutfitMembershipResponse, OutfitRequest, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, ProjectileStateMessage, ProximityTerminalUseMessage, ReleaseAvatarRequestMessage, ReloadMessage, RequestDestroyMessage, SetChatFilterMessage, SpawnRequestMessage, SplashHitMessage, SquadDefinitionActionMessage, SquadMembershipRequest, SquadWaypointRequest, TargetingImplantRequest, TradeMessage, TrainingZoneMessage, UnuseItemMessage, UplinkRequest, UseItemMessage, VehicleStateMessage, VehicleSubStateMessage, VoiceHostInfo, VoiceHostRequest, WarpgateRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage, ZipLineMessage} import net.psforever.services.{InterstellarClusterService => ICS} import net.psforever.services.CavernRotationService import net.psforever.services.CavernRotationService.SendCavernRotationUpdates @@ -619,6 +619,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con case packet: OutfitMembershipResponse => logic.general.handleOutfitMembershipResponse(packet) + case packet: TrainingZoneMessage => + data.zoning.handleTrainingZoneMessage(packet) + case pkt => data.log.warn(s"Unhandled GamePacket $pkt") } diff --git a/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala b/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala index b7dc548eb..a9692605a 100644 --- a/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala @@ -1059,6 +1059,12 @@ class GeneralOperations( sendResponse(ChatMsg(ChatMessageType.UNK_227, wideContents=false, "", "@charsaved", None)) } + def trainingGriefWarning(): Unit = { + //don't know the correct ChatMessageType for this one yet; + //it's supposed to use a pop-up that takes 5 seconds before you are allowed to close it, so disabled for now + //sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, wideContents=true, "", "@TrainingGriefWarning", None)) + } + def noVoicedChat(pkt: PlanetSideGamePacket): Unit = { log.debug(s"$pkt") sendResponse(VoiceHostKill()) diff --git a/src/main/scala/net/psforever/actors/session/support/SessionData.scala b/src/main/scala/net/psforever/actors/session/support/SessionData.scala index 44769b870..f812504fd 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionData.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionData.scala @@ -422,7 +422,12 @@ class SessionData( if (obj.spectator && obj != player) { administrativeKick(player) } else { - obj.Actor ! Vitality.Damage(func) + if (obj.IsInVRZone && obj.Faction == player.Faction && obj.CharId != player.CharId) { + //don't do friendly-fire in VR zones + general.trainingGriefWarning() + } else { + obj.Actor ! Vitality.Damage(func) + } } case obj: Vehicle if obj.CanDamage => @@ -433,10 +438,20 @@ class SessionData( } else { log.info(s"$name is attacking $ownerName's ${obj.Definition.Name}") } - obj.Actor ! Vitality.Damage(func) + if (obj.IsInVRZone && obj.Faction == player.Faction && !ownerName.equals(name)) { + //don't do friendly-fire in VR zones + general.trainingGriefWarning() + } else { + obj.Actor ! Vitality.Damage(func) + } case obj: Amenity if obj.CanDamage => - obj.Actor ! Vitality.Damage(func) + if (obj.IsInVRZone && obj.Faction == player.Faction) { + //don't do friendly-fire in VR zones + general.trainingGriefWarning() + } else { + obj.Actor ! Vitality.Damage(func) + } case obj: Deployable if obj.CanDamage => val name = player.Name @@ -446,7 +461,12 @@ class SessionData( } else { log.info(s"$name is attacking $ownerName's ${obj.Definition.Name}") } - obj.Actor ! Vitality.Damage(func) + if (obj.IsInVRZone && obj.Faction == player.Faction && !ownerName.equals(name)) { + //don't do friendly-fire in VR zones + general.trainingGriefWarning() + } else { + obj.Actor ! Vitality.Damage(func) + } case _ => () } diff --git a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala index a6ab82fd4..2e32d426a 100644 --- a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala @@ -409,7 +409,8 @@ class WeaponAndProjectileOperations( } else { equipment foreach { case obj: ConstructionItem => - if (Deployables.performConstructionItemAmmoChange(player.avatar.certifications, obj, obj.AmmoTypeIndex)) { + val certifications = if (player.IsInVRZone) GlobalDefinitions.vrZoneTempEngineeringCerts() else player.avatar.certifications + if (Deployables.performConstructionItemAmmoChange(certifications, obj, obj.AmmoTypeIndex)) { log.info( s"${player.Name} switched ${player.Sex.possessive} ${obj.Definition.Name} to construct ${obj.AmmoType} (option #${obj.FireModeIndex})" ) @@ -437,8 +438,9 @@ class WeaponAndProjectileOperations( val originalModeIndex = obj.FireModeIndex if (obj match { case citem: ConstructionItem => + val certifications = if (player.IsInVRZone) GlobalDefinitions.vrZoneTempEngineeringCerts() else player.avatar.certifications val modeChanged = Deployables.performConstructionItemFireModeChange( - player.avatar.certifications, + certifications, citem, originalModeIndex ) diff --git a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala index bbbb3a982..99edd6d02 100644 --- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala @@ -21,7 +21,7 @@ import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource} import net.psforever.objects.vital.{InGameHistory, IncarnationActivity, ReconstructionActivity, SpawningActivity} import net.psforever.objects.zones.blockmap.BlockMapEntity import net.psforever.packet.game.GenericAction.FirstPersonViewWithEffect -import net.psforever.packet.game.{CampaignStatistic, ChangeFireStateMessage_Start, CloudInfo, GenericActionMessage, GenericObjectActionEnum, HackState7, MailMessage, ObjectDetectedMessage, SessionStatistic, StormInfo, TriggeredSound, WeatherMessage} +import net.psforever.packet.game.{CampaignStatistic, ChangeFireStateMessage_Start, CloudInfo, GenericActionMessage, GenericObjectActionEnum, HackState7, MailMessage, ObjectDetectedMessage, SessionStatistic, StormInfo, TriggeredSound, TrainingZoneMessage, WeatherMessage} import net.psforever.services.chat.DefaultChannel import scala.concurrent.duration._ @@ -863,6 +863,66 @@ class ZoningOperations( } } + import scala.util.Random + private val rand = Random + // temp, will be replaced by proper spawn point logic in the future (hopefully) + val vrShootingZoneSpawns: List[Vector3] = List[Vector3]( + Vector3(491.91f, 531.82f, 13.72f), + Vector3(508.36f, 531.56f, 13.72f), + Vector3(508.56f, 525.34f, 13.72f), + Vector3(500.88f, 521.95f, 13.72f), + Vector3(492.85f, 526.03f, 13.72f), + Vector3(500.48f, 534.74f, 13.72f) + ) + val vrDrivingAreaSpawns: List[Vector3] = List[Vector3]( + Vector3(1893.53f, 2988.72f, 19.1f), + Vector3(1716.85f, 3198.06f, 17.2f), + Vector3(2176.49f, 3174.48f, 16.4f), + Vector3(2431.75f, 2995.72f, 19.7f), + Vector3(2775.13f, 2562.91f, 19.2f), + Vector3(2752.12f, 1442.14f, 13.9f), + Vector3(1216.68f, 2283.52f, 19.4f), + Vector3(1318.21f, 1786.59f, 14.6f), + Vector3(1471.92f, 834.2f, 18.8f), + Vector3(2419f, 1167.62f, 15.5f), + Vector3(2455.81f, 1860.8f, 23.5f) + ) + + def handleTrainingZoneMessage(pkt: TrainingZoneMessage): Unit = { + pkt.zone.guid match { + case 11 | 12 | 13 => + player.avatar.deployables.UpdateMaxCounts(player.avatar.certifications) + RequestSanctuaryZoneSpawn(player, player.Zone.Number) + // leveraging SetZone for now because the VR zones have no "proper" spawn points configured yet + case 17 => + if (player.Zone.id != "tzshtr") { + context.self ! SessionActor.SetZone("tzshtr", vrShootingZoneSpawns.toArray.apply(rand.nextInt(vrShootingZoneSpawns.size))) + } + case 18 => + if (player.Zone.id != "tzshnc") { + context.self ! SessionActor.SetZone("tzshnc", vrShootingZoneSpawns.toArray.apply(rand.nextInt(vrShootingZoneSpawns.size))) + } + case 19 => + if (player.Zone.id != "tzshvs") { + context.self ! SessionActor.SetZone("tzshvs", vrShootingZoneSpawns.toArray.apply(rand.nextInt(vrShootingZoneSpawns.size))) + } + case 20 => + if (player.Zone.id != "tzdrtr") { + context.self ! SessionActor.SetZone("tzdrtr", vrDrivingAreaSpawns.toArray.apply(rand.nextInt(vrDrivingAreaSpawns.size))) + } + case 21 => + if (player.Zone.id != "tzdrnc") { + context.self ! SessionActor.SetZone("tzdrnc", vrDrivingAreaSpawns.toArray.apply(rand.nextInt(vrDrivingAreaSpawns.size))) + } + case 22 => + if (player.Zone.id != "tzdrvs") { + context.self ! SessionActor.SetZone("tzdrvs", vrDrivingAreaSpawns.toArray.apply(rand.nextInt(vrDrivingAreaSpawns.size))) + } + case _ => + log.warn(s"Received TrainingZoneMessage that requests unexpected zone number ${pkt.zone.guid}?") + } + } + /* support functions */ /** @@ -918,13 +978,23 @@ class ZoningOperations( * the time representative of the the tower has priority. * When no spheres of influence are being encroached, one is considered "in the wilderness". * The messaging is different but the location is normally treated the same as if in a neutral sphere of influence. - * Being anywhere in one's faction's own sanctuary is a special case. + * Being anywhere in one's faction's own sanctuary or VR training area(s) is a special case. * * @return a `Tuple` composed of the initial countdown time and the descriptor for message composition */ def ZoningStartInitialMessageAndTimer(): (Int, String) = { val location = if (Zones.sanctuaryZoneNumber(player.Faction) == continent.Number) { Zoning.Time.Sanctuary + } else if (player.IsInVRZone) { + continent.id match { + case "tzshtr" | "tzdrtr" | "tzcotr" => + if (player.Faction == PlanetSideEmpire.TR) Zoning.Time.Friendly else Zoning.Time.None + case "tzshnc" | "tzdrnc" | "tzconc" => + if (player.Faction == PlanetSideEmpire.NC) Zoning.Time.Friendly else Zoning.Time.None + case "tzshvs" | "tzdrvs" | "tzcovs" => + if (player.Faction == PlanetSideEmpire.VS) Zoning.Time.Friendly else Zoning.Time.None + case _ => Zoning.Time.None + } } else { ZoningOperations.findBuildingsBySoiOccupancy(continent, player.Position) match { case Nil => @@ -1218,6 +1288,13 @@ class ZoningOperations( ICS.FindZone(_.id.equals(zoneId), context.self) )) } else if (player.HasGUID) { + if (player.IsInVRZone && !zoneId.startsWith("tz")) { + // reset the player to default gear to prevent them from smuggling things out of VR + log.info(s"${player.Name} is exiting VR Training, resetting ${player.Sex.possessive} loadout") + val newPlayer = Player.Respawn(player) + DefinitionUtil.applyDefaultLoadout(newPlayer) + session = session.copy(player = newPlayer) + } TaskWorkflow.execute(taskThenZoneChange( GUIDTask.unregisterAvatar(continent.GUID, original), ICS.FindZone(_.id.equals(zoneId), context.self) @@ -2051,6 +2128,30 @@ class ZoningOperations( SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@reset_sanctuary_locked")), 20) ) //You have been returned to the sanctuary because the location you logged out is not available. player.Zone = Zone.Nowhere + } else if (inZone.id.startsWith("tz")) { + inZone.id match { + case "tzshtr" | "tzdrtr" | "tzcotr" => + if (player.Faction != PlanetSideEmpire.TR) { + enqueueNewActivity(ActivityQueuedTask( + SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@reset_sanctuary_locked")), 20) + ) //You have been returned to the sanctuary because the location you logged out is not available. + player.Zone = Zone.Nowhere + } + case "tzshnc" | "tzdrnc" | "tzconc" => + if (player.Faction != PlanetSideEmpire.NC) { + enqueueNewActivity(ActivityQueuedTask( + SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@reset_sanctuary_locked")), 20) + ) //You have been returned to the sanctuary because the location you logged out is not available. + player.Zone = Zone.Nowhere + } + case "tzshvs" | "tzdrvs" | "tzcovs" => + if (player.Faction != PlanetSideEmpire.VS) { + enqueueNewActivity(ActivityQueuedTask( + SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@reset_sanctuary_locked")), 20) + ) //You have been returned to the sanctuary because the location you logged out is not available. + player.Zone = Zone.Nowhere + } + } } else if (ourBuildings.isEmpty && (amsSpawnPoints.isEmpty || noFriendlyPlayersInZone)) { enqueueNewActivity(ActivityQueuedTask( SpawnOperations.sendEventMessage(ChatMsg(ChatMessageType.CMT_QUIT, "@reset_sanctuary_locked")), 20) @@ -2230,6 +2331,12 @@ class ZoningOperations( sessionLogic.persist = UpdatePersistenceAndRefs tplayer.avatar = avatar session = session.copy(player = tplayer) + if (id.startsWith("tz")) { + // have to set the deployable counts as if the player has all the certs so they can deploy CE in VR zones without having the certs + player.avatar.deployables.UpdateMaxCounts(GlobalDefinitions.vrZoneTempEngineeringCerts()) + } else { + player.avatar.deployables.UpdateMaxCounts(player.avatar.certifications) + } //LoadMapMessage causes the client to send BeginZoningMessage, eventually leading to SetCurrentAvatar //val weaponsEnabled = !(mapName.equals("map11") || mapName.equals("map12") || mapName.equals("map13")) sendResponse(LoadMapMessage(mapName, id, 40100, 25, zone.LiveFireAllowed(tplayer.Faction), map.checksum)) diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index c5b198fb1..70e950f7e 100644 --- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -1,6 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.objects +import net.psforever.objects.avatar.Certification import net.psforever.objects.ballistics._ import net.psforever.objects.ce.DeployedItem import net.psforever.objects.definition._ @@ -17,7 +18,7 @@ import net.psforever.objects.serverobject.painbox.PainboxDefinition import net.psforever.objects.serverobject.terminals._ import net.psforever.objects.serverobject.tube.SpawnTubeDefinition import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition -import net.psforever.objects.serverobject.structures.{AmenityDefinition, BuildingDefinition, WarpGateDefinition} +import net.psforever.objects.serverobject.structures.{AmenityDefinition, BuildingDefinition, VirtualTrainingTeleporterDefinition, WarpGateDefinition} import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalDefinition import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMechDefinition import net.psforever.objects.serverobject.turret.FacilityTurretDefinition @@ -1280,6 +1281,10 @@ object GlobalDefinitions { val obbasemesh: AmenityDefinition = new AmenityDefinition(598) {} + val spawn_pad = new VirtualTrainingTeleporterDefinition() + + val spawn_zone = new VirtualTrainingTeleporterDefinition() + val targeting_laser_dispenser = new OrderTerminalDefinition(851) val stationaryteleportpad = new GenericTeleportationDefinition(836) @@ -1376,6 +1381,10 @@ object GlobalDefinitions { val orbital_building_vs = new BuildingDefinition(607) + val vr_training = new BuildingDefinition(955) + + val vt_air_vehicle = new BuildingDefinition(976) + val VT_building_nc = new BuildingDefinition(978) val VT_building_tr = new BuildingDefinition(979) @@ -2060,6 +2069,19 @@ object GlobalDefinitions { } } + /** + * Engineering certifications to temporarily apply to the avatar in VR Training zones. + * @return the set of certifications to apply. + */ + def vrZoneTempEngineeringCerts(): Set[Certification] = { + Set[Certification]( + Certification.AdvancedEngineering, + Certification.AdvancedHacking, + Certification.CombatEngineering, + Certification.GroundSupport + ) + } + GlobalDefinitionsImplant.init() GlobalDefinitionsExoSuit.init() GlobalDefinitionsKit.init() diff --git a/src/main/scala/net/psforever/objects/Players.scala b/src/main/scala/net/psforever/objects/Players.scala index 55fa08123..720a84728 100644 --- a/src/main/scala/net/psforever/objects/Players.scala +++ b/src/main/scala/net/psforever/objects/Players.scala @@ -163,6 +163,8 @@ object Players { ExoSuitDefinition.Select(exosuit, player.Faction).Permissions match { case Nil => true + case permissions if player.IsInVRZone => + true case permissions if subtype != 0 => val certs = player.avatar.certifications certs.intersect(permissions.toSet).nonEmpty && diff --git a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsBuilding.scala b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsBuilding.scala index ca18a2e0a..4c5d4c346 100644 --- a/src/main/scala/net/psforever/objects/global/GlobalDefinitionsBuilding.scala +++ b/src/main/scala/net/psforever/objects/global/GlobalDefinitionsBuilding.scala @@ -160,7 +160,11 @@ object GlobalDefinitionsBuilding { orbital_building_tr.Name = "orbital_building_tr" orbital_building_vs.Name = "orbital_building_vs" - + + vr_training.Name = "vr_training" + + vt_air_vehicle.Name = "vt_air_vehicle" + VT_building_nc.Name = "VT_building_nc" VT_building_tr.Name = "VT_building_tr" diff --git a/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala b/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala index 4373dd9da..494deb6ac 100644 --- a/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala +++ b/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala @@ -45,6 +45,8 @@ abstract class PlanetSideServerObject actor = ActorRef.noSender out } + + def IsInVRZone: Boolean = Zone.id.startsWith("tz") } object PlanetSideServerObject { diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/VirtualTrainingTeleporter.scala b/src/main/scala/net/psforever/objects/serverobject/structures/VirtualTrainingTeleporter.scala new file mode 100644 index 000000000..eb99dd23f --- /dev/null +++ b/src/main/scala/net/psforever/objects/serverobject/structures/VirtualTrainingTeleporter.scala @@ -0,0 +1,42 @@ +// Copyright (c) 2026 PSForever +package net.psforever.objects.serverobject.structures + +import akka.actor.ActorContext +import net.psforever.objects.GlobalDefinitions +import net.psforever.objects.serverobject.PlanetSideServerObject +import net.psforever.types.{PlanetSideEmpire, Vector3} + +class VirtualTrainingTeleporter extends PlanetSideServerObject { + def Definition: VirtualTrainingTeleporterDefinition = GlobalDefinitions.spawn_zone + + def Faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL +} + +object VirtualTrainingTeleporter { + + /** + * Overloaded constructor. + * @return the `VirtualTrainingTeleporter` object + */ + def apply(): VirtualTrainingTeleporter = { + new VirtualTrainingTeleporter() + } + + /** + * Instantiate an configure a `VirtualTrainingTeleporter` object + * @param id the unique id that will be assigned to this entity + * @param context a context to allow the object to properly set up `ActorSystem` functionality; + * not necessary for this object, but required by signature + * @return the `VirtualTrainingTeleporter` object + */ + def Constructor(id: Int, context: ActorContext): VirtualTrainingTeleporter = { + val obj = VirtualTrainingTeleporter() + obj + } + + def Constructor(pos: Vector3)(id: Int, context: ActorContext): VirtualTrainingTeleporter = { + val obj = VirtualTrainingTeleporter() + obj.Position = pos + obj + } +} diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/VirtualTrainingTeleporterDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/structures/VirtualTrainingTeleporterDefinition.scala new file mode 100644 index 000000000..f45d6a616 --- /dev/null +++ b/src/main/scala/net/psforever/objects/serverobject/structures/VirtualTrainingTeleporterDefinition.scala @@ -0,0 +1,11 @@ +// Copyright (c) 2026 PSForever +package net.psforever.objects.serverobject.structures + +import net.psforever.objects.definition.ObjectDefinition + +/** + * Shared definition for spawn_pad and spawn_zone objects. + */ +class VirtualTrainingTeleporterDefinition extends ObjectDefinition(815) { + Name = "spawn_zone" +} diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/tabs/EquipmentPage.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/tabs/EquipmentPage.scala index bff66ee89..06b8048c3 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/tabs/EquipmentPage.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/tabs/EquipmentPage.scala @@ -17,7 +17,7 @@ final case class EquipmentPage(stock: Map[String, () => Equipment]) extends Scru stock.get(msg.item_name) match { case Some(item) => val createdItem = item() - if (!Exclude.exists(_.checkRule(player, msg, createdItem))) { + if (!Exclude.exists(_.checkRule(player, msg, createdItem)) || player.IsInVRZone) { Terminal.BuyEquipment(createdItem) } else { Terminal.NoDeal() diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/tabs/VehiclePage.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/tabs/VehiclePage.scala index 3f9716d7c..8c553b051 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/tabs/VehiclePage.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/tabs/VehiclePage.scala @@ -24,7 +24,7 @@ final case class VehiclePage(stock: Map[String, () => Vehicle], trunk: Map[Strin stock.get(msg.item_name) match { case Some(vehicle) => val createdVehicle = vehicle() - if(!Exclude.exists(_.checkRule(player, msg, createdVehicle))) { + if(!Exclude.exists(_.checkRule(player, msg, createdVehicle)) || player.IsInVRZone) { val (weapons, inventory) = trunk.get(msg.item_name) match { case Some(loadout: VehicleLoadout) => ( diff --git a/src/main/scala/net/psforever/objects/zones/MapInfo.scala b/src/main/scala/net/psforever/objects/zones/MapInfo.scala index 29a31bb76..7100d1f14 100644 --- a/src/main/scala/net/psforever/objects/zones/MapInfo.scala +++ b/src/main/scala/net/psforever/objects/zones/MapInfo.scala @@ -309,21 +309,21 @@ case object MapInfo extends StringEnum[MapInfo] { case object Map14 extends MapInfo( value = "map14", - checksum = 0L, + checksum = 4276645952L, scale = MapScale.Dim1024, hotSpotSpan = 0, - environment = List(SeaLevel(EnvironmentAttribute.Water, 0)) ++ + environment = List(SeaLevel(EnvironmentAttribute.Water, 10)) ++ MapEnvironment.dim1024MapEdgeKillPlanes ) case object Map15 extends MapInfo( value = "map15", - checksum = 0L, - scale = MapScale.Dim8192, + checksum = 3628825458L, + scale = MapScale.Dim4096, hotSpotSpan = 0, - environment = List(SeaLevel(EnvironmentAttribute.Water, 0)) ++ - MapEnvironment.dim8192MapEdgeKillPlanes + environment = List(SeaLevel(EnvironmentAttribute.Water, 8)) ++ + MapEnvironment.dim4096MapEdgeKillPlanes ) case object Map16 diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala index 1caf87d72..05a871fa2 100644 --- a/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -1871,7 +1871,13 @@ object Zone { val allAffectedTargets = pssos.filter { target => testTargetsFromZone(source, target, radius) } //inform remaining targets that they have suffered damage allAffectedTargets - .foreach { target => target.Actor ! Vitality.Damage(createInteraction(source, target).calculate()) } + .foreach { target => + if (zone.id.startsWith("tz") && source.Faction == target.Faction) { + //do not perform friendly-fire in VR zones + } else { + target.Actor ! Vitality.Damage(createInteraction(source, target).calculate()) + } + } allAffectedTargets } diff --git a/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala b/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala index 1be70996c..481cd014b 100644 --- a/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala +++ b/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala @@ -422,6 +422,8 @@ object ObjectClass { final val order_terminalb = 614 final val portable_ammo_terminal = 684 final val portable_order_terminal = 690 + final val spawn_pad = 800 + final val spawn_zone = 815 final val targeting_laser_dispenser = 851 final val teleportpad_terminal = 853 diff --git a/src/main/scala/net/psforever/services/properties/PropertyOverrideManager.scala b/src/main/scala/net/psforever/services/properties/PropertyOverrideManager.scala index de26388d3..9af42b969 100644 --- a/src/main/scala/net/psforever/services/properties/PropertyOverrideManager.scala +++ b/src/main/scala/net/psforever/services/properties/PropertyOverrideManager.scala @@ -30,7 +30,11 @@ class PropertyOverrideManager extends Actor { } private def LoadOverridesFromFile(zoneId: Int): Unit = { - val zoneOverrides = LoadFile(s"overrides/game_objects$zoneId.adb.lst") + val zoneOverrides = if (zoneId > 13 && zoneId < 23) { + LoadFile(s"overrides/game_objectstz.adb.lst") + } else { + LoadFile(s"overrides/game_objects$zoneId.adb.lst") + } if (zoneOverrides == null) { log.debug(s"PropertyOverride: no overrides found for zone $zoneId using filename game_objects$zoneId.adb.lst") return diff --git a/src/main/scala/net/psforever/util/PointOfInterest.scala b/src/main/scala/net/psforever/util/PointOfInterest.scala index ae841fe5e..4ea787398 100644 --- a/src/main/scala/net/psforever/util/PointOfInterest.scala +++ b/src/main/scala/net/psforever/util/PointOfInterest.scala @@ -492,19 +492,19 @@ object PointOfInterest { "gate3" -> Vector3(4079, 2467, 155) ) zones("home3").locations += "hart_c" -> Vector3(3675, 2727, 91) - zones("tzshtr").locations += "roof" -> Vector3(499, 1568, 25) + zones("tzshtr").locations += "roof" -> Vector3(499, 544, 25) zones("tzcotr").locations += "spawn" -> Vector3(960, 1002, 32) zones("tzdrtr").locations ++= Map( "start" -> Vector3(2457, 1864, 23), "air_pad" -> Vector3(1700, 1900, 32) ) - zones("tzshvs").locations += "roof" -> Vector3(499, 1568, 25) + zones("tzshvs").locations += "roof" -> Vector3(499, 544, 25) zones("tzcovs").locations += "spawn" -> Vector3(960, 1002, 32) zones("tzdrvs").locations ++= Map( "start" -> Vector3(2457, 1864, 23), "air_pad" -> Vector3(1700, 1900, 32) ) - zones("tzshnc").locations += "roof" -> Vector3(499, 1568, 25) + zones("tzshnc").locations += "roof" -> Vector3(499, 544, 25) zones("tzconc").locations += "spawn" -> Vector3(960, 1002, 32) zones("tzdrnc").locations ++= Map( "start" -> Vector3(2457, 1864, 23), diff --git a/src/main/scala/net/psforever/zones/Zones.scala b/src/main/scala/net/psforever/zones/Zones.scala index 4ac2da6a6..9417603de 100644 --- a/src/main/scala/net/psforever/zones/Zones.scala +++ b/src/main/scala/net/psforever/zones/Zones.scala @@ -19,7 +19,7 @@ import net.psforever.objects.serverobject.pad.{VehicleSpawnPad, VehicleSpawnPadD import net.psforever.objects.serverobject.painbox.{Painbox, PainboxDefinition} import net.psforever.objects.serverobject.resourcesilo.ResourceSilo import net.psforever.objects.serverobject.shuttle.OrbitalShuttlePad -import net.psforever.objects.serverobject.structures.{Building, BuildingDefinition, FoundationBuilder, StructureType, WarpGate} +import net.psforever.objects.serverobject.structures.{Building, BuildingDefinition, FoundationBuilder, StructureType, VirtualTrainingTeleporter, WarpGate} import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalDefinition} import net.psforever.objects.serverobject.terminals.implant.{ImplantTerminalInterface, ImplantTerminalMech} import net.psforever.objects.serverobject.tube.SpawnTube @@ -120,6 +120,8 @@ object Zones { "orbital_building_vs", "orbital_building_tr", "orbital_building_nc", + "vr_training", + "vt_air_vehicle", "VT_building_vs", "VT_building_tr", "VT_building_nc", @@ -708,6 +710,10 @@ object Zones { ) zoneMap.linkShuttleToBay(obj.guid) + case "spawn_pad" | "spawn_zone" => + zoneMap + .addLocalObject(obj.guid, VirtualTrainingTeleporter.Constructor(obj.position)) + case _ => () } @@ -765,10 +771,12 @@ object Zones { this.HotSpotCoordinateFunction = Zones.HotSpots.standardRemapping(info.map.scale, info.map.hotSpotSpan, info.map.hotSpotSpan) this.HotSpotTimeFunction = Zones.HotSpots.standardTimeRules Zones.initZoneAmenities(zone = this) + } else { + Zones.initZoneAmenities(zone = this) } //special conditions - //1. sanctuaries are completely owned by a single faction + //1. sanctuaries and VR training zones are completely owned by a single faction //2. set up the third warp gate on sanctuaries to be a broadcast warp gate //3. set up sanctuary-linked warp gates on "home continents" (the names make no sense anymore, don't even ask) //4. assign the caverns internally @@ -818,6 +826,12 @@ object Zones { bldgs.filter(_.Name.startsWith("WG_")).map { case gate: WarpGate => gate.AllowBroadcastFor = PlanetSideEmpire.VS } + case "tzshtr" | "tzdrtr" | "tzcotr" => + bldgs.foreach(_.Faction = PlanetSideEmpire.TR) + case "tzshnc" | "tzdrnc" | "tzconc" => + bldgs.foreach(_.Faction = PlanetSideEmpire.NC) + case "tzshvs" | "tzdrvs" | "tzcovs" => + bldgs.foreach(_.Faction = PlanetSideEmpire.VS) case "i4" => bldgs.find(_.Name.equals("Map96_Gate_Three")).map { case gate: WarpGate => gate.Active = false