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, "MapID": null,
"IsChildObject": true "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, "Id": 796,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -8566,45 +8553,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 936,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -8618,32 +8566,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 242,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",

View file

@ -10256,32 +10256,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 1211,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -10308,58 +10282,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 247,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -10373,32 +10295,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 2148,
"ObjectName": "lock_external", "ObjectName": "lock_external",

View file

@ -15508,32 +15508,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 109,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -15547,19 +15521,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 1302,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -15573,32 +15534,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 2008,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -15612,71 +15547,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 2137,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -15703,32 +15573,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 760,
"ObjectName": "lock_external", "ObjectName": "lock_external",

View file

@ -12076,58 +12076,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 536,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -12154,32 +12102,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 1621,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -12193,45 +12115,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 245,
"ObjectName": "lock_external", "ObjectName": "lock_external",

View file

@ -8969,32 +8969,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 136,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -9008,45 +8982,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 819,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -9060,32 +8995,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 440,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",

View file

@ -9346,45 +9346,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 501,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -9411,32 +9372,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 1215,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -9450,19 +9385,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 1048,
"ObjectName": "lock_external", "ObjectName": "lock_external",

View file

@ -11608,19 +11608,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 1454,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -11634,71 +11621,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 1302,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -11712,32 +11634,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 1052,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -11751,32 +11647,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 632,
"ObjectName": "lock_external", "ObjectName": "lock_external",

View file

@ -8683,32 +8683,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 915,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -8735,32 +8709,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 1067,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -8774,32 +8722,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 624,
"ObjectName": "lock_external", "ObjectName": "lock_external",

View file

@ -12297,84 +12297,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 1349,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -12388,19 +12310,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 841,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -12414,19 +12323,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 135,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -12440,45 +12336,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 1739,
"ObjectName": "lock_external", "ObjectName": "lock_external",

View file

@ -10932,45 +10932,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 1082,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -10997,58 +10958,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 820,
"ObjectName": "llm_socket", "ObjectName": "llm_socket",
@ -11062,32 +10971,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 513,
"ObjectName": "lock_external", "ObjectName": "lock_external",

View file

@ -1169,19 +1169,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 109,
"ObjectName": "lock_external", "ObjectName": "lock_external",

View file

@ -3782,45 +3782,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 434,
"ObjectName": "lock_external", "ObjectName": "lock_external",

View file

@ -3249,45 +3249,6 @@
"MapID": null, "MapID": null,
"IsChildObject": true "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, "Id": 678,
"ObjectName": "lock_external", "ObjectName": "lock_external",

View file

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

View file

@ -1,21 +1,11 @@
package net.psforever.actors.session package net.psforever.actors.session
import akka.actor.typed
import akka.actor.typed.receptionist.Receptionist import akka.actor.typed.receptionist.Receptionist
import akka.actor.typed.scaladsl.adapter._ 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.pattern.ask
import akka.util.Timeout import akka.util.Timeout
import net.psforever.actors.net.MiddlewareActor 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.login.WorldSession._
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.avatar._ 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.equipment._
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver} import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
import net.psforever.objects.inventory.{Container, InventoryItem} 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.affinity.FactionAffinity
import net.psforever.objects.serverobject.containable.Containable import net.psforever.objects.serverobject.containable.Containable
import net.psforever.objects.serverobject.damage.Damageable 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.doors.Door
import net.psforever.objects.serverobject.generator.Generator import net.psforever.objects.serverobject.generator.Generator
import net.psforever.objects.serverobject.hackable.Hackable 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.locks.IFFLock
import net.psforever.objects.serverobject.mblocker.Locker import net.psforever.objects.serverobject.mblocker.Locker
import net.psforever.objects.serverobject.mount.Mountable 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.tube.SpawnTube
import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret} import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret}
import net.psforever.objects.serverobject.zipline.ZipLinePath import net.psforever.objects.serverobject.zipline.ZipLinePath
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.teamwork.Squad import net.psforever.objects.teamwork.Squad
import net.psforever.objects.vehicles._
import net.psforever.objects.vehicles.Utility.InternalTelepad import net.psforever.objects.vehicles.Utility.InternalTelepad
import net.psforever.objects.vehicles._
import net.psforever.objects.vital._ import net.psforever.objects.vital._
import net.psforever.objects.vital.base._ import net.psforever.objects.vital.base._
import net.psforever.objects.vital.interaction.DamageInteraction 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.objects.zones.{Zone, ZoneHotSpotProjector, Zoning}
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
import net.psforever.packet.game.{HotSpotInfo => PacketHotSpotInfo, _}
import net.psforever.packet.game.objectcreate._ 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.account.{AccountPersistenceService, PlayerToken, ReceiveAccountData, RetrieveAccountData}
import net.psforever.services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse} import net.psforever.services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse}
import net.psforever.services.chat.ChatService import net.psforever.services.chat.ChatService
import net.psforever.services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage, GalaxyServiceResponse} 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.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse}
import net.psforever.services.properties.PropertyOverrideManager import net.psforever.services.properties.PropertyOverrideManager
import net.psforever.services.support.SupportActor 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.types._
import net.psforever.util.{Config, DefinitionUtil} import net.psforever.util.{Config, DefinitionUtil}
import net.psforever.zones.Zones 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 { object SessionActor {
sealed trait Command sealed trait Command
@ -203,6 +202,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
var setupAvatarFunc: () => Unit = AvatarCreate var setupAvatarFunc: () => Unit = AvatarCreate
var setCurrentAvatarFunc: Player => Unit = SetCurrentAvatarNormally var setCurrentAvatarFunc: Player => Unit = SetCurrentAvatarNormally
var persist: () => Unit = NoPersistence 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) * 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) => case GalaxyResponse.MapUpdate(msg) =>
sendResponse(msg) sendResponse(msg)
case GalaxyResponse.FlagMapUpdate(msg) =>
sendResponse(msg)
case GalaxyResponse.TransferPassenger(temp_channel, vehicle, vehicle_to_delete, manifest) => case GalaxyResponse.TransferPassenger(temp_channel, vehicle, vehicle_to_delete, manifest) =>
(manifest.passengers.find { case (name, _) => player.Name.equals(name) } match { (manifest.passengers.find { case (name, _) => player.Name.equals(name) } match {
case Some((name, index)) if vehicle.Seats(index).occupant.isEmpty => case Some((name, index)) if vehicle.Seats(index).occupant.isEmpty =>
@ -968,6 +971,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
CancelZoningProcess() CancelZoningProcess()
PlayerActionsToCancel() PlayerActionsToCancel()
CancelAllProximityUnits() CancelAllProximityUnits()
DropSpecialSlotItem()
continent.Population ! Zone.Population.Release(avatar) continent.Population ! Zone.Population.Release(avatar)
response match { response match {
case Some((zone, spawnPoint)) => case Some((zone, spawnPoint)) =>
@ -1802,6 +1806,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
CancelZoningProcessWithDescriptiveReason("cancel_dmg") CancelZoningProcessWithDescriptiveReason("cancel_dmg")
} }
case AvatarResponse.DropSpecialItem() =>
DropSpecialSlotItem()
case AvatarResponse.Killed(mount) => case AvatarResponse.Killed(mount) =>
val respawnTimer = 300.seconds val respawnTimer = 300.seconds
//drop free hand item //drop free hand item
@ -1810,25 +1817,32 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
DropEquipmentFromInventory(player)(item) DropEquipmentFromInventory(player)(item)
case None => ; case None => ;
} }
DropSpecialSlotItem()
ToggleMaxSpecialState(enable = false) ToggleMaxSpecialState(enable = false)
keepAliveFunc = NormalKeepAlive keepAliveFunc = NormalKeepAlive
zoningStatus = Zoning.Status.None zoningStatus = Zoning.Status.None
deadState = DeadState.Dead deadState = DeadState.Dead
continent.GUID(mount) match { continent.GUID(mount) match {
case Some(obj: Vehicle) => case Some(obj: Vehicle) =>
TotalDriverVehicleControl(obj) TotalDriverVehicleControl(obj)
UnaccessContainer(obj) UnaccessContainer(obj)
case _ => ; case _ => ;
} }
PlayerActionsToCancel() PlayerActionsToCancel()
CancelAllProximityUnits() CancelAllProximityUnits()
CancelZoningProcessWithDescriptiveReason("cancel") CancelZoningProcessWithDescriptiveReason("cancel")
if (shotsWhileDead > 0) { if (shotsWhileDead > 0) {
log.warn( log.warn(
s"KillPlayer/SHOTS_WHILE_DEAD: client of ${avatar.name} fired $shotsWhileDead rounds while character was dead on server" s"KillPlayer/SHOTS_WHILE_DEAD: client of ${avatar.name} fired $shotsWhileDead rounds while character was dead on server"
) )
shotsWhileDead = 0 shotsWhileDead = 0
} }
reviveTimer.cancel() reviveTimer.cancel()
if (player.death_by == 0) { if (player.death_by == 0) {
reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer) { 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. * 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. * 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) => case LocalResponse.SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value) =>
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) => case LocalResponse.ObjectDelete(object_guid, unk) =>
if (tplayer_guid != guid) { if (tplayer_guid != guid) {
sendResponse(ObjectDeleteMessage(object_guid, unk)) sendResponse(ObjectDeleteMessage(object_guid, unk))
@ -4144,7 +4214,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case None => case None =>
log.warn(s"DropItem: ${player.Name} wanted to drop a $anItem, but it wasn't at hand") 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") log.warn(s"DropItem: ${player.Name} wanted to drop a $obj, but that isn't possible")
case None => case None =>
sendResponse(ObjectDeleteMessage(item_guid, 0)) //this is fine; item doesn't exist to the server anyway 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) => case Some(item) =>
CancelZoningProcessWithDescriptiveReason("cancel_use") CancelZoningProcessWithDescriptiveReason("cancel_use")
captureTerminal.Actor ! CommonMessages.Use(player, Some(item)) 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) => case Some(obj: FacilityTurret) =>
@ -4945,6 +5024,19 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case None => ; 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) => case Some(obj) =>
CancelZoningProcessWithDescriptiveReason("cancel_use") CancelZoningProcessWithDescriptiveReason("cancel_use")
log.warn(s"UseItem: ${player.Name} does not know how to handle $obj") 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") log.info(s"${player.Name} is back")
player.AwayFromKeyboard = false player.AwayFromKeyboard = false
} }
if (action == GenericActionEnum.DropSpecialItem.id) {
DropSpecialSlotItem()
}
if (action == 15) { //max deployment if (action == 15) { //max deployment
log.info(s"${player.Name} has anchored ${player.Sex.pronounObject}self to the ground") log.info(s"${player.Name} has anchored ${player.Sex.pronounObject}self to the ground")
player.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored player.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored
@ -6715,11 +6810,13 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
//sync model access state //sync model access state
sendResponse(PlanetsideAttributeMessage(amenityId, 50, 0)) sendResponse(PlanetsideAttributeMessage(amenityId, 50, 0))
sendResponse(PlanetsideAttributeMessage(amenityId, 51, 0)) sendResponse(PlanetsideAttributeMessage(amenityId, 51, 0))
//sync damageable, if //sync damageable, if
val health = amenity.Health val health = amenity.Health
if (amenity.Definition.Damageable && health < amenity.MaxHealth) { if (amenity.Definition.Damageable && health < amenity.MaxHealth) {
sendResponse(PlanetsideAttributeMessage(amenityId, 0, health)) sendResponse(PlanetsideAttributeMessage(amenityId, 0, health))
} }
//sync special object type cases //sync special object type cases
amenity match { amenity match {
case silo: ResourceSilo => case silo: ResourceSilo =>
@ -6733,19 +6830,30 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case door: Door if door.isOpen => case door: Door if door.isOpen =>
sendResponse(GenericObjectStateMsg(amenityId, 16)) sendResponse(GenericObjectStateMsg(amenityId, 16))
case _ => ;
}
//sync hack state
amenity match {
case obj: Hackable if obj.HackedBy.nonEmpty => case obj: Hackable if obj.HackedBy.nonEmpty =>
//sync hack state
amenity.Definition match { amenity.Definition match {
case GlobalDefinitions.capture_terminal => case GlobalDefinitions.capture_terminal =>
SendPlanetsideAttributeMessage( SendPlanetsideAttributeMessage(
amenity.GUID, amenity.GUID,
PlanetsideAttributeEnum.ControlConsoleHackUpdate, PlanetsideAttributeEnum.ControlConsoleHackUpdate,
HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false)) HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false))
case _ => case _ =>
HackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object 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 _ => ; 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.objects.zones.Zone
import net.psforever.persistence import net.psforever.persistence
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} 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.galaxy.{GalaxyAction, GalaxyServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.services.{InterstellarClusterService, Service, ServiceManager} 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.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success} import scala.util.{Failure, Success}
@ -197,8 +197,8 @@ class BuildingActor(
case AmenityStateChange(gen: Generator, data) => case AmenityStateChange(gen: Generator, data) =>
if (generatorStateChange(gen, data)) { if (generatorStateChange(gen, data)) {
//update the map // Request all buildings update their map data to refresh lattice linked benefits
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage())) zone.actor ! ZoneActor.ZoneMapUpdate()
} }
Behaviors.same Behaviors.same
@ -364,7 +364,7 @@ class BuildingActor(
} }
building.Faction = faction building.Faction = faction
alignForceDomeStatus(mapUpdateOnChange = false) 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)) 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.objects.vital.{ComplexDeployableResolutions, MaxResolutions, SimpleResolutions}
import net.psforever.types.{ExoSuitType, ImplantType, PlanetSideEmpire, Vector3} import net.psforever.types.{ExoSuitType, ImplantType, PlanetSideEmpire, Vector3}
import net.psforever.types._ import net.psforever.types._
import net.psforever.objects.serverobject.llu.{CaptureFlagDefinition, CaptureFlagSocketDefinition}
import scala.collection.mutable import scala.collection.mutable
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -1069,6 +1070,11 @@ object GlobalDefinitions {
val vanu_control_console = new CaptureTerminalDefinition(930) // Cavern CC 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 lodestar_repair_terminal = new MedicalTerminalDefinition(461)
val multivehicle_rearm_terminal = new OrderTerminalDefinition(576) val multivehicle_rearm_terminal = new OrderTerminalDefinition(576)

View file

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

View file

@ -710,12 +710,18 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
super.CancelJammeredStatus(target) super.CancelJammeredStatus(target)
//uninitialize implants //uninitialize implants
avatarActor ! AvatarActor.DeinitializeImplants() avatarActor ! AvatarActor.DeinitializeImplants()
cause.adversarial match { cause.adversarial match {
case Some(a) => case Some(a) =>
damageLog.info(s"DisplayDestroy: ${a.defender} was killed by ${a.attacker}") damageLog.info(s"DisplayDestroy: ${a.defender} was killed by ${a.attacker}")
case _ => case _ =>
damageLog.info(s"DisplayDestroy: ${player.Name} killed ${player.Sex.pronounObject}self.") 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( events ! AvatarServiceMessage(
nameChannel, nameChannel,
AvatarAction.Killed(player_guid, target.VehicleSeated) 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 obj.Owner = this
amenities 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 net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState, Vector3}
import scalax.collection.{Graph, GraphEdge} import scalax.collection.{Graph, GraphEdge}
import akka.actor.typed.scaladsl.adapter._ import akka.actor.typed.scaladsl.adapter._
import net.psforever.objects.serverobject.llu.{CaptureFlag, CaptureFlagSocket}
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
class Building( class Building(
@ -27,10 +28,6 @@ class Building(
private val buildingDefinition: BuildingDefinition private val buildingDefinition: BuildingDefinition
) extends AmenityOwner { ) 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 faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var playersInSOI: List[Player] = List.empty private var playersInSOI: List[Player] = List.empty
private val capitols = List("Thoth", "Voltan", "Neit", "Anguta", "Eisa", "Verica") private val capitols = List("Thoth", "Voltan", "Neit", "Anguta", "Eisa", "Verica")
@ -43,6 +40,10 @@ class Building(
def Name: String = name 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 MapId: Int = map_id
def IsCapitol: Boolean = capitols.contains(name) 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] = { def HackableAmenities: List[Amenity with Hackable] = {
Amenities.filter(x => x.isInstanceOf[Hackable]).map(x => x.asInstanceOf[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 // Check this Building is on the lattice first
zone.Lattice find this match { zone.Lattice find this match {
case Some(_) => case Some(_) =>
// todo: generator destruction state
val subGraph = Zone.Lattice filter ((b: Building) => 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 var stackedBenefit = 0
@ -226,13 +237,13 @@ class Building(
hackingFaction, hackingFaction,
hackTime, hackTime,
if (ntuLevel > 0) Faction else PlanetSideEmpire.NEUTRAL, if (ntuLevel > 0) Faction else PlanetSideEmpire.NEUTRAL,
0, // Field != 0 will cause malformed packet 0, // unk1 Field != 0 will cause malformed packet
None, None, // unk1x
generatorState, generatorState,
spawnTubesNormal, spawnTubesNormal,
forceDomeActive, forceDomeActive,
latticeBenefit, if (generatorState != PlanetSideGeneratorState.Destroyed) latticeBenefit else 0,
48, // cavern benefit if (generatorState != PlanetSideGeneratorState.Destroyed) 48 else 0, // cavern benefit
Nil, // unk4, Nil, // unk4,
0, // unk5 0, // unk5
false, // unk6 false, // unk6

View file

@ -531,7 +531,7 @@ object GamePacketOpcode extends Enumeration {
case 0xbf => game.ZipLineMessage.decode case 0xbf => game.ZipLineMessage.decode
// OPCODES 0xc0-cf // OPCODES 0xc0-cf
case 0xc0 => noDecoder(CaptureFlagUpdateMessage) case 0xc0 => game.CaptureFlagUpdateMessage.decode
case 0xc1 => noDecoder(VanuModuleUpdateMessage) case 0xc1 => noDecoder(VanuModuleUpdateMessage)
case 0xc2 => game.FacilityBenefitShieldChargeRequestMessage.decode case 0xc2 => game.FacilityBenefitShieldChargeRequestMessage.decode
case 0xc3 => game.ProximityTerminalUseMessage.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> * 07 - warning: missile lock<br>
* 08 - warning: Wasp missile lock<br> * 08 - warning: Wasp missile lock<br>
* 09 - warning: T-REK lock<br> * 09 - warning: T-REK lock<br>
* 11 - Drop special item e.g. LLU<br>
* 12 - sound: base captured fanfare<br> * 12 - sound: base captured fanfare<br>
* 14 - prompt: new character basic training<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> * 22 - message: awarded a cavern capture (updates cavern capture status)<br>
* 23 - award a cavern kill<br> * 23 - award a cavern kill<br>
* 24 - message: you have been imprinted (updates imprinted status; does it?)<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] { object GenericActionMessage extends Marshallable[GenericActionMessage] {
def apply(action: GenericActionEnum.GenericActionEnum): GenericActionMessage = {
GenericActionMessage(action.id)
}
implicit val codec: Codec[GenericActionMessage] = ( implicit val codec: Codec[GenericActionMessage] = (
"action" | uint(6) "action" | uint(6)
).as[GenericActionMessage] ).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. * Dispatched by the server to enact an effect on some game object.
* (Write more some other time.) * (Write more some other time.)
* @param object_guid the target object * @param object_guid the target object<br/>
* @param code the action code (0-63) * @param code the action code (0-63)<br/>
* 6 - Deconstructs player * 6 - Deconstructs player<br/>
* 7 - Start imprinting process (progress bar + character animation) * 7 - Start imprinting process (progress bar + character animation)<br/>
* 8 - Finish imprinting? * 8 - Finish imprinting?<br/>
* 9 - Cloak * 9 - Cloak<br/>
* 10 - Uncloak * 10 - Uncloak<br/>
* 11 - Deploy capital base shield pole with animation and broadcasts "The capitol force dome at X has been activated" * 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" * 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) * 13 - Deploy capital base shield pole (instantly, unless still in the middle of the stow animation)<br/>
* 15 - Displays "This facility's generator is under attack!" * 14 - Changes capture console to say "Facility hacked by the <Faction> LLU has been spawned." when looked at<br/>
* 16 - Displays "Generator has Overloaded! Evacuate Generator Room Immediately!" * 15 - Displays "This facility's generator is under attack!"<br/>
* 17 - Displays "This facility's generator is back on line" * 16 - Displays "Generator has Overloaded! Evacuate Generator Room Immediately!"<br/>
* 19 - Cause mines to explode * 17 - Displays "This facility's generator is back on line"<br/>
* 20 - Hit flinch? (orig, 82->80) * 19 - Cause mines to explode<br/>
* 21 - Reset build cooldown from using an ACE * 20 - Hit flinch? (orig, 82->80)<br/>
* 22 - ???? (Has been seen on vehicle pad objects, possibly some sort of reset flag after base faction flip / hack clear?) * 21 - Reset build cooldown from using an ACE<br/>
* 23 - Plays vehicle pad animation moving downwards * 22 - ???? (Has been seen on vehicle pad objects, possibly some sort of reset flag after base faction flip / hack clear?)<br/>
* 24 - Makes the vehicle bounce slightly. Have seen this in packet captures after taking a vehicle through a warpgate * 23 - Plays vehicle pad animation moving downwards<br/>
* 27 - Activates the router internal telepad for linking * 24 - Makes the vehicle bounce slightly. Have seen this in packet captures after taking a vehicle through a warpgate<br/>
* 28 - Activates the router internal telepad for linking * 27 - Activates the router internal telepad for linking<br/>
* 29 - Activates the telepad deployable (also used on the router's internal telepad) * 28 - Activates the router internal telepad for linking<br/>
* 30 - Activates the telepad deployable (also used on the router's internal telepad) * 29 - Activates the telepad deployable (also used on the router's internal telepad)<br/>
* 31 - Animation during router teleportation (source) * 30 - Activates the telepad deployable (also used on the router's internal telepad)<br/>
* 32 - Animation during router teleportation (destination) * 31 - Animation during router teleportation (source)<br/>
* 34 - Time until item can be used ????? * 32 - Animation during router teleportation (destination)<br/>
* 50 - For aircraft - client shows "The bailing mechanism failed! To fix the mechanism, land and repair the vehicle!" * 34 - Time until item can be used ?????<br/>
* 53 - Put down an FDU * 50 - For aircraft - client shows "The bailing mechanism failed! To fix the mechanism, land and repair the vehicle!"<br/>
* 56 - Sets vehicle or player to be black ops * 53 - Put down an FDU<br/>
* 57 - Reverts player from black ops * 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 { final case class GenericObjectActionMessage(object_guid: PlanetSideGUID, code: Int) extends PlanetSideGamePacket {
type Packet = GenericObjectActionMessage type Packet = GenericObjectActionMessage
@ -49,6 +51,10 @@ final case class GenericObjectActionMessage(object_guid: PlanetSideGUID, code: I
} }
object GenericObjectActionMessage extends Marshallable[GenericObjectActionMessage] { object GenericObjectActionMessage extends Marshallable[GenericObjectActionMessage] {
def apply(object_guid: PlanetSideGUID, code: GenericObjectActionEnum.GenericObjectActionEnum): GenericObjectActionMessage = {
GenericObjectActionMessage(object_guid, code.id)
}
implicit val codec: Codec[GenericObjectActionMessage] = ( implicit val codec: Codec[GenericObjectActionMessage] = (
("object_guid" | PlanetSideGUID.codec) :: ("object_guid" | PlanetSideGUID.codec) ::
("code" | uint(bits = 6)) :: ("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> * `0x84` - 4 - knife holster<br>
* `0x86` - 6 - grid (1,1)<br> * `0x86` - 6 - grid (1,1)<br>
* `0x00FA` - 250 - is a special dest/extra code that "attaches the item to the player's cursor" * `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 parent_guid the parent object
* @param child_guid the child object * @param child_guid the child object
* @param slot a codified location within the parent object's inventory; * @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> * `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> * `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` * `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> * `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. 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.<br>
* `49 - Vehicle texture effects state? (>0 turns on ANT panel glow or ntu silo panel glow + orbs) (bit?)`<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> * `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> * `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> * `55 - "Someone is attempting to Heal you". Value is 1`<br>
* `56 - "Someone is attempting to Repair you". Value is 1`<br> * `56 - "Someone is attempting to Repair you". Value is 1`<br>
* `64 - ????? related to using router telepads` * `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> * `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> * `77 - Cavern Facility Captures. Value is the number of captures`<br>
* `78 - Cavern Kills. Value is the number of kills`<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. * 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. * 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. * 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 faction the empire whose players may interact with this capture flag
* @param unk1 na * @param owningBaseGuid The GUID of the base that this LLU belongs to
* @param unk2 na * @param targetBaseGuid The GUID of the base that this LLU must be taken to
* @param unk3 na * @param milliseconds_remaining The number of milliseconds left on the timer for this LLU - should match the CC timer
* @param unk4 na
*/ */
final case class CaptureFlagData( final case class CaptureFlagData(
pos: PlacementData, pos: PlacementData,
faction: PlanetSideEmpire.Value, faction: PlanetSideEmpire.Value,
unk1: Int, owningBaseGuid : Int,
unk2: Int, targetBaseGuid : Int,
unk3: Int, milliseconds_remaining : Long
unk4: Int
) extends ConstructorData { ) extends ConstructorData {
override def bitsize: Long = 88L + pos.bitsize override def bitsize: Long = 88L + pos.bitsize
} }
@ -40,24 +39,23 @@ object CaptureFlagData extends Marshallable[CaptureFlagData] {
bool :: bool ::
uint4L :: uint4L ::
uint16L :: uint16L ::
("unk1" | uint8L) :: ("owningBaseGuid" | uint8L) ::
uint8L :: uint8L ::
("unk2" | uint8L) :: ("targetBaseGuid" | uint8L) ::
uint8L :: uint8L ::
("unk3" | uint16L) :: //probably a PlanetSideGUID ("milliseconds_remaining" | uint32L) ::
("unk4" | uint8L) :: uint(1)
uint(9)
).exmap[CaptureFlagData]( ).exmap[CaptureFlagData](
{ {
case pos :: fac :: false :: 4 :: 0 :: unk1 :: 0 :: unk2 :: 0 :: unk3 :: unk4 :: 0 :: HNil => case pos :: faction :: false :: 4 :: 0 :: owningBaseGuid :: 0 :: targetBaseGuid :: 0 :: milliseconds_remaining :: 0 :: HNil =>
Attempt.Successful(CaptureFlagData(pos, fac, unk1, unk2, unk3, unk4)) Attempt.Successful(CaptureFlagData(pos, faction, owningBaseGuid, targetBaseGuid, milliseconds_remaining))
case data => case data =>
Attempt.failure(Err(s"invalid capture flag data format - $data")) Attempt.failure(Err(s"invalid capture flag data format - $data"))
}, },
{ {
case CaptureFlagData(pos, fac, unk1, unk2, unk3, unk4) => case CaptureFlagData(pos, faction, owningBaseGuid, targetBaseGuid, milliseconds_remaining) =>
Attempt.successful(pos :: fac :: false :: 4 :: 0 :: unk1 :: 0 :: unk2 :: 0 :: unk3 :: unk4 :: 0 :: HNil) 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 _ => ; case _ => ;
} }

View file

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

View file

@ -117,6 +117,7 @@ object AvatarResponse {
inventory: List[InventoryItem], inventory: List[InventoryItem],
drop: List[InventoryItem] drop: List[InventoryItem]
) extends Response ) extends Response
final case class DropSpecialItem() extends Response
final case class TeardownConnection() extends Response final case class TeardownConnection() extends Response
// final case class PlayerStateShift(itemID : PlanetSideGUID) 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 akka.actor.Actor
import net.psforever.objects.zones.Zone 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} import net.psforever.services.{GenericEventBus, Service}
class GalaxyService extends Actor { class GalaxyService extends Actor {
@ -41,6 +41,11 @@ class GalaxyService extends Actor {
GalaxyServiceResponse(s"/Galaxy", GalaxyResponse.MapUpdate(msg)) 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) => case GalaxyAction.TransferPassenger(_, temp_channel, vehicle, vehicle_to_delete, manifest) =>
GalaxyEvents.publish( GalaxyEvents.publish(
GalaxyServiceResponse( GalaxyServiceResponse(

View file

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

View file

@ -4,7 +4,7 @@ package net.psforever.services.galaxy
import net.psforever.objects.Vehicle import net.psforever.objects.Vehicle
import net.psforever.objects.vehicles.VehicleManifest import net.psforever.objects.vehicles.VehicleManifest
import net.psforever.objects.zones.HotSpotInfo 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.types.PlanetSideGUID
import net.psforever.services.GenericEventBusMsg 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 HotSpotUpdate(zone_id: Int, priority: Int, host_spot_info: List[HotSpotInfo]) extends Response
final case class MapUpdate(msg: BuildingInfoUpdateMessage) extends Response final case class MapUpdate(msg: BuildingInfoUpdateMessage) extends Response
final case class FlagMapUpdate(msg: CaptureFlagUpdateMessage) extends Response
final case class TransferPassenger( final case class TransferPassenger(
temp_channel: String, temp_channel: String,

View file

@ -2,27 +2,26 @@
package net.psforever.services.local package net.psforever.services.local
import akka.actor.{Actor, ActorRef, Props} import akka.actor.{Actor, ActorRef, Props}
import net.psforever.objects._
import net.psforever.objects.ce.Deployable import net.psforever.objects.ce.Deployable
import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone import net.psforever.objects.vehicles.{Utility, UtilityType}
import net.psforever.objects._
import net.psforever.packet.game.{TriggeredEffect, TriggeredEffectLocation}
import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.Vitality
import net.psforever.types.{PlanetSideGUID, Vector3} import net.psforever.objects.zones.Zone
import net.psforever.services.local.support._ 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.vehicle.{VehicleAction, VehicleServiceMessage}
import net.psforever.services.{GenericEventBus, RemoverActor, Service} import net.psforever.services.{GenericEventBus, RemoverActor, Service}
import net.psforever.types.{PlanetSideGUID, Vector3}
import scala.concurrent.duration._ import scala.concurrent.duration.{Duration, _}
import net.psforever.objects.vehicles.{Utility, UtilityType}
import net.psforever.services.support.SupportActor
import scala.concurrent.duration.Duration
class LocalService(zone: Zone) extends Actor { class LocalService(zone: Zone) extends Actor {
private val doorCloser = context.actorOf(Props[DoorCloseActor](), s"${zone.id}-local-door-closer") 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 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 engineer = context.actorOf(Props(classOf[DeployableRemover], zone.tasks), s"${zone.id}-deployable-remover-agent")
private val teleportDeployment: ActorRef = private val teleportDeployment: ActorRef =
context.actorOf(Props[RouterTelepadActivation](), s"${zone.id}-telepad-activate-agent") context.actorOf(Props[RouterTelepadActivation](), s"${zone.id}-telepad-activate-agent")
@ -93,10 +92,43 @@ class LocalService(zone: Zone) extends Actor {
) )
case LocalAction.ClearTemporaryHack(_, target) => case LocalAction.ClearTemporaryHack(_, target) =>
hackClearer ! HackClearActor.ObjectIsResecured(target) hackClearer ! HackClearActor.ObjectIsResecured(target)
case LocalAction.ResecureCaptureTerminal(target) => case LocalAction.ResecureCaptureTerminal(target) =>
hackCapturer ! HackCaptureActor.ResecureCaptureTerminal(target, zone) hackCapturer ! HackCaptureActor.ResecureCaptureTerminal(target, zone)
case LocalAction.StartCaptureTerminalHack(target) => case LocalAction.StartCaptureTerminalHack(target) =>
hackCapturer ! HackCaptureActor.StartCaptureTerminalHack(target, zone, 0, 8L) 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) => case LocalAction.SendPlanetsideAttributeMessage(player_guid, target_guid, attribute_number, attribute_value) =>
LocalEvents.publish( LocalEvents.publish(
LocalServiceResponse( LocalServiceResponse(
@ -105,6 +137,34 @@ class LocalService(zone: Zone) extends Actor {
LocalResponse.SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value) 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) => case LocalAction.RouterTelepadTransport(player_guid, passenger_guid, src_guid, dest_guid) =>
LocalEvents.publish( LocalEvents.publish(
LocalServiceResponse( LocalServiceResponse(
@ -357,6 +417,16 @@ class LocalService(zone: Zone) extends Actor {
val cause = damage_func(target) val cause = damage_func(target)
sender() ! Vitality.DamageResolution(target, cause) 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 => case msg =>
log.warn(s"Unhandled message $msg from ${sender()}") log.warn(s"Unhandled message $msg from ${sender()}")
} }

View file

@ -1,17 +1,20 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.services.local package net.psforever.services.local
import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle}
import net.psforever.objects.ce.Deployable import net.psforever.objects.ce.Deployable
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.hackable.Hackable 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.serverobject.terminals.capture.CaptureTerminal
import net.psforever.objects.vehicles.Utility import net.psforever.objects.vehicles.Utility
import net.psforever.objects.zones.Zone 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.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.services.hart.HartTimer.OrbitalShuttleEvent
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3} import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
@ -49,14 +52,37 @@ object LocalAction {
) extends Action ) extends Action
final case class ClearTemporaryHack(player_guid: PlanetSideGUID, target: PlanetSideServerObject with Hackable) final case class ClearTemporaryHack(player_guid: PlanetSideGUID, target: PlanetSideServerObject with Hackable)
extends Action extends Action
final case class ResecureCaptureTerminal(target: CaptureTerminal) extends Action final case class ResecureCaptureTerminal(target: CaptureTerminal) extends Action
final case class StartCaptureTerminalHack(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( final case class SendPlanetsideAttributeMessage(
player_guid: PlanetSideGUID, player_guid: PlanetSideGUID,
target: PlanetSideGUID, target: PlanetSideGUID,
attribute_number: PlanetsideAttributeEnum, attribute_number: PlanetsideAttributeEnum,
attribute_value: Long attribute_value: Long
) extends Action ) 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( final case class RouterTelepadTransport(
player_guid: PlanetSideGUID, player_guid: PlanetSideGUID,
passenger_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.{PlanetSideGameObject, TelepadDeployable, Vehicle}
import net.psforever.objects.ce.Deployable 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.serverobject.terminals.{ProximityUnit, Terminal}
import net.psforever.objects.vehicles.Utility 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.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game._ import net.psforever.packet.game._
@ -33,8 +38,18 @@ object LocalResponse {
) extends Response ) extends Response
final case class SendHackMessageHackCleared(target_guid: PlanetSideGUID, unk1: Long, unk2: Long) 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 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) final case class SendPlanetsideAttributeMessage(target_guid: PlanetSideGUID, attribute_number: PlanetsideAttributeEnum, attribute_value: Long)
extends Response 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 ObjectDelete(item_guid: PlanetSideGUID, unk: Int) extends Response
final case class ProximityTerminalAction(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject) final case class ProximityTerminalAction(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject)
extends Response 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 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.actors.zone.{BuildingActor, ZoneActor}
import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.hackable.Hackable 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.structures.Building
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.objects.{Default, GlobalDefinitions} 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.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import scala.concurrent.duration.{FiniteDuration, _} 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[this] val log = org.log4s.getLogger
private var clearTrigger: Cancellable = Default.Cancellable private var clearTrigger: Cancellable = Default.Cancellable
@ -61,6 +66,7 @@ class HackCaptureActor extends Actor {
RestartTimer() RestartTimer()
NotifyHackStateChange(target, isResecured = false) NotifyHackStateChange(target, isResecured = false)
TrySpawnCaptureFlag(target)
case HackCaptureActor.ProcessCompleteHacks() => case HackCaptureActor.ProcessCompleteHacks() =>
log.trace("Processing complete hacks") log.trace("Processing complete hacks")
@ -75,7 +81,19 @@ class HackCaptureActor extends Actor {
val hackedByFaction = entry.target.HackedBy.get.hackerFaction val hackedByFaction = entry.target.HackedBy.get.hackerFaction
entry.target.Actor ! CommonMessages.ClearHack() 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 // 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, _) => case HackCaptureActor.ResecureCaptureTerminal(target, _) =>
hackedObjects = hackedObjects.filterNot(x => x.target == 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) NotifyHackStateChange(target, isResecured = true)
// Restart the timer in case the object we just removed was the next one scheduled // Restart the timer in case the object we just removed was the next one scheduled
RestartTimer() 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 _ => ; case _ => ;
} }
private def NotifyHackStateChange(target: CaptureTerminal, isResecured: Boolean): Unit = { private def TrySpawnCaptureFlag(terminal: CaptureTerminal): Unit = {
val attribute_value = HackCaptureActor.GetHackUpdateAttributeValue(target, isResecured) // 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 // Notify all clients that CC has been hacked
target.Zone.LocalEvents ! LocalServiceMessage( terminal.Zone.LocalEvents ! LocalServiceMessage(
target.Zone.id, terminal.Zone.id,
LocalAction.SendPlanetsideAttributeMessage( LocalAction.SendPlanetsideAttributeMessage(
PlanetSideGUID(-1), PlanetSideGUID(-1),
target.GUID, terminal.GUID,
PlanetsideAttributeEnum.ControlConsoleHackUpdate, PlanetsideAttributeEnum.ControlConsoleHackUpdate,
attribute_value attribute_value
) )
) )
val owner = terminal.Owner.asInstanceOf[Building]
// Notify parent building that state has changed // 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 // 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 = { private def HackCompleted(terminal: CaptureTerminal with Hackable, hackedByFaction: PlanetSideEmpire.Value): Unit = {
@ -117,6 +182,9 @@ class HackCaptureActor extends Actor {
if (building.NtuLevel > 0) { if (building.NtuLevel > 0) {
log.info(s"Setting base ${building.GUID} / MapId: ${building.MapId} as owned by $hackedByFaction") log.info(s"Setting base ${building.GUID} / MapId: ${building.MapId} as owned by $hackedByFaction")
building.Actor! BuildingActor.SetFaction(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 { } else {
log.info("Base hack completed, but base was out of NTU.") 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 ResecureCaptureTerminal(target: CaptureTerminal, zone: Zone)
final case class FlagCaptured(flag: CaptureFlag)
private final case class ProcessCompleteHacks() private final case class ProcessCompleteHacks()
@ -172,11 +241,19 @@ object HackCaptureActor {
hack_timestamp: Long 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) { if (isResecured) {
17039360L 17039360L
} else { } else {
target.HackedBy match { terminal.HackedBy match {
case Some(Hackable.HackInfo(_, _, hackingFaction, _, start, length)) => case Some(Hackable.HackInfo(_, _, hackingFaction, _, start, length)) =>
// See PlanetSideAttributeMessage #20 documentation for an explanation of how the timer is calculated // See PlanetSideAttributeMessage #20 documentation for an explanation of how the timer is calculated
val hack_time_remaining_ms = 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_REMOVE_VANUMODULE extends ChatMessageType // /moduleremove OR /modulerm
case object CMT_DEBUG_MASSIVE extends ChatMessageType // /debugmassive case object CMT_DEBUG_MASSIVE extends ChatMessageType // /debugmassive
case object CMT_WARP_TO_NEXT_BILLBOARD extends ChatMessageType // ??? case object CMT_WARP_TO_NEXT_BILLBOARD extends ChatMessageType // ???
case object UNK_222 extends ChatMessageType // ??? "CTF Flag stolen" case object UNK_222 extends ChatMessageType // "CTF Flag stolen"
case object UNK_223 extends ChatMessageType // ??? "CTF Flag lost" // 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_224 extends ChatMessageType // ??? "Vehicle Dismount"
case object UNK_225 extends ChatMessageType // ??? empty case object UNK_225 extends ChatMessageType // ??? empty
case object UNK_226 extends ChatMessageType // ??? empty case object UNK_226 extends ChatMessageType // ??? empty
case object UNK_227 extends ChatMessageType // ??? empty case object UNK_227 extends ChatMessageType // ??? empty
case object UNK_228 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_230 extends ChatMessageType // ??? "Vehicle Mount"
case object UNK_231 extends ChatMessageType // ??? empty case object UNK_231 extends ChatMessageType // ??? empty
case object UNK_232 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.definition.BasicDefinition
import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.generator.Generator 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.locks.IFFLock
import net.psforever.objects.serverobject.pad.{VehicleSpawnPad, VehicleSpawnPadDefinition} import net.psforever.objects.serverobject.pad.{VehicleSpawnPad, VehicleSpawnPadDefinition}
import net.psforever.objects.serverobject.painbox.{Painbox, PainboxDefinition} import net.psforever.objects.serverobject.painbox.{Painbox, PainboxDefinition}
@ -338,6 +339,15 @@ object Zones {
), ),
owningBuildingGuid = ownerGuid owningBuildingGuid = ownerGuid
) )
case "llm_socket" =>
zoneMap.addLocalObject(
obj.guid,
CaptureFlagSocket.Constructor(
obj.objectDefinition.asInstanceOf[CaptureFlagSocketDefinition],
obj.position
),
owningBuildingGuid = ownerGuid
)
case "gr_door_mb_orb" => case "gr_door_mb_orb" =>
zoneMap 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.y mustEqual 0f
flag.pos.orient.z mustEqual 47.8125f flag.pos.orient.z mustEqual 47.8125f
flag.faction mustEqual PlanetSideEmpire.NC flag.faction mustEqual PlanetSideEmpire.NC
flag.unk1 mustEqual 21 flag.owningBaseGuid mustEqual 21
flag.unk2 mustEqual 4 flag.targetBaseGuid mustEqual 4
flag.unk3 mustEqual 2838 flag.milliseconds_remaining mustEqual 592662
flag.unk4 mustEqual 9
case _ => case _ =>
ko ko
} }
} }
"encode" in { "encode" in {
val obj = CaptureFlagData( val obj = CaptureFlagData(PlacementData(3912.0312f, 5169.4375f, 59.96875f, 0f, 0f, 47.8125f), PlanetSideEmpire.NC, 21, 4, 592662)
PlacementData(3912.0312f, 5169.4375f, 59.96875f, 0f, 0f, 47.8125f),
PlanetSideEmpire.NC,
21,
4,
2838,
9
)
val msg = ObjectCreateMessage(ObjectClass.capture_flag, PlanetSideGUID(4330), obj) val msg = ObjectCreateMessage(ObjectClass.capture_flag, PlanetSideGUID(4330), obj)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string_captureflag pkt mustEqual string_captureflag

View file

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