Merge pull request #678 from Mazo/llu-2021

LLUs. 🤯
This commit is contained in:
Mazo 2021-04-02 17:20:20 +01:00 committed by GitHub
commit 9ce60e7eff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 1074 additions and 1338 deletions

View file

@ -8540,19 +8540,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 1219,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1097,
"AbsX": 3096.68848,
"AbsY": 3181.219,
"AbsZ": 81.5115,
"Yaw": 0.0,
"GUID": 676,
"MapID": null,
"IsChildObject": true
},
{
"Id": 796,
"ObjectName": "llm_socket",
@ -8566,45 +8553,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 1077,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 945,
"AbsX": 3709.01025,
"AbsY": 2115.38623,
"AbsZ": 57.0175171,
"Yaw": 0.0,
"GUID": 678,
"MapID": null,
"IsChildObject": true
},
{
"Id": 394,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 262,
"AbsX": 3807.348,
"AbsY": 5458.795,
"AbsZ": 38.2669373,
"Yaw": 0.0,
"GUID": 679,
"MapID": null,
"IsChildObject": true
},
{
"Id": 515,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 414,
"AbsX": 4271.57471,
"AbsY": 4504.82471,
"AbsZ": 73.70659,
"Yaw": 0.0,
"GUID": 680,
"MapID": null,
"IsChildObject": true
},
{
"Id": 936,
"ObjectName": "llm_socket",
@ -8618,32 +8566,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 124,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 4,
"AbsX": 4503.59473,
"AbsY": 6168.89941,
"AbsZ": 52.4856033,
"Yaw": 0.0,
"GUID": 682,
"MapID": null,
"IsChildObject": true
},
{
"Id": 667,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 535,
"AbsX": 4602.546,
"AbsY": 3341.289,
"AbsZ": 37.3147659,
"Yaw": 0.0,
"GUID": 683,
"MapID": null,
"IsChildObject": true
},
{
"Id": 242,
"ObjectName": "llm_socket",

View file

@ -10256,32 +10256,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 2157,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 2056,
"AbsX": 1896.58,
"AbsY": 2983.99878,
"AbsZ": 52.99063,
"Yaw": 0.0,
"GUID": 812,
"MapID": null,
"IsChildObject": true
},
{
"Id": 972,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 852,
"AbsX": 2434.74072,
"AbsY": 3695.22168,
"AbsZ": 35.8926926,
"Yaw": 0.0,
"GUID": 813,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1211,
"ObjectName": "llm_socket",
@ -10308,58 +10282,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 551,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 419,
"AbsX": 3499.93652,
"AbsY": 4014.81616,
"AbsZ": 13.9436874,
"Yaw": 0.0,
"GUID": 816,
"MapID": null,
"IsChildObject": true
},
{
"Id": 843,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 723,
"AbsX": 3972.74072,
"AbsY": 5945.22168,
"AbsZ": 33.9761276,
"Yaw": 0.0,
"GUID": 817,
"MapID": null,
"IsChildObject": true
},
{
"Id": 105,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 4,
"AbsX": 4488.001,
"AbsY": 3474.57983,
"AbsZ": 42.84017,
"Yaw": 0.0,
"GUID": 818,
"MapID": null,
"IsChildObject": true
},
{
"Id": 703,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 571,
"AbsX": 4520.795,
"AbsY": 2592.652,
"AbsZ": 14.5540581,
"Yaw": 0.0,
"GUID": 819,
"MapID": null,
"IsChildObject": true
},
{
"Id": 247,
"ObjectName": "llm_socket",
@ -10373,32 +10295,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 1351,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1231,
"AbsX": 5602.91162,
"AbsY": 2873.561,
"AbsZ": 31.6628265,
"Yaw": 0.0,
"GUID": 821,
"MapID": null,
"IsChildObject": true
},
{
"Id": 399,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 267,
"AbsX": 6707.348,
"AbsY": 2332.795,
"AbsZ": 113.330261,
"Yaw": 0.0,
"GUID": 822,
"MapID": null,
"IsChildObject": true
},
{
"Id": 2148,
"ObjectName": "lock_external",

View file

@ -15508,32 +15508,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 781,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 661,
"AbsX": 579.2592,
"AbsY": 7032.77832,
"AbsZ": 54.5858879,
"Yaw": 0.0,
"GUID": 1234,
"MapID": null,
"IsChildObject": true
},
{
"Id": 2289,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 2157,
"AbsX": 621.848267,
"AbsY": 2375.897,
"AbsZ": 38.1845856,
"Yaw": 0.0,
"GUID": 1235,
"MapID": null,
"IsChildObject": true
},
{
"Id": 109,
"ObjectName": "llm_socket",
@ -15547,19 +15521,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 230,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 129,
"AbsX": 1193.9021,
"AbsY": 4565.57031,
"AbsZ": 72.3534,
"Yaw": 0.0,
"GUID": 1237,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1302,
"ObjectName": "llm_socket",
@ -15573,32 +15534,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 1063,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 943,
"AbsX": 2755.58032,
"AbsY": 1433.88428,
"AbsZ": 58.6747551,
"Yaw": 0.0,
"GUID": 1239,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1173,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1072,
"AbsX": 2962.00122,
"AbsY": 2320.57983,
"AbsZ": 72.81362,
"Yaw": 0.0,
"GUID": 1240,
"MapID": null,
"IsChildObject": true
},
{
"Id": 2008,
"ObjectName": "llm_socket",
@ -15612,71 +15547,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 383,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 251,
"AbsX": 4202.795,
"AbsY": 6984.652,
"AbsZ": 38.1436462,
"Yaw": 0.0,
"GUID": 1242,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1856,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1734,
"AbsX": 4988.68848,
"AbsY": 4439.21875,
"AbsZ": 60.90174,
"Yaw": 0.0,
"GUID": 1243,
"MapID": null,
"IsChildObject": true
},
{
"Id": 652,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 532,
"AbsX": 5045.78662,
"AbsY": 5840.68945,
"AbsZ": 51.4351578,
"Yaw": 0.0,
"GUID": 1244,
"MapID": null,
"IsChildObject": true
},
{
"Id": 922,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 790,
"AbsX": 5172.795,
"AbsY": 3356.652,
"AbsZ": 32.1784973,
"Yaw": 0.0,
"GUID": 1245,
"MapID": null,
"IsChildObject": true
},
{
"Id": 512,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 403,
"AbsX": 5638.105,
"AbsY": 6598.934,
"AbsZ": 57.89732,
"Yaw": 0.0,
"GUID": 1246,
"MapID": null,
"IsChildObject": true
},
{
"Id": 2137,
"ObjectName": "llm_socket",
@ -15703,32 +15573,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 1443,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1323,
"AbsX": 7037.47266,
"AbsY": 1186.01941,
"AbsZ": 51.6652679,
"Yaw": 0.0,
"GUID": 1249,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1561,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1452,
"AbsX": 7331.6543,
"AbsY": 3069.78076,
"AbsZ": 69.8796158,
"Yaw": 0.0,
"GUID": 1250,
"MapID": null,
"IsChildObject": true
},
{
"Id": 760,
"ObjectName": "lock_external",

View file

@ -12076,58 +12076,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 266,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 146,
"AbsX": 928.198,
"AbsY": 5564.63672,
"AbsZ": 67.040596,
"Yaw": 0.0,
"GUID": 954,
"MapID": null,
"IsChildObject": true
},
{
"Id": 407,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 275,
"AbsX": 1742.43945,
"AbsY": 5716.78564,
"AbsZ": 23.85953,
"Yaw": 0.0,
"GUID": 955,
"MapID": null,
"IsChildObject": true
},
{
"Id": 126,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 4,
"AbsX": 2746.68848,
"AbsY": 4419.21875,
"AbsZ": 46.284523,
"Yaw": 0.0,
"GUID": 956,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1359,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1239,
"AbsX": 3070.05664,
"AbsY": 2282.41748,
"AbsZ": 65.20178,
"Yaw": 0.0,
"GUID": 957,
"MapID": null,
"IsChildObject": true
},
{
"Id": 536,
"ObjectName": "llm_socket",
@ -12154,32 +12102,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 1469,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1368,
"AbsX": 3986.8877,
"AbsY": 2494.16553,
"AbsZ": 94.4665756,
"Yaw": 0.0,
"GUID": 960,
"MapID": null,
"IsChildObject": true
},
{
"Id": 676,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 556,
"AbsX": 4336.439,
"AbsY": 5866.91162,
"AbsZ": 72.2181549,
"Yaw": 0.0,
"GUID": 961,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1621,
"ObjectName": "llm_socket",
@ -12193,45 +12115,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 938,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 837,
"AbsX": 4818.013,
"AbsY": 5254.69775,
"AbsZ": 80.30203,
"Yaw": 0.0,
"GUID": 963,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1078,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 958,
"AbsX": 6638.752,
"AbsY": 4596.15527,
"AbsZ": 40.2804756,
"Yaw": 0.0,
"GUID": 964,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1219,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1087,
"AbsX": 6998.91943,
"AbsY": 5326.044,
"AbsZ": 21.9767818,
"Yaw": 0.0,
"GUID": 965,
"MapID": null,
"IsChildObject": true
},
{
"Id": 245,
"ObjectName": "lock_external",

View file

@ -8969,32 +8969,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 1219,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1110,
"AbsX": 2700.56641,
"AbsY": 2938.72949,
"AbsZ": 60.2481575,
"Yaw": 0.0,
"GUID": 711,
"MapID": null,
"IsChildObject": true
},
{
"Id": 959,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 839,
"AbsX": 3376.60767,
"AbsY": 2457.89429,
"AbsZ": 50.8711739,
"Yaw": 0.0,
"GUID": 712,
"MapID": null,
"IsChildObject": true
},
{
"Id": 136,
"ObjectName": "llm_socket",
@ -9008,45 +8982,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 1090,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 968,
"AbsX": 3664.68848,
"AbsY": 4789.21875,
"AbsZ": 64.9002762,
"Yaw": 0.0,
"GUID": 714,
"MapID": null,
"IsChildObject": true
},
{
"Id": 288,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 156,
"AbsX": 4365.159,
"AbsY": 4282.131,
"AbsZ": 60.087677,
"Yaw": 0.0,
"GUID": 715,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1817,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1716,
"AbsX": 4649.79736,
"AbsY": 2642.05566,
"AbsZ": 70.51741,
"Yaw": 0.0,
"GUID": 716,
"MapID": null,
"IsChildObject": true
},
{
"Id": 819,
"ObjectName": "llm_socket",
@ -9060,32 +8995,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 580,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 460,
"AbsX": 5467.6167,
"AbsY": 3842.79272,
"AbsZ": 55.66716,
"Yaw": 0.0,
"GUID": 718,
"MapID": null,
"IsChildObject": true
},
{
"Id": 690,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 589,
"AbsX": 5904.2207,
"AbsY": 4439.24854,
"AbsZ": 69.2569962,
"Yaw": 0.0,
"GUID": 719,
"MapID": null,
"IsChildObject": true
},
{
"Id": 440,
"ObjectName": "llm_socket",

View file

@ -9346,45 +9346,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 1063,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 931,
"AbsX": 2961.79932,
"AbsY": 2185.03418,
"AbsZ": 221.338272,
"Yaw": 0.0,
"GUID": 738,
"MapID": null,
"IsChildObject": true
},
{
"Id": 391,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 271,
"AbsX": 3254.05664,
"AbsY": 5804.41748,
"AbsZ": 229.117279,
"Yaw": 0.0,
"GUID": 739,
"MapID": null,
"IsChildObject": true
},
{
"Id": 641,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 521,
"AbsX": 3631.02051,
"AbsY": 3791.069,
"AbsZ": 217.111908,
"Yaw": 0.0,
"GUID": 740,
"MapID": null,
"IsChildObject": true
},
{
"Id": 501,
"ObjectName": "llm_socket",
@ -9411,32 +9372,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 251,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 129,
"AbsX": 3996.68848,
"AbsY": 4335.21875,
"AbsZ": 273.429138,
"Yaw": 0.0,
"GUID": 743,
"MapID": null,
"IsChildObject": true
},
{
"Id": 109,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 0,
"AbsX": 4421.5625,
"AbsY": 3720.8457,
"AbsZ": 225.8448,
"Yaw": 0.0,
"GUID": 744,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1215,
"ObjectName": "llm_socket",
@ -9450,19 +9385,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 770,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 650,
"AbsX": 5926.77832,
"AbsY": 3396.74072,
"AbsZ": 91.09625,
"Yaw": 0.0,
"GUID": 746,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1048,
"ObjectName": "lock_external",

View file

@ -11608,19 +11608,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 653,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 533,
"AbsX": 910.7408,
"AbsY": 2667.22168,
"AbsZ": 68.7458649,
"Yaw": 0.0,
"GUID": 920,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1454,
"ObjectName": "llm_socket",
@ -11634,71 +11621,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 1575,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1474,
"AbsX": 2006.28674,
"AbsY": 5523.80127,
"AbsZ": 82.80721,
"Yaw": 0.0,
"GUID": 922,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1727,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1595,
"AbsX": 2346.10083,
"AbsY": 1945.51868,
"AbsZ": 69.6338654,
"Yaw": 0.0,
"GUID": 923,
"MapID": null,
"IsChildObject": true
},
{
"Id": 513,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 404,
"AbsX": 2915.07471,
"AbsY": 3768.05469,
"AbsZ": 62.5202637,
"Yaw": 0.0,
"GUID": 924,
"MapID": null,
"IsChildObject": true
},
{
"Id": 126,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 4,
"AbsX": 3224.68848,
"AbsY": 7367.21875,
"AbsZ": 84.523674,
"Yaw": 0.0,
"GUID": 925,
"MapID": null,
"IsChildObject": true
},
{
"Id": 395,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 275,
"AbsX": 3324.40527,
"AbsY": 4578.996,
"AbsZ": 69.99102,
"Yaw": 0.0,
"GUID": 926,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1302,
"ObjectName": "llm_socket",
@ -11712,32 +11634,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 266,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 146,
"AbsX": 3992.53369,
"AbsY": 6048.867,
"AbsZ": 54.7470245,
"Yaw": 0.0,
"GUID": 928,
"MapID": null,
"IsChildObject": true
},
{
"Id": 900,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 791,
"AbsX": 4019.30933,
"AbsY": 1613.07593,
"AbsZ": 75.31056,
"Yaw": 0.0,
"GUID": 929,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1052,
"ObjectName": "llm_socket",
@ -11751,32 +11647,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 782,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 662,
"AbsX": 5400.74072,
"AbsY": 2541.22168,
"AbsZ": 48.0176964,
"Yaw": 0.0,
"GUID": 931,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1181,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1072,
"AbsX": 6832.797,
"AbsY": 4254.491,
"AbsZ": 53.264,
"Yaw": 0.0,
"GUID": 932,
"MapID": null,
"IsChildObject": true
},
{
"Id": 632,
"ObjectName": "lock_external",

View file

@ -8683,32 +8683,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 645,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 525,
"AbsX": 2226.74072,
"AbsY": 3381.22168,
"AbsZ": 47.1299973,
"Yaw": 0.0,
"GUID": 687,
"MapID": null,
"IsChildObject": true
},
{
"Id": 763,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 654,
"AbsX": 2532.84058,
"AbsY": 4528.31738,
"AbsZ": 50.48315,
"Yaw": 0.0,
"GUID": 688,
"MapID": null,
"IsChildObject": true
},
{
"Id": 915,
"ObjectName": "llm_socket",
@ -8735,32 +8709,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 376,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 275,
"AbsX": 3904.129,
"AbsY": 4695.489,
"AbsZ": 63.15471,
"Yaw": 0.0,
"GUID": 691,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1219,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1087,
"AbsX": 3958.74463,
"AbsY": 2196.716,
"AbsZ": 45.3599777,
"Yaw": 0.0,
"GUID": 692,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1067,
"ObjectName": "llm_socket",
@ -8774,32 +8722,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 266,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 146,
"AbsX": 4748.437,
"AbsY": 5469.24854,
"AbsZ": 48.33355,
"Yaw": 0.0,
"GUID": 694,
"MapID": null,
"IsChildObject": true
},
{
"Id": 516,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 396,
"AbsX": 5497.22266,
"AbsY": 3957.127,
"AbsZ": 45.2092972,
"Yaw": 0.0,
"GUID": 695,
"MapID": null,
"IsChildObject": true
},
{
"Id": 624,
"ObjectName": "lock_external",

View file

@ -12297,84 +12297,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 1760,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1640,
"AbsX": 1224.89941,
"AbsY": 4202.40527,
"AbsZ": 54.66005,
"Yaw": 0.0,
"GUID": 975,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1870,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1769,
"AbsX": 1742.0011,
"AbsY": 5306.58,
"AbsZ": 77.51988,
"Yaw": 0.0,
"GUID": 976,
"MapID": null,
"IsChildObject": true
},
{
"Id": 731,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 611,
"AbsX": 3749.88428,
"AbsY": 5510.41943,
"AbsZ": 230.786942,
"Yaw": 0.0,
"GUID": 977,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1478,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1369,
"AbsX": 3756.67651,
"AbsY": 2149.56226,
"AbsZ": 66.70892,
"Yaw": 0.0,
"GUID": 978,
"MapID": null,
"IsChildObject": true
},
{
"Id": 287,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 155,
"AbsX": 3765.75122,
"AbsY": 2659.72437,
"AbsZ": 135.187622,
"Yaw": 0.0,
"GUID": 979,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1110,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 990,
"AbsX": 4018.3623,
"AbsY": 4194.70264,
"AbsZ": 199.684,
"Yaw": 0.0,
"GUID": 980,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1349,
"ObjectName": "llm_socket",
@ -12388,19 +12310,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 439,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 307,
"AbsX": 4610.795,
"AbsY": 5718.652,
"AbsZ": 174.196381,
"Yaw": 0.0,
"GUID": 982,
"MapID": null,
"IsChildObject": true
},
{
"Id": 841,
"ObjectName": "llm_socket",
@ -12414,19 +12323,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 1220,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1119,
"AbsX": 4890.966,
"AbsY": 4531.238,
"AbsZ": 222.527054,
"Yaw": 0.0,
"GUID": 984,
"MapID": null,
"IsChildObject": true
},
{
"Id": 135,
"ObjectName": "llm_socket",
@ -12440,45 +12336,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 970,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 861,
"AbsX": 5289.592,
"AbsY": 5012.38135,
"AbsZ": 246.0418,
"Yaw": 0.0,
"GUID": 986,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1620,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1498,
"AbsX": 5819.02344,
"AbsY": 2305.157,
"AbsZ": 97.94419,
"Yaw": 0.0,
"GUID": 987,
"MapID": null,
"IsChildObject": true
},
{
"Id": 591,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 459,
"AbsX": 6428.63965,
"AbsY": 5190.393,
"AbsZ": 39.42817,
"Yaw": 0.0,
"GUID": 988,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1739,
"ObjectName": "lock_external",

View file

@ -10932,45 +10932,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 528,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 396,
"AbsX": 2734.795,
"AbsY": 2374.652,
"AbsZ": 41.2234421,
"Yaw": 0.0,
"GUID": 866,
"MapID": null,
"IsChildObject": true
},
{
"Id": 961,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 829,
"AbsX": 3390.795,
"AbsY": 5754.652,
"AbsZ": 32.0256653,
"Yaw": 0.0,
"GUID": 867,
"MapID": null,
"IsChildObject": true
},
{
"Id": 247,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 146,
"AbsX": 3507.72119,
"AbsY": 2540.61621,
"AbsZ": 62.4884148,
"Yaw": 0.0,
"GUID": 868,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1082,
"ObjectName": "llm_socket",
@ -10997,58 +10958,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 1351,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1231,
"AbsX": 4675.94336,
"AbsY": 6243.58252,
"AbsZ": 64.11465,
"Yaw": 0.0,
"GUID": 871,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1211,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1102,
"AbsX": 4686.31738,
"AbsY": 5569.15967,
"AbsZ": 78.99428,
"Yaw": 0.0,
"GUID": 872,
"MapID": null,
"IsChildObject": true
},
{
"Id": 2403,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 2294,
"AbsX": 4902.087,
"AbsY": 3510.83838,
"AbsZ": 54.2062263,
"Yaw": 0.0,
"GUID": 873,
"MapID": null,
"IsChildObject": true
},
{
"Id": 680,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 548,
"AbsX": 5534.78564,
"AbsY": 2273.56055,
"AbsZ": 46.44301,
"Yaw": 0.0,
"GUID": 874,
"MapID": null,
"IsChildObject": true
},
{
"Id": 820,
"ObjectName": "llm_socket",
@ -11062,32 +10971,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 126,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 4,
"AbsX": 6312.68848,
"AbsY": 5329.21875,
"AbsZ": 68.8674545,
"Yaw": 0.0,
"GUID": 876,
"MapID": null,
"IsChildObject": true
},
{
"Id": 1492,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 1360,
"AbsX": 6643.15137,
"AbsY": 4448.6543,
"AbsZ": 40.43171,
"Yaw": 0.0,
"GUID": 877,
"MapID": null,
"IsChildObject": true
},
{
"Id": 513,
"ObjectName": "lock_external",

View file

@ -1169,19 +1169,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 122,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 0,
"AbsX": 1974.68848,
"AbsY": 2035.21887,
"AbsZ": 42.69806,
"Yaw": 0.0,
"GUID": 93,
"MapID": null,
"IsChildObject": true
},
{
"Id": 109,
"ObjectName": "lock_external",

View file

@ -3782,45 +3782,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 449,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 317,
"AbsX": 1739.221,
"AbsY": 1776.55054,
"AbsZ": 74.7683,
"Yaw": 0.0,
"GUID": 298,
"MapID": null,
"IsChildObject": true
},
{
"Id": 299,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 198,
"AbsX": 1839.926,
"AbsY": 2669.99561,
"AbsZ": 105.1724,
"Yaw": 0.0,
"GUID": 299,
"MapID": null,
"IsChildObject": true
},
{
"Id": 831,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 722,
"AbsX": 2672.99951,
"AbsY": 2277.26343,
"AbsZ": 97.5842,
"Yaw": 0.0,
"GUID": 300,
"MapID": null,
"IsChildObject": true
},
{
"Id": 434,
"ObjectName": "lock_external",

View file

@ -3249,45 +3249,6 @@
"MapID": null,
"IsChildObject": true
},
{
"Id": 693,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 561,
"AbsX": 1273.68921,
"AbsY": 2544.30444,
"AbsZ": 72.1122742,
"Yaw": 0.0,
"GUID": 257,
"MapID": null,
"IsChildObject": true
},
{
"Id": 246,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 145,
"AbsX": 1496.34106,
"AbsY": 1233.22192,
"AbsZ": 103.060211,
"Yaw": 0.0,
"GUID": 258,
"MapID": null,
"IsChildObject": true
},
{
"Id": 127,
"ObjectName": "llm_socket",
"ObjectType": "llm_socket",
"Owner": 18,
"AbsX": 2862.687,
"AbsY": 2759.684,
"AbsZ": 95.47642,
"Yaw": 0.0,
"GUID": 259,
"MapID": null,
"IsChildObject": true
},
{
"Id": 678,
"ObjectName": "lock_external",

View file

@ -404,6 +404,8 @@ class ChatActor(
case ("nc", pos) => Some(PlanetSideEmpire.NC, pos)
case ("vs", pos) => Some(PlanetSideEmpire.VS, pos)
case ("none", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
case ("bo", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
case ("neutral", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
case _ => None
}
.headOption match {

View file

@ -1,21 +1,11 @@
package net.psforever.actors.session
import akka.actor.typed
import akka.actor.typed.receptionist.Receptionist
import akka.actor.typed.scaladsl.adapter._
import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware}
import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware, typed}
import akka.pattern.ask
import akka.util.Timeout
import net.psforever.actors.net.MiddlewareActor
import net.psforever.services.ServiceManager.Lookup
import net.psforever.objects.locker.LockerContainer
import org.log4s.MDC
import scala.collection.mutable
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Success
import net.psforever.login.WorldSession._
import net.psforever.objects._
import net.psforever.objects.avatar._
@ -27,7 +17,7 @@ import net.psforever.objects.entity.{SimpleWorldEntity, WorldEntity}
import net.psforever.objects.equipment._
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
import net.psforever.objects.inventory.{Container, InventoryItem}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.locker.LockerContainer
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.containable.Containable
import net.psforever.objects.serverobject.damage.Damageable
@ -35,6 +25,7 @@ import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.generator.Generator
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.llu.CaptureFlag
import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.mblocker.Locker
import net.psforever.objects.serverobject.mount.Mountable
@ -47,9 +38,10 @@ import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret}
import net.psforever.objects.serverobject.zipline.ZipLinePath
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.teamwork.Squad
import net.psforever.objects.vehicles._
import net.psforever.objects.vehicles.Utility.InternalTelepad
import net.psforever.objects.vehicles._
import net.psforever.objects.vital._
import net.psforever.objects.vital.base._
import net.psforever.objects.vital.interaction.DamageInteraction
@ -57,14 +49,14 @@ import net.psforever.objects.vital.projectile.ProjectileReason
import net.psforever.objects.zones.{Zone, ZoneHotSpotProjector, Zoning}
import net.psforever.packet._
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
import net.psforever.packet.game.{HotSpotInfo => PacketHotSpotInfo, _}
import net.psforever.packet.game.objectcreate._
import net.psforever.services.ServiceManager.LookupResult
import net.psforever.packet.game.{HotSpotInfo => PacketHotSpotInfo, _}
import net.psforever.services.ServiceManager.{Lookup, LookupResult}
import net.psforever.services.account.{AccountPersistenceService, PlayerToken, ReceiveAccountData, RetrieveAccountData}
import net.psforever.services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse}
import net.psforever.services.chat.ChatService
import net.psforever.services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage, GalaxyServiceResponse}
import net.psforever.services.local.support.{HackCaptureActor, RouterTelepadActivation}
import net.psforever.services.local.support.{CaptureFlagManager, HackCaptureActor, RouterTelepadActivation}
import net.psforever.services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse}
import net.psforever.services.properties.PropertyOverrideManager
import net.psforever.services.support.SupportActor
@ -75,6 +67,13 @@ import net.psforever.services.{RemoverActor, Service, ServiceManager, Interstell
import net.psforever.types._
import net.psforever.util.{Config, DefinitionUtil}
import net.psforever.zones.Zones
import org.log4s.MDC
import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
import scala.util.Success
object SessionActor {
sealed trait Command
@ -203,6 +202,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
var setupAvatarFunc: () => Unit = AvatarCreate
var setCurrentAvatarFunc: Player => Unit = SetCurrentAvatarNormally
var persist: () => Unit = NoPersistence
var specialItemSlotGuid : Option[PlanetSideGUID] = None // If a special item (e.g. LLU) has been attached to the player the GUID should be stored here, or cleared when dropped, since the drop hotkey doesn't send the GUID of the object to be dropped.
/**
* used during zone transfers to maintain reference to seated vehicle (which does not yet exist in the new zone)
@ -555,6 +555,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case GalaxyResponse.MapUpdate(msg) =>
sendResponse(msg)
case GalaxyResponse.FlagMapUpdate(msg) =>
sendResponse(msg)
case GalaxyResponse.TransferPassenger(temp_channel, vehicle, vehicle_to_delete, manifest) =>
(manifest.passengers.find { case (name, _) => player.Name.equals(name) } match {
case Some((name, index)) if vehicle.Seats(index).occupant.isEmpty =>
@ -968,6 +971,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
CancelZoningProcess()
PlayerActionsToCancel()
CancelAllProximityUnits()
DropSpecialSlotItem()
continent.Population ! Zone.Population.Release(avatar)
response match {
case Some((zone, spawnPoint)) =>
@ -1802,6 +1806,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
CancelZoningProcessWithDescriptiveReason("cancel_dmg")
}
case AvatarResponse.DropSpecialItem() =>
DropSpecialSlotItem()
case AvatarResponse.Killed(mount) =>
val respawnTimer = 300.seconds
//drop free hand item
@ -1810,25 +1817,32 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
DropEquipmentFromInventory(player)(item)
case None => ;
}
DropSpecialSlotItem()
ToggleMaxSpecialState(enable = false)
keepAliveFunc = NormalKeepAlive
zoningStatus = Zoning.Status.None
deadState = DeadState.Dead
continent.GUID(mount) match {
case Some(obj: Vehicle) =>
TotalDriverVehicleControl(obj)
UnaccessContainer(obj)
case _ => ;
}
PlayerActionsToCancel()
CancelAllProximityUnits()
CancelZoningProcessWithDescriptiveReason("cancel")
if (shotsWhileDead > 0) {
log.warn(
s"KillPlayer/SHOTS_WHILE_DEAD: client of ${avatar.name} fired $shotsWhileDead rounds while character was dead on server"
)
shotsWhileDead = 0
}
reviveTimer.cancel()
if (player.death_by == 0) {
reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer) {
@ -2133,6 +2147,28 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
}
}
def DropSpecialSlotItem(): Unit = {
specialItemSlotGuid match {
case Some(guid: PlanetSideGUID) =>
specialItemSlotGuid = None
continent.GUID(guid) match {
case Some(llu: CaptureFlag) =>
llu.Carrier match {
case Some(carrier: Player) if carrier.GUID == player.GUID =>
continent.LocalEvents ! CaptureFlagManager.DropFlag(llu)
case Some(carrier: Player) =>
log.warn(s"${player.toString} tried to drop LLU, but it is currently held by ${carrier.toString}")
case None =>
log.warn(s"${player.toString} tried to drop LLU, but nobody is holding it.")
}
case _ =>
log.warn(s"${player.toString} Tried to drop a special item that wasn't recognized. GUID: $guid")
}
case _ => ; // Nothing to drop, do nothing.
}
}
/**
* Enforce constraints on bulk purchases as determined by a given player's previous purchase times and hard acquisition delays.
* Intended to assist in sanitizing loadout information from the perspective of the player, or target owner.
@ -2290,6 +2326,40 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case LocalResponse.SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value) =>
SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value)
case LocalResponse.SendGenericObjectActionMessage(target_guid, action_number) =>
sendResponse(GenericObjectActionMessage(target_guid, action_number))
case LocalResponse.SendGenericActionMessage(action_number) =>
sendResponse(GenericActionMessage(action_number))
case LocalResponse.SendChatMsg(msg) =>
sendResponse(msg)
case LocalResponse.SendPacket(packet) =>
sendResponse(packet)
case LocalResponse.LluSpawned(llu) =>
// Create LLU on client
sendResponse(ObjectCreateMessage(
llu.Definition.ObjectId,
llu.GUID,
llu.Definition.Packet.ConstructorData(llu).get
))
sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk = 20, 0.8000001f))
case LocalResponse.LluDespawned(llu) =>
sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, llu.Position, unk = 20, 0.8000001f))
sendResponse(ObjectDeleteMessage(llu.GUID, 0))
// If the player was holding the LLU, remove it from their tracked special item slot
specialItemSlotGuid match {
case Some(guid) =>
if (guid == llu.GUID) {
specialItemSlotGuid = None
}
case _ => ;
}
case LocalResponse.ObjectDelete(object_guid, unk) =>
if (tplayer_guid != guid) {
sendResponse(ObjectDeleteMessage(object_guid, unk))
@ -4144,7 +4214,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case None =>
log.warn(s"DropItem: ${player.Name} wanted to drop a $anItem, but it wasn't at hand")
}
case Some(obj) => //TODO LLU
case Some(obj) =>
log.warn(s"DropItem: ${player.Name} wanted to drop a $obj, but that isn't possible")
case None =>
sendResponse(ObjectDeleteMessage(item_guid, 0)) //this is fine; item doesn't exist to the server anyway
@ -4719,7 +4789,16 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case Some(item) =>
CancelZoningProcessWithDescriptiveReason("cancel_use")
captureTerminal.Actor ! CommonMessages.Use(player, Some(item))
case _ => ;
case _ if specialItemSlotGuid.nonEmpty =>
continent.GUID(specialItemSlotGuid) match {
case Some(llu: CaptureFlag) =>
if (llu.Target.GUID == captureTerminal.Owner.GUID) {
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.LluCaptured(llu))
} else {
log.info(s"LLU target is not this base. Target GUID: ${llu.Target.GUID} This base: ${captureTerminal.Owner.GUID}")
}
case _ => log.warn("Item in specialItemSlotGuid is not registered with continent or is not a LLU")
}
}
case Some(obj: FacilityTurret) =>
@ -4945,6 +5024,19 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case None => ;
}
case Some(obj: CaptureFlag) =>
// LLU can normally only be picked up the faction that owns it
if (specialItemSlotGuid.isEmpty) {
if(obj.Faction == player.Faction) {
specialItemSlotGuid = Some(obj.GUID)
continent.LocalEvents ! CaptureFlagManager.PickupFlag(obj, player)
} else {
log.warn(s"Player ${player.toString} tried to pick up LLU ${obj.GUID} - ${obj.Faction} that doesn't belong to their faction")
}
} else if(specialItemSlotGuid.get != obj.GUID) { // Ignore duplicate pickup requests
log.warn(s"Player ${player.toString} tried to pick up LLU ${obj.GUID} - ${obj.Faction} but their special slot already contains $specialItemSlotGuid")
}
case Some(obj) =>
CancelZoningProcessWithDescriptiveReason("cancel_use")
log.warn(s"UseItem: ${player.Name} does not know how to handle $obj")
@ -5048,6 +5140,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
log.info(s"${player.Name} is back")
player.AwayFromKeyboard = false
}
if (action == GenericActionEnum.DropSpecialItem.id) {
DropSpecialSlotItem()
}
if (action == 15) { //max deployment
log.info(s"${player.Name} has anchored ${player.Sex.pronounObject}self to the ground")
player.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
@ -6715,11 +6810,13 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
//sync model access state
sendResponse(PlanetsideAttributeMessage(amenityId, 50, 0))
sendResponse(PlanetsideAttributeMessage(amenityId, 51, 0))
//sync damageable, if
val health = amenity.Health
if (amenity.Definition.Damageable && health < amenity.MaxHealth) {
sendResponse(PlanetsideAttributeMessage(amenityId, 0, health))
}
//sync special object type cases
amenity match {
case silo: ResourceSilo =>
@ -6733,19 +6830,30 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case door: Door if door.isOpen =>
sendResponse(GenericObjectStateMsg(amenityId, 16))
case _ => ;
}
//sync hack state
amenity match {
case obj: Hackable if obj.HackedBy.nonEmpty =>
//sync hack state
amenity.Definition match {
case GlobalDefinitions.capture_terminal =>
SendPlanetsideAttributeMessage(
amenity.GUID,
PlanetsideAttributeEnum.ControlConsoleHackUpdate,
HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false))
case _ =>
HackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object
case GlobalDefinitions.capture_terminal =>
SendPlanetsideAttributeMessage(
amenity.GUID,
PlanetsideAttributeEnum.ControlConsoleHackUpdate,
HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false))
case _ =>
HackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object
}
// sync capture flags
case llu: CaptureFlag =>
// Create LLU
sendResponse(ObjectCreateMessage(
llu.Definition.ObjectId,
llu.GUID,
llu.Definition.Packet.ConstructorData(llu).get
))
// Attach it to a player if it has a carrier
if (llu.Carrier.nonEmpty) {
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.SendPacket(ObjectAttachMessage(llu.Carrier.get.GUID, llu.GUID, 252)))
}
case _ => ;
}

View file

@ -13,11 +13,11 @@ import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, Ca
import net.psforever.objects.zones.Zone
import net.psforever.persistence
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState}
import net.psforever.util.Database._
import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.services.{InterstellarClusterService, Service, ServiceManager}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState}
import net.psforever.util.Database._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
@ -197,8 +197,8 @@ class BuildingActor(
case AmenityStateChange(gen: Generator, data) =>
if (generatorStateChange(gen, data)) {
//update the map
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
// Request all buildings update their map data to refresh lattice linked benefits
zone.actor ! ZoneActor.ZoneMapUpdate()
}
Behaviors.same
@ -364,7 +364,7 @@ class BuildingActor(
}
building.Faction = faction
alignForceDomeStatus(mapUpdateOnChange = false)
galaxy ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage()))
zone.actor ! ZoneActor.ZoneMapUpdate() // Update entire lattice to show lattice benefits
zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SetEmpire(building.GUID, faction))
}
}

View file

@ -33,6 +33,7 @@ import net.psforever.objects.vital.prop.DamageWithPosition
import net.psforever.objects.vital.{ComplexDeployableResolutions, MaxResolutions, SimpleResolutions}
import net.psforever.types.{ExoSuitType, ImplantType, PlanetSideEmpire, Vector3}
import net.psforever.types._
import net.psforever.objects.serverobject.llu.{CaptureFlagDefinition, CaptureFlagSocketDefinition}
import scala.collection.mutable
import scala.concurrent.duration._
@ -1069,6 +1070,11 @@ object GlobalDefinitions {
val vanu_control_console = new CaptureTerminalDefinition(930) // Cavern CC
val llm_socket = new CaptureFlagSocketDefinition()
val capture_flag = new CaptureFlagDefinition()
capture_flag.Packet = new CaptureFlagConverter
val lodestar_repair_terminal = new MedicalTerminalDefinition(461)
val multivehicle_rearm_terminal = new OrderTerminalDefinition(576)

View file

@ -22,7 +22,7 @@ object ObjectType extends Enumeration {
val BoomerTrigger = "boomer_trigger"
val Building = "building"
val CaptureFlag = "capture_flag"
val CaptureFlagSocket = "capture_flag_socket"
val CaptureFlagSocket = "llm_socket"
val CaptureTerminal = "capture_terminal"
val CertTerminal = "cert_terminal"
val ChainLashDamager = "chain_lash_damager"

View file

@ -710,12 +710,18 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
super.CancelJammeredStatus(target)
//uninitialize implants
avatarActor ! AvatarActor.DeinitializeImplants()
cause.adversarial match {
case Some(a) =>
damageLog.info(s"DisplayDestroy: ${a.defender} was killed by ${a.attacker}")
case _ =>
damageLog.info(s"DisplayDestroy: ${player.Name} killed ${player.Sex.pronounObject}self.")
}
// This would normally happen async as part of AvatarAction.Killed, but if it doesn't happen before deleting calling AvatarAction.ObjectDelete on the player the LLU will end up invisible to others if carried
// Therefore, queue it up to happen first.
events ! AvatarServiceMessage(nameChannel, AvatarAction.DropSpecialItem())
events ! AvatarServiceMessage(
nameChannel,
AvatarAction.Killed(player_guid, target.VehicleSeated)

View file

@ -0,0 +1,25 @@
package net.psforever.objects.definition.converter
import net.psforever.objects.serverobject.llu.CaptureFlag
import net.psforever.objects.serverobject.structures.Building
import net.psforever.packet.game.objectcreate.{CaptureFlagData, PlacementData}
import java.util.concurrent.TimeUnit
import scala.util.{Success, Try}
class CaptureFlagConverter extends ObjectCreateConverter[CaptureFlag]() {
override def ConstructorData(obj : CaptureFlag) : Try[CaptureFlagData] = {
val hackInfo = obj.Owner.asInstanceOf[Building].CaptureTerminal.get.HackedBy.get
val millisecondsRemaining = TimeUnit.MILLISECONDS.convert(math.max(0, hackInfo.hackStartTime + hackInfo.hackDuration - System.nanoTime), TimeUnit.NANOSECONDS)
Success(
CaptureFlagData(
new PlacementData(obj.Position, obj.Orientation, obj.Velocity),
obj.Faction,
obj.Owner.asInstanceOf[Building].GUID.guid,
obj.Target.GUID.guid,
millisecondsRemaining
)
)
}
}

View file

@ -0,0 +1,59 @@
package net.psforever.objects.serverobject.llu
import net.psforever.objects.serverobject.structures.{Amenity, AmenityOwner, Building}
import net.psforever.objects.{GlobalDefinitions, Player}
import net.psforever.types.{PlanetSideEmpire, Vector3}
/**
* This object represents the LLU that gets spawned at a LLU socket when a LLU control console is hacked
*/
class CaptureFlag(tDef: CaptureFlagDefinition) extends Amenity {
def Definition : CaptureFlagDefinition = tDef
private var target: Building = Building.NoBuilding
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var carrier: Option[Player] = None
def Target: Building = target
def Target_=(new_target: Building): Building = {
target = new_target
target
}
// Since a LLU belongs to a base, but needs to be picked up by the enemy faction we need to be able to override the faction that owns the LLU to the hacker faction
override def Faction: PlanetSideEmpire.Value = faction
override def Faction_=(new_faction: PlanetSideEmpire.Value): PlanetSideEmpire.Value = {
faction = new_faction
faction
}
// When the flag is carried by a player, the position returned should be that of the carrier not the flag
override def Position: Vector3 = if (Carrier.nonEmpty) {
carrier.get.Position
} else {
Entity.Position
}
def Carrier: Option[Player] = carrier
def Carrier_=(new_carrier: Option[Player]) : Option[Player] = {
carrier = new_carrier
carrier
}
}
object CaptureFlag {
def apply(tDef: CaptureFlagDefinition): CaptureFlag = {
new CaptureFlag(tDef)
}
def Constructor(pos: Vector3, ori: Vector3, target: Building, owner: AmenityOwner, faction: PlanetSideEmpire.Value) : CaptureFlag = {
val obj = CaptureFlag(GlobalDefinitions.capture_flag)
obj.Position = pos
obj.Orientation = ori
obj.Target = target
obj.Owner = owner
obj.Faction = faction
obj
}
}

View file

@ -0,0 +1,11 @@
package net.psforever.objects.serverobject.llu
import net.psforever.objects.serverobject.structures.AmenityDefinition
/**
* The definition for any 'CaptureFlag'
* Object Id 157
*/
class CaptureFlagDefinition extends AmenityDefinition(157) {
Name = "capture_flag"
}

View file

@ -0,0 +1,32 @@
package net.psforever.objects.serverobject.llu
import akka.actor.ActorContext
import net.psforever.objects.GlobalDefinitions
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.types.Vector3
/**
* Represents the LLU sockets found within bases that require LLU hacks.
* It is used as a position reference for spawning the LLU in the correct location when the base is hacked
* @param tDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/
class CaptureFlagSocket(tDef: CaptureFlagSocketDefinition) extends Amenity {
def Definition : CaptureFlagSocketDefinition = tDef
}
object CaptureFlagSocket {
def apply(tDef: CaptureFlagSocketDefinition) : CaptureFlagSocket = {
new CaptureFlagSocket(tDef)
}
def Constructor(pos: Vector3)(id: Int, context: ActorContext) : CaptureFlagSocket = {
Constructor(GlobalDefinitions.llm_socket, pos)(id, context)
}
def Constructor(tdef: CaptureFlagSocketDefinition, pos: Vector3)(id: Int, context: ActorContext) : CaptureFlagSocket = {
val obj = CaptureFlagSocket(tdef)
obj.Position = pos
obj
}
}

View file

@ -0,0 +1,11 @@
package net.psforever.objects.serverobject.llu
import net.psforever.objects.serverobject.structures.AmenityDefinition
/**
* The definition for any 'CaptureFlagSocket'
* Object Id 450
*/
class CaptureFlagSocketDefinition extends AmenityDefinition(450) {
Name = "llm_socket"
}

View file

@ -18,4 +18,9 @@ abstract class AmenityOwner extends PlanetSideServerObject {
obj.Owner = this
amenities
}
def RemoveAmenity(obj: Amenity): List[Amenity] = {
amenities = amenities.filterNot(x => x == obj)
amenities
}
}

View file

@ -16,6 +16,7 @@ import net.psforever.packet.game.BuildingInfoUpdateMessage
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState, Vector3}
import scalax.collection.{Graph, GraphEdge}
import akka.actor.typed.scaladsl.adapter._
import net.psforever.objects.serverobject.llu.{CaptureFlag, CaptureFlagSocket}
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
class Building(
@ -27,10 +28,6 @@ class Building(
private val buildingDefinition: BuildingDefinition
) extends AmenityOwner {
/**
* The map_id is the identifier number used in BuildingInfoUpdateMessage. This is the index that the building appears in the MPO file starting from index 1
* The GUID is the identifier number used in SetEmpireMessage / Facility hacking / PlanetSideAttributeMessage.
*/
private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var playersInSOI: List[Player] = List.empty
private val capitols = List("Thoth", "Voltan", "Neit", "Anguta", "Eisa", "Verica")
@ -43,6 +40,10 @@ class Building(
def Name: String = name
/**
* The map_id is the identifier number used in BuildingInfoUpdateMessage. This is the index that the building appears in the MPO file starting from index 1
* The GUID is the identifier number used in SetEmpireMessage / Facility hacking / PlanetSideAttributeMessage.
*/
def MapId: Int = map_id
def IsCapitol: Boolean = capitols.contains(name)
@ -122,6 +123,14 @@ class Building(
}
}
def IsCtfBase: Boolean = GetFlagSocket match {
case Some(_) => true
case _ => false
}
def GetFlagSocket: Option[CaptureFlagSocket] = this.Amenities.find(_.Definition == GlobalDefinitions.llm_socket).asInstanceOf[Option[CaptureFlagSocket]]
def GetFlag: Option[CaptureFlag] = this.Amenities.find(_.Definition == GlobalDefinitions.capture_flag).asInstanceOf[Option[CaptureFlag]]
def HackableAmenities: List[Amenity with Hackable] = {
Amenities.filter(x => x.isInstanceOf[Hackable]).map(x => x.asInstanceOf[Amenity with Hackable])
}
@ -200,9 +209,11 @@ class Building(
// Check this Building is on the lattice first
zone.Lattice find this match {
case Some(_) =>
// todo: generator destruction state
val subGraph = Zone.Lattice filter ((b: Building) =>
b.Faction == this.Faction && !b.CaptureTerminalIsHacked && b.NtuLevel > 0
b.Faction == this.Faction
&& !b.CaptureTerminalIsHacked
&& b.NtuLevel > 0
&& (b.Generator.isEmpty || b.Generator.get.Condition != PlanetSideGeneratorState.Destroyed)
)
var stackedBenefit = 0
@ -226,13 +237,13 @@ class Building(
hackingFaction,
hackTime,
if (ntuLevel > 0) Faction else PlanetSideEmpire.NEUTRAL,
0, // Field != 0 will cause malformed packet
None,
0, // unk1 Field != 0 will cause malformed packet
None, // unk1x
generatorState,
spawnTubesNormal,
forceDomeActive,
latticeBenefit,
48, // cavern benefit
if (generatorState != PlanetSideGeneratorState.Destroyed) latticeBenefit else 0,
if (generatorState != PlanetSideGeneratorState.Destroyed) 48 else 0, // cavern benefit
Nil, // unk4,
0, // unk5
false, // unk6

View file

@ -531,7 +531,7 @@ object GamePacketOpcode extends Enumeration {
case 0xbf => game.ZipLineMessage.decode
// OPCODES 0xc0-cf
case 0xc0 => noDecoder(CaptureFlagUpdateMessage)
case 0xc0 => game.CaptureFlagUpdateMessage.decode
case 0xc1 => noDecoder(VanuModuleUpdateMessage)
case 0xc2 => game.FacilityBenefitShieldChargeRequestMessage.decode
case 0xc3 => game.ProximityTerminalUseMessage.decode

View file

@ -0,0 +1,57 @@
package net.psforever.packet.game
import net.psforever.newcodecs.newcodecs
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import scodec.Codec
import scodec.codecs._
import shapeless.{::, HNil}
/**
*
* @param zone_number The zone number this packet applies to
* @param flagInfoList The list of LLUs/Monolith units for this zone
*/
final case class CaptureFlagUpdateMessage(zone_number: Int, flagInfoList: List[FlagInfo]) extends PlanetSideGamePacket {
type Packet = CaptureFlagUpdateMessage
def opcode = GamePacketOpcode.CaptureFlagUpdateMessage
def encode = CaptureFlagUpdateMessage.encode(this)
}
/**
*
* @param u1 No effect. Faction ID perhaps?
* @param owner_map_id The mapID of the base the LLU belongs to
* @param target_map_id The mapID of the base the LLU must be delivered to
* @param x X map position
* @param y Y map position
* @param hack_time_remaining Time remaining on hack - will override BuildingInfoUpdateMessage when displaying hack timer on map base details
* @param is_monolith_unit Changes the icon on the map to the monolith unit icon
*/
final case class FlagInfo(u1: Int, owner_map_id: Int, target_map_id: Int, x: Float, y: Float, hack_time_remaining: Long, is_monolith_unit: Boolean)
object FlagInfo extends Marshallable[FlagInfo] {
implicit val codec: Codec[FlagInfo] = {
(("u1" | uint2L)
:: ("owner_map_id" | uint16L)
:: ("target_map_id" | uint16L)
:: ("u4" | newcodecs.q_float(0.0, 8192.0, 20))
:: ("u5" | newcodecs.q_float(0.0, 8192.0, 20))
:: ("hack_time_remaining" | uint32L)
:: ("is_monolith_unit" | bool))
}.as[FlagInfo]
}
object CaptureFlagUpdateMessage extends Marshallable[CaptureFlagUpdateMessage] {
implicit val codec: Codec[CaptureFlagUpdateMessage] = (
("zone_number" | uint16L)
:: ("flagInfoList" | PacketHelpers.listOfNAligned(longL(4), alignment = 0, FlagInfo.codec)) // Maximum of 7 on any map
).xmap[CaptureFlagUpdateMessage] (
{
case zone_number :: flagInfoList :: HNil =>
CaptureFlagUpdateMessage(zone_number, flagInfoList)
},
{
case CaptureFlagUpdateMessage(zone_number, flagInfoList) =>
zone_number :: flagInfoList :: HNil
}
)
}

View file

@ -19,8 +19,11 @@ import scodec.codecs._
* 07 - warning: missile lock<br>
* 08 - warning: Wasp missile lock<br>
* 09 - warning: T-REK lock<br>
* 11 - Drop special item e.g. LLU<br>
* 12 - sound: base captured fanfare<br>
* 14 - prompt: new character basic training<br>
* 15 - MAX Deploy<br>
* 16 - MAX Undeploy<br>
* 22 - message: awarded a cavern capture (updates cavern capture status)<br>
* 23 - award a cavern kill<br>
* 24 - message: you have been imprinted (updates imprinted status; does it?)<br>
@ -54,7 +57,21 @@ final case class GenericActionMessage(action: Int) extends PlanetSideGamePacket
}
object GenericActionMessage extends Marshallable[GenericActionMessage] {
def apply(action: GenericActionEnum.GenericActionEnum): GenericActionMessage = {
GenericActionMessage(action.id)
}
implicit val codec: Codec[GenericActionMessage] = (
"action" | uint(6)
).as[GenericActionMessage]
}
object GenericActionEnum extends Enumeration {
type GenericActionEnum = Value
/** Drop special item e.g. LLU */
val DropSpecialItem = Value(11)
/** Plays the base capture fanfare sound */
val BaseCaptureFanfare = Value(12)
}

View file

@ -11,36 +11,38 @@ import shapeless.{::, HNil}
/**
* Dispatched by the server to enact an effect on some game object.
* (Write more some other time.)
* @param object_guid the target object
* @param code the action code (0-63)
* 6 - Deconstructs player
* 7 - Start imprinting process (progress bar + character animation)
* 8 - Finish imprinting?
* 9 - Cloak
* 10 - Uncloak
* 11 - Deploy capital base shield pole with animation and broadcasts "The capitol force dome at X has been activated"
* 12 - Stow capital base shield pole with animation and broadcasts "The capitol force dome at X has been deactivated"
* 13 - Deploy capital base shield pole (instantly, unless still in the middle of the stow animation)
* 15 - Displays "This facility's generator is under attack!"
* 16 - Displays "Generator has Overloaded! Evacuate Generator Room Immediately!"
* 17 - Displays "This facility's generator is back on line"
* 19 - Cause mines to explode
* 20 - Hit flinch? (orig, 82->80)
* 21 - Reset build cooldown from using an ACE
* 22 - ???? (Has been seen on vehicle pad objects, possibly some sort of reset flag after base faction flip / hack clear?)
* 23 - Plays vehicle pad animation moving downwards
* 24 - Makes the vehicle bounce slightly. Have seen this in packet captures after taking a vehicle through a warpgate
* 27 - Activates the router internal telepad for linking
* 28 - Activates the router internal telepad for linking
* 29 - Activates the telepad deployable (also used on the router's internal telepad)
* 30 - Activates the telepad deployable (also used on the router's internal telepad)
* 31 - Animation during router teleportation (source)
* 32 - Animation during router teleportation (destination)
* 34 - Time until item can be used ?????
* 50 - For aircraft - client shows "The bailing mechanism failed! To fix the mechanism, land and repair the vehicle!"
* 53 - Put down an FDU
* 56 - Sets vehicle or player to be black ops
* 57 - Reverts player from black ops
* @param object_guid the target object<br/>
* @param code the action code (0-63)<br/>
* 6 - Deconstructs player<br/>
* 7 - Start imprinting process (progress bar + character animation)<br/>
* 8 - Finish imprinting?<br/>
* 9 - Cloak<br/>
* 10 - Uncloak<br/>
* 11 - Deploy capital base shield pole with animation and broadcasts "The capitol force dome at X has been activated"<br/>
* 12 - Stow capital base shield pole with animation and broadcasts "The capitol force dome at X has been deactivated"<br/>
* 13 - Deploy capital base shield pole (instantly, unless still in the middle of the stow animation)<br/>
* 14 - Changes capture console to say "Facility hacked by the <Faction> LLU has been spawned." when looked at<br/>
* 15 - Displays "This facility's generator is under attack!"<br/>
* 16 - Displays "Generator has Overloaded! Evacuate Generator Room Immediately!"<br/>
* 17 - Displays "This facility's generator is back on line"<br/>
* 19 - Cause mines to explode<br/>
* 20 - Hit flinch? (orig, 82->80)<br/>
* 21 - Reset build cooldown from using an ACE<br/>
* 22 - ???? (Has been seen on vehicle pad objects, possibly some sort of reset flag after base faction flip / hack clear?)<br/>
* 23 - Plays vehicle pad animation moving downwards<br/>
* 24 - Makes the vehicle bounce slightly. Have seen this in packet captures after taking a vehicle through a warpgate<br/>
* 27 - Activates the router internal telepad for linking<br/>
* 28 - Activates the router internal telepad for linking<br/>
* 29 - Activates the telepad deployable (also used on the router's internal telepad)<br/>
* 30 - Activates the telepad deployable (also used on the router's internal telepad)<br/>
* 31 - Animation during router teleportation (source)<br/>
* 32 - Animation during router teleportation (destination)<br/>
* 34 - Time until item can be used ?????<br/>
* 50 - For aircraft - client shows "The bailing mechanism failed! To fix the mechanism, land and repair the vehicle!"<br/>
* 53 - Put down an FDU<br/>
* 56 - Sets vehicle or player to be black ops<br/>
* 57 - Reverts player from black ops<br/>
* @see GenericObjectActionEnum
*/
final case class GenericObjectActionMessage(object_guid: PlanetSideGUID, code: Int) extends PlanetSideGamePacket {
type Packet = GenericObjectActionMessage
@ -49,6 +51,10 @@ final case class GenericObjectActionMessage(object_guid: PlanetSideGUID, code: I
}
object GenericObjectActionMessage extends Marshallable[GenericObjectActionMessage] {
def apply(object_guid: PlanetSideGUID, code: GenericObjectActionEnum.GenericObjectActionEnum): GenericObjectActionMessage = {
GenericObjectActionMessage(object_guid, code.id)
}
implicit val codec: Codec[GenericObjectActionMessage] = (
("object_guid" | PlanetSideGUID.codec) ::
("code" | uint(bits = 6)) ::
@ -64,3 +70,12 @@ object GenericObjectActionMessage extends Marshallable[GenericObjectActionMessag
}
)
}
object GenericObjectActionEnum extends Enumeration {
type GenericObjectActionEnum = Value
/** <b>Effect:</b> Capture console displays "Facility hacked by the <Faction> LLU has been spawned." when looked at<br>
* <b>Target</b>: CaptureTerminal
*/
val FlagSpawned = Value(14)
}

View file

@ -33,6 +33,7 @@ import scodec.codecs._
* `0x84` - 4 - knife holster<br>
* `0x86` - 6 - grid (1,1)<br>
* `0x00FA` - 250 - is a special dest/extra code that "attaches the item to the player's cursor"
* `0x00FC` - 252 - special item slot e.g. LLU (Possibly also cavern modules)
* @param parent_guid the parent object
* @param child_guid the child object
* @param slot a codified location within the parent object's inventory;

View file

@ -144,8 +144,8 @@ import scodec.codecs._
* `43 - Info on avatar name : 0 = Nothing, 1 = "(LD)" message`<br>
* `45 - NTU charge bar 0-10, 5 = 50% full. Seems to apply to both ANT and NTU Silo (possibly siphons?)`<br>
* `46 - Sends "Generator damage is at a critical level!" message`
* `47 - Sets base NTU level to CRITICAL. MUST use base MapId not base GUID`<br>
* `48 - Set to 1 to send base power loss message & turns on red warning lights throughout base. MUST use base MapId not base GUID`?<br>
* `47 - Sets base NTU level to CRITICAL.`<br>
* `48 - Set to 1 to send base power loss message & turns on red warning lights throughout base.<br>
* `49 - Vehicle texture effects state? (>0 turns on ANT panel glow or ntu silo panel glow + orbs) (bit?)`<br>
* `52 - Vehicle particle effects? (>0 turns on orbs going towards ANT. Doesn't affect silo) (bit?)`<br>
* `53 - LFS. Value is 1 to flag LFS`<br>
@ -161,7 +161,7 @@ import scodec.codecs._
* `55 - "Someone is attempting to Heal you". Value is 1`<br>
* `56 - "Someone is attempting to Repair you". Value is 1`<br>
* `64 - ????? related to using router telepads`
* `67 - Enables base shields (from cavern module/lock). MUST use base MapId not GUID`<br>
* `67 - Enables base shields (from cavern module/lock)`<br>
* `73 - "You are locked into the Core Beam. Charging your Module now.". Value is 1 to active`<br>
* `77 - Cavern Facility Captures. Value is the number of captures`<br>
* `78 - Cavern Kills. Value is the number of kills`<br>

View file

@ -16,19 +16,18 @@ import shapeless.{::, HNil}
* Whenever an applicable player is nearby, that client will rapidly fire off `ItemUseMessage` packets to the server.
* The capture flag will be picked-up by the player and stored in a special slot that is not part of their inventory.
* A special dropping keybind has been prepared to relinquish the capture flag back to the game world.
* @param pos the position + orientation + velocity of the LLU where it is dropped/spawned
* @param faction the empire whose players may interact with this capture flag
* @param unk1 na
* @param unk2 na
* @param unk3 na
* @param unk4 na
* @param owningBaseGuid The GUID of the base that this LLU belongs to
* @param targetBaseGuid The GUID of the base that this LLU must be taken to
* @param milliseconds_remaining The number of milliseconds left on the timer for this LLU - should match the CC timer
*/
final case class CaptureFlagData(
pos: PlacementData,
faction: PlanetSideEmpire.Value,
unk1: Int,
unk2: Int,
unk3: Int,
unk4: Int
owningBaseGuid : Int,
targetBaseGuid : Int,
milliseconds_remaining : Long
) extends ConstructorData {
override def bitsize: Long = 88L + pos.bitsize
}
@ -40,24 +39,23 @@ object CaptureFlagData extends Marshallable[CaptureFlagData] {
bool ::
uint4L ::
uint16L ::
("unk1" | uint8L) ::
("owningBaseGuid" | uint8L) ::
uint8L ::
("unk2" | uint8L) ::
("targetBaseGuid" | uint8L) ::
uint8L ::
("unk3" | uint16L) :: //probably a PlanetSideGUID
("unk4" | uint8L) ::
uint(9)
("milliseconds_remaining" | uint32L) ::
uint(1)
).exmap[CaptureFlagData](
{
case pos :: fac :: false :: 4 :: 0 :: unk1 :: 0 :: unk2 :: 0 :: unk3 :: unk4 :: 0 :: HNil =>
Attempt.Successful(CaptureFlagData(pos, fac, unk1, unk2, unk3, unk4))
case pos :: faction :: false :: 4 :: 0 :: owningBaseGuid :: 0 :: targetBaseGuid :: 0 :: milliseconds_remaining :: 0 :: HNil =>
Attempt.Successful(CaptureFlagData(pos, faction, owningBaseGuid, targetBaseGuid, milliseconds_remaining))
case data =>
Attempt.failure(Err(s"invalid capture flag data format - $data"))
},
{
case CaptureFlagData(pos, fac, unk1, unk2, unk3, unk4) =>
Attempt.successful(pos :: fac :: false :: 4 :: 0 :: unk1 :: 0 :: unk2 :: 0 :: unk3 :: unk4 :: 0 :: HNil)
case CaptureFlagData(pos, faction, owningBaseGuid, targetBaseGuid, milliseconds_remaining) =>
Attempt.successful(pos :: faction :: false :: 4 :: 0 :: owningBaseGuid :: 0 :: targetBaseGuid :: 0 :: milliseconds_remaining :: 0 :: HNil)
}
)
}

View file

@ -408,6 +408,9 @@ class AvatarService(zone: Zone) extends Actor {
)
)
case AvatarAction.DropSpecialItem() =>
AvatarEvents.publish(AvatarServiceResponse(s"/$forChannel/Avatar", Service.defaultPlayerGUID, AvatarResponse.DropSpecialItem()))
case _ => ;
}

View file

@ -149,6 +149,7 @@ object AvatarAction {
inventory: List[InventoryItem],
drop: List[InventoryItem]
) extends Action
final case class DropSpecialItem() extends Action
final case class TeardownConnection() extends Action
// final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action

View file

@ -117,6 +117,7 @@ object AvatarResponse {
inventory: List[InventoryItem],
drop: List[InventoryItem]
) extends Response
final case class DropSpecialItem() extends Response
final case class TeardownConnection() extends Response
// final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response

View file

@ -3,7 +3,7 @@ package net.psforever.services.galaxy
import akka.actor.Actor
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.BuildingInfoUpdateMessage
import net.psforever.packet.game.{BuildingInfoUpdateMessage, FlagInfo}
import net.psforever.services.{GenericEventBus, Service}
class GalaxyService extends Actor {
@ -41,6 +41,11 @@ class GalaxyService extends Actor {
GalaxyServiceResponse(s"/Galaxy", GalaxyResponse.MapUpdate(msg))
)
case GalaxyAction.FlagMapUpdate(msg) =>
GalaxyEvents.publish(
GalaxyServiceResponse(s"/Galaxy", GalaxyResponse.FlagMapUpdate(msg))
)
case GalaxyAction.TransferPassenger(_, temp_channel, vehicle, vehicle_to_delete, manifest) =>
GalaxyEvents.publish(
GalaxyServiceResponse(

View file

@ -3,7 +3,7 @@ package net.psforever.services.galaxy
import net.psforever.objects.Vehicle
import net.psforever.objects.vehicles.VehicleManifest
import net.psforever.packet.game.BuildingInfoUpdateMessage
import net.psforever.packet.game.{BuildingInfoUpdateMessage, CaptureFlagUpdateMessage, FlagInfo}
import net.psforever.types.PlanetSideGUID
final case class GalaxyServiceMessage(forChannel: String, actionMessage: GalaxyAction.Action)
@ -16,6 +16,7 @@ object GalaxyAction {
trait Action
final case class MapUpdate(msg: BuildingInfoUpdateMessage) extends Action
final case class FlagMapUpdate(msg: CaptureFlagUpdateMessage) extends Action
final case class TransferPassenger(
player_guid: PlanetSideGUID,

View file

@ -4,7 +4,7 @@ package net.psforever.services.galaxy
import net.psforever.objects.Vehicle
import net.psforever.objects.vehicles.VehicleManifest
import net.psforever.objects.zones.HotSpotInfo
import net.psforever.packet.game.BuildingInfoUpdateMessage
import net.psforever.packet.game.{BuildingInfoUpdateMessage, CaptureFlagUpdateMessage, FlagInfo}
import net.psforever.types.PlanetSideGUID
import net.psforever.services.GenericEventBusMsg
@ -16,6 +16,8 @@ object GalaxyResponse {
final case class HotSpotUpdate(zone_id: Int, priority: Int, host_spot_info: List[HotSpotInfo]) extends Response
final case class MapUpdate(msg: BuildingInfoUpdateMessage) extends Response
final case class FlagMapUpdate(msg: CaptureFlagUpdateMessage) extends Response
final case class TransferPassenger(
temp_channel: String,

View file

@ -2,27 +2,26 @@
package net.psforever.services.local
import akka.actor.{Actor, ActorRef, Props}
import net.psforever.objects._
import net.psforever.objects.ce.Deployable
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
import net.psforever.objects._
import net.psforever.packet.game.{TriggeredEffect, TriggeredEffectLocation}
import net.psforever.objects.vehicles.{Utility, UtilityType}
import net.psforever.objects.vital.Vitality
import net.psforever.types.{PlanetSideGUID, Vector3}
import net.psforever.services.local.support._
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{TriggeredEffect, TriggeredEffectLocation}
import net.psforever.services.local.support.{CaptureFlagManager, _}
import net.psforever.services.support.SupportActor
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import net.psforever.services.{GenericEventBus, RemoverActor, Service}
import net.psforever.types.{PlanetSideGUID, Vector3}
import scala.concurrent.duration._
import net.psforever.objects.vehicles.{Utility, UtilityType}
import net.psforever.services.support.SupportActor
import scala.concurrent.duration.Duration
import scala.concurrent.duration.{Duration, _}
class LocalService(zone: Zone) extends Actor {
private val doorCloser = context.actorOf(Props[DoorCloseActor](), s"${zone.id}-local-door-closer")
private val hackClearer = context.actorOf(Props[HackClearActor](), s"${zone.id}-local-hack-clearer")
private val hackCapturer = context.actorOf(Props[HackCaptureActor](), s"${zone.id}-local-hack-capturer")
private val hackCapturer = context.actorOf(Props(classOf[HackCaptureActor], zone.tasks), s"${zone.id}-local-hack-capturer")
private val captureFlagManager = context.actorOf(Props(classOf[CaptureFlagManager], zone.tasks, zone), s"${zone.id}-local-capture-flag-manager")
private val engineer = context.actorOf(Props(classOf[DeployableRemover], zone.tasks), s"${zone.id}-deployable-remover-agent")
private val teleportDeployment: ActorRef =
context.actorOf(Props[RouterTelepadActivation](), s"${zone.id}-telepad-activate-agent")
@ -93,10 +92,43 @@ class LocalService(zone: Zone) extends Actor {
)
case LocalAction.ClearTemporaryHack(_, target) =>
hackClearer ! HackClearActor.ObjectIsResecured(target)
case LocalAction.ResecureCaptureTerminal(target) =>
hackCapturer ! HackCaptureActor.ResecureCaptureTerminal(target, zone)
case LocalAction.StartCaptureTerminalHack(target) =>
hackCapturer ! HackCaptureActor.StartCaptureTerminalHack(target, zone, 0, 8L)
case LocalAction.LluCaptured(llu) =>
hackCapturer ! HackCaptureActor.FlagCaptured(llu)
case LocalAction.LluSpawned(player_guid, llu) =>
// Forward to all clients to create object locally
LocalEvents.publish(
LocalServiceResponse(
s"/$forChannel/Local",
player_guid,
LocalResponse.LluSpawned(llu)
)
)
case LocalAction.LluDespawned(player_guid, llu) =>
// Forward to all clients to destroy object locally
LocalEvents.publish(
LocalServiceResponse(
s"/$forChannel/Local",
player_guid,
LocalResponse.LluDespawned(llu)
)
)
case LocalAction.SendPacket(packet) =>
LocalEvents.publish(
LocalServiceResponse(
s"/$forChannel/Local",
PlanetSideGUID(-1),
LocalResponse.SendPacket(packet)
)
)
case LocalAction.SendPlanetsideAttributeMessage(player_guid, target_guid, attribute_number, attribute_value) =>
LocalEvents.publish(
LocalServiceResponse(
@ -105,6 +137,34 @@ class LocalService(zone: Zone) extends Actor {
LocalResponse.SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value)
)
)
case LocalAction.SendGenericObjectActionMessage(player_guid, target_guid, action_number) =>
LocalEvents.publish(
LocalServiceResponse(
s"/$forChannel/Local",
player_guid,
LocalResponse.SendGenericObjectActionMessage(target_guid, action_number)
)
)
case LocalAction.SendChatMsg(player_guid, msg) =>
LocalEvents.publish(
LocalServiceResponse(
s"/$forChannel/Local",
player_guid,
LocalResponse.SendChatMsg(msg)
)
)
case LocalAction.SendGenericActionMessage(player_guid, action_number) =>
LocalEvents.publish(
LocalServiceResponse(
s"/$forChannel/Local",
player_guid,
LocalResponse.SendGenericActionMessage(action_number)
)
)
case LocalAction.RouterTelepadTransport(player_guid, passenger_guid, src_guid, dest_guid) =>
LocalEvents.publish(
LocalServiceResponse(
@ -357,6 +417,16 @@ class LocalService(zone: Zone) extends Actor {
val cause = damage_func(target)
sender() ! Vitality.DamageResolution(target, cause)
// Forward all CaptureFlagManager messages
case msg @
(CaptureFlagManager.SpawnCaptureFlag(_, _, _)
| CaptureFlagManager.PickupFlag(_, _)
| CaptureFlagManager.DropFlag(_)
| CaptureFlagManager.Captured(_)
| CaptureFlagManager.Lost(_, _)
| CaptureFlagManager) =>
captureFlagManager.forward(msg)
case msg =>
log.warn(s"Unhandled message $msg from ${sender()}")
}

View file

@ -1,17 +1,20 @@
// Copyright (c) 2017 PSForever
package net.psforever.services.local
import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle}
import net.psforever.objects.ce.Deployable
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.llu.CaptureFlag
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
import net.psforever.objects.vehicles.Utility
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle}
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.{DeployableInfo, DeploymentAction, TriggeredSound}
import net.psforever.packet.game.GenericActionEnum.GenericActionEnum
import net.psforever.packet.game.GenericObjectActionEnum.GenericObjectActionEnum
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
import net.psforever.packet.game.{ChatMsg, DeployableInfo, DeploymentAction, TriggeredSound}
import net.psforever.services.hart.HartTimer.OrbitalShuttleEvent
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
@ -49,14 +52,37 @@ object LocalAction {
) extends Action
final case class ClearTemporaryHack(player_guid: PlanetSideGUID, target: PlanetSideServerObject with Hackable)
extends Action
final case class ResecureCaptureTerminal(target: CaptureTerminal) extends Action
final case class StartCaptureTerminalHack(target: CaptureTerminal) extends Action
final case class LluCaptured(llu: CaptureFlag) extends Action
final case class LluSpawned(player_guid: PlanetSideGUID, llu: CaptureFlag) extends Action
final case class LluDespawned(player_guid: PlanetSideGUID, llu: CaptureFlag) extends Action
final case class SendPacket(packet: PlanetSideGamePacket) extends Action
final case class SendPlanetsideAttributeMessage(
player_guid: PlanetSideGUID,
target: PlanetSideGUID,
attribute_number: PlanetsideAttributeEnum,
attribute_value: Long
) extends Action
final case class SendGenericObjectActionMessage(
player_guid: PlanetSideGUID,
target: PlanetSideGUID,
action_number: GenericObjectActionEnum
) extends Action
final case class SendChatMsg(
player_guid: PlanetSideGUID,
msg: ChatMsg
) extends Action
final case class SendGenericActionMessage(
player_guid: PlanetSideGUID,
action_number: GenericActionEnum
) extends Action
final case class RouterTelepadTransport(
player_guid: PlanetSideGUID,
passenger_guid: PlanetSideGUID,

View file

@ -3,8 +3,13 @@ package net.psforever.services.local
import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle}
import net.psforever.objects.ce.Deployable
import net.psforever.objects.serverobject.llu.CaptureFlag
import net.psforever.objects.serverobject.structures.{AmenityOwner, Building}
import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
import net.psforever.objects.vehicles.Utility
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.GenericActionEnum.GenericActionEnum
import net.psforever.packet.game.GenericObjectActionEnum.GenericObjectActionEnum
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game._
@ -33,8 +38,18 @@ object LocalResponse {
) extends Response
final case class SendHackMessageHackCleared(target_guid: PlanetSideGUID, unk1: Long, unk2: Long) extends Response
final case class HackObject(target_guid: PlanetSideGUID, unk1: Long, unk2: Long) extends Response
final case class SendPacket(packet: PlanetSideGamePacket) extends Response
final case class SendPlanetsideAttributeMessage(target_guid: PlanetSideGUID, attribute_number: PlanetsideAttributeEnum, attribute_value: Long)
extends Response
final case class SendGenericObjectActionMessage(target_guid: PlanetSideGUID, action_number: GenericObjectActionEnum)
extends Response
final case class SendChatMsg(msg: ChatMsg) extends Response
final case class SendGenericActionMessage(action_num: GenericActionEnum) extends Response
final case class LluSpawned(llu: CaptureFlag) extends Response
final case class LluDespawned(llu: CaptureFlag) extends Response
final case class ObjectDelete(item_guid: PlanetSideGUID, unk: Int) extends Response
final case class ProximityTerminalAction(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject)
extends Response

View file

@ -0,0 +1,282 @@
package net.psforever.services.local.support
import akka.actor.{Actor, ActorRef, Cancellable}
import net.psforever.objects.{Default, Player}
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
import net.psforever.objects.serverobject.llu.CaptureFlag
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
import net.psforever.objects.zones.Zone
import net.psforever.packet.game._
import net.psforever.services.ServiceManager
import net.psforever.services.ServiceManager.{Lookup, LookupResult}
import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage}
import net.psforever.services.local.support.CaptureFlagLostReasonEnum.CaptureFlagLostReasonEnum
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGUID, Vector3}
import scala.concurrent.duration.DurationInt
import scala.util.Success
/**
* Responsible for handling capture flag related lifecycles
* @param taskResolver A reference to a zone's task resolver actor
*/
class CaptureFlagManager(val taskResolver: ActorRef, zone: Zone) extends Actor{
private[this] val log = org.log4s.getLogger(self.path.name)
var galaxyService: ActorRef = ActorRef.noSender
private var mapUpdateTick: Cancellable = Default.Cancellable
/** An internally tracked list of current flags, to avoid querying AmenityOwners each second for flag lookups */
private var flags: List[CaptureFlag] = Nil
private def TrackFlag(flag: CaptureFlag): Unit = {
flag.Owner.Amenities = flag
flags = flags :+ flag
if (mapUpdateTick.isCancelled) {
// Start sending map updates periodically
import scala.concurrent.ExecutionContext.Implicits.global
mapUpdateTick = context.system.scheduler.scheduleAtFixedRate(0 seconds, 1 second, self, CaptureFlagManager.MapUpdate())
}
}
private def UntrackFlag(flag: CaptureFlag): Unit = {
flag.Owner.RemoveAmenity(flag)
flags = flags.filterNot(x => x == flag)
if (flags.isEmpty) {
mapUpdateTick.cancel()
// Send one final map update to clear the last flag from the map
self ! CaptureFlagManager.MapUpdate()
}
}
val serviceManager = ServiceManager.serviceManager
serviceManager ! Lookup("galaxy")
def receive: Receive = {
case LookupResult("galaxy", endpoint) =>
galaxyService = endpoint
case CaptureFlagManager.MapUpdate() =>
val flagInfo = flags.map(flag =>
FlagInfo(
u1 = 0,
owner_map_id = flag.Owner.asInstanceOf[Building].MapId,
target_map_id = flag.Target.MapId,
x = flag.Position.x,
y = flag.Position.y,
hack_time_remaining = flag.Owner.asInstanceOf[Building].infoUpdateMessage().hack_time_remaining,
is_monolith_unit = false
)
)
galaxyService ! GalaxyServiceMessage(GalaxyAction.FlagMapUpdate(CaptureFlagUpdateMessage(zone.Number, flagInfo)))
case CaptureFlagManager.SpawnCaptureFlag(capture_terminal, target, hackingFaction) =>
val zone = capture_terminal.Zone
val socket = capture_terminal.Owner.asInstanceOf[Building].GetFlagSocket.get
// Override CC message when looked at
zone.LocalEvents ! LocalServiceMessage(
zone.id,
LocalAction.SendGenericObjectActionMessage(
PlanetSideGUID(-1),
capture_terminal.GUID,
GenericObjectActionEnum.FlagSpawned
)
)
// Register LLU object create task and callback to create on clients
val flag: CaptureFlag = CaptureFlag.Constructor(
Vector3(socket.Position.x, socket.Position.y, socket.Position.z - 1),
socket.Orientation,
target,
socket.Owner,
hackingFaction
)
// Add the flag as an amenity and track it internally
TrackFlag(flag)
taskResolver ! CallBackForTask(
TaskResolver.GiveTask(GUIDTask.RegisterObjectTask(flag)(socket.Zone.GUID).task),
socket.Zone.LocalEvents,
LocalServiceMessage(
socket.Zone.id,
LocalAction.LluSpawned(PlanetSideGUID(-1), flag)
)
)
// Broadcast chat message for LLU spawn
val owner = flag.Owner.asInstanceOf[Building]
ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_FlagSpawned(owner, flag.Target))
case CaptureFlagManager.Captured(flag: CaptureFlag) =>
// Trigger Install sound
flag.Zone.LocalEvents ! LocalServiceMessage(flag.Zone.id, LocalAction.TriggerSound(PlanetSideGUID(-1), TriggeredSound.LLUInstall, flag.Target.CaptureTerminal.get.Position, 20, 0.8000001f))
// Broadcast capture chat message
ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_Success(flag.Carrier.get, flag.Owner.asInstanceOf[Building].Name))
// Despawn flag
HandleFlagDespawn(flag)
case CaptureFlagManager.Lost(flag: CaptureFlag, reason: CaptureFlagLostReasonEnum) =>
val message = reason match {
case CaptureFlagLostReasonEnum.Resecured =>
CaptureFlagChatMessageStrings.CTF_Failed_SourceResecured(flag.Owner.asInstanceOf[Building])
case CaptureFlagLostReasonEnum.TimedOut =>
CaptureFlagChatMessageStrings.CTF_Failed_TimedOut(flag.Owner.asInstanceOf[Building].Name, flag.Target)
}
ChatBroadcast(flag.Zone, message)
HandleFlagDespawn(flag)
case CaptureFlagManager.PickupFlag(flag: CaptureFlag, player: Player) =>
val continent = flag.Zone
flag.Carrier = Some(player)
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.SendPacket(ObjectAttachMessage(player.GUID, flag.GUID, 252)))
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.TriggerSound(PlanetSideGUID(-1), TriggeredSound.LLUPickup, player.Position, 15, volume = 0.8f))
ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_FlagPickedUp(player, flag.Owner.asInstanceOf[Building].Name), fanfare = false)
case CaptureFlagManager.DropFlag(flag: CaptureFlag) =>
flag.Carrier match {
case Some(player: Player) =>
// Set the flag position to where the player is that dropped it
flag.Position = player.Position
// Remove attached player from flag
flag.Carrier = None
// Send drop packet
flag.Zone.LocalEvents ! LocalServiceMessage(flag.Zone.id, LocalAction.SendPacket(ObjectDetachMessage(player.GUID, flag.GUID, player.Position, 0, 0, 0)))
// Send dropped chat message
ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_FlagDropped(player, flag.Owner.asInstanceOf[Building].Name), fanfare = false)
case None =>
log.warn("Tried to drop flag but flag has no carrier")
}
case _ =>
log.warn("Received unhandled message");
}
private def HandleFlagDespawn(flag: CaptureFlag): Unit = {
// Unregister LLU from clients,
flag.Zone.LocalEvents ! LocalServiceMessage(flag.Zone.id, LocalAction.LluDespawned(PlanetSideGUID(-1), flag))
// Then unregister it from the GUID pool
taskResolver ! TaskResolver.GiveTask(GUIDTask.UnregisterObjectTask(flag)(flag.Zone.GUID).task)
// Remove the flag as an amenity
UntrackFlag(flag)
}
private def ChatBroadcast(zone: Zone, message: String, fanfare: Boolean = true): Unit = {
val messageType: ChatMessageType = if (fanfare) {
ChatMessageType.UNK_223
} else {
ChatMessageType.UNK_229
}
zone.LocalEvents ! LocalServiceMessage(
zone.id,
LocalAction.SendChatMsg(
PlanetSideGUID(-1),
ChatMsg(messageType, wideContents = false, "", message, None)
)
)
}
// Todo: Duplicate from SessionActor. Make common.
def CallBackForTask(task: TaskResolver.GiveTask, sendTo: ActorRef, pass: Any): TaskResolver.GiveTask = {
TaskResolver.GiveTask(
new Task() {
private val localDesc = task.task.Description
private val destination = sendTo
private val passMsg = pass
override def Description: String = s"callback for tasking $localDesc"
def Execute(resolver: ActorRef): Unit = {
destination ! passMsg
resolver ! Success(this)
}
},
List(task)
)
}
}
object CaptureFlagManager {
final case class SpawnCaptureFlag(capture_terminal: CaptureTerminal, target: Building, hackingFaction: PlanetSideEmpire.Value)
final case class PickupFlag(flag: CaptureFlag, player: Player)
final case class DropFlag(flag: CaptureFlag)
final case class Captured(flag: CaptureFlag)
final case class Lost(flag: CaptureFlag, reason: CaptureFlagLostReasonEnum)
final case class MapUpdate()
}
object CaptureFlagChatMessageStrings {
/*
@CTF_Failed_TargetLost=%1's LLU target facility %2 was lost!\nHack canceled!
@CTF_Failed_FlagLost=The %1 lost %2's LLU!\nHack canceled!
@CTF_Warning_Carrier=%1 of the %2 has %3's LLU.\nIt must be taken to %4 within %5 minutes!
@CTF_Warning_NoCarrier=%1's LLU is in the field.\nThe %2 must take it to %3 within %4 minutes!
@CTF_Warning_Carrier1Min=%1 of the %2 has %3's LLU.\nIt must be taken to %4 within the next minute!
@CTF_Warning_NoCarrier1Min=%1's LLU is in the field.\nThe %2 must take it to %3 within the next minute!
*/
// @CTF_Success=%1 captured %2's LLU for the %3!
/** {player.Name} captured {owner_name}'s LLU for the {player.Faction}! */
def CTF_Success(player: Player, owner_name: String): String = s"@CTF_Success^${player.Name}~^@$owner_name~^@${GetFactionString(player.Faction)}~"
// @CTF_Failed_TimedOut=The %1 failed to deliver %2's LLU to %3 in time!\nHack canceled!
/** The {target.Faction} failed to deliver {owner_name}'s LLU to {target.Name} in time!\nHack canceled! */
def CTF_Failed_TimedOut(owner_name: String, target: Building): String = s"@CTF_Failed_TimedOut^@${GetFactionString(target.Faction)}~^@$owner_name~^@${target.Name}~"
// @CTF_Failed_SourceResecured=The %1 resecured %2!\nThe LLU was lost!
/** The {owner.Faction} resecured {owner.Name}!\nThe LLU was lost! */
def CTF_Failed_SourceResecured(owner: Building): String = s"@CTF_Failed_SourceResecured^@${CaptureFlagChatMessageStrings.GetFactionString(owner.Faction)}~^@${owner.Name}~"
// @CTF_FlagSpawned=%1 %2 has spawned a LLU.\nIt must be taken to %3 %4's Control Console within %5 minutes or the hack will fail!
/** {facilityType} {facilityName} has spawned a LLU.\nIt must be taken to {targetFacilityType} {targetFacilityName}'s Control Console within 15 minutes or the hack will fail! */
def CTF_FlagSpawned(owner: Building, target: Building): String = s"@CTF_FlagSpawned^@${owner.Definition.Name}~^@${owner.Name}~^@${target.Definition.Name}~^@${target.Name}~^15~"
// @CTF_FlagPickedUp=%1 of the %2 picked up %3's LLU
/** {playerName} of the {faction} picked up {facilityName}'s LLU */
def CTF_FlagPickedUp(player: Player, owner_name: String): String = s"@CTF_FlagPickedUp^${player.Name}~^@${CaptureFlagChatMessageStrings.GetFactionString(player.Faction)}~^@$owner_name~"
// @CTF_FlagDropped=%1 of the %2 dropped %3's LLU
/** {playerName} of the {faction} dropped {facilityName}'s LLU */
def CTF_FlagDropped(player: Player, owner_name: String): String = s"@CTF_FlagDropped^${player.Name}~^@${CaptureFlagChatMessageStrings.GetFactionString(player.Faction)}~^@$owner_name~"
// todo: make private
private def GetFactionString(faction: PlanetSideEmpire.Value): String = {
faction match {
case PlanetSideEmpire.TR => "TerranRepublic"
case PlanetSideEmpire.NC => "NewConglomerate"
case PlanetSideEmpire.VS => "VanuSovereigncy" // Yes, this is wrong. It is like that in packet captures.
}
}
}
object CaptureFlagLostReasonEnum extends Enumeration {
type CaptureFlagLostReasonEnum = Value
val Resecured, TimedOut = Value
}

View file

@ -1,21 +1,26 @@
package net.psforever.services.local.support
import akka.actor.{Actor, Cancellable}
import akka.actor.{Actor, ActorRef, Cancellable}
import net.psforever.actors.zone.{BuildingActor, ZoneActor}
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.llu.CaptureFlag
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
import net.psforever.objects.zones.Zone
import net.psforever.objects.{Default, GlobalDefinitions}
import net.psforever.packet.game.PlanetsideAttributeEnum
import net.psforever.packet.game.{GenericActionEnum, PlanetsideAttributeEnum}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
import java.util.concurrent.TimeUnit
import scala.concurrent.duration.{FiniteDuration, _}
import scala.util.Random
class HackCaptureActor extends Actor {
/**
* Responsible for handling the aspects related to hacking control consoles and capturing bases.
*/
class HackCaptureActor(val taskResolver: ActorRef) extends Actor {
private[this] val log = org.log4s.getLogger
private var clearTrigger: Cancellable = Default.Cancellable
@ -61,6 +66,7 @@ class HackCaptureActor extends Actor {
RestartTimer()
NotifyHackStateChange(target, isResecured = false)
TrySpawnCaptureFlag(target)
case HackCaptureActor.ProcessCompleteHacks() =>
log.trace("Processing complete hacks")
@ -75,7 +81,19 @@ class HackCaptureActor extends Actor {
val hackedByFaction = entry.target.HackedBy.get.hackerFaction
entry.target.Actor ! CommonMessages.ClearHack()
HackCompleted(entry.target, hackedByFaction)
entry.target.Owner.asInstanceOf[Building].GetFlagSocket match {
case Some(socket) =>
// LLU was not delivered in time. Send resecured notifications
entry.target.Owner.asInstanceOf[Building].GetFlag match {
case Some(flag: CaptureFlag) => entry.target.Zone.LocalEvents ! CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.TimedOut)
case None => log.warn(s"Failed to find capture flag matching socket ${socket.GUID}")
}
NotifyHackStateChange(entry.target, isResecured = true)
case None =>
// Timed hack finished, capture the base
HackCompleted(entry.target, hackedByFaction)
}
})
// If there's hacked objects left in the list restart the timer with the shortest hack time left
@ -84,32 +102,79 @@ class HackCaptureActor extends Actor {
case HackCaptureActor.ResecureCaptureTerminal(target, _) =>
hackedObjects = hackedObjects.filterNot(x => x.target == target)
// If LLU exists it was not delivered. Send resecured notifications
target.Owner.asInstanceOf[Building].GetFlag match {
case Some(flag: CaptureFlag) => target.Zone.LocalEvents ! CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.Resecured)
case None => ;
}
NotifyHackStateChange(target, isResecured = true)
// Restart the timer in case the object we just removed was the next one scheduled
RestartTimer()
case HackCaptureActor.FlagCaptured(flag) =>
log.warn(hackedObjects.toString())
hackedObjects.find(_.target.GUID == flag.Owner.asInstanceOf[Building].CaptureTerminal.get.GUID) match {
case Some(entry) =>
val hackedByFaction = entry.target.HackedBy.get.hackerFaction
hackedObjects = hackedObjects.filterNot(x => x == entry)
HackCompleted(entry.target, hackedByFaction)
entry.target.Actor ! CommonMessages.ClearHack()
flag.Zone.LocalEvents ! CaptureFlagManager.Captured(flag)
// If there's hacked objects left in the list restart the timer with the shortest hack time left
RestartTimer()
case _ =>
log.error(s"Attempted LLU capture for ${flag.Owner.asInstanceOf[Building].Name} but CC GUID ${flag.Owner.asInstanceOf[Building].CaptureTerminal.get.GUID} was not in list of hacked objects")
}
case _ => ;
}
private def NotifyHackStateChange(target: CaptureTerminal, isResecured: Boolean): Unit = {
val attribute_value = HackCaptureActor.GetHackUpdateAttributeValue(target, isResecured)
private def TrySpawnCaptureFlag(terminal: CaptureTerminal): Unit = {
// Handle LLUs if the base contains a LLU socket
// If there are no neighbouring bases belonging to the hacking faction this will be handled as a regular timed hack (e.g. neutral base in enemy territory)
val owner = terminal.Owner.asInstanceOf[Building]
val hackingFaction = HackCaptureActor.GetHackingFaction(terminal).get
val hackingFactionNeighbourBases = owner.Neighbours(hackingFaction)
hackingFactionNeighbourBases match {
case Some(neighbours) =>
if(owner.IsCtfBase) {
// Find a random neighbouring base matching the hacking faction
val targetBase = neighbours.toVector((new Random).nextInt(neighbours.size))
// Request LLU is created by CaptureFlagActor via LocalService
terminal.Zone.LocalEvents ! CaptureFlagManager.SpawnCaptureFlag(terminal, targetBase, hackingFaction)
}
case None =>
log.info("Couldn't find any neighbouring bases for LLU hack.")
}
}
private def NotifyHackStateChange(terminal: CaptureTerminal, isResecured: Boolean): Unit = {
val attribute_value = HackCaptureActor.GetHackUpdateAttributeValue(terminal, isResecured)
// Notify all clients that CC has been hacked
target.Zone.LocalEvents ! LocalServiceMessage(
target.Zone.id,
terminal.Zone.LocalEvents ! LocalServiceMessage(
terminal.Zone.id,
LocalAction.SendPlanetsideAttributeMessage(
PlanetSideGUID(-1),
target.GUID,
terminal.GUID,
PlanetsideAttributeEnum.ControlConsoleHackUpdate,
attribute_value
)
)
val owner = terminal.Owner.asInstanceOf[Building]
// Notify parent building that state has changed
target.Owner.Actor ! BuildingActor.AmenityStateChange(target, Some(isResecured))
owner.Actor ! BuildingActor.AmenityStateChange(terminal, Some(isResecured))
// Push map update to clients
target.Owner.asInstanceOf[Building].Zone.actor ! ZoneActor.ZoneMapUpdate()
owner.Zone.actor ! ZoneActor.ZoneMapUpdate()
}
private def HackCompleted(terminal: CaptureTerminal with Hackable, hackedByFaction: PlanetSideEmpire.Value): Unit = {
@ -117,6 +182,9 @@ class HackCaptureActor extends Actor {
if (building.NtuLevel > 0) {
log.info(s"Setting base ${building.GUID} / MapId: ${building.MapId} as owned by $hackedByFaction")
building.Actor! BuildingActor.SetFaction(hackedByFaction)
// todo: This should probably only go to those within the captured SOI who belong to the capturing faction
building.Zone.LocalEvents ! LocalServiceMessage(building.Zone.id, LocalAction.SendGenericActionMessage(PlanetSideGUID(-1), GenericActionEnum.BaseCaptureFanfare))
} else {
log.info("Base hack completed, but base was out of NTU.")
}
@ -160,6 +228,7 @@ object HackCaptureActor {
)
final case class ResecureCaptureTerminal(target: CaptureTerminal, zone: Zone)
final case class FlagCaptured(flag: CaptureFlag)
private final case class ProcessCompleteHacks()
@ -172,11 +241,19 @@ object HackCaptureActor {
hack_timestamp: Long
)
def GetHackUpdateAttributeValue(target: CaptureTerminal, isResecured: Boolean): Long = {
def GetHackingFaction(terminal: CaptureTerminal): Option[PlanetSideEmpire.Value] = {
terminal.HackedBy match {
case Some(Hackable.HackInfo(_, _, hackingFaction, _, _, _)) =>
Some(hackingFaction)
case _ => None
}
}
def GetHackUpdateAttributeValue(terminal: CaptureTerminal, isResecured: Boolean): Long = {
if (isResecured) {
17039360L
} else {
target.HackedBy match {
terminal.HackedBy match {
case Some(Hackable.HackInfo(_, _, hackingFaction, _, start, length)) =>
// See PlanetSideAttributeMessage #20 documentation for an explanation of how the timer is calculated
val hack_time_remaining_ms =

View file

@ -248,14 +248,24 @@ object ChatMessageType extends Enum[ChatMessageType] {
case object CMT_REMOVE_VANUMODULE extends ChatMessageType // /moduleremove OR /modulerm
case object CMT_DEBUG_MASSIVE extends ChatMessageType // /debugmassive
case object CMT_WARP_TO_NEXT_BILLBOARD extends ChatMessageType // ???
case object UNK_222 extends ChatMessageType // ??? "CTF Flag stolen"
case object UNK_223 extends ChatMessageType // ??? "CTF Flag lost"
case object UNK_222 extends ChatMessageType // "CTF Flag stolen"
// Plays a trumpet-like sound and displays the message: "<Base Type> <Base Name> has spawned a LLU"
// "It must be taken to <Base Type> <Base Name>'s Control Console within 15 minutes or the hack will fail!"
// Example string: @CTF_FlagSpawned^@cryo_facility~^@Hanish~^@comm_station_dsp~^@Akkan~^15~
case object UNK_223 extends ChatMessageType // "CTF Flag lost"
// Handles both LLU lost and LLU resecured
// Example string: @CTF_Failed_SourceResecured^@TerranRepublic~^@Hanish~
// Output: "The Terran Republic resecured Hanish!" "The LLU was lost!"
// Example string: @CTF_Failed_FlagLost^@TerranRepublic~^@Hanish~
// Output: "The Terran Republic lost Hanish's LLU" "Hack canceled!"
case object UNK_224 extends ChatMessageType // ??? "Vehicle Dismount"
case object UNK_225 extends ChatMessageType // ??? empty
case object UNK_226 extends ChatMessageType // ??? empty
case object UNK_227 extends ChatMessageType // ??? empty
case object UNK_228 extends ChatMessageType // ??? empty
case object UNK_229 extends ChatMessageType // ??? empty
case object UNK_229 extends ChatMessageType // See UNK_222 - seems to do the same except without trumpet-like sound
// Is used for flag pickup @CTF_FlagPickedUp^OzTurtle~^@NewConglomerate~^@Qumu~
// Or flag warning @CTF_Warning_Carrier^VanuHemlock~^@VanuSovereigncy~^@Mani~^@Dagur~^10~
case object UNK_230 extends ChatMessageType // ??? "Vehicle Mount"
case object UNK_231 extends ChatMessageType // ??? empty
case object UNK_232 extends ChatMessageType // ??? empty

View file

@ -13,6 +13,7 @@ import net.psforever.objects.ballistics.Projectile
import net.psforever.objects.definition.BasicDefinition
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.generator.Generator
import net.psforever.objects.serverobject.llu.{CaptureFlagSocket, CaptureFlagSocketDefinition}
import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.pad.{VehicleSpawnPad, VehicleSpawnPadDefinition}
import net.psforever.objects.serverobject.painbox.{Painbox, PainboxDefinition}
@ -338,6 +339,15 @@ object Zones {
),
owningBuildingGuid = ownerGuid
)
case "llm_socket" =>
zoneMap.addLocalObject(
obj.guid,
CaptureFlagSocket.Constructor(
obj.objectDefinition.asInstanceOf[CaptureFlagSocketDefinition],
obj.position
),
owningBuildingGuid = ownerGuid
)
case "gr_door_mb_orb" =>
zoneMap

View file

@ -0,0 +1,48 @@
package game
import net.psforever.packet.PacketCoding
import net.psforever.packet.game.{CaptureFlagUpdateMessage, FlagInfo}
import org.specs2.control.Debug
import org.specs2.mutable.Specification
import scodec.bits._
class CaptureFlagUpdateMessageTest extends Specification with Debug {
val stringZero = hex"c0 0a0000"
val stringOne = hex"c0 0a0014300018025281dd852830803000"
"decode (zero)" in {
PacketCoding.decodePacket(stringZero).require match {
case CaptureFlagUpdateMessage(zone_number, flagInfoList) =>
zone_number mustEqual 10
flagInfoList.length mustEqual 0
case _ =>
ko
}
}
"decode (one)" in {
PacketCoding.decodePacket(stringOne).require match {
case CaptureFlagUpdateMessage(zone_number, flagInfoList) =>
zone_number mustEqual 10
flagInfoList.length mustEqual 1
flagInfoList.head mustEqual FlagInfo(1, 12, 6, 3905.1562f, 5160.922f, 794636L, false)
case _ =>
ko
}
}
"encode (zero)" in {
val msg = CaptureFlagUpdateMessage(10, Nil)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual stringZero
}
"encode (one)" in {
val msg = CaptureFlagUpdateMessage(10, List(FlagInfo(1, 12, 6, 3905.1562f, 5160.922f, 794636L, false)))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual stringOne
}
}

View file

@ -29,24 +29,16 @@ class CaptureFlagDataTest extends Specification {
flag.pos.orient.y mustEqual 0f
flag.pos.orient.z mustEqual 47.8125f
flag.faction mustEqual PlanetSideEmpire.NC
flag.unk1 mustEqual 21
flag.unk2 mustEqual 4
flag.unk3 mustEqual 2838
flag.unk4 mustEqual 9
flag.owningBaseGuid mustEqual 21
flag.targetBaseGuid mustEqual 4
flag.milliseconds_remaining mustEqual 592662
case _ =>
ko
}
}
"encode" in {
val obj = CaptureFlagData(
PlacementData(3912.0312f, 5169.4375f, 59.96875f, 0f, 0f, 47.8125f),
PlanetSideEmpire.NC,
21,
4,
2838,
9
)
val obj = CaptureFlagData(PlacementData(3912.0312f, 5169.4375f, 59.96875f, 0f, 0f, 47.8125f), PlanetSideEmpire.NC, 21, 4, 592662)
val msg = ObjectCreateMessage(ObjectClass.capture_flag, PlanetSideGUID(4330), obj)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string_captureflag

View file

@ -526,7 +526,7 @@ class PlayerControlDeathStandingTest extends ActorTest {
assert(player2.isAlive)
player2.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar = avatarProbe.receiveN(7, 500 milliseconds)
val msg_avatar = avatarProbe.receiveN(8, 500 milliseconds)
val msg_stamina = probe.receiveOne(500 milliseconds)
activityProbe.expectNoMessage(200 milliseconds)
assert(
@ -543,25 +543,31 @@ class PlayerControlDeathStandingTest extends ActorTest {
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.DropSpecialItem()) => true
case _ => false
}
)
assert(
msg_avatar(2) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.Killed(PlanetSideGUID(2), None)) => true
case _ => false
}
)
assert(
msg_avatar(2) match {
msg_avatar(3) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar(3) match {
msg_avatar(4) match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 7, _)) =>
true
case _ => false
}
)
assert(
msg_avatar(4) match {
msg_avatar(5) match {
case AvatarServiceMessage(
"TestCharacter2",
AvatarAction.SendResponse(_, DestroyMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _))
@ -571,7 +577,7 @@ class PlayerControlDeathStandingTest extends ActorTest {
}
)
assert(
msg_avatar(5) match {
msg_avatar(6) match {
case AvatarServiceMessage(
"TestCharacter2",
AvatarAction.SendResponse(
@ -584,7 +590,7 @@ class PlayerControlDeathStandingTest extends ActorTest {
}
)
assert(
msg_avatar(6) match {
msg_avatar(7) match {
case AvatarServiceMessage("test", AvatarAction.DestroyDisplay(killer, victim, _, _))
if killer.Name.equals(player1.Name) && victim.Name.equals(player2.Name) =>
true
@ -666,7 +672,7 @@ class PlayerControlDeathSeatedTest extends ActorTest {
assert(player2.isAlive)
player2.Actor ! Vitality.Damage(applyDamageTo)
val msg_avatar = avatarProbe.receiveN(8, 500 milliseconds)
val msg_avatar = avatarProbe.receiveN(9, 500 milliseconds)
val msg_stamina = probe.receiveOne(500 milliseconds)
activityProbe.expectNoMessage(200 milliseconds)
assert(
@ -677,6 +683,12 @@ class PlayerControlDeathSeatedTest extends ActorTest {
)
assert(
msg_avatar.head match {
case AvatarServiceMessage("TestCharacter2", AvatarAction.DropSpecialItem()) => true
case _ => false
}
)
assert(
msg_avatar(1) match {
case AvatarServiceMessage(
"TestCharacter2",
AvatarAction.Killed(PlanetSideGUID(2), Some(PlanetSideGUID(7)))
@ -686,7 +698,7 @@ class PlayerControlDeathSeatedTest extends ActorTest {
}
)
assert(
msg_avatar(1) match {
msg_avatar(2) match {
case AvatarServiceMessage(
"TestCharacter2",
AvatarAction.SendResponse(_, ObjectDetachMessage(PlanetSideGUID(7), PlanetSideGUID(2), _, _, _, _))
@ -696,7 +708,7 @@ class PlayerControlDeathSeatedTest extends ActorTest {
}
)
assert(
msg_avatar(2) match {
msg_avatar(3) match {
case AvatarServiceMessage(
"TestCharacter2",
AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 29, 1)
@ -706,19 +718,19 @@ class PlayerControlDeathSeatedTest extends ActorTest {
}
)
assert(
msg_avatar(3) match {
msg_avatar(4) match {
case AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(2), PlanetSideGUID(2), _)) => true
case _ => false
}
)
assert(
msg_avatar(4) match {
msg_avatar(5) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg_avatar(5) match {
msg_avatar(6) match {
case AvatarServiceMessage(
"TestCharacter2",
AvatarAction.SendResponse(_, DestroyMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _))
@ -728,7 +740,7 @@ class PlayerControlDeathSeatedTest extends ActorTest {
}
)
assert(
msg_avatar(6) match {
msg_avatar(7) match {
case AvatarServiceMessage(
"TestCharacter2",
AvatarAction.SendResponse(
@ -741,7 +753,7 @@ class PlayerControlDeathSeatedTest extends ActorTest {
}
)
assert(
msg_avatar(7) match {
msg_avatar(8) match {
case AvatarServiceMessage("test", AvatarAction.DestroyDisplay(killer, victim, _, _))
if killer.Name.equals(player1.Name) && victim.Name.equals(player2.Name) =>
true