{"FileModelVersion":{"Major":1,"Minor":0},"Name":"V7_Bomber_Diplomat","Version":29,"SavedTotalBlockCount":1270,"SavedMaterialCost":91674.0,"ContainedMaterialCost":1072.0,"ItemDictionary":{"1102":"a6cfd078-bc39-4ad8-a47f-49097913a27b","1061":"54b18bde-2698-4840-a2aa-a37efba89ff4","1100":"90c9965a-1dcc-4786-a2d2-6299fed7260f","1083":"ba5c8d03-9093-47a9-a8cd-b721ceeee1dd","1068":"9411e401-27da-4546-b805-3334f200f055","1029":"e3921d2a-d812-4c80-a4f6-4360b1af8631","1066":"8f9dbf41-6c2d-4e7b-855d-b2432c6942a2","227":"3cc75979-18ac-46c4-9a5b-25b327d99410","1075":"911fe222-f9b2-4892-9cd6-8b154d55b2aa","1067":"649f2aec-6f59-4157-ac01-0122ce2e6dad","1044":"ccb16d68-186c-4896-82e3-9e9213271ace","1062":"fdba2b5d-3570-43fb-a9e1-10a0c0bba0ef","1078":"2a3905ff-2030-421d-a2bf-90fba71c1c5e","1080":"95a626e6-f1b8-491a-aa31-8de5a2beb513","1073":"9af822b3-80d9-46bd-9ce0-e6e5c2e61da4","1084":"967f98c3-d708-4602-9c71-0f34562cdfca","1099":"a4b0d100-c480-4697-b606-489d80a6d376","1076":"c6176cb5-0a32-4d68-a749-8ee33b2230c1","1081":"51b37dbf-2beb-425b-a817-89434838c857","1104":"27692d7a-c05f-4ac4-ac98-f25e359faebc","1106":"2b1b3011-0b81-472e-aa42-26383f453268","1054":"903ed616-47a5-4aa2-a85c-f3e18ce9e620","1086":"afb33fc2-3071-408a-b312-da22478c0a14","1650":"a6ad7eaa-e826-42d5-b4f5-7358a842623f","1654":"020fae5d-c759-4456-87e6-7e423752a427","1028":"4929dc91-e2af-42cd-a672-b86571a0b92f","1043":"20f74da8-3052-4e5a-8b9a-28252781f37b","1079":"62f61c31-a453-44ce-87a2-b5ce142fb052","1074":"b5d46bb7-d49e-42f6-817b-323fbf6d62b0","1103":"ad3e7665-cf63-4135-835f-2f743c69bb36","1053":"77b5b1be-56a8-4699-b9fb-a967a03897c5","848":"639405b3-d2bf-4eb6-aef3-72f7e62bf151","849":"2cf00d3a-02a2-4d77-a432-9b1e7d52aa3a","851":"26b0f142-9419-41f1-9c13-3a2b62437f60","810":"310a7a9a-8d4e-4365-a877-1a8e2bac2a81","858":"d30acc39-3742-47bd-82de-f5e1b9af565d","1195":"97e8a443-a784-4ca7-a47a-b13fdcebbb99","1199":"df5b0e98-14a7-4f72-9036-3036486c4f60","75":"0d1d97b5-3c99-45bf-8a5d-e7a624b3ddb6","113":"e6bb2b09-daee-432d-9aff-45fc63d24fca","1023":"accbfca6-ba3b-49df-83e3-96007b1dc571","771":"9a564782-3a07-472b-ae06-5a7dfd123e7d","38":"3a59c6f8-59dc-4a12-8fd3-c509a9d84906","1022":"df7ab21c-f0ed-4d37-9348-67c652239a48","640":"cf1cffd2-27e9-45b7-9720-0025e890f99b","641":"f05191b9-468c-407b-9601-453a46fbaf1a","739":"46fc87ec-0f7f-4c55-a5d3-721365c5f364","664":"9413049d-a4f7-4c1c-8b8a-d2f1f4c18e53","61":"edd65760-896f-4f6a-ba8c-eb2b072635f4","110":"2c557c38-4136-436a-bae2-c5cd1102690c","66":"9d2ff3d6-abc2-4378-b47c-cad42d4af1bc","64":"f29b1372-f4f3-4305-aa85-5fe214361fb7","65":"26a604f0-071c-49ca-8a0e-e1d9c959b1c5","87":"3ed4d2a4-62ce-4807-b516-6da685804e1e","115":"a56e0dcf-94e4-48a0-b8b9-5e33e0ab69cf","752":"d3924bae-be5a-43a0-be19-64fc7c8d6059","734":"a3d914e9-697d-425f-abda-a6b21b4de952","1599":"c7a19161-b361-4074-8c51-2398a0a70d1b","1598":"d47815a1-9052-4885-8d17-8c9cb3eab72b","819":"87d9e52e-6386-4e93-ae29-aab2522f3edb","820":"cd788eca-ec52-40d2-bf17-48a6d47e569e","822":"9424812a-274e-4223-a3ae-9ee86a7db46b","811":"86a6eb4b-cb64-4451-9dc5-94efff6b8ed6","68":"f1070c58-4f5d-40c2-b5ed-8c08673d71a5","825":"9815045c-62a7-4bef-90f6-b385a26754ca","1820":"e62413c3-e841-4eda-9e2a-ceaec75e8b64","809":"154e5001-729c-4a9f-983b-342d3092a0f3","818":"4aa8cfcb-7aeb-430b-9726-f8bb8d8f4124","662":"4f87eff7-616b-4663-8f11-cd7a7ddd3649","738":"0706d2b0-c244-4e0f-8577-79338cb073cf","1423":"2a22f176-01c2-42f2-a7d2-2c7054504aa9","1424":"46f54639-5f91-4731-93eb-e5c0a7460538","758":"bf0db0d2-8582-41d2-bcfe-5299bec3d06b","88":"5292dcab-a674-467a-a21a-702d2c2a152b","540":"e63040c9-0027-4fd3-be30-67fe3e950140","528":"cdca5dec-eeee-4849-9b2e-73b23e216465","197":"97ab0a32-c1c7-4663-a497-574fb15d9318","190":"3dd5c28f-501e-436a-821d-46874ca9c5d1","199":"1fb31e58-d0bf-4234-8e92-ae6e46869e55","192":"84b391ce-db19-4cd2-b034-e520fef31cf0","207":"0096dd3a-44ff-4a6d-b51a-a79973bc8163","1425":"a7f5d8de-4882-4111-9d01-436493e5b2d8","126":"d93e3937-d015-44bc-95d4-84a4d264de32","527":"788700be-1e74-46d7-9e34-1a5ec2747548","228":"ab699540-efc8-4592-bc97-204f6a874b3a","629":"82b2446e-c12e-436a-8340-76d38372ead1","1107":"f1a3f1bd-b5f7-43bf-9b72-c7a060c24f73","1202":"c0be616d-9f71-48e3-bcc7-357b07f6d0b4","1653":"bdf96eb8-6d43-4b2e-8610-68952484ca04","1186":"48360916-a894-4bd8-b8b5-4595bb7ccf2e","92":"1bb2d71d-cd5f-4a23-844b-8005dec7bd5c","77":"62e2b663-c26f-461e-ae26-43068010f993","100":"10a165d1-1333-4c57-a942-b857542c4b45","224":"0c03433e-8947-4e7d-9dec-793526fe06d1"},"Blueprint":{"ContainedMaterialCost":1072.0,"CSI":[4.0,8.0,-1.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,54218.339643,58.633636,1.0,1.0,0.0,1.0,136.708206,624.603088,-1.0,0.0,65000.0,1.0,1.0,0.0,1.0,0.0,0.0,466.740021,70.0,0.1,0.1,1.0,1.0,3.0,-1.0,0.0,-1.0,0.0,76.800407,1.0,58.633636,-1.0,0.0,0.0,0.0,0.0,0.0,50.0,720.0,-1.0,2683.699951,1703.949463,2.931437,0.0,0.0,500.0,-1.0,12.0,0.0,0.0,0.424462,7.943928,0.0,0.0,0.0,0.0,0.122227,0.0,0.0,0.0,0.0,1.0,1.0,0.0,0.0,-1.0,-1.0],"COL":["0.011764,0,0,0","1,0,0,0.5","0,1,0,0.5","0,0,1,0.5","0.1,0.1,0.1,0.5","0.3,0.3,0.3,0.5","0.5,0.5,0.5,0.5","1,1,1,0.1","1,1,1,0.5","1,1,1,0.99","1,0.921568,0.015686,0.99","0,1,1,0.99","1,0,1,0.99","0.141341,0.017046,0.841524,0.393345","0.948217,0.82744,0.421474,0.289292","0.834057,0.556662,0.014432,0.971673","0.04401,0.193172,0.145379,0.728312","0.855953,0.12804,0.022378,0.442508","0.877442,0.751758,0.120519,0.694579","0.325394,0.281061,0.4414,0.016855","0.509432,0.19256,0.614911,0.75343","0.195856,0.353375,0.563212,0.827161","0.26868,0.818015,0.434873,0.348313","0.383587,0.22171,0.040169,0.138522","0.738855,0.388219,0.829737,0.547114","0.214044,0.296311,0.967564,0.492633","0.855545,0.153684,0.441526,0.385297","0,0,0,0","0,0,0,0","0,0,0,0","0,0,0,0","0,0,0,0"],"SCs":[{"ContainedMaterialCost":0.0,"CSI":[-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0],"COL":null,"SCs":[{"ContainedMaterialCost":0.0,"CSI":[-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0],"COL":null,"SCs":[],"BLP":["0,0,0","0,0,-1","0,1,2","0,1,1","0,1,-1","0,1,-5","0,2,-4","0,2,-2","0,2,0","0,3,-5","0,3,-3","0,3,-1","0,0,1","0,0,4","0,1,3","0,2,2","0,3,1","0,4,0","0,4,-1","-1,0,0","-1,1,-1","-1,2,-2","-1,3,-3","-1,4,-4","1,4,-4","1,3,-3","1,2,-2","1,1,-1","1,0,0","-1,4,-5","1,4,-5","-1,3,-6","-1,2,-6","-1,1,-6","-1,0,-6","0,2,-6","0,0,-5","0,1,-7","1,0,-6","1,1,-6","1,2,-6","1,3,-6","0,3,-8","0,2,-8","0,1,-8","0,0,-8","0,4,-7","0,4,-8","0,0,5","0,1,4","0,2,3","0,3,2","0,4,1","0,5,0","-1,3,-5","-1,3,-4","-1,2,-3","-1,1,-2","-1,0,-1","1,0,-1","1,0,-2","1,0,-3","1,0,-4","1,0,-5","0,5,-7","0,6,-4","0,5,-6","0,4,-6","0,5,-5","0,5,-4","-1,5,-4","1,5,-4","-1,5,-5","1,5,-5","0,6,-5","0,6,-6","0,4,-5","0,3,-6","0,3,-4","0,3,-2","0,3,0","0,2,1","0,2,-1","0,2,-3","0,2,-5","0,1,-6","0,1,-3","0,1,-4","0,1,-2","0,1,0"],"BLR":[0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,18,18,18,18,18,16,16,16,16,16,19,17,19,19,19,19,0,14,10,17,17,17,17,2,2,2,2,2,2,8,8,8,8,8,8,7,7,7,7,0,0,11,11,11,11,2,0,0,6,0,0,18,0,18,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"BP1":null,"BP2":null,"BCI":[9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,9,9,9,9,9,1,9,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,9,9,9,9,9,9,9,9,0,9,9,9,9,9,9,9,9,0,0,0,0,0,0,0,0,0,0,9,0,0,0],"BEI":null,"BlockData":"AAAAKgAAACAAAgAAAAAAAAMAAAAADgABAAAAABUAJIgAAAAZAF0AAAAAGQBdGwEAABkAAgAEzczMvQMABJqZGb5kAAQAAPBBCQABAQAABA8AAABDAAAOAAAABwBpAQAAAAAAXRsBAAAAAAAABM0AAABIAAAOAAAABwAAAAAAAAAAXRsBAAAAAAAABM4AAABJAAAOAAAABwAAAAAAAAAAXRsBAAAAAAAABM8AAABKAAAOAAAABwAAAAAAAAAAXRsBAAAAAAAABNAAAABLAAAOAAAABwAAAAAAAAAAXRsBAAAAAAAABNcAAAA=","VehicleData":"8FsKAQAAAAAOAAAAAAAPJwAAAAAA0AcAAAAAAJ9xBAAAAAAADgAAAAAADycAAAAAAKCGAQAAAABXLgAAAAAAAAcAAAAAAAAAAAAAAABHKQEAAAAAABUAAAAAAAAAAAAAAABzJwAAAAAAZAAAAAAAAA==","designChanged":false,"blueprintVersion":0,"blueprintName":"Spin  Turn block","SerialisedInfo":{"JsonDictionary":{},"IsEmpty":true},"Name":null,"ItemNumber":527,"LocalPosition":"0,3,0","LocalRotation":"0,0,0,-0.999999","ForceId":0,"TotalBlockCount":90,"MaxCords":"1,6,5","MinCords":"-1,0,-8","BlockIds":[528,1425,228,629,629,629,629,629,629,629,629,629,1424,1650,1650,1650,1650,1650,1425,1061,1061,1061,1061,1061,1062,1062,1062,1062,1062,1044,1043,1076,1076,1076,1076,629,1424,1424,1076,1076,1076,1076,1107,1107,1107,1107,1650,1654,1654,1654,1654,1654,1654,1654,1068,1068,1067,1066,227,227,1066,1067,1068,1068,1654,1202,64,113,64,1653,1186,1186,92,92,77,100,224,224,224,224,224,224,224,224,224,224,629,224,224,224],"BlockState":"=0,90","AliveCount":90,"BlockStringData":[],"BlockStringDataIds":[],"GameVersion":"4.1.2.0","PersistentSubObjectIndex":6,"PersistentBlockIndex":-1,"AuthorDetails":{"Valid":true,"ForeignBlocks":0,"CreatorId":"600c8935-f843-451b-9c6a-2949e940f82f","ObjectId":"c7cdfecd-189c-4818-b73d-e3fecb3aa890","CreatorReadableName":"Pendaelose","HashV1":"a738e75089417f785b2ec2114b65e631"},"BlockCount":90}],"BLP":["0,0,0","0,1,0","1,1,0","-1,1,0","1,1,-1","0,1,-1","-1,1,-1","-1,1,-2","-1,1,-3","-1,1,-4","-1,1,-5","-1,1,-6","-1,1,-7","0,1,-7","1,1,-7","1,1,-6","1,1,-5","1,1,-4","1,1,-3","1,1,-2","0,1,-2","0,1,-3","0,1,-4","0,1,-5","0,1,-6","-1,1,-8","0,1,-8","1,1,-8","1,1,-9","0,1,-9","-1,1,-9","-1,1,1","0,1,1","1,1,1","1,1,2","1,1,3","1,1,4","1,1,5","1,1,6","1,1,7","1,1,8","0,1,8","-1,1,8","-1,1,7","-1,1,6","-1,1,5","-1,1,4","-1,1,3","-1,1,2","0,1,2","0,1,3","0,1,4","0,1,5","0,1,6","0,1,7","-1,1,9","0,1,9","1,1,9","0,0,1","0,0,-1","-1,0,1","1,0,1","-1,1,10","0,1,10","1,1,10","1,0,9","0,0,9","-1,0,9","-2,1,9","-1,2,9","0,2,9","1,2,9","2,1,9","2,0,9","2,2,9","-2,2,9","-2,0,9","1,0,5","0,0,5","-1,0,5","-2,1,5","-1,2,5","0,2,5","1,2,5","2,1,5","1,0,2","1,0,-4","-1,0,-4","-1,0,2","1,2,0","2,0,0","-2,2,0","-2,1,1","-2,0,1","-2,2,1","2,2,1","2,0,1","2,0,5","2,2,5","-2,2,5","-2,0,5","2,1,1","2,2,-1","-2,2,-1","-2,0,-1","2,0,-1","2,0,-5","2,2,-5","-2,2,-5","-2,0,-5","-2,1,-5","-2,1,-1","-1,2,-5","0,2,-5","1,2,-5","1,2,-1","0,2,-1","-1,2,-1","-1,2,4","0,2,4","1,2,4","1,0,-5","0,0,-5","-1,0,-5","-1,1,-10","0,1,-10","1,1,-10","2,1,-5","2,1,-1","2,2,-9","-2,2,-9","-2,0,-9","2,0,-9","2,1,-9","1,2,-9","0,2,-9","-1,2,-9","-2,1,-9","-1,0,-9","0,0,-9","1,0,-9"],"BLR":[0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,19,19,19,19,19,19,12,12,12,18,0,0,0,16,16,0,18,12,16,16,16,16,16,16,16,16,16,16,16,16,21,8,4,0,12,18,0,16,12,16,0,18,18,17,2,19,14,17,2,19,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,0,0,0,14,14,17,2,19,14,17,2,2,2,19,14,14,14],"BP1":null,"BP2":null,"BCI":[9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9],"BEI":null,"BlockData":"AAAAKgAAACAAAgAAAAAAAAMAAAAADgABAAAAABUAJIgAAAAZAF0AAAAAGQBdGwEAABkAAgAEzczMPQMABJqZGT5kAAQAAPBBCQABAQAABAUAAAABAAAjAAAAHACJAAAAAAAA7GIAAAAAAIZ7AAAAAABdAAAAABUAXRsBAAAVAAEABJqZGb8EAAQAAIA/BwAEAACAPwAABAYAAAACAAAjAAAAHACJAAAAAAAA7GIAAAAAAIZ7AAAAAABdAAAAABUAXRsBAAAVAAEABJqZGb8EAAQAAIA/BwAEAACAPwAABAcAAAADAAAjAAAAHACJAAAAAAAA7GIAAAAAAIZ7AAAAAABdAAAAABUAXRsBAAAVAAEABJqZGb8EAAQAAIA/BwAEAACAPwAABAgAAAA=","VehicleData":"8FsKAQAAAAAOAAAAAAAPJwAAAAAA0AcAAAAAAJ9xBAAAAAAADgAAAAAADycAAAAAAKCGAQAAAABXLgAAAAAAAAcAAAAAAAAAAAAAAABHKQEAAAAAABUAAAAAAAAAAAAAAABzJwAAAAAAZAAAAAAAAA==","designChanged":false,"blueprintVersion":0,"blueprintName":"Spin  Turn block","SerialisedInfo":{"JsonDictionary":{},"IsEmpty":true},"Name":null,"ItemNumber":527,"LocalPosition":"12,2,4","LocalRotation":"0,0,-0.707106,0.707106","ForceId":0,"TotalBlockCount":231,"MaxCords":"2,3,10","MinCords":"-2,0,-10","BlockIds":[528,197,197,197,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,199,199,199,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,207,207,207,1425,1425,1424,1424,126,126,126,1076,1076,1076,1076,1076,1076,1076,1076,1100,1100,1100,1100,1068,1068,1068,1068,1068,1068,1068,1068,1067,1067,1067,1067,1067,1067,1067,1068,1083,1083,1083,1083,1073,1073,1073,1073,1068,1083,1083,1083,1083,1073,1073,1073,1073,1068,1068,1068,1068,1068,1068,1068,1068,1068,1068,1068,1068,1068,1068,126,126,126,1068,1068,1100,1100,1100,1100,1076,1076,1076,1076,1076,1076,1076,1076],"BlockState":"=0,141","AliveCount":141,"BlockStringData":[],"BlockStringDataIds":[],"GameVersion":"4.1.2.0","PersistentSubObjectIndex":5,"PersistentBlockIndex":-1,"AuthorDetails":{"Valid":true,"ForeignBlocks":0,"CreatorId":"600c8935-f843-451b-9c6a-2949e940f82f","ObjectId":"c6f4a1af-07e1-495d-b47b-13cfb10c67d6","CreatorReadableName":"Pendaelose","HashV1":"a1754f26013e6d97d016e66709ebeefd"},"BlockCount":141},{"ContainedMaterialCost":0.0,"CSI":[-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0],"COL":null,"SCs":[{"ContainedMaterialCost":0.0,"CSI":[-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0],"COL":null,"SCs":[],"BLP":["0,0,0","0,0,-1","0,1,-1","0,1,-3","0,1,-5","0,2,-4","0,2,-2","0,2,0","0,3,-5","0,3,-3","0,3,-1","0,0,1","0,0,4","0,1,3","0,2,2","0,3,1","0,4,0","0,4,-1","-1,0,0","-1,1,-1","-1,2,-2","-1,3,-3","-1,4,-4","1,4,-4","1,3,-3","1,2,-2","1,1,-1","1,0,0","-1,4,-5","1,4,-5","-1,3,-6","-1,2,-6","-1,1,-6","-1,0,-6","0,2,-6","0,0,-5","0,1,-7","1,0,-6","1,1,-6","1,2,-6","1,3,-6","0,3,-8","0,2,-8","0,1,-8","0,0,-8","0,4,-7","0,4,-8","0,0,5","0,1,4","0,2,3","0,3,2","0,4,1","0,5,0","0,5,-4","-1,3,-5","-1,3,-4","-1,2,-3","-1,1,-2","-1,0,-1","1,0,-1","1,0,-2","1,0,-3","1,0,-4","1,0,-5","0,6,-4","0,5,-7","0,5,-6","0,6,-5","1,5,-4","-1,5,-4","0,4,-6","0,5,-5","-1,5,-5","1,5,-5","0,6,-6","0,1,1","0,1,2","0,4,-5","0,3,-6","0,3,-4","0,3,-2","0,3,0","0,2,1","0,2,-1","0,2,-3","0,2,-5","0,1,-6","0,1,-4","0,1,-2","0,1,0"],"BLR":[0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,18,18,18,18,18,16,16,16,16,16,19,17,19,19,19,19,0,14,10,17,17,17,17,2,2,2,2,2,2,8,8,8,8,8,8,0,7,7,7,7,0,0,11,11,11,11,0,2,0,0,0,18,4,0,18,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"BP1":null,"BP2":null,"BCI":[9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,9,1,9,9,9,9,9,9,9,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0],"BEI":null,"BlockData":"AAAAKgAAACAAAgAAAAAAAAMAAAAADgABAAAAABUAJIgAAAAZAF0AAAAAGQBdGwEAABkAAgAEzczMvQMABJqZGT5kAAQAAPBBCQABAQAABA8AAABDAAAOAAAABwAAAAAAAAAAXRsBAAAAAAAABL0AAABGAAAOAAAABwBpAQAAAAAAXRsBAAAAAAAABNEAAABIAAAOAAAABwAAAAAAAAAAXRsBAAAAAAAABNIAAABJAAAOAAAABwAAAAAAAAAAXRsBAAAAAAAABNMAAABKAAAOAAAABwAAAAAAAAAAXRsBAAAAAAAABNYAAAA=","VehicleData":"8FsKAQAAAAAOAAAAAAAPJwAAAAAA0AcAAAAAAJ9xBAAAAAAADgAAAAAADycAAAAAAKCGAQAAAABXLgAAAAAAAAcAAAAAAAAAAAAAAABHKQEAAAAAABUAAAAAAAAAAAAAAABzJwAAAAAAZAAAAAAAAA==","designChanged":false,"blueprintVersion":0,"blueprintName":"Spin  Turn block","SerialisedInfo":{"JsonDictionary":{},"IsEmpty":true},"Name":null,"ItemNumber":527,"LocalPosition":"0,3,0","LocalRotation":"0,0,0,-0.999999","ForceId":0,"TotalBlockCount":90,"MaxCords":"1,6,5","MinCords":"-1,0,-8","BlockIds":[528,1425,629,629,629,629,629,629,629,629,629,1424,1650,1650,1650,1650,1650,1425,1061,1061,1061,1061,1061,1062,1062,1062,1062,1062,1044,1043,1076,1076,1076,1076,629,1424,1424,1076,1076,1076,1076,1107,1107,1107,1107,1650,1654,1654,1654,1654,1654,1654,1654,1653,1068,1068,1067,1066,227,227,1066,1067,1068,1068,1202,1654,64,77,1186,1186,113,64,92,92,100,629,228,224,224,224,224,224,224,224,224,224,224,224,224,224],"BlockState":"=0,90","AliveCount":90,"BlockStringData":[],"BlockStringDataIds":[],"GameVersion":"4.1.2.0","PersistentSubObjectIndex":8,"PersistentBlockIndex":-1,"AuthorDetails":{"Valid":true,"ForeignBlocks":0,"CreatorId":"600c8935-f843-451b-9c6a-2949e940f82f","ObjectId":"c7cdfecd-189c-4818-b73d-e3fecb3aa890","CreatorReadableName":"Pendaelose","HashV1":"a738e75089417f785b2ec2114b65e631"},"BlockCount":90}],"BLP":["0,0,0","0,1,0","1,1,0","-1,1,0","1,1,-1","0,1,-1","-1,1,-1","-1,1,-2","-1,1,-3","-1,1,-4","-1,1,-5","-1,1,-6","-1,1,-7","0,1,-7","1,1,-7","1,1,-6","1,1,-5","1,1,-4","1,1,-3","1,1,-2","0,1,-2","0,1,-3","0,1,-4","0,1,-5","0,1,-6","-1,1,-8","0,1,-8","1,1,-8","-1,1,1","0,1,1","1,1,1","1,1,2","1,1,3","1,1,4","1,1,5","1,1,6","1,1,7","1,1,8","0,1,8","-1,1,8","-1,1,7","-1,1,6","-1,1,5","-1,1,4","-1,1,3","-1,1,2","0,1,2","0,1,3","0,1,4","0,1,5","0,1,6","0,1,7","-1,1,9","0,1,9","1,1,9","0,0,1","0,0,-1","-1,0,1","1,0,1","-1,1,10","0,1,10","1,1,10","1,0,9","0,0,9","-1,0,9","-2,1,9","-1,2,9","0,2,9","1,2,9","2,1,9","2,0,9","2,2,9","-2,2,9","-2,0,9","1,0,5","0,0,5","-1,0,5","-2,1,5","-1,2,5","0,2,5","1,2,5","2,1,5","1,0,2","1,0,-4","-1,0,-4","-1,0,2","1,2,0","2,0,0","-2,2,0","-2,1,1","-2,0,1","-2,2,1","2,2,1","2,0,1","2,0,5","2,2,5","-2,2,5","-2,0,5","2,1,1","2,2,-1","-2,2,-1","-2,0,-1","2,0,-1","2,0,-5","2,2,-5","-2,2,-5","-2,0,-5","-2,1,-5","-2,1,-1","-1,2,-5","0,2,-5","1,2,-5","1,2,-1","0,2,-1","-1,2,-1","-1,2,4","0,2,4","1,2,4","1,0,-5","0,0,-5","-1,0,-5","-1,1,-10","0,1,-10","1,1,-10","2,1,-5","2,1,-1","2,2,-9","-2,2,-9","-2,0,-9","2,0,-9","2,1,-9","1,2,-9","0,2,-9","-1,2,-9","-2,1,-9","-1,0,-9","0,0,-9","1,0,-9","-1,1,-9","0,1,-9","1,1,-9"],"BLR":[0,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,19,19,19,19,19,19,12,12,12,18,0,0,0,16,16,0,18,12,16,16,16,16,16,16,16,16,16,16,16,16,21,8,4,0,12,18,0,16,12,16,0,18,18,17,2,19,14,17,2,19,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,0,0,0,14,14,17,2,19,14,17,2,2,2,19,14,14,14,18,18,18],"BP1":null,"BP2":null,"BCI":[9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9],"BEI":null,"BlockData":"AAAAKgAAACAAAgAAAAAAAAMAAAAADgABAAAAABUAJIgAAAAZAF0AAAAAGQBdGwEAABkAAgAEzczMPQMABJqZGb5kAAQAAPBBCQABAQAABAUAAAABAAAjAAAAIwCJAAAAAAAA7GIAAAAAAIZ7AAAAAABdAAAAABUAXRsBAAAVAAEABJqZGT8EAAQAAIA/BwAEAACAPwAABAYAAAABAARNYWluAgAAIwAAACMAiQAAAAAAAOxiAAAAAACGewAAAAAAXQAAAAAVAF0bAQAAFQABAASamRk/BAAEAACAPwcABAAAgD8AAAQHAAAAAQAETWFpbgMAACMAAAAjAIkAAAAAAADsYgAAAAAAhnsAAAAAAF0AAAAAFQBdGwEAABUAAQAEmpkZPwQABAAAgD8HAAQAAIA/AAAECAAAAAEABE1haW4=","VehicleData":"8FsKAQAAAAAOAAAAAAAPJwAAAAAA0AcAAAAAAJ9xBAAAAAAADgAAAAAADycAAAAAAKCGAQAAAABXLgAAAAAAAAcAAAAAAAAAAAAAAABHKQEAAAAAABUAAAAAAAAAAAAAAABzJwAAAAAAZAAAAAAAAA==","designChanged":false,"blueprintVersion":0,"blueprintName":"Spin  Turn block","SerialisedInfo":{"JsonDictionary":{},"IsEmpty":true},"Name":null,"ItemNumber":527,"LocalPosition":"-12,2,4","LocalRotation":"0,0,0.707106,0.707106","ForceId":0,"TotalBlockCount":231,"MaxCords":"2,3,10","MinCords":"-2,0,-10","BlockIds":[528,197,197,197,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,207,207,207,1425,1425,1424,1424,126,126,126,1076,1076,1076,1076,1076,1076,1076,1076,1100,1100,1100,1100,1068,1068,1068,1068,1068,1068,1068,1068,1067,1067,1067,1067,1067,1067,1067,1068,1083,1083,1083,1083,1073,1073,1073,1073,1068,1083,1083,1083,1083,1073,1073,1073,1073,1068,1068,1068,1068,1068,1068,1068,1068,1068,1068,1068,1068,1068,1068,126,126,126,1068,1068,1100,1100,1100,1100,1076,1076,1076,1076,1076,1076,1076,1076,199,199,199],"BlockState":"=0,141","AliveCount":141,"BlockStringData":[],"BlockStringDataIds":[],"GameVersion":"4.1.2.0","PersistentSubObjectIndex":7,"PersistentBlockIndex":-1,"AuthorDetails":{"Valid":true,"ForeignBlocks":0,"CreatorId":"600c8935-f843-451b-9c6a-2949e940f82f","ObjectId":"c6f4a1af-07e1-495d-b47b-13cfb10c67d6","CreatorReadableName":"Pendaelose","HashV1":"a1754f26013e6d97d016e66709ebeefd"},"BlockCount":141}],"BLP":["-11,0,1","-11,0,6","-11,1,-3","-11,1,1","-11,1,5","-11,1,6","-11,1,10","-11,2,-3","-11,2,1","-11,2,5","-11,2,6","-11,2,10","-11,2,14","-11,3,1","-11,3,5","-11,3,7","-11,3,8","-11,3,12","-10,0,1","-10,0,5","-10,0,6","-10,1,-3","-10,1,1","-10,1,11","-10,2,-3","-10,2,1","-10,2,14","-10,2,15","-10,3,1","-10,3,5","-10,3,6","-10,3,9","-10,3,12","-9,0,5","-9,0,6","-9,0,7","-9,1,2","-9,1,3","-9,1,6","-9,1,9","-9,1,12","-9,2,2","-9,2,15","-9,2,16","-9,3,6","-9,3,7","-9,3,10","-9,3,13","-8,1,3","-8,1,4","-8,1,7","-8,1,10","-8,1,13","-8,2,3","-8,2,16","-8,2,17","-8,3,7","-8,3,8","-8,3,11","-8,3,14","-7,1,13","-7,1,14","-7,2,3","-7,2,17","-7,2,18","-7,2,19","-7,3,7","-7,3,8","-7,3,11","-7,3,15","-6,0,6","-6,0,7","-6,0,8","-6,0,9","-6,0,10","-6,0,14","-6,0,18","-6,1,3","-6,1,14","-6,1,18","-6,2,3","-6,2,18","-6,2,19","-6,2,21","-6,3,7","-6,3,8","-6,3,12","-6,3,16","-5,-1,9","-5,-1,10","-5,-1,14","-5,-1,18","-5,0,6","-5,0,18","-5,0,22","-5,1,2","-5,1,22","-5,2,2","-5,2,19","-5,2,21","-5,2,23","-5,3,6","-5,3,7","-5,3,10","-5,3,13","-5,3,17","-5,3,19","-4,-1,5","-4,-1,9","-4,-1,10","-4,-1,14","-4,-1,18","-4,0,5","-4,0,22","-4,1,22","-4,1,24","-4,2,21","-4,2,23","-4,2,25","-4,3,5","-4,3,6","-4,3,13","-4,3,15","-4,3,19","-4,3,21","-4,4,8","-4,4,9","-4,4,13","-3,-1,4","-3,-1,5","-3,-1,6","-3,-1,10","-3,-1,14","-3,-1,18","-3,-1,22","-3,0,4","-3,0,24","-3,1,24","-3,1,26","-3,2,23","-3,2,25","-3,2,27","-3,3,4","-3,3,17","-3,3,19","-3,3,21","-3,3,23","-3,4,8","-3,4,9","-3,4,13","-3,4,17","-2,-1,3","-2,-1,4","-2,-1,6","-2,-1,14","-2,-1,18","-2,-1,22","-2,-1,26","-2,0,3","-2,0,27","-2,1,28","-2,2,25","-2,2,27","-2,2,29","-2,3,3","-2,3,21","-2,3,23","-2,3,25","-2,4,7","-2,4,17","-2,4,21","-1,-1,3","-1,-1,14","-1,-1,18","-1,-1,22","-1,-1,26","-1,-1,30","-1,0,2","-1,0,31","-1,1,32","-1,2,29","-1,2,33","-1,3,2","-1,3,25","-1,4,6","-1,4,21","0,1,36","0,-1,34","0,-1,35","0,1,37","11,0,1","11,0,6","11,1,-3","11,1,1","11,1,5","11,1,6","11,1,10","11,2,-3","11,2,1","11,2,5","11,2,6","11,2,10","11,2,14","11,3,1","11,3,5","11,3,7","11,3,8","11,3,12","10,0,1","10,0,5","10,0,6","10,1,-3","10,1,1","10,1,11","10,2,-3","10,2,1","10,2,14","10,2,15","10,3,1","10,3,5","10,3,6","10,3,9","10,3,12","9,0,5","9,0,6","9,0,7","9,1,2","9,1,3","9,1,6","9,1,9","9,1,12","9,2,2","9,2,15","9,2,16","9,3,6","9,3,7","9,3,10","9,3,13","8,1,3","8,1,4","8,1,7","8,1,10","8,1,13","8,2,3","8,2,16","8,2,17","8,3,7","8,3,8","8,3,11","8,3,14","7,1,13","7,1,14","7,2,3","7,2,17","7,2,18","7,2,19","7,3,7","7,3,8","7,3,11","7,3,15","6,0,6","6,0,7","6,0,8","6,0,9","6,0,10","6,0,14","6,0,18","6,1,3","6,1,14","6,1,18","6,2,3","6,2,18","6,2,19","6,2,21","6,3,7","6,3,8","6,3,12","6,3,16","5,-1,9","5,-1,10","5,-1,14","5,-1,18","5,0,6","5,0,18","5,0,22","5,1,2","5,1,22","5,2,2","5,2,19","5,2,21","5,2,23","5,3,6","5,3,7","5,3,10","5,3,13","5,3,17","5,3,19","4,-1,5","4,-1,9","4,-1,10","4,-1,14","4,-1,18","4,0,5","4,0,22","4,1,22","4,1,24","4,2,21","4,2,23","4,2,25","4,3,5","4,3,6","4,3,13","4,3,15","4,3,19","4,3,21","4,4,8","4,4,9","4,4,13","3,-1,4","3,-1,5","3,-1,6","3,-1,10","3,-1,14","3,-1,18","3,-1,22","3,0,4","3,0,24","3,1,24","3,1,26","3,2,23","3,2,25","3,2,27","3,3,4","3,3,17","3,3,19","3,3,21","3,3,23","3,4,8","3,4,9","3,4,13","3,4,17","2,-1,3","2,-1,4","2,-1,6","2,-1,14","2,-1,18","2,-1,22","2,-1,26","2,0,3","2,0,27","2,1,28","2,2,25","2,2,27","2,2,29","2,3,3","2,3,21","2,3,23","2,3,25","2,4,7","2,4,17","2,4,21","1,-1,3","1,-1,14","1,-1,18","1,-1,22","1,-1,26","1,-1,30","1,0,2","1,0,31","1,1,32","1,2,29","1,2,33","1,3,2","1,3,25","1,4,6","1,4,21","0,-1,26","0,-1,22","0,-1,18","0,-1,14","0,-1,6","1,-1,4","1,-1,5","0,4,6","0,3,2","0,0,2","0,-1,3","11,0,5","-11,0,5","0,2,33","0,3,26","0,4,22","0,2,30","0,4,18","2,4,13","-2,4,13","2,4,9","-2,4,9","1,4,13","-1,4,13","0,4,14","2,4,8","-2,4,8","-2,-1,10","2,-1,10","10,0,7","-10,0,7","9,0,8","-9,0,8","-1,4,7","1,4,7","1,-1,6","-1,-1,6","2,-1,5","-2,-1,5","-7,1,3","7,1,3","-7,1,2","7,1,2","-7,1,1","7,1,1","-7,1,0","7,1,0","-6,1,6","6,1,6","-7,1,9","7,1,9","-7,1,5","7,1,5","-7,1,4","7,1,4","-7,2,5","7,2,5","-7,2,4","7,2,4","0,0,35","0,0,36","0,0,34","0,1,34","9,2,11","-9,2,11","-8,2,12","8,2,12","7,2,16","7,2,12","7,2,8","-7,2,8","-7,2,12","-7,2,16","0,4,7","1,4,12","-1,4,12","0,4,13","5,0,14","-5,0,14","5,0,13","-5,0,13","5,0,12","-5,0,12","5,0,11","-5,0,11","5,0,10","-5,0,10","5,1,9","-5,1,9","5,1,8","-5,1,8","-2,0,4","2,0,4","3,0,5","-3,0,5","-3,1,20","3,1,20","-3,2,20","3,2,20","5,0,9","-5,0,9","-3,0,20","3,0,20","-6,2,4","6,2,4","-6,2,5","6,2,5","0,-1,30","0,-1,33","0,1,35","0,1,29","0,0,29","-5,2,15","5,2,15","-6,2,10","6,2,10","-6,1,9","6,1,9","-5,0,15","5,0,15","-6,2,14","6,2,14","10,1,3","10,1,5","10,1,7","10,1,9","-10,1,3","-10,1,5","-10,1,7","-10,1,9","-10,1,10","10,1,10","5,1,3","5,2,3","-5,2,3","-5,1,3","0,1,30","0,0,30","1,4,17","-1,4,17","0,-1,10","-1,-1,10","1,-1,10","0,1,24","-1,1,24","1,1,24","0,1,23","0,1,22","0,1,21","0,1,20","1,1,20","1,1,21","1,1,22","1,1,23","-1,1,23","-1,1,22","-1,1,21","-1,1,20","0,2,20","-1,2,20","1,2,20","2,0,20","2,0,21","2,0,22","2,0,23","2,0,24","-2,0,24","-2,0,23","-2,0,22","-2,0,21","-2,0,20","-2,0,19","2,0,19","1,0,20","1,0,21","1,0,22","1,0,23","1,0,24","1,3,24","1,3,23","1,3,22","1,3,21","1,3,20","0,1,-1","1,0,6","-1,0,6","2,1,13","-2,1,13","2,1,12","-2,1,12","2,1,11","-2,1,11","2,1,10","-2,1,10","2,1,9","-2,1,9","2,1,8","-2,1,8","2,1,7","-2,1,7","2,1,6","-2,1,6","2,1,5","-2,1,5","2,1,4","-2,1,4","2,1,3","-2,1,3","2,1,2","-2,1,2","4,1,2","-4,1,2","2,1,0","-2,1,0","2,1,1","-2,1,1","4,1,3","-4,1,3","4,1,4","-4,1,4","4,1,5","-4,1,5","4,1,6","-4,1,6","4,1,7","-4,1,7","4,1,8","-4,1,8","4,1,9","-4,1,9","4,1,10","-4,1,10","4,1,11","-4,1,11","4,1,12","-4,1,12","4,1,13","-4,1,13","4,1,14","-4,1,14","4,1,15","-4,1,15","4,1,16","-2,0,9","2,0,9","-2,0,13","2,0,13","-1,0,14","-2,0,14","3,0,16","4,0,16","-3,1,16","2,1,14","-1,1,14","0,1,14","4,1,1","2,1,-1","-1,1,-1","-3,1,1","-2,2,-2","-4,2,0","1,2,-2","3,2,0","10,2,12","-10,2,12","5,1,20","-5,1,20","9,2,3","-9,2,3","1,3,3","2,3,4","1,3,4","0,3,4","-1,3,4","-2,3,4","-3,3,5","3,3,5","4,3,10","-4,3,10","-3,3,9","3,3,9","-2,3,8","2,3,8","-1,3,8","1,3,8","0,3,8","-3,3,13","3,3,13","-2,3,12","2,3,12","0,2,15","-1,2,15","1,2,15","-2,2,15","2,2,15","-2,2,16","2,2,16","-3,0,16","-4,0,16","1,0,14","2,0,14","0,2,14","1,1,15","-1,1,15","1,3,12","0,3,12","-1,3,12","0,0,12","0,1,0","0,2,0","0,2,11","0,2,10","0,2,9","0,2,8","0,2,7","1,0,15","-1,0,15","0,2,23","0,2,24","0,2,22","1,2,22","1,2,23","1,2,24","-1,2,24","-1,2,23","-1,2,22","1,0,25","1,0,26","1,2,25","1,2,26","1,2,27","1,2,28","1,0,30","-1,0,30","-2,0,26","2,0,26","1,0,27","1,0,28","0,1,25","0,1,26","0,1,27","1,1,28","2,1,27","-2,1,27","-1,1,25","-1,1,26","-1,1,27","1,1,27","1,1,26","1,1,25","0,0,5","1,0,19","1,0,18","1,0,17","1,0,16","1,1,16","1,2,16","1,3,16","1,3,17","1,3,18","1,3,19","0,1,19","0,1,18","0,1,17","0,2,17","0,2,18","0,2,19","-1,2,19","-1,2,18","-1,2,17","-1,1,17","-1,1,18","-1,1,19","1,1,19","1,1,18","1,1,17","1,2,17","1,2,18","1,2,19","2,0,18","2,0,17","-2,0,17","-2,0,18","-2,0,15","-2,0,16","2,0,16","2,0,15","3,0,19","4,1,21","4,2,20","4,2,19","5,1,15","5,1,19","-5,1,15","-4,0,21","-4,0,20","-4,0,19","-5,1,19","-3,0,19","-3,1,19","-3,2,19","3,1,19","3,2,19","1,1,29","-1,1,29","0,1,1","0,2,1","0,1,2","0,0,3","0,0,4","5,2,4","5,1,4","5,2,5","5,1,5","-5,2,5","-5,1,5","-5,2,4","-5,1,4","-4,0,18","-4,1,18","-4,2,18","4,0,18","4,1,18","4,2,18","8,2,10","8,2,8","8,2,6","-8,2,6","-8,2,8","0,2,21","1,2,21","-1,2,21"],"BLR":[19,12,19,19,19,18,12,2,2,19,18,0,0,2,2,2,18,18,14,14,0,14,14,12,17,17,12,0,17,17,12,12,0,14,13,13,14,16,16,16,12,2,12,0,2,12,12,0,14,16,16,16,12,2,12,0,2,12,12,0,19,12,2,12,3,18,2,12,12,0,19,7,7,7,18,18,12,14,18,18,2,12,0,18,2,12,12,0,19,18,18,12,14,12,12,14,18,2,12,0,18,2,12,12,12,18,18,19,19,18,18,12,14,18,0,18,12,0,0,2,12,12,12,18,18,19,0,0,19,19,18,18,18,18,18,14,18,0,18,12,0,0,2,12,12,18,18,2,18,0,0,19,19,18,18,18,18,18,14,18,18,12,0,0,2,12,18,18,2,0,0,14,18,18,18,18,18,14,18,18,0,0,2,0,2,0,12,12,12,12,17,12,17,17,17,16,12,2,2,17,16,0,0,2,2,2,16,16,14,14,0,14,14,12,19,19,12,0,19,19,12,12,0,14,15,15,14,18,18,18,12,2,12,0,2,12,12,0,14,18,18,18,12,2,12,0,2,12,12,0,17,12,2,12,1,16,2,12,12,0,17,5,5,5,16,16,12,14,16,16,2,12,0,16,2,12,12,0,17,16,16,12,14,12,12,14,16,2,12,0,16,2,12,12,12,16,16,17,17,16,16,12,14,16,0,16,12,0,0,2,12,12,12,16,16,17,0,0,17,17,16,16,16,16,16,14,16,0,16,12,0,0,2,12,12,16,16,2,16,0,0,17,17,16,16,16,16,16,14,16,16,12,0,0,2,12,16,16,2,0,0,14,16,16,16,16,16,14,16,16,0,0,2,0,2,0,16,16,16,16,16,21,21,2,2,14,14,17,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,12,18,16,0,0,16,16,0,0,14,14,14,14,14,14,14,14,19,17,19,17,14,14,14,14,14,14,14,14,12,12,12,0,0,0,0,0,17,17,17,17,17,17,0,17,17,17,9,11,9,11,9,11,9,11,9,11,9,11,9,11,16,16,0,0,16,18,16,18,17,17,16,16,2,2,2,2,16,9,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,18,18,16,16,12,12,0,0,16,16,18,0,0,0,0,0,0,0,3,3,3,3,13,13,13,13,0,0,15,8,8,8,8,8,8,8,8,8,8,8,8,21,21,21,21,21,21,21,21,21,21,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,14,14,14,2,2,2,2,2,2,2,2,14,14,14,14,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,14,14,14,14,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,13,13,13,21,21,21,21,21,21,14,14,14,14,21,21,0,0,0,21,19,19,13,13,13,3,3,3,0,21,21,21,21,21,21,21,21,21,21,0,0,0,0,0,0,13,13,13,13,13,13,3,3,3,3,3,3,8,8,8,8,0,0,0,0,0,4,4,4,0,14,0,10,10,10,14,0,0,0,0,0,0,0,0,0,10,10,10,18,18,18,18,16,16,16,16,22,22,22,23,23,23,22,22,22,23,23,0,3,3],"BP1":null,"BP2":null,"BCI":[9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,9,9,9,9,9,9,9,9,1,1,1,1,9,9,9,9,9,9,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,9,9,9,9,9,9,9,9,1,1,1,1,9,9,9,9,9,9,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,1,1,9,9,9,9,9,9,9,9,9,9,1,9,9,9,9,9,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,0,0,0,0,0,0,0,0,0,9,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,0,0,0,0,0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"BEI":null,"BlockData":"nwEADgAAABwAWgAAAAAAAF0bAQAAHADGBARrAAAAHQkEawAAAHQNBGsAAADLEQRrAAAAoAEADgAAABwAWgAAAAAAAF0bAQAAHADGBARDAAAAHQkEQwAAAHQNBEMAAADLEQRDAAAAoQEADgAAABwAWgAAAAAAAF0bAQAAHADGBARrAAAAHQkEawAAAHQNBGsAAADLEQRrAAAAogEADgAAABwAWgAAAAAAAF0bAQAAHADGBARDAAAAHQkEQwAAAHQNBEMAAADLEQRDAAAAowEADgAAABwAWgAAAAAAAF0bAQAAHADGBARrAAAAHQkEawAAAHQNBGsAAADLEQRrAAAApAEADgAAABwAWgAAAAAAAF0bAQAAHADGBARDAAAAHQkEQwAAAHQNBEMAAADLEQRDAAAApQEADgAAABwAWgAAAAAAAF0bAQAAHADGBARrAAAAHQkEawAAAHQNBGsAAADLEQRTAAAApgEADgAAABwAWgAAAAAAAF0bAQAAHADGBARDAAAAHQkEQwAAAHQNBEMAAADLEQRTAAAAqwEAFQAAACoAWgAAAAAAABMAAAAAKgBdGwEAACoAxgQENAAAAFgEBAAAC0NZBAQAAAA/HQkEFAAAAHQNBCgAAADLEQQoAAAArAEAFQAAACoAWgAAAAAAABMAAAAAKgBdGwEAACoAxgQENAAAAFgEBAAAC0NZBAQAAAA/HQkEFAAAAHQNBCgAAADLEQQoAAAArQEADgAAABwAWgAAAAAAAF0bAQAAHADGBARmAAAAHQkEawAAAHQNBGsAAADLEQRrAAAArgEADgAAABwAWgAAAAAAAF0bAQAAHADGBARmAAAAHQkEQwAAAHQNBEMAAADLEQRDAAAArwEAKgAAAB0ABQAAAAAAAAEAAAAAAAACAAAAAAAAdiYAAAAHAEIYAAAABwBdGwEAAA4AAQAEAAA0QgEABAAAyUMAAAQDAAAAAQAFRGVjb3mwAQAqAAAAHQAFAAAAAAAAAQAAAAAAAAIAAAAAAAB2JgAAAAcAQhgAAAAHAF0bAQAADgABAAQAADRCAQAEAADJQwAABAQAAAABAAVEZWNvebEBAA4AAAAHAAEAAAAAAABdGwEAAAcAAAAEAACgQLIBAA4AAAAHAAEAAAAAAABdGwEAAAcAAAAEAACgQLUBAA4AAAAHAAAAAAAAAABdGwEAAAAAAAAEIQAAALYBAA4AAAAHAGkBAAAAAABdGwEAAAAAAAAEIwAAALcBAA4AAAAHAAAAAAAAAABdGwEAAAcAAAAEAACAP7gBAA4AAAAHAAAAAAAAAABdGwEAAAcAAAAEAACAP7kBAA4AAAAHAAAAAAAAAABdGwEAAAcAAAAEAACAP7oBAA4AAAAHAAAAAAAAAABdGwEAAAcAAAAEAACAP+QBACMAAAAVACSIAAAAAACGewAAAAAA7GIAAAAOAF0AAAAADgBdGwEAAA4AAAAEAACAQAMABAAAgD8AAATbAAAA6AEADgAAAAcAAAAAAAAAAF0bAQAABwAAAAQAAIA/6QEADgAAAAcAAAAAAAAAAF0bAQAABwAAAAQAAIA/6gEADgAAAAcAAAAAAAAAAF0bAQAABwAAAAQAAIA/6wEADgAAAAcAAAAAAAAAAF0bAQAABwAAAAQAAIA/7AEADgAAAAcAAAAAAAAAAF0bAQAABwAAAAQAAIA/7QEADgAAAAcAAAAAAAAAAF0bAQAABwAAAAQAAIA/7gEADgAAAAcAAAAAAAAAAF0bAQAABwAAAAQAAIA/7wEADgAAAAcAAAAAAAAAAF0bAQAABwAAAAQAAIA/8AEADgAAAAcAAAAAAAAAAF0bAQAABwAAAAQAAIA/8QEADgAAAAcAAAAAAAAAAF0bAQAABwAAAAQAAIA/CAIADgAAABwAAgAAAAAAAF0bAQAAHAAAAAQAAIA/BQAEmpmZvgYABAAAIMIIAAQAAMjCCQIADgAAABkAAAAAAAAAAF0bAQAAEgAAAAQAAHBCAQAEAQAAAAIAAQEAAATjAAAADQIAOAAAAOMBDycAAAAAAITNAAAATgABAAAAABkBAgAAAABOAQMAAAAAlgGFzQAAAMcBhs0AAADVAV0bAQAA3AEAABBhipblo1mLQof075//f8GzAQAEAQAAAAIAEE1U3/t26kZAmFVGpGF2utwDAAQCAAAABAAQm0rrY0hREUqCQDa+PSqqZwUABAMAAAACAAQDAAAAAwAEAgAAANwABM3MzD3dAAQAAHpD3gAEAADAP0ABBArXIz1BAQQzM7M/QgEEzczMPaQBBI/CdT2lAQSamRk/pgEEmpkZPwgCBM3MTD0JAgQAAHpDCgIEmpmZPvcBBAAAAABsAgTNzEw9bQIEAAB6Q24CBJqZmT5bAgQAAAAA0AIEzcxMPdECBAAAekPSAgSamZk+vwIEAAAAANAHBAMAAADRBwQAAEhD0gcEAABIQ9MHBAAA+kPUBwQAAEhC1QcEAAAgQQEACAAANEMAADRDAgAEAAAAAPwFAQEoIxx8SbiJnvz8TKV0Ka0CEvU8QWlycGxhbmUgMS4wZQAEAAAAAGgABAAA8EFqAATNzKBBCwAEAIA7RQEABAAAlkMoIyL5lsAfK9wLRbTL+JINSrPNQ2lyY2xlIGF0IGRpc3RhbmNl/AUBAVUKBAAAyEFbCgQBAAAAKCMcjPMOPo91ek2QTVWyK+8DxUFpcnBsYW5lIDIuMAIABAAAgD8DAAQAAIBAAwAEAAAAAAAABOQAAAAWAgAOAAAABwBpAQAAAAAAXRsBAAAAAAAABOUAAAAXAgAHAAAABwBdGwEAAAAAAAAE5gAAABgCACMAAAAkAAEAAAAAAAACAAAAAAcAAwAAAAAOAF0AAAAAFgBdGwEAAB0AmAAEAAAAAJgABCgAAAAFAAVEZWNveQAABAEAAAAAAAToAAAAMgIADgAAABwAWgAAAAAAAF0bAQAAHADGBAQ0AAAAVwQEAADAP1gEBABIxUZZBAQzM7M/MwIADgAAABUAWgAAAAAAAF0bAQAAFQDGBAQ0AAAAWAQEAGDqRlkEBAAAgD80AgAOAAAADgBaAAAAAAAAXRsBAAAOAMYEBBQAAABZBAQAACBBNQIADgAAAA4AWgAAAAAAAF0bAQAADgDGBAQUAAAAWQQEAAAgQTYCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEFAAAADcCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAADgCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAADkCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAADoCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAADsCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAADwCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAAD0CAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAAD4CAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEHwAAAD8CAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEHwAAAEACAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEQgAAAEECAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEQgAAAEICAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQERgAAAEMCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQERgAAAEQCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEUAAAAEYCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEUAAAAEgCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEUAAAAEoCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEFAAAAEsCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEFAAAAEwCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEUAAAAE0CAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEFAAAAE4CAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEUAAAAFgCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQERgAAAFkCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQERgAAAFoCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEQgAAAFsCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEQgAAAFwCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEHwAAAF0CAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEHwAAAF4CAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAAF8CAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAAGACAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAAGECAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAAGICAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAAGMCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAAGQCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAAGUCAA4AAAAHAFoAAAAAAABdGwEAAAcAxgQEKAAAAGYCAA4AAAAOAFoAAAAAAABdGwEAAA4AxgQEFAAAAFkEBAAAIEFnAgAOAAAADgBaAAAAAAAAXRsBAAAOAMYEBBQAAABZBAQAACBBaAIADgAAABUAWgAAAAAAAF0bAQAAFQDGBAQ0AAAAWAQEAGDqRlkEBAAAgD9pAgAOAAAAFQBaAAAAAAAAXRsBAAAVAMYEBDQAAABYBAQAYOpGWQQEAACAP2oCAA4AAAAHABMAAAAAAABdGwEAAAcABQAEAADGQnYCACoAAAArAIsAAAAAAABpAQAAAAAAAgAAAAAAAAEAAAAAHQCOHAAAAB0AXRsBAAArAAQABAAAIEEHAAgAAAAAAABIQwgACAAAgD8AQJxFAAAEnVv3SQEABABANEh3AgAOAAAABwBaAAAAAAAAXRsBAAAHAMYEBFMAAAB4AgAOAAAADgBaAAAAAAAAXRsBAAAOAMYEBFQAAABXBAQAAIA/eQIADgAAAAcAWgAAAAAAAF0bAQAABwDGBARTAAAAegIADgAAAAcAWgAAAAAAAF0bAQAABwDGBARTAAAApQIAKgAAABwABQAAAAAAAAEAAAAAAAACAAAAAAAAdiYAAAAHAEIYAAAABwBdGwEAABUAAQAEAAA0QgAABCTW4kkBAAQAQCNIAAAEAQAAANYCAA4AAAAHAAAAAAAAAABdGwEAAAAAAAAE7AAAAP8CAA4AAAAHAAAAAAAAAABdGwEAAAcAAAAEAACAPwEDAA4AAAAHAAAAAAAAAABdGwEAAAcAAAAEAACAPw==","VehicleData":"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","designChanged":false,"blueprintVersion":29,"blueprintName":"V7_Bomber_Diplomat","SerialisedInfo":{"JsonDictionary":{},"IsEmpty":true},"Name":null,"ItemNumber":540,"LocalPosition":"10162.48,239.5023,13836.12","LocalRotation":"-0.03803571,-0.7596132,-0.05216251,0.6471633","ForceId":1993874018,"TotalBlockCount":1270,"MaxCords":"12,4,37","MinCords":"-12,-1,-4","BlockIds":[1102,1061,1100,1083,1068,1068,1061,1100,1083,1068,1068,1068,1029,1102,1083,1066,1083,1100,1102,1083,227,1100,1083,1061,1100,1083,1075,1029,1102,1083,1067,1067,1044,1102,1075,1075,1061,1067,1067,1067,1061,1062,1075,1029,1062,1067,1067,1044,1061,1067,1067,1067,1061,1062,1075,1029,1062,1067,1067,1044,1068,1061,1078,1075,1080,1100,1078,1067,1068,1044,1102,1075,1075,1075,1073,1073,1102,1062,1068,1062,1061,1075,1084,1100,1061,1068,1068,1044,1102,1073,1073,1102,1062,1083,1099,1062,1044,1061,1076,1084,1100,1061,1067,1067,1068,1081,1100,1099,1083,1068,1068,1061,1062,1044,1076,1044,1076,1084,1104,1061,1068,1076,1068,1081,1100,1106,1073,1106,1099,1080,1068,1068,1068,1068,1062,1062,1054,1076,1044,1076,1084,1104,1061,1076,1076,1081,1100,1061,1068,1086,1106,1099,1080,1068,1068,1068,1068,1062,1062,1062,1062,1076,1084,1106,1061,1076,1081,1102,1061,1086,1106,1075,1068,1068,1068,1068,1062,1078,1062,1062,1086,1106,1078,1062,1078,1062,1650,1650,1654,1654,1106,1062,1104,1086,1068,1068,1062,1104,1086,1068,1068,1068,1028,1106,1086,1066,1086,1104,1106,1086,227,1104,1086,1062,1104,1086,1075,1028,1106,1086,1067,1067,1043,1106,1075,1075,1062,1067,1067,1067,1062,1061,1075,1028,1061,1067,1067,1043,1062,1067,1067,1067,1062,1061,1075,1028,1061,1067,1067,1043,1068,1062,1078,1075,1079,1104,1078,1067,1068,1043,1106,1075,1075,1075,1074,1074,1106,1061,1068,1061,1062,1075,1081,1104,1062,1068,1068,1043,1106,1074,1074,1106,1061,1086,1103,1061,1043,1062,1076,1081,1104,1062,1067,1067,1068,1084,1104,1103,1086,1068,1068,1062,1061,1043,1076,1043,1076,1081,1100,1062,1068,1076,1068,1084,1104,1102,1074,1102,1103,1079,1068,1068,1068,1068,1061,1061,1053,1076,1043,1076,1081,1100,1062,1076,1076,1084,1104,1062,1068,1083,1102,1103,1079,1068,1068,1068,1068,1061,1061,1061,1061,1076,1081,1102,1062,1076,1084,1106,1062,1083,1102,1075,1068,1068,1068,1068,1061,1078,1061,1061,1083,1102,1078,1061,1078,1061,1068,1068,1068,1068,1068,1067,1067,1078,1078,1078,1075,1086,1083,1078,1078,1078,1067,1068,1068,1068,1068,1068,1068,1068,1068,227,227,1068,1068,1062,1061,1106,1102,1067,1067,1068,1068,227,227,848,848,848,848,848,848,849,849,1067,1067,1068,1068,851,851,848,848,810,810,858,858,1195,1199,75,113,1023,1023,1023,1023,1068,1068,1067,1067,1068,1068,1067,1067,1067,1068,1067,1067,1067,1067,1067,1067,1067,1067,1067,1067,1066,1066,1066,1066,1066,1066,227,227,1068,1068,1067,1067,1067,1067,1068,1068,771,771,771,771,1067,38,771,771,771,1023,1023,1023,1023,1023,1023,1022,1022,1023,1023,640,640,640,640,640,640,640,640,641,641,739,739,739,739,664,664,1068,1068,1068,1068,1068,61,110,66,64,64,64,65,87,87,87,87,87,87,87,87,115,752,734,1599,1598,1598,1598,1598,1598,1598,1598,1598,1599,1599,1599,1598,1598,1598,1598,1598,1598,1598,1598,1598,1598,1066,1068,1068,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,819,820,822,1068,1068,1068,1068,811,811,811,811,822,822,822,68,819,819,819,819,825,825,825,825,1820,1820,1820,1820,1820,1820,1067,1068,1068,1068,1068,1068,1068,1068,1067,1067,1068,1068,1068,1068,1068,1068,1068,227,227,1066,1066,809,809,809,809,809,809,809,811,811,811,811,810,818,858,1599,1599,1599,1599,771,771,771,771,771,771,771,771,771,64,64,64,87,87,87,87,87,87,1598,1598,1598,1598,1598,1598,662,662,662,662,1598,1598,64,64,64,1598,1598,1598,87,87,87,87,87,87,738,1598,1598,1598,1598,1598,1598,1598,1598,1598,1598,64,64,64,64,64,64,87,87,87,87,87,87,87,87,87,87,87,87,1599,1599,1599,1599,771,771,771,771,771,1423,1424,1424,1022,1423,1022,1423,1424,1424,1423,771,771,771,771,771,771,771,771,771,1423,1424,1424,758,758,758,758,758,758,758,758,640,640,640,640,640,640,640,640,640,640,640,64,87,88],"BlockState":"=0,808","AliveCount":808,"BlockStringData":["HomeCarrier = 'HomeCarrierBlueprintName'\r\n\r\n\r\n--Debug Settings\r\n--These will enable extra logging to the LUA console\r\nDebugLuaCore = false\r\nDebugAltitudeSafety = false\r\nDebugAltitudeSafetyToHud = false\r\nDebugThrust = false\r\nDebugTurning = false\r\nDebugAIMode = true\r\nDebugControlOutputs = true\r\nDebugEvasionDetails = false\r\nDebugDeltaDegDetails = false\r\nDebugTargetPriority = false\r\n\r\n\r\n--Generic Flight Settings\r\nMinAltitude = 200\r\nMaxAltitude = 630\r\nAltSafetyPadding = 30 \t\t\t--Within this many meters of min/max alt we start limiting our pitch to prevent overshooting\r\nMinPitch = -60\r\nMaxPitch =  60\r\nEmergencyMaxPitch =  89 \t\t-- Used when we go below MinAlt\r\nEmergencyMinPitch = -89 \t\t-- Used when we go above MaxAlt\r\nPitchOffset = 6 \t\t\t\t-- If your craft needs extra pitch to fly, add it here.\r\n\r\nCombinePitchYaw = true\t\t\t-- If true we use Sign/Cosign to rotate our yaw/pitch outputs to match our roll orientation.\r\nMaxRollToTurn = 0\t\t\t\t-- 180, we ignore \"up\" and roll the craft as needed in any direction\r\nTurnRollOffset = 90 \t\t\t--90 means yaw to turn instead of pitch to turn\r\n\r\nMinCruiseAltitude = 150\t\t\t-- We pick a random cruise altitude between min and max.\r\nMaxCruiseAltitude = 450\t\t\t-- \r\n\r\nCruiseAtMaxSpeed = true \t\t-- If true we cruise at full speed.\r\nDesiredCruiseSpeed = 115 \t\t-- If CruiseAtMaxSpeed is false, we will attempt to fly at this speed\r\nMinSafeSpeed = 10\t\t\t\t-- We will try and stay at or above this speed at all times\r\nSetCruiseSpeedWhile = true\t\t-- If we are allowing the DefaultAI to takeover do we still want to limit speed?\r\n\r\n--Target Priorty Brackets and Weights\r\nDefaultAIScoreScale = 1\r\nIsChaseValidBase = 0\t\t-- We have a VERY strong pro-dogfight bias in targets.\r\nMaintainTargetBase = 0\t\t-- We have a small bias infavor of maintaining our old target\r\nChaseDotBehindScale = 0 \t\t-- multiplied by -2 to 2 depending on orientation\r\nBelowMinSpeedPenPerMPS = 0\t-- subtract this per mps below chase speed.\r\nMinTargetAltitude = 0\t\t\t--\r\nMaxTargetAltitude = 0\t\t\t--\r\nBelowMinAltPenPerM = 0\t\t--\r\nAboveMaxAltPenPerM = 0\t\t\t--\r\nBelowChaseAltPenPerM = 0\t\t--\r\nAboveChaseAltPenPerM = 0         --\r\nDistancePenPerM = 0\t\t\t\t--\r\nTurnRatePenPerDPerSec = 0\t\t-- We have a strong bias in favor of targets flying straight lines.\r\n\r\n--Attack run settings\r\nUseAttackRunBehavior = false\t\t-- if this is true we will attempt to attack run targets we cannot dogfight.\r\nAttackRunAltOffset = 100 \t\t\t-- elevation above the target during an attack run\r\nAttackRunAimToTargetDistance = 600\r\nAttackRunSlowApproach = false\r\nAttackRunStartRange = 1650 \t\t-- outside this range we start an attack run\r\nAttackRunAbortRange = 300 \t\t-- inside this range we abort an attack run\r\nAttackRunAimingYawOffset = 0 \t\t-- when attacking we will approach at this angle (should be less than 90)\r\nAttackRunClosingYawOffset = 40\t\t\t\t\t\t\t\t-- also used when closing range to start chasing \r\nAttackRunEscapingYawOffset = 40\r\nAttackRunClosingYawOffsetMinTTI = 10\r\n\r\n--Chase/dogfight settings\r\nUseChaseBehavior = false \t\t-- if true we will attempt to chase targets dogfight style\r\nPreferredChaseDistance = 280 \t-- the preferred distance we try and maintain behind the target\r\nMinChaseDistance = 10\t\t\t-- if the target turn radius is too small for our preferred shrink as needed.\r\nChaseAltOffset = 0\t\t\t\t-- Chase above/below our target?\r\nMinChaseSpeed = 30 \t\t\t\t-- targets below this speed we will only attack run.\r\nMinChaseAltitude = 30.0 \t\t-- targets below this altitude we only attack run.\r\nMaxChaseAltitude = 750.0 \t\t-- targets above this altitude we only attack run.\r\nPrepChaseAimToTargetDistance = 400\r\nPrepChaseClosingYawOffsetMinTTI = 10\r\nPrepChaseAimingYawOffset = 0\r\nPrepChaseClosingYawOffset = 30\r\nTryChaseMinAngle = 35\r\n\r\n-- StrafeAndHover settings\r\nUseDragonFlyBehavior = false\r\nDragonFlyRange = 200\r\nMinHoverAltAdjustment = 5\r\n\r\n--Collision Avoidance settings\r\nAvoidCollisionsFriendly = true\r\nAvoidCollisionsEnemy = true\r\nAvoidCollisionsRadius = 40\t\t-- reserved area around outselves and other craft\r\nAvoidanceTurnRate = 20 \t\t\t-- how quickly we turn to avoid collisions.  High values may interfere with other behaviors\r\nAvoidancePitchRate =20\t\t\t-- only pitch a little to dodge\r\nAvoidanceTimeThreshold = 0.5\t-- Seconds Ahead to avoid collision\r\n\r\n--Missile Avoidance settings\r\nUseMissileAvoidance = true\r\nUseDragonFlyEvasionOnly = false\r\nAvoidanceRadius = 60\t\t\t-- We add this to the area we want to keep missiles out of.\r\nPanicTti  = 0.35\t\t\t\t-- intercept time in seconds to \r\nUrgentTti = 0.60\t\t\t\t\t\r\nWarnedTti = 1.20\t\r\nEvasionYawAngle = 35\r\nEvasionPitchAngle = -35\r\n\r\n--Orbit settings\r\nUseOrbitSpawnWhenIdle = false\t--if false we will do nothing, so we will fall back on std AI.\r\nOrbitAngle = 90\r\nOrbitBeginAngle = 85\r\nOrbitAbortAngle = 105\r\nOrbitAltitudeOffset = 350\r\nOrbitUseBothSides = true\r\nOrbitAbortRadius = 1000\r\nOrbitBeginRadius = 200\r\nOrbitCloseRadius = 1700\r\n\r\n-- Return for Repair Support settings\r\nAlwaysSeekRepairWhenAiDestroyed = true\r\nReturnForLowHealth = 93\t\t\t-- if below 0 we will never use this.\r\nReturnToTheFightHealth = 99.99\t-- The game never reports 100%\r\nReturnForLowHealthRange = 90\t-- we must be this close to be \"home\"\t\t\r\nReturnIgnoreHealthRange = 5000 \t\r\nReturnForLowHealthAltitudeOffset = 50\t\r\nMatchSpeedWithHealthSupport = true\r\nRepairSupportBluePrintNames = {HomeCarrier, 'CargoShip'}\r\n\r\n-- Return for Battery Support settings\r\nReturnForLowBattery = 50\t\t-- if below 0 we will never use this.\r\nReturnToTheFightBattery = 99.99 -- The game never reports 100%\r\nReturnForLowBatteryRange = 1000\t-- we must be this close to be \"home\"\t\t\r\nReturnIgnoreBatteryRange = 10000 \t\r\nReturnForLowBatteryAltitudeOffset = 100\t\r\nMatchSpeedWithBatterySupport = true\r\nBatterySupportBluePrintNames = {HomeCarrier, 'CargoShip'}\r\n\r\n-- escort orbit  --uses orbit settings to stay with escort target\r\nUseEscortBehavior = true\r\nEscortEngageRadius = 2600\r\nEscortBluePrintNames = {HomeCarrier, 'CargoShip'}\r\n\r\n\r\n--ComplexControls  -- T G Y H U J I K O L up down left right\r\n--If a value is entered the complex control will be activated when this AI state is true.  \r\n--Empty Values are ignored.\r\n--Dups values are allowed, so multiple staes can call the same key\r\n--Values are not case sensitive.\r\n--We have more states than complex controls.  Comment out those you don't need.\r\n\r\n--IsChasingBehindTarget = \"T\"\r\n--IsChasingInfrontOfTarget = \"G\"\r\n--IsAttackRunAttacking = \"Y\"\r\n--IsAttackRunRetreating = \"H\"\r\n--IsOrbiting = \"U\"\r\n--IsOrbitingTooClose = \"J\"\r\n--IsOrbitingTooFar = \"I\"\r\n--IsOrbitingTraveling = \"K\"\r\n--IsJoiningFlock = \"O\"\r\n--IsAvoidingCollision = \"L\"\r\nIsAvoidingMissile = \"T\"  -- This is my default, using a breadboard it can trigger an ACB to fire flares.\r\n--IsAvoidingMissileUrgent = \"T\"\r\nIsAvoidingMissilePanic = \"Y\"\r\n--IsReturningForSupport = \"right\"\r\n--IsInFormation = \"G\"\r\n--IsJoiningFormation = \"U\"\r\n--IsInDragonFlyMode = \"L\"\r\nIsLettingAiTakeControl = \"O\"\r\nIsStrafeLeft = \"left\"\r\nIsStrafeRight = \"right\"\r\nIsHoverUp = \"up\"\r\nIsHoverDown = \"down\"\r\n\r\n\r\n-- PID DATA\r\n--                P,           D,       I,    OutMax,   OutMin,    IMax,    IMin\r\nyawPIDData     = {0.016,     0.002,     0.00,         1,       -1,       1,      -1}\r\npitchPIDData   = {0.018,     0.022,     0.00,         1,       -1,       1,      -1}\r\nrollPIDData    = {0.009,     0.003,     0.00,         1,       -1,       1,      -1}\r\nrangePIDData   = {0.010,     0.100,     0.10,         1,       -1,       1,      -1}\r\n\r\n\r\n--The Basic Update function.  The game calls this every frame.  All other functions are called from here.\r\nfunction Update(I)  \r\n\tI:ClearLogs()\r\n\t\r\n\tInitAI(I)\t\t\t\t\t\t\t\t-- This runs once to prepare PIDs and capture a couple one-time values.\r\n\tSetCurrentValues(I)\t\t\t\t\t\t-- This captures info like our current pitch and altitude for use in other functions.\r\n\tChooseCurrentTarget(I)\t\t\t\t\t-- This looks over the list of enemies and chooses our targets using the weights above.\r\n\r\n\t--rearanging this list will change the priority we place on various actions.\r\n\t--These actions return bool values, a true means our AI has acted and we \"return\" out of update, ending the AI for this pass.\r\n\t--all of these can be skipped via settings above.\r\n\t\r\n\tif AvoidCollisions(I) then return end\t-- Check for collisions and avoid them.\r\n\tif MissileAvoidance(I) then return end\t-- Check for missiles and avoid them.\r\n\tif ReturnForRepair(I) then return end\r\n\tif ReturnForBattery(I) then return end\r\n\tif EscortFriendly(I) then return end\r\n\tStrafeAndHover(I)\r\n\tif TryChase(I) then return end\t\t\t-- Check for a target that meets chase conditions.  If found, chase it (or prep chase)\r\n\tif AttackRun(I) then return end\t\t\t-- Check for targets to make attack runs on.\r\n\tif OrbitSpawnWhenIdle(I) then return end-- Orbit the spawn zone we saved during init.\r\n\t\r\n\t--If no mode was selected we do nothing.\r\n\tDefaultAIIdle(I)\t\t\t\t\t\t-- Let the default AI take control of the aircraft.\r\nend\r\n\r\n-- Internal Variables\r\n-- These are used by various functions and store values accross multiple frames.\r\nFirstPass = true\r\nYawCurrent = 0\r\nYawDrive = 0\r\nPitchCurrent = 0\r\nPitchDrive = 0\r\nRollCurrent = 0\r\nRollDrive = 0\r\nAltitudeCurrent = 0\r\nAltitudeDesired = 0\r\nSpeedDesired = 0\r\nSpeedCurrent = 0\r\nThrustDrive = 0\r\nLastRunTime = 0\r\nFrameTime = 0\r\nAttackRunEscaping = false\r\nSpawnPOS = Vector3(0,0,0)\r\nIsChasing = false\r\nMyEscortTargetId = nil\r\nCruiseAltitude = 0 \r\nMaxSafeThrottle = 1\r\n\r\nMyPrevVel = Vector3(0,0,0)\r\nMyCurrVel = Vector3(0,0,0)\r\nMyDeltaDeg = 0\r\nMyMaxDeltaDeg = 10\r\nMyMinTurnRadius = 0\r\n\r\nCurrentTargetId = nil\r\nCurrentTargetPosInfo = nil\r\nCurrentTargetPrevVel = Vector3(0,0,0)\r\nCurrentTargetCurrVel = Vector3(0,0,0)\r\nCurrentTargetDeltaDeg = 0\r\nCurrentTargetTurnRadius = 0\r\n\r\nCurrentMissileEvasionPosition = nil\r\n\r\nAllTargetInfo = {}\r\n\r\n-- This runs once to prepare PIDs and capture a couple one-time values.\r\nfunction InitAI(I)\r\n\t--I:Log(tostring(SpawnPOS))\r\n\r\n\tif not FirstPass then return end\r\n\tFirstPass = false\r\n\t\r\n\tSpawnPOS = I:GetConstructPosition()\r\n\tInitPIDs()\r\n\t\r\n\tmath.randomseed(I:GetTime()*100000000000)\r\n\tCruiseAltitude = math.random(MinCruiseAltitude, MaxCruiseAltitude)\r\n\t\r\nend\r\n\r\n-- This is used to start an individual PID\r\nfunction InitPID(pidData)\r\n\tPID = {}\r\n\tPID.Kp            = pidData[1] \r\n\tPID.Kd            = pidData[2]\r\n\tPID.Ki            = pidData[3] \r\n\tPID.OutMax        = pidData[4] \r\n\tPID.OutMin        = pidData[5] \r\n\tPID.IMax          = pidData[6] \r\n\tPID.IMin          = pidData[7] \r\n\tPID.integral      = 0\r\n\tPID.previousError = 0\r\n\r\n\treturn PID\r\nend\r\n\r\n-- This is used to call each PID we want to start.\r\nfunction InitPIDs()\r\n\trollPID     = InitPID(rollPIDData)\r\n\tpitchPID    = InitPID(pitchPIDData)\r\n\tyawPID      = InitPID(yawPIDData)\r\n\trangePID    = InitPID(rangePIDData)\r\nend\r\n\r\n--A useful function.  Added for an action that ended up unused, but remains valuable so I didn't delete it.\r\nfunction AngleDir(fwd, targetDir, up) -- -1 when the target direction is left, +1 when it is right\r\n\r\n\tperp = Vector3.Cross(fwd, targetDir);\r\n\tdir = Vector3.Dot(perp, up);\r\n\t\r\n\tif (dir > 0.0) then return 1.0\r\n\t\r\n\telseif (dir < 0.0) then return -1.0 \r\n\t\r\n\telse return 0.0 \r\n\tend\r\n\r\nend\r\n\r\n--Executes the actual PID action.  Called from several functions.\r\nfunction GetPIDOutput(SetPoint, ProcessVariable, PID)\r\n\t\r\n\tlocal error     = SetPoint - ProcessVariable\r\n\tlocal timeDelta = FrameTime\r\n\tlocal derivative\r\n\tlocal output\r\n\r\n\tPID.integral = PID.integral + (error*timeDelta) * PID.Ki\r\n\tif PID.integral > PID.IMax then PID.integral = PID.IMax end\r\n\tif PID.integral < PID.IMin then PID.integral = PID.IMin end\r\n\r\n\tderivative = (error - PID.previousError)/timeDelta\r\n\r\n\toutput = PID.Kp*error + PID.Kd*derivative + PID.integral\r\n\tif (output > PID.OutMax) then output = PID.OutMax end\r\n\tif (output < PID.OutMin) then output = PID.OutMin end\r\n\r\n\tPID.previousError = error\r\n\treturn output,PID\r\nend\r\n\r\n--Most functions have an option to call a complex control as feedback.  This is handled here.\r\nfunction CallComplexControl(I, keyRequested)\r\n\r\n\t--T G Y H U J I K O L up down left right\r\n\r\n\tif keyRequested == nil then return end\r\n\tif string.len(keyRequested) == 0 then return end\r\n\r\n\tkeyRequested = string.upper(keyRequested)\r\n\r\n\tstim = 0\r\n\tif     keyRequested == \"T\" then stim = 1 \r\n\telseif keyRequested == \"G\" then stim = 2 \r\n\telseif keyRequested == \"Y\" then stim = 3 \r\n\telseif keyRequested == \"H\" then stim = 4 \r\n\telseif keyRequested == \"U\" then stim = 5 \r\n\telseif keyRequested == \"J\" then stim = 6 \r\n\telseif keyRequested == \"I\" then stim = 7 \r\n\telseif keyRequested == \"K\" then stim = 8 \r\n\telseif keyRequested == \"O\" then stim = 9 \r\n\telseif keyRequested == \"L\" then stim = 10 \r\n\telseif keyRequested == \"UP\" then stim = 11 \r\n\telseif keyRequested == \"DOWN\" then stim = 12 \r\n\telseif keyRequested == \"LEFT\" then stim = 13 \r\n\telseif keyRequested == \"RIGHT\" then stim = 14 end\r\n\r\n\tif stim > 0 then \r\n\t\tI:RequestComplexControllerStimulus(stim) \r\n\t\tif DebugControlOutputs then\r\n\t\t\tI:Log(\"ComplexKey Output: \" .. keyRequested)\r\n\t\tend\r\n\telse\r\n\t\tif DebugControlOutputs then\r\n\t\t\tI:Log(\"Requested Output '\" .. keyRequested .. \"' not recognized\")\r\n\t\tend\r\n\tend\r\n\r\nend\r\n\r\n--Many functions call for pitch changes.  Before those are sent to drives they are sent here for adjustments based on min/max height and terrain.\r\nfunction AdjustPitch(I, TargetPitch)\r\n\tfutureTerrain = I:GetTerrainAltitudeForLocalPosition(0,0,SpeedCurrent)\r\n\tcurrentTerrain = I:GetTerrainAltitudeForLocalPosition(0,0,0)\r\n\t\r\n\tcurrentMin = math.max(currentTerrain + AltSafetyPadding, MinAltitude)\r\n\tfutureMin = math.max(futureTerrain + AltSafetyPadding, MinAltitude)\r\n\t\r\n\tnewPitch = TargetPitch\r\n\tif AltitudeCurrent > MaxAltitude then\r\n\t   return EmergencyMinPitch\r\n\t\r\n\telseif AltitudeCurrent > MaxAltitude - AltSafetyPadding then\r\n\t   return math.min(0,TargetPitch+PitchOffset)\r\n\t   \t   \r\n\telseif AltitudeCurrent < currentMin then\r\n\t\treturn EmergencyMaxPitch\r\n\t\r\n\telseif AltitudeCurrent < currentMin + AltSafetyPadding then\r\n\t   return math.max(0,TargetPitch+PitchOffset)\r\n\r\n\telseif futureMin > AltitudeCurrent * 2 then\r\n\t   return EmergencyMaxPitch\r\n\t   \r\n\telseif futureMin + AltSafetyPadding > AltitudeCurrent then\r\n\t   altAdjustment = (futureMin + AltSafetyPadding) - AltitudeCurrent\r\n\t   altAdjustment = math.deg(math.atan(altAdjustment/(SpeedCurrent * 0.5)))\r\n\t   newPitch = math.max(altAdjustment, TargetPitch)\r\n\tend\r\n\r\n\tnewPitch = math.min(newPitch  + PitchOffset, MaxPitch)\r\n\tnewPitch = math.max(newPitch  + PitchOffset, MinPitch)\r\n\treturn newPitch\r\nend\r\n\r\n--When sending drive requests for pitch,roll,yaw they are sent here to be rotated so they have the desired affect regardless of our aircraft's orientation.\r\nfunction ApplyDrives(I, yawRequest, pitchRequest, rollRequest)\r\n  \r\n\tif CombinePitchYaw then\r\n\t\tYawDrive = (yawRequest * math.cos(math.rad(RollCurrent))) - (pitchRequest * math.sin(math.rad(RollCurrent)))\r\n\t\tPitchDrive = (pitchRequest * math.cos(math.rad(RollCurrent))) + (yawRequest * math.sin(math.rad(RollCurrent)))\r\n\t\tRollDrive = rollRequest\r\n\telseif math.abs(RollCurrent) <= 90 then\r\n\t\tYawDrive = yawRequest\r\n\t\tPitchDrive = pitchRequest\r\n\t\tRollDrive = rollRequest\r\n\telseif math.abs(RollCurrent) > 90 then\r\n\t\tYawDrive = -yawRequest\r\n\t\tPitchDrive = -pitchRequest\r\n\t\tRollDrive = rollRequest\r\n\tend\r\n\r\n\tif DebugTurning then\r\n\t\tI:Log(string.format(\"Drive Request Yaw:%.2f Pitch:%.2f Roll:%.2f\", YawDrive, PitchDrive, rollRequest))\r\n\tend\r\n\tI:TellAiThatWeAreTakingControl()\r\n\tI:RequestControl(2,1,YawDrive)\r\n\tI:RequestControl(2,4,PitchDrive)\r\n\tI:RequestControl(2,3,rollRequest)\r\nend\r\n\r\n--Some functions want to follow a target at a specified range.  This function used to find the preferred thrust to keep that range.\r\nfunction AdjustSpeedToFollowAtRange(I, range, targetPos)\r\n\r\n\tdistancePos = targetPos - I:GetConstructPosition()\r\n\tdistance = distancePos.magnitude\r\n\r\n\tthrustDrive,rangePID = GetPIDOutput(range, distance, rangePID)\r\n\tthrustDrive = -thrustDrive\r\n\r\n\treturn thrustDrive\r\n \r\nend\r\n\r\n--Some functions want a specified speed.  This funtion is used to find the preferred thrust to keep that speed.\r\nfunction AdjustSpeedForCruise(I, desiredCruiseSpeed, CruiseAtMax)\r\n\tif AmFormationLeader then\r\n\t\tdesiredCruiseSpeed = math.min(IdealFormationSpeed, desiredCruiseSpeed)\r\n\telseif CruiseAtMax then \r\n\t\treturn MaxSafeThrottle\r\n\tend\r\n\t\r\n\tsetSpeed = math.max(MinSafeSpeed, desiredCruiseSpeed)\r\n\tThrustDrive,rangePID = GetPIDOutput(setSpeed, SpeedCurrent, rangePID)\r\n\tmath.min(ThrustDrive, MaxSafeThrottle)\r\n\treturn ThrustDrive\r\n \r\nend\r\n\r\n--This function takes in our desired bearing and pitch, and outputs the required drive requests to get there.  Usually used just before ApplyDrives\r\nfunction CallAimPids(I, YawDesired, PitchDesired, RollDesired)\r\n\r\n\tPitchDesired = AdjustPitch(I, PitchDesired)\r\n\r\n\tRollDesired = math.deg(math.atan2(math.rad(YawCurrent - YawDesired ), math.rad(PitchCurrent - PitchDesired))) + TurnRollOffset\r\n\tif RollDesired < -MaxRollToTurn or math.abs(RollCurrent - RollDesired)  > math.abs(RollCurrent - (RollDesired +180)) then\r\n\t\tRollDesired = RollDesired + 180\r\n\telseif RollDesired > MaxRollToTurn or math.abs(RollCurrent - RollDesired)  > math.abs(RollCurrent - (RollDesired - 180)) then\r\n\t\tRollDesired = RollDesired - 180\r\n\tend\r\n\t\r\n\tRollDesired = math.min(RollDesired, MaxRollToTurn)\r\n\tRollDesired = math.max(RollDesired, -MaxRollToTurn)\r\n\t\r\n\tif DebugTurning then\r\n\t\tI:Log(string.format(\"DirError Yaw:%.2f Pitch:%.2f Roll:%.2f\", YawCurrent - YawDesired, PitchCurrent - PitchDesired, RollCurrent - RollDesired))\r\n\tend\r\n\t\r\n\tyawRequest, yawPID = GetPIDOutput(YawDesired, YawCurrent, yawPID)\r\n\tpitchRequest, pitchPID = GetPIDOutput(PitchDesired, PitchCurrent, pitchPID)\r\n\trollRequest, rollPID = GetPIDOutput(RollDesired, RollCurrent, rollPID)\r\n\r\n\treturn yawRequest, pitchRequest, rollRequest\r\n\r\nend\r\n\r\n--Many behaviors need a simple flat flight at a given alt.  This does that.\r\nfunction AimFlat(I, YawDesired, DesiredAltitude)\r\n\r\n\taltAdjustment = DesiredAltitude - AltitudeCurrent\r\n\tPitchDesired = math.deg(math.atan(altAdjustment/(SpeedCurrent * 2)))\r\n\r\n\tRollDesired = 0\t\r\n\r\n\tyawRequest, pitchRequest, rollRequest = CallAimPids(I, YawDesired, PitchDesired, RollDesired)\r\n\tApplyDrives(I, yawRequest, pitchRequest, rollRequest) \r\n\r\nend\r\n\r\n-- A basic attack run behavior similar to the default AI.\r\nfunction AttackRun(I)\r\n\r\n\tif not UseAttackRunBehavior then return false end --We are skipping.\r\n\r\n\tif CurrentTargetPosInfo == nil then\r\n\t\tI:Log(\"Attack Run Error: No Target Data\")\r\n\t\treturn false \r\n\tend\r\n\tif CurrentTargetPosInfo.Valid then\r\n\r\n\t\ttargetPosInfo = CurrentTargetPosInfo\r\n\t\t\r\n\t\tif not targetPosInfo.Valid then \r\n\t\t\tI:Log(\"Attack Run Error: Invalid Pos Data\")\r\n\t\t\treturn false \r\n\t\tend\r\n\t\t\r\n\t\ttargetAltitude = targetPosInfo.AltitudeAboveSeaLevel\r\n\r\n\t\t--If our target is to close or far we will swap between attack or escape mode, otherwise keep doing what we are doing.\r\n\t\tif targetPosInfo.GroundDistance < AttackRunAbortRange then\r\n\t\t\tAttackRunEscaping = true\r\n\t\telseif targetPosInfo.GroundDistance > AttackRunStartRange then\r\n\t\t\tAttackRunEscaping = false\r\n\t\tend\r\n\r\n\t\tif AttackRunEscaping then\r\n\t\t\t--if DebugAIMode then\r\n\t\t\t--\tI:Log(\"Attack Run Escaping\")\r\n\t\t\t--end\r\n\t\t\tCallComplexControl(I, IsAttackRunRetreating)\r\n\t\telse\r\n\t\t\t--if DebugAIMode then\r\n\t\t\t--\tI:Log(\"Attack Run Attacking\")\r\n\t\t\t--end\r\n\t\t\tCallComplexControl(I, IsAttackRunAttacking)\r\n\t\tend\r\n\r\n\r\n\t\t--If we're escaping we create a new destination directly away from the target and go there.\r\n\t\tif AttackRunEscaping then\r\n\t\t\ttargetPos = targetPosInfo.Position\r\n\t\t\tTargetDirection = myPos - targetPos\r\n\t\t\tTargetDirection = TargetDirection.normalized\r\n\t\t\tnewPos = targetPos + (TargetDirection + Vector3.forward) * (AttackRunStartRange*2)\r\n\r\n\t\t\tescapePosInfo = I:GetTargetPositionInfoForPosition(0, newPos.x, newPos.y, newPos.z)\r\n\r\n\t\t\tYawDesired = (YawCurrent + escapePosInfo.Azimuth + AttackRunEscapingYawOffset) \r\n\r\n\t\t\taltDesired = math.max(CruiseAltitude, MinAltitude, MinAltitude + I:GetTerrainAltitudeForPosition(targetPos.x,targetPos.y,targetPos.z))\r\n\t\t\tthrustRequest = AdjustSpeedForCruise(I, DesiredCruiseSpeed, true) -- We will escape as fast as possible\r\n\t\t\t\r\n\t\t\tAimFlat(I, YawDesired, altDesired) --flat along our desired altitude\r\n\t\t\t\r\n\t\t\tif DebugAIMode then\r\n\t\t\t\tI:Log(\"Attack Run Escaping\")\r\n\t\t\tend\r\n\t\telse\r\n\t\t--If we are not escaping the target is our destination.\r\n\r\n\t\t\ttargetPos = targetPosInfo.Position\r\n\t\t\ttargetVel = targetPosInfo.Velocity\r\n\r\n\t\t\tTargetDirection = myPos - targetPos\r\n\t\t\tMyDirection = targetPos - myPos\r\n\r\n\t\t\tdistance = (MyDirection.magnitude - AvoidCollisionsRadius*2)\r\n\t\t\ttargetSpeed = targetVel.magnitude\r\n\t\t\t\r\n\t\t\ttargetDot = Vector3.Dot(targetVel.normalized, TargetDirection.normalized)\r\n\t\t\tmyDot = Vector3.Dot(myVel.normalized, MyDirection.normalized)\r\n\t\t\trelativeSpeed = (targetSpeed*targetDot + mySpeed*myDot)\r\n\t\t\ttti = 99999\r\n\r\n\t\t\tif relativeSpeed > 0 then tti = distance / relativeSpeed end\r\n\r\n\t\t\tI:Log(string.format(\"AttackRun TTI:%.2f\", tti))\r\n\r\n\t\t\tYawDesired = YawCurrent + targetPosInfo.Azimuth\r\n\r\n\t\t\tAttackRunYawOffset = 0\r\n\t\t\t\r\n\t\t\tif targetPosInfo.GroundDistance < AttackRunAimToTargetDistance and tti < AttackRunClosingYawOffsetMinTTI then\r\n\t\t\t\tAttackRunYawOffset = AttackRunAimingYawOffset\r\n\t\t\telseif tti < AttackRunClosingYawOffsetMinTTI then\r\n\t\t\t\tAttackRunYawOffset = AttackRunClosingYawOffset\r\n\t\t\telse\r\n\t\t\t\tAttackRunYawOffset = 0\r\n\t\t\tend\r\n\r\n\t\t\tif AttackRunUseBothSides then\r\n\t\t\t\tif targetPosInfo.Azimuth > 0 then\r\n\t\t\t\t\tYawDesired = YawDesired - math.abs(AttackRunYawOffset)\r\n\t\t\t\telseif targetPosInfo.Azimuth < 0 then \r\n\t\t\t\t\tYawDesired = YawDesired + math.abs(AttackRunYawOffset)\r\n\t\t\t\tend\r\n\t\t\telse\r\n\t\t\t\tYawDesired = YawDesired + AttackRunYawOffset\r\n\t\t\tend\r\n\r\n\t\t\taltDesired = math.max(targetAltitude + AttackRunAltOffset, MinAltitude + (AltSafetyPadding * 1.5), MinAltitude + (AltSafetyPadding * 1.5) + I:GetTerrainAltitudeForPosition(targetPos.x,targetPos.y,targetPos.z))\r\n\t\t\taltDesired = math.min(altDesired, MaxAltitude - (AltSafetyPadding * 1.5))\r\n\t\t\tthrustRequest = AdjustSpeedForCruise(I, MinSafeSpeed, not AttackRunSlowApproach) --If AttackRunSlowApproach is true we will attack at our minSafeSpeed, for longer shooting times.\r\n\t\t\t\r\n\t\t\tif targetPosInfo.GroundDistance < AttackRunAimToTargetDistance then\r\n\t\t\t\taltAdjustment = targetPosInfo.AltitudeAboveSeaLevel - AltitudeCurrent + ChaseAltOffset\r\n\t\t\t\tPitchDesired = math.deg(math.atan(altAdjustment/targetPosInfo.GroundDistance))\r\n\t\t\t\t\r\n\t\t\t\tRollDesired = 0\r\n\t\t\t\tyawRequest, pitchRequest, rollRequest = CallAimPids(I, YawDesired, PitchDesired, RollDesired)\r\n\t\t\t\tApplyDrives(I, yawRequest, pitchRequest, rollRequest)\r\n\t\t\t\t\r\n\t\t\t\tif DebugAIMode then\r\n\t\t\t\tI:Log(\"Attack Run Attacking - Pitch To Target\")\r\n\t\t\tend\r\n\t\t\t\t\r\n\t\t\telse\r\n\t\t\t\tAimFlat(I, YawDesired, altDesired)\r\n\t\t\t\tI:Log(\"Attack Run Attacking - Flat at Attack Alt\")\r\n\t\t\tend\r\n\t\tend\r\n\r\n\t\t--We use the functions described above to navigate to these destinations.\r\n\t\t\r\n\t\t\r\n\t\t\r\n\t\tI:RequestControl(2,8,thrustRequest)\r\n\t\tif DebugThrust then\r\n\t\t\tI:Log(string.format(\"AttackRun Setting Thrust:%.2f\", thrustRequest))\r\n\t\tend\r\n\t\treturn true -- we are doing attack runs.\r\n\telse\r\n\t\treturn false -- We have no target\r\n\tend\r\nend\r\n\r\n-- Before we chase we will check to see if the target is a valid chase candidate.\r\nfunction TryChase(I)\r\n\r\n\tif CurrentTargetPosInfo == nil then return false end\r\n\tif CurrentTargetPosInfo.Valid and UseChaseBehavior then\r\n\r\n\t\ttargetPosInfo = CurrentTargetPosInfo\r\n\t\ttargetAltitude = targetPosInfo.AltitudeAboveSeaLevel\r\n\r\n\t\t--Collect data about our and our target's position, direction, and relationship\r\n\t\tmyPos = I:GetConstructPosition()\r\n\t\tmyFwd = I:GetConstructForwardVector()\r\n\t\ttargetPos = targetPosInfo.Position\r\n\t\ttargetVel = targetPosInfo.Velocity\r\n\t\tTargetToMeDirection = myPos - targetPos\r\n\t\tdistance = TargetToMeDirection.magnitude\r\n\t\ttargetDot = Vector3.Dot(targetVel, TargetToMeDirection)\r\n\t\tbehind = targetDot < 0.35\r\n\t\tfacingTarget = Vector3.Angle(targetPosInfo.Direction, myFwd) < TryChaseMinAngle\r\n\r\n\t\t--Check if the target can be chased at all.\r\n\t\tif targetPosInfo.Velocity.magnitude >= MinChaseSpeed \r\n\t\t\tand targetAltitude >= MinChaseAltitude \r\n\t\t\tand targetAltitude <= MaxChaseAltitude\r\n\t\t\t--and CurrentTargetDeltaDeg < MyMaxDeltaDeg * 0.9\r\n\t\t\tand ((CurrentTargetTurnRadius > MyMinTurnRadius + MinChaseDistance) or facingTarget == true)\r\n\t\t\tthen\r\n\t\t\t\r\n\t\t\t--The target can be chased, are we currently behind it and facing toward it?\r\n\t\t\tif behind == true and facingTarget == true\r\n\t\t\tthen\r\n\t\t\t\t--We are, so chase it.\r\n\t\t\t\tExecChase(I)\r\n\t\t\telse\r\n\t\t\t\t--We are not, so get behind it.\r\n\t\t\t\tPrepChase(I)\r\n\t\t\tend\r\n\t\t\tIsChasing = true\r\n\t\t\treturn true --we are chasing\r\n\t\telse\r\n\t\t\r\n\t\t\t--The target is not a chase candidate.  Return false and we will do something else.\r\n\t\t\tIsChasing = false\r\n\t\t\t\r\n\t\t\tif DebugAIMode then\t\r\n\t\t\t\tif targetPosInfo.Velocity.magnitude < MinChaseSpeed then\r\n\t\t\t\t\tI:Log(string.format(\"Chase failed.  Target too slow: %.2f\", targetPosInfo.Velocity.magnitude))\r\n\t\t\t\telseif targetAltitude < MinChaseAltitude  then\r\n\t\t\t\t\tI:Log(string.format(\"Chase failed.  Target too low: %.2f\", targetAltitude))\r\n\t\t\t\telseif targetAltitude > MaxChaseAltitude  then\r\n\t\t\t\t\tI:Log(string.format(\"Chase failed.  Target too high: %.2f\", targetAltitude))\r\n\t\t\t\telseif CurrentTargetTurnRadius < MyMinTurnRadius + MinChaseDistance  then\r\n\t\t\t\t\tI:Log(string.format(\"Chase failed.  Target turning too hard. Me:%.2f Target:%.2f\", MyMinTurnRadius + MinChaseDistance, CurrentTargetTurnRadius))\r\n\t\t\t\telse\r\n\t\t\t\t\tI:Log(\"Chase failed for unkown reason.\")\r\n\t\t\t\tend\t\r\n\t\t\tend\r\n\t\t\tIsChasing = false\r\n\t\t\treturn false --try something else\r\n\t\tend\r\n\r\n\telse \r\n\t\tIsChasing = false\r\n\t\treturn false -- No target or not enabled\r\n\tend\r\nend\r\n\r\n-- Behavior for chasing the target once we are behind it and lined up.\r\nfunction ExecChase(I)\r\n\r\n\tIsChasing = true\r\n\tif DebugAIMode then\r\n\t\tI:Log(\"Exec Chase\")\r\n\tend\r\n\t\r\n\tCallComplexControl(I, IsChasingBehindTarget)\r\n\r\n\ttargetPosInfo = CurrentTargetPosInfo\r\n\t\r\n\tif targetPosInfo == nil then return false end\r\n\tif not targetPosInfo.Valid then return false end\r\n\t\r\n\t--We are chasing, so aim at the target.\r\n\ttargetPos = targetPosInfo.Position\r\n\r\n\tYawDesired = YawCurrent + targetPosInfo.Azimuth\r\n\taltAdjustment = targetPosInfo.AltitudeAboveSeaLevel - AltitudeCurrent + ChaseAltOffset\r\n\tPitchDesired = math.deg(math.atan(altAdjustment/targetPosInfo.GroundDistance))\r\n\r\n\tRollDesired = 0\r\n\r\n\tyawRequest, pitchRequest, rollRequest = CallAimPids(I, YawDesired, PitchDesired, RollDesired)\r\n\tApplyDrives(I, yawRequest, pitchRequest, rollRequest)\r\n\r\n\r\n\t--The target is probably turning, we want to follow it\r\n\t--so our follow distance needs to fit within the turning circle of the target.\r\n\tmyPos = I:GetConstructPosition()\r\n\tmyFwd = I:GetConstructForwardVector()\r\n\tworldDirection = targetPosInfo.Direction\r\n\tangle = Vector3.Angle(worldDirection, myFwd)\r\n\t\r\n\topenChaseDistance = CurrentTargetTurnRadius - MyMinTurnRadius\r\n\tChaseDistance = math.min(openChaseDistance, PreferredChaseDistance)\r\n\t--Use a PID to adjust our speed to maintain our desired distance.\r\n\tfinalDriveSpeed = AdjustSpeedToFollowAtRange(I, ChaseDistance, targetPos)\r\n\tif(SpeedCurrent < MinSafeSpeed) then\r\n\t\tfinalDriveSpeed = MaxSafeThrottle\r\n\t\tif DebugThrust then\r\n\t\t\tI:Log(string.format(\"Below Min Safe Speed:%.2f\", SpeedCurrent))\r\n\t\tend\r\n\t\t\r\n\tend\r\n\r\n\tI:RequestControl(2,8,finalDriveSpeed) \r\n\tif DebugThrust then\r\n\t\tI:Log(string.format(\"Chase Setting Thrust:%.2f\", finalDriveSpeed))\r\n\tend\r\n\t\r\n\tif DebugAIMode then\r\n\t\tI:Log(string.format(\"Desired Chase Distance:%.2f Thrust:%.2f\", ChaseDistance, finalDriveSpeed))\r\n\tend\r\n\t\r\n\treturn true\r\nend\r\n\r\n-- Behavior for closing range on the target so we can prepare to chase it.\r\nfunction PrepChase(I)\r\n\r\n\tIsChasing = false\r\n\tif DebugAIMode then\r\n\t\tI:Log(\"Prep Chase\")\r\n\tend\r\n\t\r\n\tCallComplexControl(I, IsChasingInfrontOfTarget)\r\n\t--Collect target Position data.\r\n\tprePosInfo = CurrentTargetPosInfo\r\n\tprePos = prePosInfo.Position\r\n\tpreVel = prePosInfo.Velocity.normalized\r\n\t\r\n\tmyFwd = I:GetConstructForwardVector()\r\n\tdirDif = Vector3.Angle(preVel, myFwd)\r\n\t\r\n\t--We want to anticipate the turning radius needed to get behind this target.\r\n\texpectedSpeed = math.max(prePosInfo.Velocity.magnitude, MinSafeSpeed)\r\n\texpectedSpeed = math.min(expectedSpeed, SpeedCurrent)\r\n\t\r\n\tradiusToMatch = dirDif / MyMaxDeltaDeg * expectedSpeed / 3.14 / 2\r\n\r\n\t--Create a new destination off to the side of the target that we can start to move toward.\r\n\tnewPos = prePos\r\n\tif prePosInfo.Azimuth > 0 then\r\n\t\tnewPos = newPos + (preVel + Vector3.left) * -(radiusToMatch)\r\n\telseif prePosInfo.Azimuth < 0 then\r\n\t\tnewPos = newPos + (preVel + Vector3.left) * (radiusToMatch)\r\n\tend\r\n\r\n\ttargetPosInfo = I:GetTargetPositionInfoForPosition(0, newPos.x, newPos.y, newPos.z)\r\n\r\n\r\n\t--Start aiming the aircraft at our new target position.\r\n\ttargetAltitude = targetPosInfo.AltitudeAboveSeaLevel  + ChaseAltOffset\r\n\tYawDesired = YawCurrent + targetPosInfo.Azimuth\r\n\r\n\ttargetPos = targetPosInfo.Position\r\n\ttargetVel = targetPosInfo.Velocity\r\n\r\n\tTargetDirection = myPos - targetPos\r\n\tMyDirection = targetPos - myPos\r\n\r\n\tdistance = (MyDirection.magnitude - AvoidCollisionsRadius*2)\r\n\ttargetSpeed = targetVel.magnitude\r\n\t\r\n\ttargetDot = Vector3.Dot(targetVel.normalized, TargetDirection.normalized)\r\n\tmyDot = Vector3.Dot(myVel.normalized, MyDirection.normalized)\r\n\trelativeSpeed = (targetSpeed*targetDot + mySpeed*myDot)\r\n\ttti = 99999\r\n\r\n\tif relativeSpeed > 0 then tti = distance / relativeSpeed end\r\n\r\n\tI:Log(string.format(\"AttackRun TTI:%.2f\", tti))\r\n\r\n\tYawDesired = YawCurrent + targetPosInfo.Azimuth\r\n\r\n\tPrepChaseYawOffset = 0\r\n\t\r\n\tif targetPosInfo.GroundDistance < PrepChaseAimToTargetDistance and tti < PrepChaseClosingYawOffsetMinTTI then\r\n\t\tPrepChaseYawOffset = PrepChaseAimingYawOffset\r\n\telseif tti < AttackRunClosingYawOffsetMinTTI then\r\n\t\tPrepChaseYawOffset = PrepChaseClosingYawOffset\r\n\telse\r\n\t\tPrepChaseYawOffset = 0\r\n\tend\r\n\r\n\tif AttackRunUseBothSides then\r\n\t\tif targetPosInfo.Azimuth > 0 then\r\n\t\t\tattackAngle = YawDesired - math.abs(PrepChaseYawOffset)\r\n\t\telseif targetPosInfo.Azimuth < 0 then \r\n\t\t\tattackAngle = YawDesired + math.abs(PrepChaseYawOffset)\r\n\t\tend\r\n\telse\r\n\t\tattackAngle = YawDesired + PrepChaseYawOffset\r\n\tend\r\n\tYawDesired = attackAngle\r\n\r\n\ttargetPos = targetPosInfo.Position\r\n\t\r\n\tgoalAltitude = math.max(targetAltitude, MinAltitude + (AltSafetyPadding * 1.5), MinAltitude + (AltSafetyPadding * 1.5) + I:GetTerrainAltitudeForPosition(targetPos.x,targetPos.y,targetPos.z))\r\n\tgoalAltitude = math.min(goalAltitude, MaxAltitude - (AltSafetyPadding * 1.5))\r\n\t\r\n\tAimFlat(I, YawDesired, goalAltitude)\r\n\tI:RequestControl(2,8,MaxSafeThrottle)\r\n\t\r\n\t\r\n\tif DebugThrust then\r\n\t\tI:Log(string.format(\"Chase Setting Thrust:%.2f\", driveScale))\r\n\tend\r\n\treturn true\r\nend\r\n\r\n\r\n-- A simple behavior that tries to orbit a position.  Used when idle.\r\nfunction OrbitPos(I, x,y,z)\r\n\r\n\tPosInfo = I:GetTargetPositionInfoForPosition(0, x, y, z)\r\n\r\n\tif PosInfo == nil then return end\r\n\r\n\ttargetAltitude = OrbitAltitudeOffset\r\n\tYawDesired = YawCurrent + PosInfo.Azimuth\r\n\t \r\n\t--We are too close to the orbit, pull away\r\n\tif PosInfo.GroundDistance < OrbitAbortRadius then\r\n\t\tif DebugAIMode then\r\n\t\t\tI:Log(\"Orbit Too Close\")\r\n\t\tend\r\n\t\tCallComplexControl(I, IsOrbitingTooClose)\r\n\r\n\t\tif OrbitUseBothSides then\r\n\t\t\tif PosInfo.Azimuth > 0 then\r\n\t\t\t\tYawDesired = YawDesired - math.abs(OrbitAbortAngle)\r\n\t\t\telseif PosInfo.Azimuth < 0 then \r\n\t\t\t\tYawDesired = YawDesired + math.abs(OrbitAbortAngle)\r\n\t\t\tend\r\n\t\telse\r\n\t\t\tYawDesired = YawDesired + OrbitAbortAngle\r\n\t\tend\r\n\t--We are at a good distance, start orbiting.\r\n\telseif PosInfo.GroundDistance < OrbitBeginRadius then\r\n\t\tif DebugAIMode then\r\n\t\t\tI:Log(\"Orbiting\")\r\n\t\tend\r\n\t\tCallComplexControl(I, IsOrbiting)\r\n\r\n\t\tif OrbitUseBothSides then\r\n\t\t\tif PosInfo.Azimuth > 0 then\r\n\t\t\t\tYawDesired = YawDesired - math.abs(OrbitAngle)\r\n\t\t\telseif PosInfo.Azimuth < 0 then \r\n\t\t\t\tYawDesired = YawDesired + math.abs(OrbitAngle)\r\n\t\t\tend\r\n\t\telse\r\n\t\t\tYawDesired = YawDesired + OrbitAngle\r\n\t\tend\r\n\t--We are outside our preferred orbit range, but close enough to prep.\r\n\telseif  PosInfo.GroundDistance < OrbitCloseRadius then\r\n\t\tif DebugAIMode then\r\n\t\t\tI:Log(\"Orbit Too Far\")\r\n\t\tend\r\n\t\tCallComplexControl(I, IsOrbitingTooFar)\r\n\t\t  \r\n\t\tif OrbitUseBothSides then\r\n\t\t\tif PosInfo.Azimuth > 0 then\r\n\t\t\t\tYawDesired = YawDesired - math.abs(OrbitBeginAngle)\r\n\t\t\telseif PosInfo.Azimuth < 0 then \r\n\t\t\t\tYawDesired = YawDesired + math.abs(OrbitBeginAngle)\r\n\t\t\tend\r\n\t\telse\r\n\t\t\tYawDesired = YawDesired + OrbitBeginAngle\r\n\t\tend\r\n\t--We are very far, just fly toward the target.\r\n\telse\r\n\t\tif DebugAIMode then\r\n\t\t\tI:Log(\"Traveling to Orbit\")\r\n\t\tend\r\n\t\tCallComplexControl(I, IsOrbitingTraveling)\r\n\t\t  \r\n\tend\r\n\r\n\t\tAimFlat(I, YawDesired, math.max(targetAltitude, MinAltitude))\r\n\r\nend\r\n\r\n-- Behavior to avoid collisions with other craft.\r\nfunction AvoidCollisions(I)\r\n\r\n\tif AvoidCollisionsFriendly == false and AvoidCollisionsEnemy == false then return false end --we do not avoid.\r\n\r\n\t--Collect some variables about myself.\r\n\tmyPos = I:GetConstructPosition()\r\n\tmyVel = I:GetVelocityVector()\r\n\tmySpeed = I:GetVelocityMagnitude()\r\n\tlocalTerrain = I:GetTerrainAltitudeForLocalPosition(0,0,0)\r\n\trealMinAlt = math.max(localTerrain + MinAltitude, MinAltitude)\r\n\tmyLowAlt = (MaxAltitude - realMinAlt)/4 + realMinAlt\r\n\tmyHiAlt =  MaxAltitude - (MaxAltitude - realMinAlt)/4\r\n\r\n\t--Create placeholders for craft we are about to inspect.\r\n\tnearestCollisionPos = Vector3(9999999,9999999,9999999)\r\n\tnearestTti = 9999999\r\n\tnearestDotTargetToMe = 0\r\n\tnearestDotMeToTarget = 0\r\n\tsource = 0\r\n\r\n\t--try and anticipate my future position along this path.\r\n\tmyFuturePos = I:GetConstructPosition() + (I:GetVelocityVector().normalized + I:GetVelocityVector().forward) * SpeedCurrent\r\n\r\n\t--parse a list of all friendlies and determine how likely we are to collide in the near future.\r\n\t--predictions are TTI based (comparing relative speed * DOT, since we don't know what direction everyone will turn in soon)\r\n\tif AvoidCollisionsFriendly and I:GetFriendlyCount() > 0 then\r\n\t\tfor ii=0,I:GetFriendlyCount()-1,1 do\r\n\t\t\r\n\t\t\ttargetPosInfo = I:GetFriendlyInfo(ii)\r\n\t\t\ttargetSizeVector = targetPosInfo.PositiveSize - targetPosInfo.NegativeSize\r\n\r\n\t\t\ttargetPos = targetPosInfo.CenterOfMass\r\n\t\t\ttargetVel = targetPosInfo.Velocity\r\n\r\n\t\t\tTargetDirection = myPos - targetPos\r\n\t\t\tMyDirection = targetPos - myPos\r\n\r\n\t\t\tdistance = (MyDirection.magnitude - AvoidCollisionsRadius) - targetSizeVector.magnitude\r\n\t\t\ttargetSpeed = targetVel.magnitude\r\n\t\t\t\r\n\t\t\ttargetDot = Vector3.Dot(targetVel.normalized, TargetDirection.normalized)\r\n\t\t\tmyDot = Vector3.Dot(myVel.normalized, MyDirection.normalized)\r\n\t\t\trelativeSpeed = (targetSpeed*targetDot + mySpeed*myDot)\r\n\t\t\ttti = 99999\r\n\t\t\t\r\n\t\t\tif relativeSpeed > 0 then tti = distance / relativeSpeed end\r\n\t\t\t\r\n\t\t\tif tti < nearestTti then --We record a new nearest collision threat.\r\n\t\t\t\tnearestCollisionPos = targetPos\r\n\t\t\t\tnearestTti = tti\r\n\t\t\t\tnearestDotTargetToMe = targetDot\r\n\t\t\t\tnearestDotMeToTarget = myDot\r\n\t\t\t\tsource = 1\r\n\t\t\tend\r\n\r\n\t\tend\r\n\tend\r\n\t\r\n\t--Do the same, but now for enemies.\r\n\ttargetCount = I:GetNumberOfTargets(0)\r\n\tif AvoidCollisionsEnemy and targetCount > 0 then\r\n\t\tfor ii=0,targetCount-1,1 do\r\n\t\t\ttargetPosInfo = I:GetTargetPositionInfo(0, ii)\r\n\r\n\t\t\ttargetPos = targetPosInfo.Position\r\n\t\t\ttargetVel = targetPosInfo.Velocity\r\n\r\n\t\t\tTargetDirection = myPos - targetPos\r\n\t\t\tMyDirection = targetPos - myPos\r\n\r\n\t\t\tdistance = (MyDirection.magnitude - AvoidCollisionsRadius*2)\r\n\t\t\ttargetSpeed = targetVel.magnitude\r\n\t\t\t\r\n\t\t\ttargetDot = Vector3.Dot(targetVel.normalized, TargetDirection.normalized)\r\n\t\t\tmyDot = Vector3.Dot(myVel.normalized, MyDirection.normalized)\r\n\t\t\trelativeSpeed = (targetSpeed*targetDot + mySpeed*myDot)\r\n\t\t\ttti = 99999\r\n\r\n\t\t\tif relativeSpeed > 0 then tti = distance / relativeSpeed end\r\n\r\n\t\t\tif tti < nearestTti then --We record a new nearest collision threat.\r\n\t\t\t\tnearestCollisionPos = targetPos\r\n\t\t\t\tnearestTti = tti\r\n\t\t\t\tnearestDotTargetToMe = targetDot\r\n\t\t\t\tnearestDotMeToTarget = myDot\r\n\t\t\t\tsource = 2\r\n\t\t\tend\r\n\r\n\t\tend\r\n\tend\r\n\r\n\t--now we know the most likely near future collision...\r\n\r\n\tif source == 0 or nearestTti > AvoidanceTimeThreshold or nearestCollisionPos == nil then return false end -- we found no targets worth avoiding\r\n\tif DebugAIMode == true and source > 0 then I:Log(string.format(\"Collision tti %.2f\", nearestTti)) end\r\n\r\n\tAvoidPosInfo = I:GetTargetPositionInfoForPosition(0, nearestCollisionPos.x,nearestCollisionPos.y,nearestCollisionPos.z)\r\n\r\n\t--if our imminent collision is less than 1/4 of our TTI threshold away, it's critical, so we will turn harder.\r\n\tif nearestTti < AvoidanceTimeThreshold/4 then ttiCritical = true end\r\n\r\n\tif (nearestDotMeToTarget > 0) then \r\n\t--If the target is actively moving toward us we need to dodge\r\n\r\n\t\tif ttiCritical then \r\n\t\t\tadjEvasionYawAngle = AvoidanceTurnRate * 3\r\n\t\t\tEvasionPitchAngle = AvoidancePitchRate * 3\r\n\t\telse \r\n\t\t\tadjEvasionYawAngle = AvoidanceTurnRate\r\n\t\t\tEvasionPitchAngle = AvoidancePitchRate\r\n\t\tend\r\n\t\t\r\n\t\tYawDesired = YawCurrent + AvoidPosInfo.Azimuth\r\n\t\tif AvoidPosInfo.Azimuth >= 0 then\r\n\t\t\tYawDesired = YawDesired + adjEvasionYawAngle\r\n\t\telseif AvoidPosInfo.Azimuth < 0 then\r\n\t\t\tYawDesired = YawDesired - adjEvasionYawAngle\r\n\t\tend\r\n\r\n\t\tPitchDesired = 0\r\n\t\tif myHiAlt > AltitudeCurrent and AvoidPosInfo.AltitudeAboveSeaLevel < AltitudeCurrent then\r\n\t\t\tPitchDesired = PitchDesired + EvasionPitchAngle\r\n\t\telseif myLowAlt < AltitudeCurrent and AvoidPosInfo.AltitudeAboveSeaLevel > AltitudeCurrent then\r\n\t\t\tPitchDesired = PitchDesired - EvasionPitchAngle\r\n\t\tend \r\n\t\t\r\n\t\tRollDesired = 0\r\n\t\t\r\n\t\tyawRequest, pitchRequest, rollRequest = CallAimPids(I, YawDesired, PitchDesired, RollDesired)\r\n\t\tApplyDrives(I, yawRequest, pitchRequest, rollRequest)\r\n\t\t\r\n\t\tif nearestDotMeToTarget < 0 then -- if the target is behind us we need to increase thrust.\r\n\t\t\tI:RequestControl(2,8,MaxSafeThrottle) -- Set thrust to max\r\n\t\t\tif DebugThrust then\r\n\t\t\t\tI:Log(string.format(\"Collision Setting Thrust:%.2f\", 1))\r\n\t\t\tend\r\n\t\tend\r\n\t\t\r\n\t\tif DebugAIMode then\r\n\t\t\tI:Log(\"Avoiding Collision\")\r\n\t\tend\r\n\t\tCallComplexControl(I, IsAvoidingCollision)\r\n\t\treturn true --We're avoiding\r\n\t\t\r\n\telseif nearestDotTargetToMe < 0 and nearestDotMeToTarget > 0 then\r\n\t\t--if the target is moving away, but I'm the one moving closer, then just slow down.\r\n\t\t\r\n\t\tthrustRequest = AdjustSpeedToFollowAtRange(I, math.max(AvoidCollisionsRadius, PreferredChaseDistance), AvoidPosInfo.Position)\r\n\t\tI:RequestControl(2,8,thrustRequest)\r\n\t\tif DebugThrust then\r\n\t\t\tI:Log(string.format(\"Collision Setting Thrust:%.2f\", thrustRequest))\r\n\t\tend\t\r\n\t\tif DebugAIMode then\r\n\t\t\tI:Log(\"Avoiding Collision\")\r\n\t\tend\r\n\t\tCallComplexControl(I, IsAvoidingCollision)\r\n\t\treturn true --We're avoiding\r\n\t\t\t\r\n\tend\r\n\r\n\r\n\treturn false  --We're safe\r\nend\r\n\r\n-- Not used in the Tourney Version.  \r\n--Used to check if friendly blueprint names are in a list.\r\n--Usually used for formations or escort type behaviors\r\nfunction NameMatches(NameList, Name)\r\n\r\n  if NameList=='all' then return true end\r\n  \r\n  if type(NameList) == \"table\" then\r\n\tfor k,v in pairs(NameList) do\r\n\t  if v==string.sub(Name,1,string.len(v)) then return true end\r\n\tend\r\n  elseif NameList==string.sub(Name,1,string.len(NameList)) then return true\r\n  end\r\n  return false\r\nend\r\n\r\n--I:GetNumberOfWarnings() throws errors that crash the LUA.  \r\n--This lets us call it safely and handle errors.\r\nfunction GetNumberOfWarnings(I)      \r\n\tif unexpected_condition then error() end   \r\n\treturn I:GetNumberOfWarnings()       \r\nend\r\n\r\n--I:GetMissileWarnings() throws errors that crash the LUA.  \r\n--This lets us call it safely and handle errors.\r\nfunction GetMissileWarnings(I,ii)    \r\n\tif unexpected_condition then error() end   \r\n\treturn I:GetMissileWarning(ii)\r\nend\r\n\r\n-- Behavior to avoid enemy missiles.\r\nfunction MissileAvoidance(I)\r\n\t\r\n\t--Get a protected Call of I:GetNumberOfWarnings()\r\n\tmissileRan, NumberOfWarnings = pcall(GetNumberOfWarnings, I) \r\n\tif not missileRan then \r\n\t\tI:Log(\"Captured GetMissileWarnings Error: \" .. NumberOfWarnings)\r\n\t\treturn false --This means we had an error, so just move on in the LUA.\r\n\tend\r\n\t\r\n\tif UseMissileAvoidance == false or NumberOfWarnings == 0 then \r\n\t\tCurrentMissileEvasionPosition = nil\r\n\t\treturn false \r\n\tend -- no dodge needed\r\n\t\r\n\t\r\n\t\r\n\t--Get info about me.\r\n\tmyPos = I:GetConstructPosition()\r\n\tmyVel = I:GetVelocityVector()\r\n\tmySpeed = I:GetVelocityMagnitude()\r\n\tlocalTerrain = I:GetTerrainAltitudeForLocalPosition(0,0,0)\r\n\trealMinAlt = math.max(localTerrain + MinAltitude, MinAltitude)\r\n\tmyLowAlt = (MaxAltitude - realMinAlt)/4 + realMinAlt\r\n\tmyHiAlt =  MaxAltitude - (MaxAltitude - realMinAlt)/4\r\n\t\r\n\t--Place holders for all the missiles we are about to rank.\r\n\tPanicMissilesFound = 0\r\n\tUrgentMissilesFound = 0\r\n\tWarnedMissilesFound = 0\r\n\tPanicMissilePos = {}\r\n\tUrgentMissilePos = {}\r\n\tWarnedMissilePos = {}\r\n\t\r\n\t--A loop to evaluate each missile's TTI.\r\n\tfor ii=0,NumberOfWarnings-1,1 do\r\n\r\n\t\t--Make our protected call to get missile warnings\r\n\t\tmissileInfoRan, MissileInfo = pcall(GetMissileWarnings,I, ii) \r\n\t\t\r\n\t\tif not missileInfoRan and DebugLuaCore then\r\n\t\t\tI:Log(\"Captured GetMissileWarnings Error: \" .. MissileInfo)\r\n\t\tend\r\n\t\t\r\n\t\tif MissileInfo == nil or not missileInfoRan then goto continue end --Something went wrong, skip this missile.\r\n\t\t\r\n\t\tif MissileInfo.Valid then\r\n\r\n\t\t\t--Collect info about our missile.\r\n\t\t\ttargetPos = MissileInfo.Position\r\n\t\t\ttargetVel = MissileInfo.Velocity\r\n\r\n\t\t\tMissileDirection = myPos - targetPos\r\n\t\t\tMyDirection = targetPos - myPos\r\n\r\n\t\t\tdistance = MyDirection.magnitude - AvoidanceRadius\r\n\t\t\ttargetSpeed = targetVel.magnitude\r\n\r\n\t\t\ttargetDot = Vector3.Dot(targetVel.normalized, MissileDirection.normalized)\r\n\t\t\tmyDot = Vector3.Dot(myVel.normalized, MyDirection.normalized)\r\n\t\t\t\r\n\t\t\ttti = 99999\r\n\t\t\trelativeSpeed = (targetSpeed*targetDot + mySpeed*myDot)\r\n\t\t\t\r\n\t\t\tif relativeSpeed > 0 then tti = distance / relativeSpeed end\r\n\t\t\t\r\n\t\t\tif DebugEvasionDetails then\r\n\t\t\t\tI:Log(string.format(\"Distance:  %.2f MyRelSpd: %.2f  MslRelSpd: %.2f  TTI: %.5f\", distance, myDot, targetDot, tti))\r\n\t\t\tend\r\n\t\t\t\r\n\t\t\t--group our missile by TTI\r\n\t\t\tif tti < PanicTti then\r\n\t\t\t\tPanicMissilesFound = PanicMissilesFound + 1\r\n\t\t\t\ttable.insert(PanicMissilePos, targetPos)\r\n\t\t\t\t\r\n\t\t\telseif tti < UrgentTti then\r\n\t\t\t\tUrgentMissilesFound = UrgentMissilesFound + 1\r\n\t\t\t\ttable.insert(UrgentMissilePos, targetPos)\r\n\t\t\t\t\r\n\t\t\telseif tti < WarnedTti then\r\n\t\t\t\tWarnedMissilesFound = WarnedMissilesFound + 1\r\n\t\t\t\ttable.insert(WarnedMissilePos, targetPos)\r\n\t\t\tend\r\n\t\t\r\n\t\tend\r\n\t\t::continue::\r\n\tend\r\n\tif DebugAIMode then\r\n\t\tI:Log(string.format(\"MISSILES! Panic: %.0f  Urgent: %.0f Warned: %.0f Ignored: %.0f\", PanicMissilesFound, UrgentMissilesFound, WarnedMissilesFound, NumberOfWarnings - (UrgentMissilesFound + WarnedMissilesFound)))\r\n\tend\r\n\t\r\n\t--We have our lists, lets evaluate them.\r\n\t\r\n\tif PanicMissilesFound + WarnedMissilesFound + UrgentMissilesFound == 0 then \r\n\t\tCurrentMissileEvasionPosition = nil\r\n\t\treturn false \r\n\tend --no missiles need avoiding.\r\n\r\n\t--We will create an average position for all incomming missiles to try and dodge the swarm instead of the single closest.\r\n\tavgMissilePos = Vector3(0,0,0)\r\n\tMissileSwarmPos = Vector3(0,0,0)\r\n\tMissileCount = 0\r\n\t\r\n\t--Our swarm position is based only on the most urgent group of missiles.  \r\n\tif PanicMissilesFound > 0 then\r\n\t\tfor k,v in pairs(PanicMissilePos) do\r\n\t\t\tavgMissilePos = avgMissilePos + v\r\n\t\tend\r\n\t\tMissileSwarmPos = avgMissilePos/PanicMissilesFound\t\r\n\telseif UrgentMissilesFound > 0 then\r\n\t\tfor k,v in pairs(UrgentMissilePos) do\r\n\t\t\tavgMissilePos = avgMissilePos + v\r\n\t\tend\r\n\t\tMissileSwarmPos = avgMissilePos/UrgentMissilesFound\r\n\telse\r\n\t\tfor k,v in pairs(WarnedMissilePos) do\r\n\t\t\tavgMissilePos = avgMissilePos + v\r\n\t\tend\r\n\t\tMissileSwarmPos = avgMissilePos/WarnedMissilesFound\r\n\tend\r\n\t\t\t\t\t\t\r\n\t--Create a new positionInfo based on the average position.\r\n\tAvoidPosInfo = I:GetTargetPositionInfoForPosition(0, MissileSwarmPos.x,MissileSwarmPos.y,MissileSwarmPos.z)\r\n\tCurrentMissileEvasionPosition = AvoidPosInfo\r\n\r\n\tavoidDistance = myPos - MissileSwarmPos\r\n\tif DebugEvasionDetails then\r\n\t\tI:Log(string.format(\"Missile distance: %.2f  az %.2f\", avoidDistance.magnitude, AvoidPosInfo.Azimuth))\r\n\tend\r\n\tadjEvasionYawAngle = EvasionYawAngle\r\n\t\r\n\t--We will modify our turn behaviors based on TTI of the swarm.\r\n\tif PanicMissilesFound > 0 then \r\n\t\tadjEvasionYawAngle = EvasionYawAngle * -2\r\n\t\tadjEvasionPitchAngle = EvasionPitchAngle * -1\r\n\t\tCallComplexControl(I, IsAvoidingMissilePanic)\r\n\telseif UrgentMissilesFound > 0 then \r\n\t\tadjEvasionYawAngle = EvasionYawAngle * 2 \r\n\t\tadjEvasionPitchAngle = EvasionPitchAngle * 1\r\n\t\tCallComplexControl(I, IsAvoidingMissileUrgent) \r\n\telse\r\n\t\tadjEvasionPitchAngle = EvasionPitchAngle * 0.5\r\n\t\tCallComplexControl(I, IsAvoidingMissile)\r\n\tend\r\n\t\r\n\tif(UseDragonFlyEvasionOnly == true) then \r\n\t\tif DebugEvasionDetails then\r\n\t\t\tI:Log(\"Evading with Dragonfly mode.\")\r\n\t\tend\r\n\t\treturn false\r\n\tend\r\n\t\r\n\t--Start applying the desired turn behaviors.\r\n\tYawDesired = YawCurrent + AvoidPosInfo.Azimuth\r\n\tif AvoidPosInfo.Azimuth >= 0 then\r\n\t\tYawDesired = YawDesired + adjEvasionYawAngle\r\n\telseif AvoidPosInfo.Azimuth < 0 then\r\n\t\tYawDesired = YawDesired - adjEvasionYawAngle\r\n\tend\r\n\r\n\tAvoidPos = AvoidPosInfo.Position\r\n\tMyDirection = AvoidPos - myPos\r\n\tmyDot = Vector3.Dot(myVel.normalized.up, MyDirection.normalized)\r\n\t\r\n\r\n\tPitchDesired = 0\r\n\tif AvoidPosInfo.ElevationForAltitudeComponentOnly >= 0 then\r\n\t\tPitchDesired = (YawDesired + adjEvasionPitchAngle) * myDot\r\n\telseif AvoidPosInfo.ElevationForAltitudeComponentOnly < 0 then\r\n\t\tPitchDesired = (YawDesired - adjEvasionPitchAngle) * myDot\r\n\tend\r\n\t\r\n\r\n\tRollDesired = 0\r\n\r\n\t--apply the desired turns.\r\n\tyawRequest, pitchRequest, rollRequest = CallAimPids(I, YawDesired, PitchDesired, RollDesired)\r\n\tApplyDrives(I, yawRequest, pitchRequest, rollRequest)\r\n\tI:RequestControl(2,8,MaxSafeThrottle) -- Set thrust to max\r\n\tif DebugThrust then\r\n\t\tI:Log(string.format(\"Missile Avoidance Setting Max Safe Thrust:%.2f\", MaxSafeThrottle))\r\n\tend\r\n\tif DebugAIMode then\r\n\t\tI:Log(\"Avoiding Missile\")\r\n\tend\r\n\r\n\r\n\treturn true -- we are dodging missiles\r\n\r\nend\r\n\r\n-- Behavior called when idle.\r\nfunction OrbitSpawnWhenIdle(I)\r\n\r\n\tif not UseOrbitSpawnWhenIdle then return false end --skip this.\r\n\t\r\n\t--We call OrbitPos, described above, and adjust speed.  That is all.\r\n\tOrbitPos(I, SpawnPOS.x ,CruiseAltitude + math.max(0, I:GetTerrainAltitudeForPosition(SpawnPOS.x,SpawnPOS.y,SpawnPOS.z)) ,SpawnPOS.z )\r\n\tthrustRequest = AdjustSpeedForCruise(I, DesiredCruiseSpeed, CruiseAtMaxSpeed)\r\n\tI:RequestControl(2,8,thrustRequest)\r\n\tif DebugThrust then\r\n\t\tI:Log(string.format(\"Orbit Spawn Setting Thrust:%.2f\", thrustRequest))\r\n\tend\r\n\treturn true --we are orbiting Spawn\r\n\r\nend\r\n\r\n-- One of the first things run.  We collect some basic situational info about ourselves.\r\nfunction SetCurrentValues(I)\r\n\t--We check the gametime passed since last run (gamespeed affects this, used to adjust PIDs)\r\n\tFrameTime = I:GetTimeSinceSpawn() - LastRunTime\r\n\tLastRunTime = I:GetTimeSinceSpawn()\r\n\r\n\tif DebugLuaCore then I:Log(string.format(\"FrameTime %.5f\", FrameTime)) end\r\n\r\n\t--find some turning rate and turn radius values\r\n\tMyPrevVel = MyCurrVel\r\n\tMyCurrVel = I:GetConstructForwardVector()\r\n\tMyDeltaDeg = Vector3.Angle(MyCurrVel, MyPrevVel) * (1/FrameTime)\r\n\tif(MyMaxDeltaDeg < MyDeltaDeg and MyDeltaDeg < MyMaxDeltaDeg * 5) then MyMaxDeltaDeg = MyDeltaDeg end\r\n\r\n\tMyMinTurnRadius = 360 / MyMaxDeltaDeg * MinSafeSpeed / 3.14 / 2\r\n\r\n\tif DebugDeltaDegDetails then\r\n\t\tI:Log(string.format(\"MyDeltaDeg:%.2f\", MyDeltaDeg))\r\n\t\tI:Log(string.format(\"MyMaxDeltaDeg:%.2f\", MyMaxDeltaDeg))\r\n\t\tI:Log(string.format(\"MyMinTurnRadius:%.2f\", MyMinTurnRadius))\r\n\tend\r\n\r\n\t--capture current direction data.\r\n\tYawCurrent = I:GetConstructYaw()\r\n\twhile YawCurrent > 180\r\n\tdo\r\n\t\tYawCurrent = YawCurrent - 360\r\n\tend\r\n\r\n\tPitchCurrent = -I:GetConstructPitch()\r\n\twhile PitchCurrent > 180\r\n\tdo\r\n\t\tPitchCurrent = PitchCurrent - 360\r\n\tend\r\n\r\n\tRollCurrent = I:GetConstructRoll()\r\n\twhile RollCurrent > 180\r\n\tdo\r\n\t\tRollCurrent = RollCurrent - 360\r\n\tend\r\n\tRollCurrent = -RollCurrent\r\n\r\n\tSpeedCurrent = I:GetVelocityMagnitude()\r\n\t\r\n\t--Collect altitude data, and enforce some altitude safety constraints.\r\n\tAltitudeCurrent = math.min(I:GetConstructPosition().y, I:GetConstructPosition().y - I:GetTerrainAltitudeForLocalPosition(0,0,0))\r\n\r\n\tif DebugAltitudeSafetyToHud then\r\n\t\tif AltitudeCurrent < 60 then I:LogToHud(\"TOO LOW!\") end\r\n\t\tif AltitudeCurrent > 590 then I:LogToHud(\"TOO HIGH!\") end\r\n\tend\r\n\tif DebugAltitudeSafety then\r\n\t\tif AltitudeCurrent < 60 then I:Log(string.format(\"TOO LOW!:%.2f\", AltitudeCurrent)) end\r\n\t\tif AltitudeCurrent > 590 then I:Log(string.format(\"TOO HIGH!:%.2f\", AltitudeCurrent)) end\r\n\tend\r\n\t\r\n\tif AltitudeCurrent > MaxAltitude and PitchCurrent > 0 then\r\n\t\tMaxSafeThrottle = 0.3\r\n\telseif AltitudeCurrent > MaxAltitude - AltSafetyPadding and PitchCurrent > -5 then\r\n\t\tMaxSafeThrottle = 0.5\r\n\telseif AltitudeCurrent < MinAltitude and PitchCurrent < 0 then\r\n\t\tMaxSafeThrottle = 0.3\r\n\telseif AltitudeCurrent < MinAltitude + AltSafetyPadding and PitchCurrent < 5 then\r\n\t\tMaxSafeThrottle = 0.5\r\n\telse MaxSafeThrottle = 1\r\n\tend\r\n\t\r\nend\r\n\r\n-- Our final behavior, if everything else was skipped.  \r\n--We simply log letting the default AI take control, and set cruise speed if desired.\r\nfunction DefaultAIIdle(I)\r\n\tif DebugAIMode then\r\n\t\tI:Log(\"We are letting the default AI take control.\")\r\n\tend\r\n\tif SetCruiseSpeedWhile then\r\n\t\tthrustRequest = AdjustSpeedForCruise(I, DesiredCruiseSpeed, CruiseAtMaxSpeed)\r\n\t\tI:RequestControl(2,8,thrustRequest)\r\n\t\tif DebugThrust then\r\n\t\t\tI:Log(string.format(\"Idle AI Setting Thrust:%.2f\", thrustRequest))\r\n\t\tend\r\n\tend\r\n\t\r\n\tCallComplexControl(I, IsLettingAiTakeControl)\r\nend\r\n\r\n-- Custom target priority selection.\r\nfunction ChooseCurrentTarget(I)\r\n\r\n\ttargetCount = I:GetNumberOfTargets(0)\r\n\tif targetCount == 0 then \r\n\t\tCurrentTargetPosInfo = nil\r\n\t\tCurrentTargetPrevVel = 0\r\n\t\tCurrentTargetCurrVel = 0\r\n\t\tCurrentTargetDeltaDeg = 0\r\n\t\treturn\r\n\tend\r\n\r\n\t--collect a little data about me.\r\n\tmyPos = I:GetConstructPosition()\r\n\tMyVel = I:GetConstructForwardVector()\r\n\r\n\t--placeholder for highest priority enemy.\r\n\tpreferedTarget = nil\r\n\tpreferedTargetInfo = nil\r\n\tpreferedTargetPosInfo = nill\r\n\tpreferedTargetPriority = -99999999\r\n\t\r\n\t--loop through all detected enemies.\r\n\tfor ii=0,targetCount-1,1 do\r\n\t\ttargetInfo = I:GetTargetInfo(0, ii)\r\n\r\n\t\tif(targetInfo.Valid) then\r\n\t\t\r\n\t\t\t--collect basic target data.\r\n\t\t\ttargetPosInfo = I:GetTargetPositionInfo(0, ii)\r\n\t\t\t\t\t\t\r\n\t\t\ttargetPriority = 0\r\n\t\t\ttargetPos = targetInfo.Position\r\n\t\t\ttargetVel = targetInfo.Velocity\r\n\t\t\ttargetAlt = targetPosInfo.AltitudeAboveSeaLevel\r\n\t\t\t\r\n\t\t\tTargetToMeDirection = myPos - targetPos\r\n\t\t\tMeToTargetDirection = targetPos - myPos\r\n\r\n\t\t\tdistance = MeToTargetDirection.magnitude\r\n\t\t\ttargetSpeed = targetVel.magnitude\r\n\t\t\t\r\n\t\t\ttargetDot = Vector3.Dot(targetVel.normalized, TargetToMeDirection.normalized)\r\n\t\t\tmyDot = Vector3.Dot(myVel.normalized, MeToTargetDirection.normalized)\r\n\t\t\t\t\t\r\n\t\t\t--Check a list of all previously spotted targets, search for our current, and update it's values.\r\n\t\t\t--This is to determine our target's turnrate by comparing past Vel to current Vel.\r\n\t\t\ttargetFound = nil\r\n\t\t\tif type(AllTargetInfo) == \"table\" then\r\n\t\t\t\tfor k,v in pairs(AllTargetInfo) do\r\n\t\t\t\t\tif v.Id == targetInfo.Id then\r\n\t\t\t\t\t\ttargetFound = v\r\n\t\t\t\t\t\tv.PrevVel = v.CurrVel\r\n\t\t\t\t\t\tv.CurrVel = targetInfo.Velocity\r\n\t\t\t\t\t\tv.TurnRate = Vector3.Angle(v.CurrVel, v.PrevVel) * (1/FrameTime)\r\n\t\t\t\t\t\tv.TargetTurnRadius = 360 / math.max(v.TurnRate, 0.01) * math.max(targetInfo.Velocity.magnitude,0.01) / 3.14 / 2\r\n\t\t\t\t\t\tv.CurrentSpeed = targetSpeed \r\n\t\t\t\t\t\tif targetSpeed > v.MaxSpeed then\r\n\t\t\t\t\t\t\tMaxSpeed = targetSpeed\r\n\t\t\t\t\t\tend\r\n\t\t\t\t\tend\r\n\t\t\t\tend\r\n\t\t\tend\r\n\t\t\tif targetFound == nil then\r\n\t\t\t\ttargetFound = {Id = targetInfo.Id, PrevVel = Vector3(0,0,0), CurrVel = targetInfo.Velocity, TurnRate = 0.01, TargetTurnRadius = 0.01, CurrentSpeed = targetSpeed, MaxSpeed = targetSpeed}\r\n\t\t\t\ttable.insert(AllTargetInfo, targetFound)\t\r\n\t\t\tend\r\n\t\t\r\n\t\t\t--Start using our target data to pick favorites.  \r\n\t\t\ttargetPriority = targetInfo.Score * DefaultAIScoreScale\r\n\t\t\t\r\n\t\t\tif UseChaseBehavior\r\n\t\t\tand\ttargetPosInfo.Velocity.magnitude >= MinChaseSpeed \r\n\t\t\tand targetAlt >= MinChaseAltitude \r\n\t\t\tand targetAlt <= MaxChaseAltitude\r\n\t\t\tand CurrentTargetTurnRadius > (MyMinTurnRadius + MinChaseDistance)*1.2\r\n\t\t\tthen\r\n\t\t\t\ttargetPriority = IsChaseValidBase\r\n\t\t\tend\r\n\t\t\tif targetInfo.Id == CurrentTargetId then\r\n\t\t\t\ttargetPriority = targetPriority + MaintainTargetBase\r\n\t\t\tend \r\n\t\t\tif targetSpeed < MinChaseSpeed then\r\n\t\t\t\ttargetPriority = targetPriority + ((targetSpeed - MinChaseSpeed) * BelowMinSpeedPenPerMPS)\r\n\t\t\tend\r\n\t\t\tif targetAlt < MinTargetAltitude then\r\n\t\t\t\ttargetPriority = targetPriority + ((targetAlt - MinTargetAltitude) * BelowMinAltPenPerM)\r\n\t\t\tend\r\n\t\t\tif targetAlt > MaxTargetAltitude then\r\n\t\t\t\ttargetPriority = targetPriority + ((MaxTargetAltitude - targetAlt) * AboveMaxAltPenPerM)\r\n\t\t\tend\r\n\t\t\tif targetAlt < MinChaseAltitude then\r\n\t\t\t\ttargetPriority = targetPriority + ((targetAlt - MinChaseAltitude) * BelowChaseAltPenPerM)\r\n\t\t\tend\r\n\t\t\tif targetAlt > MaxChaseAltitude then\r\n\t\t\t\ttargetPriority = targetPriority + ((MaxChaseAltitude - targetAlt) * AboveChaseAltPenPerM)\r\n\t\t\tend\r\n\t\t\ttargetPriority = targetPriority - (distance * DistancePenPerM)\r\n\t\t\ttargetPriority = targetPriority - (targetFound.TurnRate * TurnRatePenPerDPerSec)\r\n\t\t\ttargetPriority = targetPriority + ((myDot - targetDot) * ChaseDotBehindScale)\r\n\t\t\r\n\t\t\t--If this target has a higher priority than our old max, it is now the favorite.\r\n\t\t\tif preferedTargetPriority < targetPriority then\r\n\t\t\t\tpreferedTargetPriority = targetPriority\r\n\t\t\t\tpreferedTargetInfo = targetInfo\r\n\t\t\t\tpreferedTargetPosInfo = targetPosInfo\r\n\t\t\t\tpreferedTarget = targetFound\r\n\t\t\tend\r\n\t\tend\r\n\tend\r\n\r\n\r\n\t\r\n\tif preferedTargetInfo == nil then return end --We hit an error\r\n\t\r\n\t--Save data for our preferred target and make it available for other functions.\r\n\tif CurrentTargetId == preferedTargetInfo.Id and DebugTargetPriority then\r\n\t\tI:Log(string.format(\"MaintainTarget Bonus:%.2f\", MaintainTargetBase))\r\n\tend\r\n\t\r\n\tCurrentTargetId = preferedTargetInfo.Id\r\n\tCurrentTargetPosInfo = preferedTargetPosInfo\r\n\tCurrentTargetPrevVel = preferedTarget.PrevVel\r\n\tCurrentTargetCurrVel = preferedTarget.CurrVel\r\n\tCurrentTargetDeltaDeg = preferedTarget.TurnRate\r\n\tCurrentTargetTurnRadius = preferedTarget.TargetTurnRadius\r\n\r\n\tif DebugDeltaDegDetails then\r\n\t\tI:Log(string.format(\"CurrentTargetDeltaDeg:%.2f\", CurrentTargetDeltaDeg))\r\n\t\tI:Log(string.format(\"CurrentTargetTurnRadius:%.2f\", CurrentTargetTurnRadius))\r\n\tend\r\n\t\r\n\tif DebugTargetPriority then\r\n\t\tI:Log(string.format(\"TargetID:%i\", preferedTargetInfo.Id))\r\n\t\tI:Log(string.format(\"TargetPriority:%i\", preferedTargetPriority))\r\n\tend\r\n\t\r\n\tTargetToMeDirection = myPos - CurrentTargetPosInfo.Position\r\n\tMeToTargetDirection = CurrentTargetPosInfo.Position - myPos\r\n\r\n\tdistance = MeToTargetDirection.magnitude\r\n\ttargetSpeed = CurrentTargetCurrVel.magnitude\r\n\r\n\ttargetDot = Vector3.Dot(CurrentTargetCurrVel.normalized, TargetToMeDirection.normalized)\r\n\tmyDot = Vector3.Dot(myVel.normalized, MeToTargetDirection.normalized)\r\n\t\r\n\t--For our current target, log all the same data we just looked at.  For debugging.\r\n\tif DebugTargetPriority then\r\n\t\tif UseChaseBehavior\r\n\t\tand\tCurrentTargetPosInfo.Velocity.magnitude >= MinChaseSpeed \r\n\t\tand CurrentTargetPosInfo.AltitudeAboveSeaLevel >= MinChaseAltitude \r\n\t\tand CurrentTargetPosInfo.AltitudeAboveSeaLevel <= MaxChaseAltitude\r\n\t\tand CurrentTargetTurnRadius > (MyMinTurnRadius + MinChaseDistance)*1.2\r\n\t\tthen\r\n\t\t\tI:Log(string.format(\"IsChaseValid Bonus:%.2f\", IsChaseValidBase))\r\n\t\tend\r\n\t\tif CurrentTargetPosInfo.Velocity.magnitude < MinChaseSpeed then\r\n\t\t\tI:Log(string.format(\"BelowMinSpeedPenPerMPS Penalty:%.2f\", ((CurrentTargetPosInfo.Velocity.magnitude - MinChaseSpeed) * BelowMinSpeedPenPerMPS)))\r\n\t\tend\r\n\t\tif CurrentTargetPosInfo.AltitudeAboveSeaLevel < MinTargetAltitude then\r\n\t\t\tI:Log(string.format(\"BelowMinAltPenPerM Penalty:%.2f\", ((CurrentTargetPosInfo.AltitudeAboveSeaLevel - MinTargetAltitude) * BelowMinAltPenPerM)))\r\n\t\tend\r\n\t\tif CurrentTargetPosInfo.AltitudeAboveSeaLevel > MaxTargetAltitude then\r\n\t\t\tI:Log(string.format(\"AboveMaxAltPenPerM Penalty:%.2f\", ((MaxTargetAltitude - CurrentTargetPosInfo.AltitudeAboveSeaLevel) * AboveMaxAltPenPerM)))\r\n\t\tend\r\n\t\tif CurrentTargetPosInfo.AltitudeAboveSeaLevel < MinChaseAltitude then\r\n\t\t\tI:Log(string.format(\"BelowChaseAltPenPerM Penalty:%.2f\", ((CurrentTargetPosInfo.AltitudeAboveSeaLevel - MinChaseAltitude) * BelowChaseAltPenPerM)))\r\n\t\tend\r\n\t\tif CurrentTargetPosInfo.AltitudeAboveSeaLevel > MaxChaseAltitude then\r\n\t\t\tI:Log(string.format(\"AboveChaseAltPenPerM Penalty:%.2f\", ((MaxChaseAltitude - CurrentTargetPosInfo.AltitudeAboveSeaLevel) * AboveChaseAltPenPerM)))\r\n\t\tend\r\n\t\tI:Log(string.format(\"DistancePenPerM Penalty:-%.2f\", (distance * DistancePenPerM)))\r\n\t\tI:Log(string.format(\"TurnRatePenPerDPerSec Penalty:-%.2f\", (CurrentTargetDeltaDeg * TurnRatePenPerDPerSec)))\r\n\t\tI:Log(string.format(\"ChaseDotBehind Bonus:%.2f\", ((myDot - targetDot) * ChaseDotBehindScale)))\r\n\tend\r\nend\r\n\r\nfunction ReturnForRepair(I)\r\n\r\n\tFriendlyCount = I:GetFriendlyCount()\r\n\t\r\n\tif FriendlyCount == 0 then return false end --We have no friends\r\n\r\n\tmyPos = I:GetConstructPosition()\r\n\tmyVel = I:GetVelocityVector()\r\n\tmyDir = I:GetConstructForwardVector()\r\n\t\r\n\t\r\n\tnearestSupportPos = Vector3(9999999,9999999,9999999)\r\n\tnearestSupportVector = Vector3(0,0,0)\r\n\tnearestSupportDistance = 99999999\r\n\tnearestSupportSpeed = 0\r\n\tHealthSupportFound = false\r\n\t\r\n\tmyHealth = I:GetHealthFraction() * 100\r\n\t\r\n\tNeedingRepairSupport = false\r\n\tif AlwaysSeekRepairWhenAiDestroyed then\r\n\t\tAiCount = I:Component_GetCount(26)\r\n\t\tif AiCount == 0 then\r\n\t\t\tI:Log(\"No AI Cores Found.  Seeking Repair.\")\r\n\t\t\tNeedingRepairSupport = true \r\n\t\tend\r\n\tend\r\n\tif myHealth < ReturnForLowHealth then\r\n\t\tNeedingRepairSupport = true \r\n\tend\r\n\tif myHealth > ReturnToTheFightHealth then \r\n\t\tReturningForRepairSupport = false \r\n\t\treturn false --support not needed.\r\n\tend\r\n\t\r\n\tif (NeedingRepairSupport or ReturningForRepairSupport) then\r\n\t\r\n\t\tallSupportPos = {}\r\n\t\r\n\t\tfor ii=0,FriendlyCount-1,1 do\r\n\t\t\tFriendInfo = I:GetFriendlyInfo(ii)\r\n\t\t\tif NameMatches(RepairSupportBluePrintNames, FriendInfo.BlueprintName) then\r\n\t\t\t \r\n\t\t\t\tFriendInfoVector = FriendInfo.CenterOfMass - I:GetConstructPosition()\r\n\t\t\t\tFriendInfoDistance = FriendInfoVector.magnitude\r\n\t\t\t\t\r\n\t\t\t\tif FriendInfoDistance < ReturnIgnoreHealthRange then\r\n\t\t\t\t\tHealthSupportFound = true\r\n\t\t\t\t\ttable.insert(allSupportPos, FriendInfo.CenterOfMass)\r\n\t\t\t\t\t\r\n\t\t\t\t\tif FriendInfoDistance < nearestSupportDistance then\r\n\t\t\t\t\t\tnearestSupportPosInfo = FriendInfo\r\n\t\t\t\t\t\tnearestSupportPos = FriendInfo.CenterOfMass\r\n\t\t\t\t\t\tnearestSupportVector = FriendInfoVector\r\n\t\t\t\t\t\tnearestSupportDistance = FriendInfoDistance\r\n\t\t\t\t\t\tnearestSupportSpeed = FriendInfo.Velocity.magnitude\r\n\t\t\t\t\tend\r\n\t\r\n\t\t\t\tend\r\n\t\t\tend\r\n\t\tend\r\n\t\t\t\t\r\n\t\tif HealthSupportFound then \r\n\t\t\tReturningForRepairSupport = true \r\n\t\telse\r\n\t\t\tI:Log(\"Support Needed, but not found\")\r\n\t\t\treturn false -- Support Not found.\r\n\t\tend\r\n\r\n\t\ttargetAltitude = nearestSupportPos.y + ReturnForLowHealthAltitudeOffset\r\n\t\tgroundAltAtTarget = I:GetTerrainAltitudeForPosition(nearestSupportPos.x, nearestSupportPos.y, nearestSupportPos.z)\r\n\t\ttargetAltitude =  math.max(targetAltitude + groundAltAtTarget, targetAltitude, MinAltitude, MinAltitude + groundAltAtTarget)\r\n\r\n\t\tmeToTargetV3 = nearestSupportPos - myPos\r\n\t\tmeToTargetV3Norm = meToTargetV3\r\n\t\t\r\n\t\tSupportAzimuth = -Vector3.SignedAngle(meToTargetV3Norm, myDir, Vector3.up)\r\n\t\twhile SupportAzimuth > 180\r\n\t\tdo\r\n\t\t\tSupportAzimuth = SupportAzimuth - 360\r\n\t\tend\r\n\t\twhile SupportAzimuth < -180\r\n\t\tdo\r\n\t\t\tSupportAzimuth = SupportAzimuth + 360\r\n\t\tend\r\n\r\n\t\tYawDesired = YawCurrent + SupportAzimuth\r\n\r\n\t\tAimFlat(I, YawDesired, math.max(targetAltitude, MinAltitude))\r\n\t\t\r\n\t\tif nearestSupportDistance < ReturnForLowHealthRange then\r\n\t\t\tif MatchSpeedWithHealthSupport then\r\n\t\t\t\tAdjustSpeedForCruise(I, nearestSupportSpeed, false)\r\n\t\t\telseif CruiseAtMaxSpeed then \r\n\t\t\t\tAdjustSpeedForCruise(I, 0, true)\r\n\t\t\telse\r\n\t\t\t\tAdjustSpeedForCruise(I, DesiredCruiseSpeed, false)\r\n\t\t\tend\r\n\t\telse\r\n\t\t\tAdjustSpeedForCruise(I, 0, true)\r\n\t\tend\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\tI:Log(string.format(\"Moving To Support %.2f\", nearestSupportDistance))\r\n\t\tCallComplexControl(I, IsReturningForSupport)\r\n\t\treturn true -- we are moving to Support\r\n\t\t\r\n\r\n\telse\r\n\t\treturn false -- No need or no suuport found\r\n\tend\r\n\t\r\n\treturn false --we are skipping code\r\nend\r\n\r\nfunction ReturnForBattery(I)\r\n\r\n\tFriendlyCount = I:GetFriendlyCount()\r\n\t\r\n\tif FriendlyCount == 0 then return false end --We have no friends\r\n\r\n\tmyPos = I:GetConstructPosition()\r\n\tmyVel = I:GetVelocityVector()\r\n\tmyDir = I:GetConstructForwardVector()\r\n\t\r\n\t\r\n\tnearestSupportPos = Vector3(9999999,9999999,9999999)\r\n\tnearestSupportVector = Vector3(0,0,0)\r\n\tnearestSupportDistance = 99999999\r\n\tnearestSupportSpeed = 0\r\n\tBatterySupportFound = false\r\n\t\r\n\tmyBattery = I:GetEnergyFraction() * 100\r\n\t\r\n\tNeedingBatterySupport = false\r\n\tif myBattery < ReturnForLowBattery then\r\n\t\tNeedingBatterySupport = true \r\n\tend\r\n\tif myBattery > ReturnToTheFightBattery then \r\n\t\tReturningForBatterySupport = false \r\n\t\treturn false --support not needed.\r\n\tend\r\n\t\r\n\tif (NeedingBatterySupport or ReturningForBatterySupport) then\r\n\t\r\n\t\tallSupportPos = {}\r\n\t\r\n\t\tfor ii=0,FriendlyCount-1,1 do\r\n\t\t\tFriendInfo = I:GetFriendlyInfo(ii)\r\n\t\t\tif NameMatches(BatterySupportBluePrintNames, FriendInfo.BlueprintName) then\r\n\t\t\t \r\n\t\t\t\tFriendInfoVector = FriendInfo.CenterOfMass - I:GetConstructPosition()\r\n\t\t\t\tFriendInfoDistance = FriendInfoVector.magnitude\r\n\t\t\t\t\r\n\t\t\t\tif FriendInfoDistance < ReturnIgnoreBatteryRange then\r\n\t\t\t\t\tBatterySupportFound = true\r\n\t\t\t\t\ttable.insert(allSupportPos, FriendInfo.CenterOfMass)\r\n\t\t\t\t\t\r\n\t\t\t\t\tif FriendInfoDistance < nearestSupportDistance then\r\n\t\t\t\t\t\tnearestSupportPosInfo = FriendInfo\r\n\t\t\t\t\t\tnearestSupportPos = FriendInfo.CenterOfMass\r\n\t\t\t\t\t\tnearestSupportVector = FriendInfoVector\r\n\t\t\t\t\t\tnearestSupportDistance = FriendInfoDistance\r\n\t\t\t\t\t\tnearestSupportSpeed = FriendInfo.Velocity.magnitude\r\n\t\t\t\t\tend\r\n\t\r\n\t\t\t\tend\r\n\t\t\tend\r\n\t\tend\r\n\t\t\t\t\r\n\t\tif BatterySupportFound then \r\n\t\t\tReturningForBatterySupport = true \r\n\t\telse\r\n\t\t\tI:Log(\"Support Needed, but not found\")\r\n\t\t\treturn false -- Support Not found.\r\n\t\tend\r\n\r\n\t\ttargetAltitude = nearestSupportPos.y + ReturnForLowBatteryAltitudeOffset\r\n\t\tgroundAltAtTarget = I:GetTerrainAltitudeForPosition(nearestSupportPos.x, nearestSupportPos.y, nearestSupportPos.z)\r\n\t\ttargetAltitude =  math.max(targetAltitude + groundAltAtTarget, targetAltitude, MinAltitude, MinAltitude + groundAltAtTarget)\r\n\r\n\t\tmeToTargetV3 = nearestSupportPos - myPos\r\n\t\tmeToTargetV3Norm = meToTargetV3\r\n\t\t\r\n\t\tSupportAzimuth = -Vector3.SignedAngle(meToTargetV3Norm, myDir, Vector3.up)\r\n\t\twhile SupportAzimuth > 180\r\n\t\tdo\r\n\t\t\tSupportAzimuth = SupportAzimuth - 360\r\n\t\tend\r\n\t\twhile SupportAzimuth < -180\r\n\t\tdo\r\n\t\t\tSupportAzimuth = SupportAzimuth + 360\r\n\t\tend\r\n\r\n\t\tYawDesired = YawCurrent + SupportAzimuth\r\n\r\n\t\tAimFlat(I, YawDesired, math.max(targetAltitude, MinAltitude))\r\n\t\t\r\n\t\tif nearestSupportDistance < ReturnForLowBatteryRange then\r\n\t\t\tif MatchSpeedWithBatterySupport then\r\n\t\t\t\tAdjustSpeedForCruise(I, nearestSupportSpeed, false)\r\n\t\t\telseif CruiseAtMaxSpeed then \r\n\t\t\t\tAdjustSpeedForCruise(I, 0, true)\r\n\t\t\telse\r\n\t\t\t\tAdjustSpeedForCruise(I, DesiredCruiseSpeed, false)\r\n\t\t\tend\r\n\t\telse\r\n\t\t\tAdjustSpeedForCruise(I, 0, true)\r\n\t\tend\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\tI:Log(string.format(\"Moving To Support %.2f\", nearestSupportDistance))\r\n\t\tCallComplexControl(I, IsReturningForSupport)\r\n\t\treturn true -- we are moving to Support\r\n\t\t\r\n\r\n\telse\r\n\t\treturn false -- No need or no suuport found\r\n\tend\r\n\t\r\n\treturn false --we are skipping code\r\nend\r\n\r\n\r\nfunction EscortFriendly(I)\r\n\tif not UseEscortBehavior then return false end --We do not escort\r\n\t\r\n\tMyEscortInfo = nill\r\n\t\r\n\tif not MyEscortTargetId == nil then\r\n\t\tMyEscortInfo = I:GetFriendlyInfoById()\r\n\t\tif not MyEscortInfo.Valid then\r\n\t\t\tMyEscortInfo = nill\r\n\t\t\tMyEscortTargetId = nil\r\n\t\tend\r\n\tend\r\n\t\r\n\r\n\r\n\tif MyEscortTargetId == nil then\r\n\t\t\r\n\t\tnearestEscortPos = Vector3(9999999,9999999,9999999)\r\n\t\tnearestEscortVector = Vector3(0,0,0)\r\n\t\tnearestEscortDistance = 99999999\r\n\t\tEscortFound = false\r\n\t\t\r\n\t\tfor ii=0,FriendlyCount-1,1 do\r\n\t\t\tFriendInfo = I:GetFriendlyInfo(ii)\r\n\t\t\tif NameMatches(EscortBluePrintNames, FriendInfo.BlueprintName) then\r\n\t\t\t \r\n\t\t\t\tFriendInfoVector = FriendInfo.CenterOfMass - I:GetConstructPosition()\r\n\t\t\t\tFriendInfoDistance = FriendInfoVector.magnitude\r\n\t\t\t\t\r\n\t\t\t\tEscortFound = true\r\n\t\t\t\t\r\n\t\t\t\tif FriendInfoDistance < nearestEscortDistance then\r\n\t\t\t\t\tMyEscortInfo = FriendInfo\r\n\t\t\t\t\tnearestEscortPos = FriendInfo.CenterOfMass\r\n\t\t\t\t\tnearestEscortVector = FriendInfoVector\r\n\t\t\t\t\tnearestEscortDistance = FriendInfoDistance\r\n\t\t\t\tend\r\n\t\t\t\t\r\n\t\t\tend\r\n\t\tend\r\n\t\t\r\n\t\tif not MyEscortInfo == nil then \r\n\t\t\tMyEscortTargetId = MyEscortInfo.Id\r\n\t\tend\r\n\tend\r\n\t\r\n\tif MyEscortInfo == nil or not MyEscortInfo.Valid then return false end --we have no escort target\r\n\t\r\n\tEscortPos = MyEscortInfo.CenterOfMass\r\n\t\r\n\tif I:GetNumberOfTargets(0) > 0 then \r\n\t\tmyTargetInfo = I:GetTargetPositionInfo(0, 0)\r\n\t\ttargetPos = myTargetInfo.Position\r\n\t\tworldDirection = EscortPos - targetPos\r\n\t\tdistance = worldDirection.magnitude\r\n\t\t\r\n\t\tif distance <= EscortEngageRadius then\r\n\t\t\tI:Log(\"Enemy Near Escort Target\")\r\n\t\t\treturn false --stop orbiting escort, engage our target.\r\n\t\tend\r\n\tend\r\n\t\r\n\t\r\n\tDesiredAltitude = CruiseAltitude + math.max(0, I:GetTerrainAltitudeForPosition(EscortPos.x,EscortPos.y,EscortPos.z))\r\n\tOrbitPos(I, EscortPos.x , DesiredAltitude, EscortPos.z )\r\n\tAdjustSpeedForCruise(I, DesiredCruiseSpeed, CruiseAtMaxSpeed)\r\n\tI:Log(\"Escorting Target\")\r\n\treturn true -- we are orbiting our escort target.\r\n\r\nend\r\n\r\n\r\nfunction StrafeAndHover(I)\r\n\r\n-- no return true/false in this function.  It's adds to other behaviors without replacing them.\r\n\r\n\tif UseDragonFlyBehavior then\r\n\t\t\r\n\t\tif CurrentMissileEvasionPosition ~= nil then\t\t\t\r\n\t\t\tif DebugAIMode then\r\n\t\t\t\tI:Log(\"Evading Missiles With DragonFly Mode\")\r\n\t\t\tend\r\n\r\n\t\t\tCallComplexControl(I, IsInDragonFlyMode)\r\n\t\t\t\r\n\t\t\tif CurrentMissileEvasionPosition.Azimuth > 0 then\r\n\t\t\t\tdesiredStrafe = 1\r\n\t\t\telse\r\n\t\t\t\tdesiredStrafe = -1\r\n\t\t\tend\r\n\t\t\t\r\n\t\t\tif CurrentMissileEvasionPosition.AltitudeAboveSeaLevel < AltitudeCurrent then\r\n\t\t\t\tdesiredHover = 1\r\n\t\t\telse\r\n\t\t\t\tdesiredHover = -1\r\n\t\t\tend\r\n\t\t\t\r\n\t\t\tif AltitudeCurrent < MinAltitude - AltSafetyPadding then\r\n\t\t\t\tdesiredHover = 1\r\n\t\t\tend\r\n\t\t\t\r\n\t\t\tstrafeDrive = (desiredStrafe * math.cos(math.rad(RollCurrent))) - (desiredHover * math.sin(math.rad(RollCurrent)))\r\n\t\t\thoverDrive = (desiredHover * math.cos(math.rad(RollCurrent))) + (desiredStrafe * math.sin(math.rad(RollCurrent)))\r\n\t\t\t--We can only output strafe and hover to complex controls.\r\n\t\t\tif strafeDrive > 0.3 then CallComplexControl(I, IsStrafeLeft) end\r\n\t\t\tif strafeDrive < -0.3 then CallComplexControl(I, IsStrafeRight) end\r\n\t\t\tif hoverDrive > 0.3 then CallComplexControl(I, IsHoverUp) end\r\n\t\t\tif hoverDrive < -0.3 then CallComplexControl(I, IsHoverDown) end\r\n\t\t\t\r\n\t\t\tI:Log(string.format(\"strafeDrive:%.2f hoverDrive:%.2f\", strafeDrive, hoverDrive))\r\n\t\t\t\t\r\n\t\t\treturn \r\n\t\tend\r\n\t\t\r\n\t\t\r\n\t\tmyPos = I:GetConstructPosition()\r\n\t\ttargetPosInfo = I:GetTargetPositionInfo(0, 0)\r\n\t\t\r\n\t\tif not targetPosInfo.Valid then\r\n\t\t\treturn -- We are too far away.\r\n\t\tend\r\n\r\n\t\ttargetPos = targetPosInfo.Position\r\n\r\n\t\r\n\t\ttargetDir = myPos - targetPos\r\n\t\tdistance = targetDir.magnitude\r\n\t\r\n\t\tif distance > DragonFlyRange then\r\n\t\t\treturn -- We are too far away.\r\n\t\tend\r\n\t\tif DebugAIMode then\r\n\t\t\tI:Log(\"Engaging Target With DragonFly Mode\")\r\n\t\tend\r\n\t\tCallComplexControl(I, IsInDragonFlyMode)\r\n\t\t\r\n\t\t--Choose strafing direction:\r\n\t\ttargetVel = targetPosInfo.Velocity\r\n\t\ttargetDot = Vector3.Dot(targetVel.normalized, targetDir.normalized)\r\n\r\n\t\tdirNum = AngleDir(targetVel, targetDir, targetVel.up)\r\n\t\t\r\n\t\tdesiredStrafe = (targetDot)* -dirNum\r\n\r\n\t\ttargetAltitude = math.max(I:GetTerrainAltitudeForLocalPosition(0,0,0) + MinAltitude, MinAltitude, targetPosInfo.AltitudeAboveSeaLevel )\r\n\t\ttargetAltitude = math.min(targetAltitude, MaxAltitude)\r\n\t\tdesiredAltChange = targetAltitude - AltitudeCurrent\r\n\r\n\t\tdesiredHover = 0\r\n\r\n\t\tif math.abs(desiredAltChange) > MinHoverAltAdjustment then\r\n\t\t\tif desiredAltChange > 0 then desiredHover = 1\r\n\t\t\telseif desiredAltChange < 0 then desiredHover = -1\r\n\t\t\tend\r\n\t\tend\r\n\t\t\r\n\t\t\r\n\t\tstrafeDrive = (desiredStrafe * math.cos(math.rad(RollCurrent))) - (desiredHover * math.sin(math.rad(RollCurrent)))\r\n\t\thoverDrive = (desiredHover * math.cos(math.rad(RollCurrent))) + (desiredStrafe * math.sin(math.rad(RollCurrent)))\r\n\t\t\r\n\t\t--We can only output strafe and hover to complex controls.\r\n\t\tif strafeDrive > 0.3 then CallComplexControl(I, IsStrafeLeft) end\r\n\t\tif strafeDrive < -0.3 then CallComplexControl(I, IsStrafeRight) end\r\n\t\tif hoverDrive > 0.3 then CallComplexControl(I, IsHoverUp) end\r\n\t\tif hoverDrive < -0.3 then CallComplexControl(I, IsHoverDown) end\r\n\t\t\r\n\t\tI:Log(string.format(\"strafeDrive:%.2f hoverDrive:%.2f\", strafeDrive, hoverDrive))\r\n\tend\r\nend"],"BlockStringDataIds":[230],"GameVersion":"4.1.2.0","PersistentSubObjectIndex":11,"PersistentBlockIndex":270,"AuthorDetails":{"Valid":true,"ForeignBlocks":0,"CreatorId":"600c8935-f843-451b-9c6a-2949e940f82f","ObjectId":"c6f4a1af-07e1-495d-b47b-13cfb10c67d6","CreatorReadableName":"Pendaelose","HashV1":"a1754f26013e6d97d016e66709ebeefd"},"BlockCount":808}}