Initial implementation of VR Training (#1355)
Some checks failed
Publish Docs / docs (push) Has been cancelled
Publish Docker Image / docker (push) Has been cancelled
Test / test (push) Has been cancelled

This commit is contained in:
Subsonic154 2026-04-07 22:29:09 -04:00 committed by GitHub
parent 0c5b479f0f
commit 575f5fd7fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 1580 additions and 27 deletions

View file

@ -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

View file

@ -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
}
]

View file

@ -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
}
]

View file

@ -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,

View file

@ -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")
}

View file

@ -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())

View file

@ -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 _ => ()
}

View file

@ -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
)

View file

@ -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))

View file

@ -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()

View file

@ -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 &&

View file

@ -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"

View file

@ -45,6 +45,8 @@ abstract class PlanetSideServerObject
actor = ActorRef.noSender
out
}
def IsInVRZone: Boolean = Zone.id.startsWith("tz")
}
object PlanetSideServerObject {

View file

@ -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
}
}

View file

@ -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"
}

View file

@ -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()

View file

@ -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) =>
(

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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),

View file

@ -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