From ca82c3357d7685e753ce3df52584d856ac6f6f7c Mon Sep 17 00:00:00 2001 From: dutchie031 Date: Sun, 28 Dec 2025 14:35:31 +0100 Subject: [PATCH 1/9] custom drawings and animations for video --- .videos/1/animations/1.excalidraw | 1419 +++++++++ .videos/1/animations/2.excalidraw | 2829 +++++++++++++++++ .videos/1/animations/3.excalidraw | 721 +++++ DcsTypes.lua | 79 +- _docs/js/components/header.js | 5 +- _docs/pages/about_us.html | 3 +- _docs/pages/advanced/CAP.html | 3 +- _docs/pages/advanced/custom-drawings.html | 65 + _docs/pages/advanced/missions.html | 3 +- _docs/pages/persistence.html | 1 - _docs/style/style.css | 4 +- classes/spearhead_db.lua | 30 +- classes/stageClasses/GlobalStageManager.lua | 128 +- .../stageClasses/drawings/CustomDrawing.lua | 52 + .../drawings/helper/DrawingHelper.lua | 228 ++ main.lua | 2 +- 16 files changed, 5500 insertions(+), 72 deletions(-) create mode 100644 .videos/1/animations/1.excalidraw create mode 100644 .videos/1/animations/2.excalidraw create mode 100644 .videos/1/animations/3.excalidraw create mode 100644 _docs/pages/advanced/custom-drawings.html create mode 100644 classes/stageClasses/drawings/CustomDrawing.lua create mode 100644 classes/stageClasses/drawings/helper/DrawingHelper.lua diff --git a/.videos/1/animations/1.excalidraw b/.videos/1/animations/1.excalidraw new file mode 100644 index 0000000..4b8ff80 --- /dev/null +++ b/.videos/1/animations/1.excalidraw @@ -0,0 +1,1419 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "id": "C89ygEbHaSevX0UmFQa29", + "type": "rectangle", + "x": 1595.1758233422202, + "y": 982.8838329063535, + "width": 1578.7433546522943, + "height": 836.7851776620863, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1E", + "roundness": { + "type": 3 + }, + "seed": 1397971778, + "version": 165, + "versionNonce": 1976396482, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false + }, + { + "id": "zymyegNU9v9TWAaJNoUQ6", + "type": "rectangle", + "x": 2280.4835366799443, + "y": 1543.643017153118, + "width": 211.02211156486123, + "height": 96.5527520526872, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1F", + "roundness": { + "type": 3 + }, + "seed": 881500930, + "version": 302, + "versionNonce": 673520222, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false + }, + { + "id": "vKXC1aEVs5uqRsvSnnOoX", + "type": "rectangle", + "x": 2110.505541163566, + "y": 1329.93712486004, + "width": 257.3589150478706, + "height": 137.06711701441645, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1G", + "roundness": { + "type": 3 + }, + "seed": 1191375554, + "version": 539, + "versionNonce": 739689090, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false + }, + { + "id": "XaaXel4GyBx7ZE8jJFFU-", + "type": "text", + "x": 2311.8471093741773, + "y": 1562.8008746850626, + "width": 154.26666259765625, + "height": 55.62747030529269, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1H", + "roundness": null, + "seed": 1297828482, + "version": 141, + "versionNonce": 1468931742, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "Mission", + "fontSize": 44.50197624423415, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Mission", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "8Z0uVkd2T8gUqGRmKSyfY", + "type": "text", + "x": 2139.710048166189, + "y": 1352.2802946540085, + "width": 209.48391723632812, + "height": 90, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1I", + "roundness": null, + "seed": 1582475842, + "version": 273, + "versionNonce": 1189028418, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "Dynamic\nEnvironment", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Dynamic\nEnvironment", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "JWiweSzhuikJTu4HZkMGZ", + "type": "text", + "x": 1754.3449062335774, + "y": 1276.5059189319265, + "width": 55.243988037109375, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1J", + "roundness": null, + "seed": 1623724546, + "version": 231, + "versionNonce": 1942788830, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "CAP", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "CAP", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "EVHvyolSNwxiN31NPp0cq", + "type": "text", + "x": 1810.2584664781727, + "y": 1171.9477385696666, + "width": 196.58787536621094, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1K", + "roundness": null, + "seed": 129663426, + "version": 337, + "versionNonce": 1896188418, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "Randomization", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Randomization", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "_zbZUzNZ6-m_H1r6Je1Ws", + "type": "text", + "x": 1963.417918814925, + "y": 1067.4541208260175, + "width": 230.4958953857422, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1L", + "roundness": null, + "seed": 1313405314, + "version": 506, + "versionNonce": 863080222, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "Dynamic Missions", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Dynamic Missions", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "wa4xKY1Ve812njK6Hu_Df", + "type": "arrow", + "x": 2106.1255243470746, + "y": 1388.8792554044524, + "width": 308.93333412890956, + "height": 74.20142997348557, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1M", + "roundness": { + "type": 2 + }, + "seed": 1941076290, + "version": 347, + "versionNonce": 562624962, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -230.31042834295977, + 7.29165803983858 + ], + [ + -308.93333412890956, + -66.909771933647 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "LS6J2OTpx6hMih3iQPcNm", + "type": "arrow", + "x": 2116.648091215842, + "y": 1337.918660627163, + "width": 183.58214713496818, + "height": 128.22965822939244, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1N", + "roundness": { + "type": 2 + }, + "seed": 2002125058, + "version": 385, + "versionNonce": 1391328094, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -96.69419063956752, + -44.240692509901066 + ], + [ + -183.58214713496818, + -128.22965822939244 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "2pKO2mHKvKoxXycD-Trak", + "type": "arrow", + "x": 2200.5171954951866, + "y": 1327.1271867566231, + "width": 117.70638264769002, + "height": 217.8782767017758, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1O", + "roundness": { + "type": 2 + }, + "seed": 347246786, + "version": 508, + "versionNonce": 394817922, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -75.89678042654475, + -77.80677762467928 + ], + [ + -117.70638264769002, + -217.8782767017758 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "kn2jmFt51PCgv9ZSVQtPB", + "type": "arrow", + "x": 2278.431131294278, + "y": 1590.850587725762, + "width": 53.05431408484253, + "height": 122.51345010231648, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1P", + "roundness": { + "type": 2 + }, + "seed": 1063725186, + "version": 465, + "versionNonce": 1486020510, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -36.04382428028316, + -40.97518520498488 + ], + [ + -53.05431408484253, + -122.51345010231648 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "GHK711xc_0cjPoHBYSfbv", + "type": "text", + "x": 1644.731548138247, + "y": 1415.4750740468114, + "width": 146.91595458984375, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1Q", + "roundness": null, + "seed": 523421762, + "version": 274, + "versionNonce": 1447227714, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "Intercepts", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Intercepts", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "EIVk3PNzkqTtPdwmGuozM", + "type": "arrow", + "x": 2108.5571488546734, + "y": 1428.739880273081, + "width": 350.09058635791064, + "height": 66.55192331587875, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1R", + "roundness": { + "type": 2 + }, + "seed": 200568834, + "version": 253, + "versionNonce": 1485913054, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -278.8961526603057, + 66.55192331587875 + ], + [ + -350.09058635791064, + 33.43792917899509 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "hcsXIwpZ0wO4h70e_51LN", + "type": "rectangle", + "x": 2655.5017561592795, + "y": 1468.1261290741381, + "width": 241.41983158991846, + "height": 83.72991507036454, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1S", + "roundness": { + "type": 3 + }, + "seed": 162209730, + "version": 727, + "versionNonce": 555186434, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false + }, + { + "id": "3n4in__khyQeAqqaLWnvM", + "type": "arrow", + "x": 2492.8516622720417, + "y": 1591.8594320033028, + "width": 161.6044126988354, + "height": 49.931047908565915, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1T", + "roundness": { + "type": 2 + }, + "seed": 1148573570, + "version": 214, + "versionNonce": 955912222, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 82.64046560250495, + -4.690581156494773 + ], + [ + 161.6044126988354, + -49.931047908565915 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "ZRt7GH5E7FZd3q4Hlr2Wb", + "type": "rectangle", + "x": 2579.230246373966, + "y": 1306.9669606519642, + "width": 241.41983158991846, + "height": 83.72991507036454, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1U", + "roundness": { + "type": 3 + }, + "seed": 210999106, + "version": 897, + "versionNonce": 2005207234, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false + }, + { + "id": "vPN2QPcdwWvMQ-mZifFWO", + "type": "arrow", + "x": 2462.2947804605733, + "y": 1542.2877867955297, + "width": 155.45326010245628, + "height": 150.55220092483307, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1V", + "roundness": { + "type": 2 + }, + "seed": 1382244098, + "version": 347, + "versionNonce": 1975409758, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 89.01755949466553, + -55.60892268147143 + ], + [ + 155.45326010245628, + -150.55220092483307 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "eSh01gIQ8yyR2BiCNcUrb", + "type": "text", + "x": 2593.2180895445626, + "y": 1112.0447137456451, + "width": 216.1039276123047, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1W", + "roundness": null, + "seed": 576990914, + "version": 62, + "versionNonce": 288006274, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "search & rescue", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "search & rescue", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "XR8xKvJaOTtt03Yh9J3Sa", + "type": "text", + "x": 2799.860760278255, + "y": 1184.756242277849, + "width": 129.24795532226562, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1X", + "roundness": null, + "seed": 557123202, + "version": 220, + "versionNonce": 1689198750, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "resources", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "resources", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "eD07BUP5boG6Xi4PzLWE5", + "type": "text", + "x": 2884.00086110148, + "y": 1259.1706095534707, + "width": 218.51190185546875, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1Y", + "roundness": null, + "seed": 127198786, + "version": 196, + "versionNonce": 1506230338, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "troop transport", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "troop transport", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "64_IG7tB8xe9guybyLolg", + "type": "arrow", + "x": 2649.657696625549, + "y": 1306.6347710433065, + "width": 28.256251025410165, + "height": 151.34407282126563, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1Z", + "roundness": { + "type": 2 + }, + "seed": 509791746, + "version": 112, + "versionNonce": 2038258910, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 28.256251025410165, + -72.40883764124419 + ], + [ + 20.51590827401901, + -151.34407282126563 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "_ydHzRJPIF7RDMZtLa_0_", + "type": "arrow", + "x": 2729.794510760714, + "y": 1305.9045077134572, + "width": 63.979632843547506, + "height": 88.47959195466075, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1a", + "roundness": { + "type": 2 + }, + "seed": 983416258, + "version": 92, + "versionNonce": 1832511490, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -0.8165451052091157, + -40.943698335596196 + ], + [ + 63.16308773833839, + -88.47959195466075 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "u2YxBRiyb-q80-becundA", + "type": "arrow", + "x": 2779.331092045323, + "y": 1306.5338048938277, + "width": 101.2133108183566, + "height": 27.085355634320024, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1b", + "roundness": { + "type": 2 + }, + "seed": 962901378, + "version": 95, + "versionNonce": 163189022, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 2.2025290131457496, + -24.593338663309396 + ], + [ + 101.2133108183566, + -27.085355634320024 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "nQo4X4sD5508YdRbN6L2V", + "type": "text", + "x": 2626.7713951078263, + "y": 1327.6156102040861, + "width": 158.61448669433594, + "height": 45.80388202365124, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1c", + "roundness": null, + "seed": 2079765826, + "version": 181, + "versionNonce": 327260098, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "Logistics", + "fontSize": 36.64310561892099, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Logistics", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "nDM5YqB-uMJaQXm_hhPz6", + "type": "arrow", + "x": 2175.660455769157, + "y": 1467.3571367658053, + "width": 173.54014297993103, + "height": 141.663432547738, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1d", + "roundness": { + "type": 2 + }, + "seed": 1672658178, + "version": 322, + "versionNonce": 282475870, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -123.80435478180289, + 122.18352354412411 + ], + [ + -173.54014297993103, + 141.663432547738 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "KoWGPL4te_nZGB7RtDbNA", + "type": "text", + "x": 1731.6864197817795, + "y": 1601.4552001472132, + "width": 259.69989013671875, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1e", + "roundness": null, + "seed": 1310182594, + "version": 482, + "versionNonce": 1019039618, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "Front line dynamics", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Front line dynamics", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "6-2ZfqjatkazUWkp5gRva", + "type": "text", + "x": 2240.8290098027364, + "y": 1138.914974643674, + "width": 153.69192504882812, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1f", + "roundness": null, + "seed": 281339010, + "version": 73, + "versionNonce": 1948443038, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "progression", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "progression", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "jVhjdO6uRjpvu8Zo7nSGS", + "type": "arrow", + "x": 2266.15959377383, + "y": 1327.983475505304, + "width": 37.09715089166127, + "height": 142.04843769343142, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1g", + "roundness": { + "type": 2 + }, + "seed": 1970865218, + "version": 108, + "versionNonce": 2100039490, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -26.775736296397554, + -63.91159713230098 + ], + [ + 10.321414595263718, + -142.04843769343142 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "MlytaE0dWp2tfKHhOAto9", + "type": "arrow", + "x": 2596.5132212546523, + "y": 1306.953322625103, + "width": 76.2288673445837, + "height": 82.41916292472536, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1h", + "roundness": { + "type": 2 + }, + "seed": 907226114, + "version": 232, + "versionNonce": 1188101598, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -76.2288673445837, + -23.759502278248988 + ], + [ + -72.78559318187752, + -82.41916292472536 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "JhKoOvbUE4uNqMi4I3Qhi", + "type": "text", + "x": 2501.1016541350414, + "y": 1181.726840184583, + "width": 107.1839599609375, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1i", + "roundness": null, + "seed": 2022857666, + "version": 150, + "versionNonce": 153933570, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "convoys", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "convoys", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "ztZZIZZ_qBNW_4kWdz2Fi", + "type": "arrow", + "x": 2396.1874688609987, + "y": 1640.0013354931941, + "width": 202.0124120928847, + "height": 82.8810205003274, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1j", + "roundness": { + "type": 2 + }, + "seed": 1259705218, + "version": 281, + "versionNonce": 233935390, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -10.277189431840156, + 62.10612730731145 + ], + [ + 191.73522266104453, + 82.8810205003274 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "F9W7pRQ7W4kXvmBa881_r", + "type": "text", + "x": 2605.146401500665, + "y": 1669.2132132342715, + "width": 313.0916784375118, + "height": 77.07669898789328, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1k", + "roundness": null, + "seed": 534500162, + "version": 318, + "versionNonce": 826189506, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "The Story", + "fontSize": 61.66135919031466, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "The Story", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "HP7cvatU7B02lDuolKi-j", + "type": "text", + "x": 2681.145416645998, + "y": 1487.3370645335638, + "width": 187.76373087459876, + "height": 41.881647225841355, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1l", + "roundness": null, + "seed": 1229200130, + "version": 138, + "versionNonce": 74817118, + "isDeleted": false, + "boundElements": [], + "updated": 1766842694815, + "link": null, + "locked": false, + "text": "Persistence", + "fontSize": 33.505317780673074, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Persistence", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#1b1d27" + }, + "files": {} +} \ No newline at end of file diff --git a/.videos/1/animations/2.excalidraw b/.videos/1/animations/2.excalidraw new file mode 100644 index 0000000..73432f1 --- /dev/null +++ b/.videos/1/animations/2.excalidraw @@ -0,0 +1,2829 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "id": "TkmYPuA-gv0_UwIlIvUks", + "type": "rectangle", + "x": 587.6701419860844, + "y": 326.930520978411, + "width": 1578.7433546522943, + "height": 836.7851776620863, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b7r", + "roundness": { + "type": 3 + }, + "seed": 236498771, + "version": 392, + "versionNonce": 1599045171, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "3aTXQGLGN9zXVMmN2tq7t", + "type": "text", + "x": 883.7016057712028, + "y": 340.16469192742414, + "width": 185.5073309495647, + "height": 56.70928542470905, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b7s", + "roundness": null, + "seed": 1284328691, + "version": 438, + "versionNonce": 801587155, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false, + "text": "What if?", + "fontSize": 45.36742833976728, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "What if?", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "1knzWeJorG8y3HvCLncV1", + "type": "text", + "x": 936.1120196234406, + "y": 397.0639339157842, + "width": 764.116943359375, + "height": 70.92530107466699, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b7t", + "roundness": null, + "seed": 970798739, + "version": 562, + "versionNonce": 2117920659, + "isDeleted": false, + "boundElements": [], + "updated": 1766926959872, + "link": null, + "locked": false, + "text": "you could only think about what to make, \n not about how to make it work?", + "fontSize": 28.370120429866798, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "you could only think about what to make, \n not about how to make it work?", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "l2eRxUDHSX5TBIwPC3bgm", + "type": "rectangle", + "x": 621.7852981557176, + "y": 512.2577379418976, + "width": 748.5964330545568, + "height": 611.2062252729833, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b7u", + "roundness": { + "type": 3 + }, + "seed": 1958669363, + "version": 652, + "versionNonce": 1149347603, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "QXsfOg8wC1qDs4O94DH5Y", + "type": "text", + "x": 1160.3935163549238, + "y": 529.0713808440466, + "width": 195.63587951660156, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b7v", + "roundness": null, + "seed": 18410963, + "version": 443, + "versionNonce": 42028211, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false, + "text": "Mission Editor", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Mission Editor", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "TZ23U60Lz84MHHxh-CpIt", + "type": "rectangle", + "x": 1390.5089986688547, + "y": 515.4772965219338, + "width": 748.5964330545568, + "height": 611.2062252729833, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b7w", + "roundness": { + "type": 3 + }, + "seed": 570538867, + "version": 778, + "versionNonce": 1488666195, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "_j5lLDaYL7GKwVu-JzOnX", + "type": "text", + "x": 1416.7848813374105, + "y": 533.6019440545078, + "width": 87.9759521484375, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b7x", + "roundness": null, + "seed": 1941951763, + "version": 495, + "versionNonce": 1644559347, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false, + "text": "Result", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Result", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "24sk1TnRyheU_qDB6J5Yc", + "type": "rectangle", + "x": 701.7866753035726, + "y": 850.7719555895353, + "width": 320.9076668828474, + "height": 161.35983833551992, + "angle": 0.8540908114910017, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b7y", + "roundness": { + "type": 3 + }, + "seed": 1170848435, + "version": 741, + "versionNonce": 443024787, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "ch5F6Obf-VzPHBs_UO7Gb", + "type": "rectangle", + "x": 917.653181990104, + "y": 819.735904230279, + "width": 320.9076668828474, + "height": 161.35983833551992, + "angle": 0.8540908114910017, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b7z", + "roundness": { + "type": 3 + }, + "seed": 885386323, + "version": 812, + "versionNonce": 957864755, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "-waVClSnsHK3PiaDITVtk", + "type": "rectangle", + "x": 972.6195301890348, + "y": 611.7171059886023, + "width": 320.9076668828474, + "height": 161.35983833551992, + "angle": 0.8540908114910017, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b80", + "roundness": { + "type": 3 + }, + "seed": 76212723, + "version": 855, + "versionNonce": 1249639635, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "3SZLMWhT7Jt2wZAZjuLwb", + "type": "text", + "x": 807.3223145398006, + "y": 774.4644178604894, + "width": 11.95599365234375, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b81", + "roundness": null, + "seed": 975123347, + "version": 331, + "versionNonce": 170584691, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false, + "text": "1", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "1", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "4ZysJnqb84Iq1luukKMeU", + "type": "text", + "x": 1018.564353061721, + "y": 742.6965875901219, + "width": 19.599990844726562, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b82", + "roundness": null, + "seed": 1975077171, + "version": 334, + "versionNonce": 2095784979, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false, + "text": "2", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "2", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "mRtP3rW1Ev3NyaZVUD-3M", + "type": "text", + "x": 1077.7859026777692, + "y": 534.5946051741339, + "width": 17.02398681640625, + "height": 35, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b83", + "roundness": null, + "seed": 213887699, + "version": 310, + "versionNonce": 1253255603, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false, + "text": "3", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "3", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "UBuIXa1b0qeKuQ-riAVIu", + "type": "ellipse", + "x": 759.2675360884357, + "y": 852.2183281672253, + "width": 90.74748361774482, + "height": 58.01946787355587, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b84", + "roundness": { + "type": 2 + }, + "seed": 1674106995, + "version": 367, + "versionNonce": 1487502163, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "DoU8rxRvpUq5MLUrTYL3c", + "type": "ellipse", + "x": 891.0460937975554, + "y": 962.1994463295424, + "width": 90.74748361774482, + "height": 58.01946787355587, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b85", + "roundness": { + "type": 2 + }, + "seed": 144396819, + "version": 428, + "versionNonce": 589601011, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "yY_JXLWIk_K5tqXTfa2uK", + "type": "ellipse", + "x": 1093.4017190918723, + "y": 950.9721708834724, + "width": 90.74748361774482, + "height": 58.01946787355587, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b86", + "roundness": { + "type": 2 + }, + "seed": 204314547, + "version": 496, + "versionNonce": 1470825107, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "CdVHNikN5wZuN_G1_Jm-I", + "type": "ellipse", + "x": 1042.4528024394021, + "y": 608.1000475051412, + "width": 90.74748361774482, + "height": 58.01946787355587, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b87", + "roundness": { + "type": 2 + }, + "seed": 480651603, + "version": 434, + "versionNonce": 787691571, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "UPH61QDq-ZjDd-h8jlNSU", + "type": "ellipse", + "x": 1172.9627034862467, + "y": 729.2194009080924, + "width": 90.74748361774482, + "height": 58.01946787355587, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b88", + "roundness": { + "type": 2 + }, + "seed": 1599833843, + "version": 390, + "versionNonce": 115587539, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "dZ0aEZY6HaODKo61ucR78", + "type": "rectangle", + "x": 810.830605243839, + "y": 868.7838431084679, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b89", + "roundness": { + "type": 3 + }, + "seed": 181678227, + "version": 332, + "versionNonce": 1016268659, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "9Oam_fy_5_qaA-aBlbwSV", + "type": "rectangle", + "x": 806.2255242592548, + "y": 863.1895894992579, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8A", + "roundness": { + "type": 3 + }, + "seed": 1086760499, + "version": 344, + "versionNonce": 1179099411, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "vmQaw83KVGpYyNRbPtki6", + "type": "rectangle", + "x": 818.1935919876223, + "y": 876.6451169321033, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8B", + "roundness": { + "type": 3 + }, + "seed": 672753619, + "version": 406, + "versionNonce": 225969843, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "yt5zH5LwABAjtltXK-Df5", + "type": "rectangle", + "x": 782.592570410053, + "y": 895.9961486874515, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8C", + "roundness": { + "type": 3 + }, + "seed": 1189279091, + "version": 420, + "versionNonce": 147503187, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "IyzzBOFsKCNJXoKAGrwsb", + "type": "rectangle", + "x": 776.9020259396061, + "y": 890.6925718428477, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8D", + "roundness": { + "type": 3 + }, + "seed": 476696339, + "version": 374, + "versionNonce": 370554355, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "TRx5f7ejGGApFI7t5HYzb", + "type": "rectangle", + "x": 813.2783080004572, + "y": 894.2057608021289, + "width": 4.907332597593041, + "height": 1.7519561690570522, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8E", + "roundness": { + "type": 3 + }, + "seed": 2065986739, + "version": 336, + "versionNonce": 341901203, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "YHOYFe_YQCL-S2iwtn5dK", + "type": "rectangle", + "x": 929.5938851246096, + "y": 994.4869237609175, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8F", + "roundness": { + "type": 3 + }, + "seed": 582575699, + "version": 396, + "versionNonce": 1038193971, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "HZPTgXjzq7Ty_EyuRXcYe", + "type": "rectangle", + "x": 940.2766758240202, + "y": 975.0042430324053, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8G", + "roundness": { + "type": 3 + }, + "seed": 436151283, + "version": 365, + "versionNonce": 1107195603, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "fGbgSxtKP6_G38S1FLZMz", + "type": "rectangle", + "x": 952.244743552388, + "y": 988.4597704652508, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8H", + "roundness": { + "type": 3 + }, + "seed": 1224486291, + "version": 427, + "versionNonce": 525214835, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "Goc7QqKKnS3wliNzJpzrf", + "type": "rectangle", + "x": 916.6437219748184, + "y": 1007.810802220599, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8I", + "roundness": { + "type": 3 + }, + "seed": 1390871347, + "version": 441, + "versionNonce": 1033313811, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "TUFby3xlQDCcsjcDf67Go", + "type": "rectangle", + "x": 910.9531775043713, + "y": 1002.5072253759952, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8J", + "roundness": { + "type": 3 + }, + "seed": 807806163, + "version": 395, + "versionNonce": 1204806579, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "b762viWDRis73vviuF64q", + "type": "rectangle", + "x": 947.3294595652226, + "y": 1006.0204143352764, + "width": 4.907332597593041, + "height": 1.7519561690570522, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8K", + "roundness": { + "type": 3 + }, + "seed": 139465331, + "version": 357, + "versionNonce": 1176434003, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "YiJTdyoEo90WZAuPKCOJd", + "type": "rectangle", + "x": 1132.7638157766175, + "y": 984.0427735579389, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8L", + "roundness": { + "type": 3 + }, + "seed": 1277905939, + "version": 470, + "versionNonce": 2010829555, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "db2Lpa-8FK9mUmnGBHXJM", + "type": "rectangle", + "x": 1127.0732713061711, + "y": 978.739196713335, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8M", + "roundness": { + "type": 3 + }, + "seed": 263082419, + "version": 424, + "versionNonce": 1837759635, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "td3vm827hcxxNztreX7Gg", + "type": "rectangle", + "x": 1238.6723491824562, + "y": 755.5913997613773, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8N", + "roundness": { + "type": 3 + }, + "seed": 603956051, + "version": 440, + "versionNonce": 1659040307, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839359, + "link": null, + "locked": false + }, + { + "id": "FEJBINKFQHQnqOVqlNYHx", + "type": "rectangle", + "x": 1203.071327604887, + "y": 774.9424315167255, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8O", + "roundness": { + "type": 3 + }, + "seed": 1903542515, + "version": 454, + "versionNonce": 1489777619, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "yuHEk8qSEcl_N5Lzw5KsH", + "type": "rectangle", + "x": 1197.3807831344404, + "y": 769.6388546721216, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8P", + "roundness": { + "type": 3 + }, + "seed": 295119507, + "version": 408, + "versionNonce": 1868317043, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "HsXEO0avLAkvq-L1tGCem", + "type": "rectangle", + "x": 1233.7570651952913, + "y": 773.1520436314029, + "width": 4.907332597593041, + "height": 1.7519561690570522, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8Q", + "roundness": { + "type": 3 + }, + "seed": 689594419, + "version": 370, + "versionNonce": 2144967443, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "qNesuF3NbwBOtxirxtdvi", + "type": "rectangle", + "x": 1100.548760925171, + "y": 623.1742587536826, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8R", + "roundness": { + "type": 3 + }, + "seed": 31842771, + "version": 373, + "versionNonce": 1991950515, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "wtm5g1qyeRefkiHoIBNpP", + "type": "rectangle", + "x": 1095.943679940587, + "y": 617.5800051444728, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8S", + "roundness": { + "type": 3 + }, + "seed": 1695559539, + "version": 385, + "versionNonce": 1839608403, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "IvpCO85Lo0wEUAcAKnWox", + "type": "rectangle", + "x": 1107.9117476689546, + "y": 631.0355325773182, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8T", + "roundness": { + "type": 3 + }, + "seed": 887081235, + "version": 447, + "versionNonce": 91385843, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "KC7wMOUgKlzlX0w7Q-LMA", + "type": "rectangle", + "x": 1102.950080575952, + "y": 648.5961764473439, + "width": 4.907332597593041, + "height": 1.7519561690570522, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8U", + "roundness": { + "type": 3 + }, + "seed": 1187720883, + "version": 378, + "versionNonce": 2488723, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "4mqN35uuRXaEeNlaqTH3N", + "type": "rectangle", + "x": 1089.5387368309769, + "y": 631.6926219260015, + "width": 4.907332597593041, + "height": 1.7519561690570522, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8V", + "roundness": { + "type": 3 + }, + "seed": 2105073747, + "version": 392, + "versionNonce": 452415283, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "jabc7Md5sDxJnaiwVTf-R", + "type": "ellipse", + "x": 1528.4967532985086, + "y": 823.5130476967427, + "width": 90.74748361774482, + "height": 58.01946787355587, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8W", + "roundness": { + "type": 2 + }, + "seed": 582966771, + "version": 455, + "versionNonce": 676131027, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "SZI3WE7wATC01wNCUjzXL", + "type": "ellipse", + "x": 1660.2753110076285, + "y": 933.4941658590598, + "width": 90.74748361774482, + "height": 58.01946787355587, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8X", + "roundness": { + "type": 2 + }, + "seed": 27600787, + "version": 516, + "versionNonce": 1774018163, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "qDIIf3sr9GTWruKT-Rzcf", + "type": "ellipse", + "x": 1862.6309363019457, + "y": 922.2668904129897, + "width": 90.74748361774482, + "height": 58.01946787355587, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8Y", + "roundness": { + "type": 2 + }, + "seed": 1080081715, + "version": 578, + "versionNonce": 705632275, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "Tzu5_pxGjV0xnsQA8lWWi", + "type": "ellipse", + "x": 1812.1106732570668, + "y": 579.3947670346586, + "width": 90.74748361774482, + "height": 58.01946787355587, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8Z", + "roundness": { + "type": 2 + }, + "seed": 2052628179, + "version": 515, + "versionNonce": 389586355, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "Nrf3X_foSa1wu-BDRpyf3", + "type": "ellipse", + "x": 1942.1919206963196, + "y": 700.5141204376098, + "width": 90.74748361774482, + "height": 58.01946787355587, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8a", + "roundness": { + "type": 2 + }, + "seed": 438520947, + "version": 478, + "versionNonce": 1549823827, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "PwRc8NA4p20DyU-QOf_K0", + "type": "rectangle", + "x": 1580.059822453912, + "y": 840.0785626379854, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8b", + "roundness": { + "type": 3 + }, + "seed": 1938245139, + "version": 420, + "versionNonce": 2078084339, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "9u6MEDMiTcEwwU3_OlvSv", + "type": "rectangle", + "x": 1575.4547414693282, + "y": 834.4843090287752, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8c", + "roundness": { + "type": 3 + }, + "seed": 494603187, + "version": 432, + "versionNonce": 1765078675, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "jj-YrLNRqanvJuGhAhO8d", + "type": "rectangle", + "x": 1587.4228091976954, + "y": 847.9398364616206, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8d", + "roundness": { + "type": 3 + }, + "seed": 203758931, + "version": 494, + "versionNonce": 1662954547, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "ZLffjg1fFP1YAuw3Rshm6", + "type": "rectangle", + "x": 1551.8217876201263, + "y": 867.2908682169689, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8e", + "roundness": { + "type": 3 + }, + "seed": 1792801523, + "version": 508, + "versionNonce": 226536915, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "BQbx8a7Hl8XXr0y9h3-dD", + "type": "rectangle", + "x": 1546.1312431496792, + "y": 861.987291372365, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8f", + "roundness": { + "type": 3 + }, + "seed": 908469395, + "version": 462, + "versionNonce": 1265728371, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "hNuoBjuMmXkm2feGsYxro", + "type": "rectangle", + "x": 1582.50752521053, + "y": 865.5004803316463, + "width": 4.907332597593041, + "height": 1.7519561690570522, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8g", + "roundness": { + "type": 3 + }, + "seed": 364517939, + "version": 424, + "versionNonce": 1496814867, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "RxRAyqbkXLbedPcSqGVcD", + "type": "rectangle", + "x": 1698.8231023346827, + "y": 965.781643290435, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8h", + "roundness": { + "type": 3 + }, + "seed": 1987992531, + "version": 484, + "versionNonce": 642349747, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "raQp6nDDhWYOSjXT4RGNR", + "type": "rectangle", + "x": 1709.505893034093, + "y": 946.2989625619226, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8i", + "roundness": { + "type": 3 + }, + "seed": 1686727027, + "version": 453, + "versionNonce": 1867361363, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "_cX9Gf-70SFz_jIFk3haL", + "type": "rectangle", + "x": 1721.4739607624608, + "y": 959.754489994768, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8j", + "roundness": { + "type": 3 + }, + "seed": 622969619, + "version": 515, + "versionNonce": 404400627, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "jKAUwKWnxciIW6BX7jyTG", + "type": "rectangle", + "x": 1685.8729391848913, + "y": 979.1055217501165, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8k", + "roundness": { + "type": 3 + }, + "seed": 280881331, + "version": 529, + "versionNonce": 1169617811, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "-K4sPU6oUSW9xv8cdWkdV", + "type": "rectangle", + "x": 1680.1823947144442, + "y": 973.8019449055126, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8l", + "roundness": { + "type": 3 + }, + "seed": 755259987, + "version": 483, + "versionNonce": 945359155, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "V2PmdGz8LznJx21AlUzuo", + "type": "rectangle", + "x": 1716.558676775296, + "y": 977.3151338647939, + "width": 4.907332597593041, + "height": 1.7519561690570522, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8m", + "roundness": { + "type": 3 + }, + "seed": 114006003, + "version": 445, + "versionNonce": 1701276371, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "KYFPF13V7ieA-1BGTxVSz", + "type": "rectangle", + "x": 1901.9930329866909, + "y": 955.3374930874562, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8n", + "roundness": { + "type": 3 + }, + "seed": 1276780947, + "version": 558, + "versionNonce": 1366857843, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "t3YRN5lrwfChGAzCLjAvr", + "type": "rectangle", + "x": 1896.3024885162442, + "y": 950.0339162428523, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8o", + "roundness": { + "type": 3 + }, + "seed": 640055091, + "version": 512, + "versionNonce": 710087187, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "rbuigerO1z1rV_Pj64qx9", + "type": "rectangle", + "x": 2007.9015663925295, + "y": 726.8861192908946, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8p", + "roundness": { + "type": 3 + }, + "seed": 1398216915, + "version": 528, + "versionNonce": 552425395, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "Rrcb0EVB0_fP1JJnVvfw0", + "type": "rectangle", + "x": 1972.30054481496, + "y": 746.2371510462428, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8q", + "roundness": { + "type": 3 + }, + "seed": 21137011, + "version": 542, + "versionNonce": 57534803, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "ATPGpNeoeiDiUNdl1yhE-", + "type": "rectangle", + "x": 1966.6100003445138, + "y": 740.933574201639, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8r", + "roundness": { + "type": 3 + }, + "seed": 1722761235, + "version": 496, + "versionNonce": 388712179, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "RBZv8zHWCbEgKGcsghqML", + "type": "rectangle", + "x": 2002.9862824053646, + "y": 744.4467631609202, + "width": 4.907332597593041, + "height": 1.7519561690570522, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8s", + "roundness": { + "type": 3 + }, + "seed": 496223667, + "version": 458, + "versionNonce": 850129043, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "oMyw3MlTCWYaXfoynb4g4", + "type": "rectangle", + "x": 1869.777978135244, + "y": 594.4689782831999, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8t", + "roundness": { + "type": 3 + }, + "seed": 1024890707, + "version": 461, + "versionNonce": 396781107, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "_NyAxf6enZgTte0UTNbR4", + "type": "rectangle", + "x": 1865.1728971506602, + "y": 588.8747246739902, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8u", + "roundness": { + "type": 3 + }, + "seed": 864957683, + "version": 473, + "versionNonce": 218179539, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "BEM4Z1skupWybLEkHgd0y", + "type": "rectangle", + "x": 1877.140964879028, + "y": 602.3302521068356, + "width": 4.907332597593041, + "height": 1.7532814006523263, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8v", + "roundness": { + "type": 3 + }, + "seed": 1619074707, + "version": 535, + "versionNonce": 953333107, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "bORniB0YqgBM3RMCtbsf8", + "type": "rectangle", + "x": 1872.1792977860252, + "y": 619.8908959768612, + "width": 4.907332597593041, + "height": 1.7519561690570522, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8w", + "roundness": { + "type": 3 + }, + "seed": 1856361523, + "version": 466, + "versionNonce": 979472147, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "dwlSJO1-Lr7Igyghv8JLz", + "type": "rectangle", + "x": 1858.7679540410502, + "y": 602.9873414555188, + "width": 4.907332597593041, + "height": 1.7519561690570522, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8x", + "roundness": { + "type": 3 + }, + "seed": 1843582419, + "version": 480, + "versionNonce": 69481651, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false + }, + { + "id": "xnjkmVX7zBZVw9C0qHD21", + "type": "arrow", + "x": 1392.135609585854, + "y": 1049.955975206649, + "width": 154.85241376508543, + "height": 293.11223798872857, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8y", + "roundness": { + "type": 2 + }, + "seed": 760128371, + "version": 815, + "versionNonce": 1975592531, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 111.05220927949313, + -67.64874577655996 + ], + [ + 154.85241376508543, + -175.53584366105952 + ], + [ + 40.89077061275725, + -293.11223798872857 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "b5SET5jHtKd-m_kKo8r9-", + "type": "arrow", + "x": 1506.1148856707448, + "y": 1121.8542577308046, + "width": 341.02973222769674, + "height": 133.9397557457969, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b8z", + "roundness": { + "type": 2 + }, + "seed": 1429191955, + "version": 872, + "versionNonce": 906945523, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 154.77306556855365, + -133.9397557457969 + ], + [ + 306.07244342226727, + -51.435264285214544 + ], + [ + 341.02973222769674, + -2.662572816958118 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "38XPTgRZ6PTEKI50YrTqV", + "type": "arrow", + "x": 1397.409380681872, + "y": 602.9703965827928, + "width": 696.6242667521326, + "height": 515.534049333842, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b90", + "roundness": { + "type": 2 + }, + "seed": 1090298547, + "version": 687, + "versionNonce": 1293151635, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 258.34009497520674, + 66.22929470526765 + ], + [ + 485.32001938776216, + 317.9217741043599 + ], + [ + 696.6242667521326, + 515.534049333842 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "cxLCitgr-qlfizQ-SYHTO", + "type": "arrow", + "x": 1419.0258317438763, + "y": 1123.4764875265678, + "width": 701.8259818581105, + "height": 367.6113780658734, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b91", + "roundness": { + "type": 2 + }, + "seed": 425448531, + "version": 555, + "versionNonce": 2114372403, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 436.5208785206619, + -367.6113780658734 + ], + [ + 701.8259818581105, + -340.35086432403523 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "h8_v75GZGWd4ix3u6hRVW", + "type": "arrow", + "x": 1711.714117221207, + "y": 1119.3239319080665, + "width": 177.59889677088813, + "height": 589.2749733107933, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b92", + "roundness": { + "type": 2 + }, + "seed": 774099443, + "version": 545, + "versionNonce": 1632494803, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 98.7885046821782, + -147.65817727952913 + ], + [ + 144.07869196931347, + -473.0298653916034 + ], + [ + -33.520204801574664, + -589.2749733107933 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "amOkHN2BNeOxr1dSYy666", + "type": "line", + "x": 1607.2309374511917, + "y": 804.0118318229775, + "width": 76.13018633918864, + "height": 96.59320457812987, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b93", + "roundness": null, + "seed": 274406291, + "version": 240, + "versionNonce": 1048564339, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -76.13018633918864, + 96.59320457812987 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "bYCJhpny-jdexsG96oX8B", + "type": "line", + "x": 1517.1002026572685, + "y": 808.2790015031358, + "width": 92.44064895962879, + "height": 89.32843636232315, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b94", + "roundness": null, + "seed": 1815621939, + "version": 259, + "versionNonce": 644025363, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 92.44064895962879, + 89.32843636232315 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "jeHlYb8Z4jRpn5KkBYK8T", + "type": "line", + "x": 1736.3717917984652, + "y": 922.0731317961182, + "width": 61.69195309784608, + "height": 80.75001467059974, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b95", + "roundness": null, + "seed": 589402835, + "version": 402, + "versionNonce": 1290670515, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -61.69195309784608, + 80.75001467059974 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "IOMC3UwMT9JhWsNIBgrrE", + "type": "line", + "x": 1663.3345206323645, + "y": 925.6404015205222, + "width": 74.90910575922612, + "height": 74.6768116687219, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b96", + "roundness": null, + "seed": 632143987, + "version": 421, + "versionNonce": 591881043, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 74.90910575922612, + 74.6768116687219 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "qoBk0ZsNExU-QiCE-iWJd", + "type": "line", + "x": 1944.354435491349, + "y": 905.6216057151779, + "width": 61.69195309784608, + "height": 80.75001467059974, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b97", + "roundness": null, + "seed": 228801043, + "version": 459, + "versionNonce": 835027187, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -61.69195309784608, + 80.75001467059974 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "Za1FGjSU3hx-j8TgtJKXX", + "type": "line", + "x": 1871.3171643252474, + "y": 909.1888754395819, + "width": 74.90910575922612, + "height": 74.6768116687219, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b98", + "roundness": null, + "seed": 2012774323, + "version": 478, + "versionNonce": 837741203, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 74.90910575922612, + 74.6768116687219 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "Uknu8r0enr5iPg2RVXkWY", + "type": "line", + "x": 2022.6843301677266, + "y": 686.096003543478, + "width": 61.69195309784608, + "height": 80.75001467059974, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b99", + "roundness": null, + "seed": 517214547, + "version": 424, + "versionNonce": 1752834099, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -61.69195309784608, + 80.75001467059974 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "IEQ0cH3_mkuMu1S38Jy-A", + "type": "line", + "x": 1949.6470590016254, + "y": 689.663273267882, + "width": 74.90910575922612, + "height": 74.6768116687219, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b9A", + "roundness": null, + "seed": 864841459, + "version": 443, + "versionNonce": 1062052307, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 74.90910575922612, + 74.6768116687219 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "maByFZXlzPdZuDQcRcHOL", + "type": "line", + "x": 1890.9442827591186, + "y": 566.4036573082926, + "width": 61.69195309784608, + "height": 80.75001467059974, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b9B", + "roundness": null, + "seed": 2024282259, + "version": 455, + "versionNonce": 528478067, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -61.69195309784608, + 80.75001467059974 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "XLtwJNXVDH5VubCsORElQ", + "type": "line", + "x": 1817.9070115930178, + "y": 569.9709270326966, + "width": 74.90910575922612, + "height": 74.6768116687219, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b9C", + "roundness": null, + "seed": 467504691, + "version": 474, + "versionNonce": 190286099, + "isDeleted": false, + "boundElements": [], + "updated": 1766926839360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 74.90910575922612, + 74.6768116687219 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#1b1d27" + }, + "files": {} +} \ No newline at end of file diff --git a/.videos/1/animations/3.excalidraw b/.videos/1/animations/3.excalidraw new file mode 100644 index 0000000..16c44a7 --- /dev/null +++ b/.videos/1/animations/3.excalidraw @@ -0,0 +1,721 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "id": "HiVK1huIbL16ZCy3rz5t8", + "type": "rectangle", + "x": 1622.7441591132324, + "y": 984.6777917946865, + "width": 1578.7433546522943, + "height": 836.7851776620863, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2L", + "roundness": { + "type": 3 + }, + "seed": 645107296, + "version": 227, + "versionNonce": 1642809248, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false + }, + { + "id": "lTQAmT-q3EhB8l4OFoV4r", + "type": "rectangle", + "x": 2308.0518724509566, + "y": 1544.7916707023237, + "width": 211.02211156486123, + "height": 96.5527520526872, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2M", + "roundness": { + "type": 3 + }, + "seed": 28492384, + "version": 449, + "versionNonce": 702648928, + "isDeleted": false, + "boundElements": [ + { + "id": "23RNiECmee0GZfyG42WJU", + "type": "arrow" + }, + { + "id": "AS-wLfqmrJICLegtTjwKA", + "type": "arrow" + } + ], + "updated": 1766927996013, + "link": null, + "locked": false + }, + { + "id": "9aRWFt8Ol6hDDEvOstOxf", + "type": "text", + "x": 2340.7060558234443, + "y": 1563.9495282342684, + "width": 154.26666259765625, + "height": 55.62747030529269, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2N", + "roundness": null, + "seed": 384530016, + "version": 204, + "versionNonce": 2083905440, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "text": "Mission", + "fontSize": 44.50197624423415, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Mission", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "23RNiECmee0GZfyG42WJU", + "type": "arrow", + "x": 2519.9815623115946, + "y": 1608.9632387014556, + "width": 185.6851361459012, + "height": 84.58953522783993, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2O", + "roundness": { + "type": 2 + }, + "seed": 1793960544, + "version": 1436, + "versionNonce": 1331590752, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 132.75987383519941, + -15.5442452755197 + ], + [ + 185.6851361459012, + -84.58953522783993 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "lTQAmT-q3EhB8l4OFoV4r", + "focus": 0.46767568934546344, + "gap": 1 + }, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "K6jAODe27_os5DPh8967u", + "type": "text", + "x": 2689.444812908036, + "y": 1465.552661128815, + "width": 222.53535431824844, + "height": 54.779302679674046, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2P", + "roundness": null, + "seed": 1753833056, + "version": 808, + "versionNonce": 1186625440, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "text": "The Story", + "fontSize": 43.823442143739236, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "The Story", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "AS-wLfqmrJICLegtTjwKA", + "type": "arrow", + "x": 2384.330334167984, + "y": 1544.3197414150743, + "width": 49.364397989861686, + "height": 80.37199821269722, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2Q", + "roundness": { + "type": 2 + }, + "seed": 546214496, + "version": 1667, + "versionNonce": 1614569056, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -38.3710655807796, + -28.81586470940509 + ], + [ + -49.364397989861686, + -80.37199821269722 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "lTQAmT-q3EhB8l4OFoV4r", + "focus": 0.21013738563944281, + "gap": 1 + }, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "m8Er51Ess3j92_Q_brntE", + "type": "rectangle", + "x": 2159.6497125177334, + "y": 1335.721614644765, + "width": 363.99766551946664, + "height": 127.50624664551411, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "cNSAgbnsi3PlOKVDl2tCl" + ], + "frameId": null, + "index": "b2R", + "roundness": { + "type": 3 + }, + "seed": 164447840, + "version": 969, + "versionNonce": 2063142816, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false + }, + { + "id": "XCGW5ooPSVuaaam9AVDz-", + "type": "text", + "x": 2173.9396997857207, + "y": 1357.9865705806305, + "width": 333.6248911062344, + "height": 83.92658771184712, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "cNSAgbnsi3PlOKVDl2tCl" + ], + "frameId": null, + "index": "b2S", + "roundness": null, + "seed": 1091926624, + "version": 1089, + "versionNonce": 1148591008, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "text": "Spearhead", + "fontSize": 67.14127016947768, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Spearhead", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "moW1_uGfHzYIjzGogGQGK", + "type": "text", + "x": 2104.0596241560615, + "y": 1034.5614167492809, + "width": 646.7199096679688, + "height": 64.22500341608406, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2T", + "roundness": null, + "seed": 1228353120, + "version": 349, + "versionNonce": 1920051104, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "text": "Mission making, made easy", + "fontSize": 51.380002732867254, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Mission making, made easy", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "o2jZSQLCW3jrv6pCqie6_", + "type": "arrow", + "x": 2159.733022744733, + "y": 1387.9176228888964, + "width": 160.53319080851742, + "height": 70.53888252157658, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2U", + "roundness": { + "type": 2 + }, + "seed": 1258447456, + "version": 260, + "versionNonce": 1897640864, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -92.03884598362902, + -5.663893806853778 + ], + [ + -160.53319080851742, + -70.53888252157658 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "W9vcoug4Anqo3kphgtT9I", + "type": "text", + "x": 1878.031940531582, + "y": 1271.37874036732, + "width": 241.1999053955078, + "height": 45, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2V", + "roundness": null, + "seed": 1430722144, + "version": 123, + "versionNonce": 648298400, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "text": "quick creation", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "quick creation", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "vKiHSOYVz3WA8hsPa4Gz_", + "type": "text", + "x": 2048.097445465383, + "y": 1156.4671681391064, + "width": 291.2398681640625, + "height": 45, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2W", + "roundness": null, + "seed": 483740256, + "version": 118, + "versionNonce": 859524000, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "text": "multiplayer ready", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "multiplayer ready", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "slm2yLO4dbFEB4unTSEBM", + "type": "arrow", + "x": 2272.711713817616, + "y": 1335.0347744783633, + "width": 66.5822620861594, + "height": 126.32077628211141, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2X", + "roundness": { + "type": 2 + }, + "seed": 1014824544, + "version": 263, + "versionNonce": 823187360, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -54.89666258783586, + -52.65193572538732 + ], + [ + -66.5822620861594, + -126.32077628211141 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "AzL0eyJGalMEJhcGJm2_F", + "type": "arrow", + "x": 2416.5200398307725, + "y": 1335.3880374127152, + "width": 114.58165799006088, + "height": 125.00113779000418, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2Y", + "roundness": { + "type": 2 + }, + "seed": 1963864672, + "version": 298, + "versionNonce": 1951720352, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 81.1107899245286, + -56.913107137971394 + ], + [ + 114.58165799006088, + -125.00113779000418 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "9XYksy7fqu89wA1b8KHk4", + "type": "text", + "x": 2479.4611352261527, + "y": 1164.386899622711, + "width": 122.79595947265625, + "height": 45, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2Z", + "roundness": null, + "seed": 1834542688, + "version": 150, + "versionNonce": 1261924256, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "text": "flexible", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "flexible", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "O8kJBNxfzL8ncxdD2kZRM", + "type": "arrow", + "x": 2527.9914421715134, + "y": 1376.2823093418706, + "width": 161.921133337549, + "height": 61.23797674760587, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2a", + "roundness": { + "type": 2 + }, + "seed": 1915558496, + "version": 341, + "versionNonce": 1038646176, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 110.30502983196084, + -8.630813890686284 + ], + [ + 161.921133337549, + -61.23797674760587 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "1SS8AR95vxynNN_GgSOm-", + "type": "text", + "x": 2670.559620709462, + "y": 1266.635895233043, + "width": 207.61192321777344, + "height": 45, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2b", + "roundness": null, + "seed": 911920736, + "version": 322, + "versionNonce": 1318586272, + "isDeleted": false, + "boundElements": [], + "updated": 1766927996013, + "link": null, + "locked": false, + "text": "easy to use", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "easy to use", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#1b1d27" + }, + "files": {} +} \ No newline at end of file diff --git a/DcsTypes.lua b/DcsTypes.lua index e9912dc..38de46f 100644 --- a/DcsTypes.lua +++ b/DcsTypes.lua @@ -53,6 +53,83 @@ do -- env env = env end +do -- Mission table + + ---@class Mission + ---@field drawings Array + + ---@alias PrimitiveType + ---| "Polygon" + ---| "Line" + ---| "TextBox" + + ---@alias PolygonMode + ---| "circle" + ---| "free" + ---| "oval" + ---| "rect" + ---| "arrow" + + ---@alias LineMode + ---| "segment" + ---| "segments" + ---| "free" + + ---@class DrawingObject + ---@field primitiveType PrimitiveType + ---@field name string + ---@field visible boolean + ---@field layerName string? + ---@field mapX number + ---@field mapY number + ---@field colorString string Hex ARGB color + ---@field style string + ---@field thickness number + + ---@class Polygon : DrawingObject + ---@field polygonMode PolygonMode + ---@field fillColorString string? + + ---@class Circle : Polygon + ---@field radius number + + ---@class Free : Polygon + ---@field points Array + + ---@class Oval : Polygon + ---@field r1 number + ---@field r2 number + ---@field angle number? + + ---@class Rect : Polygon + ---@field width number + ---@field height number + ---@field angle number? + + ---@class Arrow : Polygon + ---@field length number + ---@field angle number? + + ---@class Line : DrawingObject + ---@field primitiveType PrimitiveType # PrimitiveType.Line + ---@field lineMode LineMode + ---@field closed boolean + ---@field points Array + + ---@class Segment : Line + + ---@class Segments : Line + + ---@class FreeLine : Line + + ---@class TextBox : DrawingObject + ---@field fontSize number + ---@field text string + ---@field fillColorString string + ---@field borderThickness number + +end + do -- timer ---@class timer ---@field getTime fun() : number returns the time in the mission (in seconds. 3 decimals) @@ -358,7 +435,7 @@ do -- trigger ---@field circleToAll fun(coalition: DrawCoalition, id: number, center: Vec3 , radius: number, color: table , fillColor: table , lineType: LineType , readOnly: boolean?, message: string?) Creates a circle on the map with a given radius, color, fill color, and outline. ---@field rectToAll fun(coalition:DrawCoalition, id:number, startPoint: Vec3, endPoint:Vec3, color: table, fillColor: table, lineType: LineType , readOnly: boolean?, message: string?) Creates a rectangle on the map from the startpoint in one corner to the endPoint in the opposite corner. ---@field quadToAll fun(coalition:DrawCoalition, id:number, point1: Vec3, point2:Vec3, point3:Vec3, point4:Vec3,color: table, fillColor: table, lineType: LineType , readOnly: boolean?, message: string?) Creates a shape defined by the 4 points on the F10 map. - ---@field textToAll fun(coalition:DrawCoalition, id: number, point:Vec3, color: table, fillColor: table, fontSize:table, readOnly:boolean, text:string) Creates a text imposed on the map at a given point. Text scales with the map. + ---@field textToAll fun(coalition:DrawCoalition, id: number, point:Vec3, color: table, fillColor: table, fontSize:number, readOnly:boolean, text:string) Creates a text imposed on the map at a given point. Text scales with the map. ---@field arrowToAll fun(coalition:DrawCoalition, id: number, startPoint:Vec3, endPoint:Vec3, color:table, fillColor:table, lineType:LineType, readonly:boolean?, message: string?) Creates an arrow from the startPoint to the endPoint on the F10 map. The arrow will be "pointing at" the startPoint ---@field setMarkupRadius fun(id:number, radius:number) Updates the radius of the specified mark to be the new value. ---@field setMarkupText fun(id:number, text: string) Updates the text value of the passed mark to the passed text value. diff --git a/_docs/js/components/header.js b/_docs/js/components/header.js index 8dadac3..ed88d76 100644 --- a/_docs/js/components/header.js +++ b/_docs/js/components/header.js @@ -18,8 +18,9 @@ class Header extends HTMLElement { Tutorials diff --git a/_docs/pages/about_us.html b/_docs/pages/about_us.html index 0e66e7c..298cd7b 100644 --- a/_docs/pages/about_us.html +++ b/_docs/pages/about_us.html @@ -63,5 +63,4 @@ - -``` \ No newline at end of file + \ No newline at end of file diff --git a/_docs/pages/advanced/CAP.html b/_docs/pages/advanced/CAP.html index 50c2462..1b44f31 100644 --- a/_docs/pages/advanced/CAP.html +++ b/_docs/pages/advanced/CAP.html @@ -220,5 +220,4 @@ - -``` \ No newline at end of file + \ No newline at end of file diff --git a/_docs/pages/advanced/custom-drawings.html b/_docs/pages/advanced/custom-drawings.html new file mode 100644 index 0000000..cbe1752 --- /dev/null +++ b/_docs/pages/advanced/custom-drawings.html @@ -0,0 +1,65 @@ + + + + + + + Spearhead Missions + + + + + + + +
+ +
+
+
+ + +
+

Advanced Tutorial: Custom Drawings

+ +

+ Drawings can be very powerful in DCS missions.
+ They can guide players towards the right objectives or warn them of dangers ahead.
+ In Spearhead, we provide an easy way to manage and update these drawings dynamically as the mission progresses.
+

+ +

How to

+

+ Adding a custom drawing is easy.
+ Simply create the drawing and then name it according to the following convention:
+ DRAWING_[a]:[b]_[FreeForm]
+ Where: +

    +
  • [a] is the stage number at which the drawing should appear.
  • +
  • [b] is the stage number at which the drawing should be removed again.
  • +
  • [FreeForm] is any name you want to make it unique or findables.
  • +
+

+ + + The custom drawings need to be in the "Author" layer of the mission editor to be managed by Spearhead. + This is due to other layers being visible by default and cannot be as easily removed when not needed. + The author layer is not visible by default in multiplayer, making it perfect for this use case. + + + +
+
+ +
+
+

© 2025 Spearhead Project

+
+ + + \ No newline at end of file diff --git a/_docs/pages/advanced/missions.html b/_docs/pages/advanced/missions.html index 6539b74..49c07bb 100644 --- a/_docs/pages/advanced/missions.html +++ b/_docs/pages/advanced/missions.html @@ -68,5 +68,4 @@ - -``` \ No newline at end of file + \ No newline at end of file diff --git a/_docs/pages/persistence.html b/_docs/pages/persistence.html index ec9e95a..fca45ac 100644 --- a/_docs/pages/persistence.html +++ b/_docs/pages/persistence.html @@ -123,4 +123,3 @@ -``` \ No newline at end of file diff --git a/_docs/style/style.css b/_docs/style/style.css index d5f589f..6df6800 100644 --- a/_docs/style/style.css +++ b/_docs/style/style.css @@ -270,11 +270,11 @@ header nav a:hover::after { border-radius: 8px; max-height: 0px; - transition: all 1.5s ease; + transition: all 1s ease; } .dropdown:hover .dropdown-content { - max-height: 200px; + max-height: 100vh; } header.collapsible { diff --git a/classes/spearhead_db.lua b/classes/spearhead_db.lua index e603f97..d29b4a6 100644 --- a/classes/spearhead_db.lua +++ b/classes/spearhead_db.lua @@ -20,7 +20,7 @@ ---@field MissionZoneData table ---@field FarpZoneData table ---@field missionCodes table - +---@field CustomDrawings Array ---@class CapRoute ---@field zones Array @@ -99,7 +99,8 @@ function Database.New(Logger) MissionZoneData = {}, FarpZoneData = {}, missionCodes = {}, - SupplyHubZones = {} + SupplyHubZones = {}, + CustomDrawings = {} } Database.__index = Database @@ -215,6 +216,23 @@ function Database.New(Logger) end end + ---@type table + do -- custom drawings + if env.mission.drawings and env.mission.drawings.layers then + for i, layer in pairs(env.mission.drawings.layers) do + if string.lower(layer.name) == "author" then + for key, layer_object in pairs(layer.objects) do + if Spearhead.Util.startswith(layer_object.name, "drawing_", true) then + local object = layer_object --[[@as DrawingObject]] + local customDrawing = Spearhead.classes.stageClasses.drawings.CustomDrawing.New(object) + table.insert(self._tables.CustomDrawings, customDrawing) + end + end + end + end + end + end + ---@type table local availableSupplyHubs = {} for _, supplyHubZoneName in pairs(self._tables.SupplyHubZones) do @@ -593,6 +611,14 @@ function Database:loadBlueSamUnits() end end +---@return Array +function Database:getCustomDrawings() + if self._tables.CustomDrawings == nil then + return {} + end + return self._tables.CustomDrawings +end + ---Loads all units, data and briefings ---@private function Database:LoadZoneData(missionZoneName) diff --git a/classes/stageClasses/GlobalStageManager.lua b/classes/stageClasses/GlobalStageManager.lua index a627b95..54f9c80 100644 --- a/classes/stageClasses/GlobalStageManager.lua +++ b/classes/stageClasses/GlobalStageManager.lua @@ -13,8 +13,12 @@ local WaitingStagesByIndex = {} local currentStage = -99 - -GlobalStageManager = {} +---@class GlobalStageManager : StageCompleteListener +---@field private database Database +---@field private logger Logger +---@field private stageConfig StageConfig +local GlobalStageManager = {} +GlobalStageManager.__index = GlobalStageManager ---comment ---@param database Database @@ -22,13 +26,14 @@ GlobalStageManager = {} ---@param logLevel LogLevel ---@param spawnManager SpawnManager ---@return nil -function GlobalStageManager:NewAndStart(database, stageConfig, logLevel, spawnManager) +function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnManager) local logger = Spearhead.LoggerTemplate.new("StageManager", logLevel) logger:info("Using Stage Log Level: " .. logLevel) - local o = {} - setmetatable(o, { __index = self }) + local self = setmetatable({}, GlobalStageManager) + self.database = database + self.stageConfig = stageConfig - o.logger = logger + self.logger = logger if stageConfig.isAutoStages ~= true then logger:warn("Spearhead will not automatically progress stages due to the given settings. If you manually have implemented this, please ignore this message") end @@ -42,53 +47,6 @@ function GlobalStageManager:NewAndStart(database, stageConfig, logLevel, spawnMa Spearhead.Events.AddStageNumberChangedListener(OnStageNumberChangedListener) - ---@type StageCompleteListener - local OnStageCompleteListener = { - OnStageComplete = function(self, stage) - logger:debug("Receiving stage complete event from: " .. stage.zoneName) - - local anyIncomplete = false - logger:debug("Checking stages for index: " .. tostring(currentStage)) - for index, stage in pairs(StagesByIndex[tostring(currentStage)]) do - if stage:IsComplete() == false then - anyIncomplete = true - logger:debug("Need to wait for Stage " .. stage.zoneName .. " to be completed") - else - logger:debug("Stage verified to be completed: " .. stage.zoneName) - end - end - - if anyIncomplete == false and stageConfig.isAutoStages == true then - - -- CHECK WAITING STAGES - local nextStage = currentStage + 1 - - if WaitingStagesByIndex[tostring(nextStage)] then - for _, waitingStage in pairs(WaitingStagesByIndex[tostring(nextStage)]) do - if waitingStage:IsActive() == false then - waitingStage:ActivateStage() - end - end - end - - local anyWaiting = false - if WaitingStagesByIndex[tostring(nextStage)] then - for _, waitingStage in pairs(WaitingStagesByIndex[tostring(nextStage)]) do - if waitingStage:IsComplete() == false then - anyWaiting = true - end - end - end - - if anyWaiting == false then - logger:debug("Setting next stage to: " .. tostring(currentStage + 1)) - Spearhead.Events.PublishStageNumberChanged(currentStage + 1) - end - end - end - } - - for _, stageName in pairs(database:getStagezoneNames()) do logger:debug("Found stage zone with name: " .. stageName) @@ -136,13 +94,13 @@ function GlobalStageManager:NewAndStart(database, stageConfig, logLevel, spawnMa if isSideStage == true then local stage = Spearhead.classes.stageClasses.Stages.ExtraStage.New(database, stageConfig, stagelogger, initData, spawnManager) - stage:AddStageCompleteListener(OnStageCompleteListener) + stage:AddStageCompleteListener(self) if SideStageByIndex[tostring(orderNumber)] == nil then SideStageByIndex[tostring(orderNumber)] = {} end table.insert(SideStageByIndex[tostring(orderNumber)], stage) else local stage = Spearhead.classes.stageClasses.Stages.PrimaryStage.New(database, stageConfig, stagelogger, initData, spawnManager) - stage:AddStageCompleteListener(OnStageCompleteListener) + stage:AddStageCompleteListener(self) if StagesByIndex[tostring(orderNumber)] == nil then StagesByIndex[tostring(orderNumber)] = {} end table.insert(StagesByIndex[tostring(orderNumber)], stage) @@ -193,15 +151,71 @@ function GlobalStageManager:NewAndStart(database, stageConfig, logLevel, spawnMa end table.insert(WaitingStagesByIndex[tostring(stageIndex)], waitingStage) - waitingStage:AddStageCompleteListener(OnStageCompleteListener) + waitingStage:AddStageCompleteListener(self) end end end end + return self +end +function GlobalStageManager:OnStageComplete(stage) + self.logger:debug("Receiving stage complete event from: " .. stage.zoneName) - return o + local anyIncomplete = false + self.logger:debug("Checking stages for index: " .. tostring(currentStage)) + for index, stage in pairs(StagesByIndex[tostring(currentStage)]) do + if stage:IsComplete() == false then + anyIncomplete = true + self.logger:debug("Need to wait for Stage " .. stage.zoneName .. " to be completed") + else + self.logger:debug("Stage verified to be completed: " .. stage.zoneName) + end + end + + if anyIncomplete == false and self.stageConfig.isAutoStages == true then + + -- CHECK WAITING STAGES + local nextStage = currentStage + 1 + + if WaitingStagesByIndex[tostring(nextStage)] then + for _, waitingStage in pairs(WaitingStagesByIndex[tostring(nextStage)]) do + if waitingStage:IsActive() == false then + waitingStage:ActivateStage() + end + end + end + + local anyWaiting = false + if WaitingStagesByIndex[tostring(nextStage)] then + for _, waitingStage in pairs(WaitingStagesByIndex[tostring(nextStage)]) do + if waitingStage:IsComplete() == false then + anyWaiting = true + end + end + end + + if anyWaiting == false then + local newStageNumber = currentStage + 1 + self:UpdateDrawings(newStageNumber) + self.logger:debug("Setting next stage to: " .. tostring(newStageNumber)) + Spearhead.Events.PublishStageNumberChanged(newStageNumber) + end + end +end + +---@private +function GlobalStageManager:UpdateDrawings(stageNumber) + local drawings = self.database:getCustomDrawings() + for _, drawing in pairs(drawings) do + local startStage, stopStage = drawing:GetStartAndStop() + if stageNumber >= startStage and stageNumber < stopStage then + drawing:Draw() + else + drawing:Remove() + end + end end diff --git a/classes/stageClasses/drawings/CustomDrawing.lua b/classes/stageClasses/drawings/CustomDrawing.lua new file mode 100644 index 0000000..4b97dda --- /dev/null +++ b/classes/stageClasses/drawings/CustomDrawing.lua @@ -0,0 +1,52 @@ + + +---@class CustomDrawing +---@field private _id integer? +---@field private _drawingObject DrawingObject +---@field private _helper DrawingHelper +---@field private _startingStage number +---@field private _removeAtStage number +local CustomDrawing = {} +CustomDrawing.__index = CustomDrawing + + +---@param drawingObject DrawingObject +---@param id integer? +---@return CustomDrawing +function CustomDrawing.New(drawingObject, id) + local self = setmetatable({}, CustomDrawing) + self._drawingObject = drawingObject + self._id = id + self._helper = Spearhead.classes.stageClasses.drawings.helper.DrawingHelper + + local name = drawingObject.name + local split = Spearhead.Util.split_string(name or "", "_") + local secondPart = split[2] or "1" + local splitPart = Spearhead.Util.split_string(secondPart, ":") + self._startingStage = tonumber(splitPart[1]) or 1 + self._removeAtStage = tonumber(splitPart[2]) or math.huge + return self +end + +---@return number start +---@return number stop +function CustomDrawing:GetStartAndStop() + return self._startingStage, self._removeAtStage +end + +function CustomDrawing:Draw() + self._id = self._helper.Draw(self._drawingObject) +end + +function CustomDrawing:Remove() + if self._id ~= nil then + self._helper.Remove(self._id) + self._id = nil + end +end + +if not Spearhead then Spearhead = {} end +if not Spearhead.classes then Spearhead.classes = {} end +if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end +if not Spearhead.classes.stageClasses.drawings then Spearhead.classes.stageClasses.drawings = {} end +Spearhead.classes.stageClasses.drawings.CustomDrawing = CustomDrawing \ No newline at end of file diff --git a/classes/stageClasses/drawings/helper/DrawingHelper.lua b/classes/stageClasses/drawings/helper/DrawingHelper.lua new file mode 100644 index 0000000..c05262d --- /dev/null +++ b/classes/stageClasses/drawings/helper/DrawingHelper.lua @@ -0,0 +1,228 @@ + + +---@class DrawingHelper +local DrawingHelper = {} +DrawingHelper.__index = DrawingHelper + +local customDrawingIdIncrementer = 4210 + +---@param object DrawingObject +---@return integer? id +function DrawingHelper.Draw(object) + if object == nil then + return nil + end + + local id = DrawingHelper.GetAndAddId() + if(object.primitiveType == "Polygon") then + DrawingHelper.DrawPolygon(object--[[@as Polygon]], id) + elseif(object.primitiveType == "Line") then + DrawingHelper.DrawLine(object--[[@as Line]], id) + elseif(object.primitiveType == "TextBox") then + DrawingHelper.DrawTextBox(object--[[@as TextBox]], id) + end + + return id +end + +function DrawingHelper.GetAndAddId() + customDrawingIdIncrementer = customDrawingIdIncrementer + 1 + return customDrawingIdIncrementer +end + +---@private +---@param shapeID ShapeId +---@param drawID integer +---@param points Array +---@param fillColor table +---@param lineColor table +local function MarkupToAll(shapeID, drawID, points, fillColor, lineColor, lineStyle) + + local functionString = "trigger.action.markupToAll(" .. shapeID .. ", -1, " .. drawID .. "," + for _, point in ipairs(points) do + functionString = functionString .. " { x=" .. point.x .. ", y=0,z=" .. point.z .. "}," + end + functionString = functionString .. + "{ " .. lineColor[1] .. "," .. lineColor[2] .. "," .. lineColor[3] .. "," .. lineColor[4] .. "}, " .. + "{ " .. fillColor[1] .. "," .. fillColor[2] .. "," .. fillColor[3] .. "," .. fillColor[4] .. "}, " .. + lineStyle .. ")" + + ---@diagnostic disable-next-line: deprecated + local f, err = loadstring(functionString) + if f then + f() + else + env.error("Something failed when drawing complex drawing" .. err) + end + +end + +---@private +---@param object Polygon +---@param id integer +function DrawingHelper.DrawPolygon(object, id) + if object == nil then + return + end + + ---@param circle Circle + local function DrawCircle(circle) + local vec3 = { x = circle.mapX, y = 0, z = circle.mapY } + local fillColor = DrawingHelper.ColorToColorTable(circle.fillColorString) + local colorString = DrawingHelper.ColorToColorTable(circle.colorString) + local style = DrawingHelper.ToLineStyleInteger(circle.style) + trigger.action.circleToAll(-1, id, vec3, circle.radius, colorString, fillColor, style, true) + end + + ---@param oval Oval + local function DrawOval(oval) + ---@type Array + local points = {} + local pointsNo = 30 + local angleStep = (2 * math.pi) / points + + local fillColor = DrawingHelper.ColorToColorTable(oval.fillColorString) + local color = DrawingHelper.ColorToColorTable(oval.colorString) + local lineStyle = DrawingHelper.ToLineStyleInteger(oval.style) + + for i = 1, pointsNo do + local angle = i * angleStep + local x = oval.mapX + (oval.r1 * math.cos(angle)) + local y = oval.mapY + (oval.r2 * math.sin(angle)) + table.insert(points, { x = x, y = 0, z = y } ) + end + MarkupToAll(7, id, points, fillColor, color, lineStyle) + end + + ---@param free Free + local function DrawFree(free) + local fillColor = DrawingHelper.ColorToColorTable(free.fillColorString) + local color = DrawingHelper.ColorToColorTable(free.colorString) + local lineStyle = DrawingHelper.ToLineStyleInteger(free.style) + + local points = {} + for _, point in ipairs(free.points) do + table.insert(points, { x = point.x, y = 0, z = point.y } ) + end + MarkupToAll(7, id, points, fillColor, color, lineStyle) + end + + ---@param rect Rect + local function DrawRect(rect) + local fillColor = DrawingHelper.ColorToColorTable(rect.fillColorString) + local color = DrawingHelper.ColorToColorTable(rect.colorString) + local lineStyle = DrawingHelper.ToLineStyleInteger(rect.style) + + local pointA = { x = rect.mapX, y = 0, z = rect.mapY } + local pointB = { x = rect.mapX + rect.width, y = 0, z = rect.mapY + rect.height } + trigger.action.rectToAll(-1, id, pointA, pointB, color, fillColor, lineStyle, true) + end + + ---@param arrow Arrow + local function DrawArrow(arrow) + local fillColor = DrawingHelper.ColorToColorTable(arrow.fillColorString) + local color = DrawingHelper.ColorToColorTable(arrow.colorString) + local lineStyle = DrawingHelper.ToLineStyleInteger(arrow.style) + + local startPoint = { x = arrow.mapX, y = 0, z = arrow.mapY } + local rad = math.rad(arrow.angle or 0) + local length = arrow.length or 100 + local endPoint = { x = arrow.mapX + length * math.cos(rad), y = 0, z = arrow.mapY + length * math.sin(rad) } + trigger.action.arrowToAll(-1, id, startPoint, endPoint, color, fillColor, lineStyle, true) + end + + if object.polygonMode == "circle" then + DrawCircle(object--[[@as Circle]]) + elseif object.polygonMode == "oval" then + DrawOval(object--[[@as Oval]]) + elseif object.polygonMode == "free" then + DrawFree(object--[[@as Free]]) + elseif object.polygonMode == "rect" then + DrawRect(object--[[@as Rect]]) + elseif object.polygonMode == "arrow" then + DrawArrow(object--[[@as Arrow]]) + end +end + +---@private +---@param object Line +---@param id integer +function DrawingHelper.DrawLine(object, id) + + ---@type Array + local points = {} + + for _, point in ipairs(object.points) do + table.insert(points, { x = point.x, y = 0, z = point.y } ) + end + + local color = DrawingHelper.ColorToColorTable(object.colorString) + local lineStyle = DrawingHelper.ToLineStyleInteger(object.style) + MarkupToAll(1, id, points, color, color, lineStyle) +end + +---@private +---@param object TextBox +---@param id integer +function DrawingHelper.DrawTextBox(object, id) + trigger.action.textToAll(-1, id, { x= object.mapX, y = 0, z = object.mapY }, + DrawingHelper.ColorToColorTable(object.colorString), + DrawingHelper.ColorToColorTable(object.fillColorString), + object.fontSize or 12, + true, + object.text or "") +end + +function DrawingHelper.Remove(id) + trigger.action.removeMark(id) +end + + + +---@param hexStr string +---@return table +function DrawingHelper.ColorToColorTable(hexStr) + hexStr = hexStr:gsub("0x", "") + local a = tonumber(hexStr:sub(1, 2), 16) / 255 + local r = tonumber(hexStr:sub(3, 4), 16) / 255 + local g = tonumber(hexStr:sub(5, 6), 16) / 255 + local b = tonumber(hexStr:sub(7, 8), 16) / 255 + + return { r, g , b , a } +end + +---@param lineStyle string +function DrawingHelper.ToLineStyleInteger(lineStyle) + lineStyle = lineStyle:lower() + if lineStyle == "no line" then + return 0 + elseif lineStyle == "solid" then + return 1 + elseif lineStyle == "dashed" then + return 2 + elseif lineStyle == "dotted" then + return 3 + elseif lineStyle == "dot dash" then + return 4 + elseif lineStyle == "long dash" then + return 5 + elseif lineStyle == "two dash" then + return 6 + else + return 0 + end +end + +---@class ARGB +---@field public a number +---@field public r number +---@field public g number +---@field public b number + + +if not Spearhead then Spearhead = {} end +if not Spearhead.classes then Spearhead.classes = {} end +if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end +if not Spearhead.classes.stageClasses.drawings then Spearhead.classes.stageClasses.drawings = {} end +if not Spearhead.classes.stageClasses.drawings.helper then Spearhead.classes.stageClasses.drawings.helper = {} end +Spearhead.classes.stageClasses.drawings.helper.DrawingHelper = DrawingHelper \ No newline at end of file diff --git a/main.lua b/main.lua index 7ab3ab7..3f851b5 100644 --- a/main.lua +++ b/main.lua @@ -39,7 +39,7 @@ local detectionLogger = Spearhead.LoggerTemplate.new("DetectionManager", default local detectionManager = Spearhead.classes.capClasses.detection.DetectionManager.New(detectionLogger) Spearhead.classes.capClasses.GlobalCapManager.start(databaseManager, capConfig, detectionManager, stageConfig, defaultLogLevel, spawnManager) -Spearhead.internal.GlobalStageManager:NewAndStart(databaseManager, stageConfig, defaultLogLevel, spawnManager) +Spearhead.internal.GlobalStageManager.NewAndStart(databaseManager, stageConfig, defaultLogLevel, spawnManager) Spearhead.internal.GlobalFleetManager.start(databaseManager) From 97f68389d4d674cfe2b140825e3ae60977b490de Mon Sep 17 00:00:00 2001 From: dutchie031 Date: Tue, 20 Jan 2026 15:58:40 +0100 Subject: [PATCH 2/9] wip --- .DS_Store | Bin 8196 -> 8196 bytes _docs/index.html | 12 +- _docs/js/components.js | 1 + _docs/js/components/header.js | 11 +- .../js/components/latest-version-download.js | 119 ++ ...custom-drawings.html => map-markings.html} | 36 +- _docs/pages/first-start.html | 208 +++ _docs/pages/include-script.html | 69 + _docs/pages/tutorials.html | 72 +- .../capClasses/airGroups/InterceptGroup.lua | 4 +- logistics.excalidraw.json | 1242 ++++++++--------- 11 files changed, 1097 insertions(+), 677 deletions(-) create mode 100644 _docs/js/components/latest-version-download.js rename _docs/pages/advanced/{custom-drawings.html => map-markings.html} (50%) create mode 100644 _docs/pages/first-start.html create mode 100644 _docs/pages/include-script.html diff --git a/.DS_Store b/.DS_Store index 478ee77ab22f1b507ab23e014731aa2a67a72971..4b3a3c0283ea79121b114dc723b8e5e496a85858 100644 GIT binary patch delta 396 zcmZp1XmOa}RCU^hRb-eevD$+-JW;S3B6EDU-K=?s|+CAs-7E=f80NkB0U|F)n# z{7uIlQRP$c$`@o91}Ep|76A1yFu4d!ZWfqTpUIHIkjjwHP@I#FkYr%IbsVUM9cXbG z&|;_xWb1coF7jk<09%it3gHOcs$foG2f2lT@xx?c!Rt&eB9q?;CNKs}_7!qg@SFye zU}1=7$OXEf3fb--8?WrSR5N*lkQ5t~c|k~qv0?ICA%CXXxs&aL<+X2sR6t#q%#g#7 p$WRP~sSL%r`3Tqj1*@9eA*{scuz9uc8RpIG65m)h=Zdg10|4tHa5ew{ delta 266 zcmZp1XmOa}LAU^hRb)?^+5$+#OX3m6y}SQzve(it)tN^ - -

Latest Version: #{VERSION}# created at: #{VERSION_DATE}#

-

Latest Beta Version: #{BETA_VERSION}# created at: #{BETA_VERSION_DATE}#

- See All releases here + the hard core scripters. +

+

+ Whether this is your first mission or your hundredth, we hope Spearhead will make your life easier. +

+ + diff --git a/_docs/js/components.js b/_docs/js/components.js index cab2766..6ab904a 100644 --- a/_docs/js/components.js +++ b/_docs/js/components.js @@ -3,3 +3,4 @@ import * as sidebar from "./components/sidebar.js"; import './components/code-block.js'; import './components/code-inline.js'; import './components/note.js'; +import './components/latest-version-download.js'; diff --git a/_docs/js/components/header.js b/_docs/js/components/header.js index ed88d76..fa59be2 100644 --- a/_docs/js/components/header.js +++ b/_docs/js/components/header.js @@ -6,7 +6,7 @@ class Header extends HTMLElement { } connectedCallback() { - this.innerHTML = ` + this.innerHTML = /*html*/`
mission making, made easy @@ -17,10 +17,11 @@ class Header extends HTMLElement { diff --git a/_docs/js/components/latest-version-download.js b/_docs/js/components/latest-version-download.js new file mode 100644 index 0000000..d0a2e9c --- /dev/null +++ b/_docs/js/components/latest-version-download.js @@ -0,0 +1,119 @@ +class DownloadScript extends HTMLElement { + + constructor(){ + super(); + this.cacheKey = 'spearhead-latest-release'; + this.cacheExpiryMinutes = 15; + } + + getCachedData() { + const cached = localStorage.getItem(this.cacheKey); + if (!cached) return null; + + const { data, timestamp } = JSON.parse(cached); + const now = Date.now(); + const expiry = this.cacheExpiryMinutes * 60 * 1000; // 15 minutes in ms + + if (now - timestamp > expiry) { + localStorage.removeItem(this.cacheKey); + return null; + } + + return data; + } + + setCachedData(data) { + const cacheItem = { + data: data, + timestamp: Date.now() + }; + localStorage.setItem(this.cacheKey, JSON.stringify(cacheItem)); + } + + async fetchLatestRelease() { + // Check cache first + const cachedData = this.getCachedData(); + if (cachedData) { + return cachedData; + } + + try { + const response = await fetch('https://api.github.com/repos/dutchie031/Spearhead/releases/latest'); + const data = await response.json(); + + // Cache the data + this.setCachedData(data); + return data; + } catch (error) { + console.error('Error fetching latest release:', error); + return null; + } + } + + async fetchReleaseAssets(url){ + try { + const response = await fetch(url); + const data = await response.json(); + return data; + } catch (error) { + console.error('Error fetching release assets:', error); + return null; + } + } + + async connectedCallback() { + + var version = "?"; + var timestamp = "?"; + var size = "?"; + var href = "https://github.com/dutchie031/Spearhead/releases" + + const latestRelease = await this.fetchLatestRelease(); + if (latestRelease) { + version = latestRelease.tag_name; + timestamp = new Date(latestRelease.published_at).toLocaleDateString('en-CA'); + + const assets = await this.fetchReleaseAssets(latestRelease.assets_url); + if (assets && assets.length > 0) { + const spearheadAsset = assets.find(asset => asset.name.includes('spearhead')); + if (spearheadAsset) { + size = (spearheadAsset.size / 1024).toFixed(2) + 'kb'; + href = spearheadAsset.browser_download_url; + } + } + } + + this.innerHTML = /*html*/` +
+ +
+

+ ${version} (${timestamp})
+ Download (${size})
+ See All Versions here +

+
+ + +
+ `; + } + +} + +customElements.define('download-spearhead', DownloadScript); \ No newline at end of file diff --git a/_docs/pages/advanced/custom-drawings.html b/_docs/pages/advanced/map-markings.html similarity index 50% rename from _docs/pages/advanced/custom-drawings.html rename to _docs/pages/advanced/map-markings.html index cbe1752..b76579a 100644 --- a/_docs/pages/advanced/custom-drawings.html +++ b/_docs/pages/advanced/map-markings.html @@ -25,24 +25,46 @@
-

Advanced Tutorial: Custom Drawings

+

Map Markings

Drawings can be very powerful in DCS missions.
They can guide players towards the right objectives or warn them of dangers ahead.
- In Spearhead, we provide an easy way to manage and update these drawings dynamically as the mission progresses.
+ In Spearhead, we have both automatic markers and drawings as well as the possibility to integrate custom drawings.

-

How to

+

Automated

+ + +

Stages

+

+ Spearhead automatically draws stage markings on the map for players to see.
+ These are drawn when a stage becomes active and removed when the stage is completed.
+ This gives players a clear visual indication of where they need to go next.
+
+ These can however be disabled in the configuration if desired.
+ SpearheadConfig.StageConfig.drawStages
+ SpearheadConfig.StageConfig.drawPreActived
+

+ +

Last Contact

+

+ If enabled, Spearhead will draw a marker on the map where the enemy was last killed.
+ This might help in big multiplayer missions where players log off without sharing intel.
+ This can be enabled or disabled in the configuration.
+ SpearheadConfig.StageConfig.markLastContact +

+ +

Custom

Adding a custom drawing is easy.
Simply create the drawing and then name it according to the following convention:
DRAWING_[a]:[b]_[FreeForm]
Where:

    -
  • [a] is the stage number at which the drawing should appear.
  • -
  • [b] is the stage number at which the drawing should be removed again.
  • -
  • [FreeForm] is any name you want to make it unique or findables.
  • +
  • [a] is the stage number at which the drawing should appear.
  • +
  • [b] is the stage number at which the drawing should be removed again.
  • +
  • [FreeForm] is any name you want to make it unique or findables.

@@ -51,8 +73,6 @@ This is due to other layers being visible by default and cannot be as easily removed when not needed. The author layer is not visible by default in multiplayer, making it perfect for this use case. - -
diff --git a/_docs/pages/first-start.html b/_docs/pages/first-start.html new file mode 100644 index 0000000..4c68496 --- /dev/null +++ b/_docs/pages/first-start.html @@ -0,0 +1,208 @@ + + + + + + + Spearhead Tutorials + + + + + + + + + + + +
+ +
+
+ + +
+

Tutorials

+

+ This guide is to get you started building your first Spearhead mission.
+ Spearhead was created to enable the mission maker to worry as little about the running, timing and scripting + and most about the setting and looks and feel of the mission.
+ In the example we'll show how you can create a simple island hopping mission +

+ +

Include the script

+

+ Read here how to include the Spearhead script in your mission first.
+

+ +

Stages

+ +

+ In general, stages are the backbone of a Spearhead mission.
+ They define the flow of the mission and what is active at what time.
+ + Don't worry, it can be changed and adapted later on, but it's best to translate your mission idea into stages first.
+

+ +

+ Stages are defined by trigger zones.
+ They'll be picked up by Spearhead when named according to the naming convention:
+ MISSIONSTAGE_[OrderNumber]_[FreeForm]
+
+ Stages are divided in primary stages: MISSIONSTAGE_[number]
+ or secondary stages: MISSIONSTAGE_x[number]
+ Note the x before the number. This marks it as "extra"
+ Secondary stages will follow the order number, but are not required to be completed before moving on to the next stage numbers.
+

+ +

+ For this Demo mission we'll create 3 stages.
+ MissionStage_1_start
+ MissionStage_2_continuation
+ MissionStage_3_finale
+ Each stage will have it's own trigger zone.
+

+ The first stages displayed + +

+ There's 3 stages now.
+ The first MissionStage_1_start is the first stage and will be active at mission start.
+ The second MissionStage_2_continuation will be pre-activated when stage 1 is active.
+ But will be fully activated when stage 1 is complete.
+ Pre-activated means that the SAM missions and Airbase units will be activated.
+ This is the difference between SAM and DEAD missions, but let's not get into too much detail at this time.
+ + You can ofcourse create as many stages as you want.
+ You can also change the amount of stages that get pre-activated.
+ By default Spearhead will pre-activate 1 stage ahead.
+ This can be changed in the configuration.
+

+ +

Create Missions

+

+ Now that we've created the stages we can start adding missions to them.
+ Missions are also defined by trigger zones.
+ They'll be picked up by Spearhead when named according to the naming convention:
+ MISSION_[Type]_[FreeForm]
+ Where [Type] is the type of mission.
+ +

+ +

CAS

+

+ Personally CAS is one of our favorite mission types.
+ Mostly because it's easy to set up and creates a truly immersive experience.
+ +

+ +

SAM

+

+ +

+ +

STRIKE

+

+ +

+ + +

Setting up CAP

+

+ If you don't want to use the CAP managers withing Spearhead you can skip this and continue to Setting up Missions.
+ However CAP is one of the painpoints in a lot of missions and setting up a dynamic feeling airspace can be + quite the challenge.
+ With the CAP managers we've tried to make this a lot easier.
+ + A CAP group needs to follow the following naming convention: CAP_[A|B][CONFIG]_[Free Form] + + For details on config read this: CAP Group Config + + For now I set up 3 groups with the following names. CAP_A[1]1_Rota1, CAP_A[1]1_Rota1-1, + CAP_B[1]1_Rota1
+ The first two are marked with A and will therefore be primary CAP units. They will be + scheduled and make up for the total count.
+ Meaning that for this airbase there is 2 CAP units max at a time flying out.
+ In this case all groups have [1]1 in the name, (This would be the same as [1]A) which means + that when stage 1 is active the groups will activate and fly out to stage 1. + + I also set up a few groups further back. One example: CAP_A[1-3]3_Group1. This group will + protect zone 3 when zones 1 through 3 are active. + + CAP units fly out, fly their CAP zone for x amount of minutes and will then RTB.
+ Before they actually RTB an event is triggered 10 minutes before the actual RTB task. This event + will trigger a backup unit to startup and fly out to take over.
+

+ + + You can start a mission, speed up the simulation and make the CAP fly out to see what happens + + +

Creating CAP Routes

+

+ Creating CAP routes is not needed per se, but with a multi-stage stage (we have 2 stages with _1_) it is recommended.
+ Similarly with huge stages.
+ If there is multiple zones it will "round-robin" over them.
+

+

+ If no CAP route is present the unit will fly a route generated differently per zone:
+ quad zone => race-track between the corner closest to the origin airbase to the center point of the zone
+ circle zone => race-track between the closest point on circle to the origin airbase to the center
+

+

+ If you want to create your own CAP Routes you can!
+ For this example I created 2 CAP routes inside of the 2 _1_ stages.
+

+

+ As you can see below there's a nice feature you can exploit. As long as the X of the zone is inside of the the CAPROUTE will be used for that stage! +

+ CAP Routes Image + +

+ Well, nice, we're done setting up the initial CAP effort.
+ If you want to change values for the CAP routes please read about how to configure it here: Cap Config +

+ +

Mission Briefings

+

+ So now we've created some missions we also want to add briefings to them. This is pretty easy with Spearhead.
+

+

+ To do so click on draw on the left hand pane in the mission editor. This opens up the drawing tools in the editor.
+ On the right click TextBox and click somewhere inside the zone to which you want to add the briefing.
+ Give the briefing a name (It's not used, but can be nice to use to reference the briefing later) and add the briefing.
+ The text box is quite small, but can have a lot of text. Easiest is to edit the text in an editor of choice and paste it into the box afterwards.
+

+

+ Keep the binding layer to "Author" only. That way it doesn't show up for anyone other than in the mission editor. +

+

+ See the two images below. The left shows the Text Box drawing. The right shows the briefing as shown in the mission. +

+
+
+ +
+
+ +
+
+

+ We are now done creating a first mission. Hit fly and test it.
+ Check all references for way more features and keep up to date with the latest changes as they come along!
+

+ +
+
+
+
+

© 2025 Spearhead Project

+
+ + + \ No newline at end of file diff --git a/_docs/pages/include-script.html b/_docs/pages/include-script.html new file mode 100644 index 0000000..1a61e96 --- /dev/null +++ b/_docs/pages/include-script.html @@ -0,0 +1,69 @@ + + + + + + + Spearhead Tutorials + + + + + + + + + + + +
+ +
+
+ + +
+

Include the Spearhead Script

+ +

Include the Script

+

+ Download the latest version of Spearhead. You can choose which version you feel comfortable with.
+ Versions that end in -rc are not stable. Versions that do not have the "release candidate" (rc) tag are.
+ Stable does not mean bug-free, so if you find any issues let us know! +
+ Here is the latest stable version: + +

+ +

+ Then run the script in the mission.
+ Please do exactly as it's done below.
+

+
+ +
+ + + Spearhead does not require any dependencies (eg. MIST or MOOSE). Compatibility with other frameworks is + not tested at this time, so cannot be guaranteed, but there should be no conflicts if they are not + controlling the same units. + + + + +
+
+
+
+

© 2025 Spearhead Project

+
+ + + \ No newline at end of file diff --git a/_docs/pages/tutorials.html b/_docs/pages/tutorials.html index 6f78322..7ae7177 100644 --- a/_docs/pages/tutorials.html +++ b/_docs/pages/tutorials.html @@ -66,24 +66,23 @@ These are logically ordered zones that will activate one by one based on the mission status in them.
There is a little more to it, but you'll find out.
- Stages need to be named according to the convention: MISSIONSTAGE_[OrderNumber]_[FreeForm]
- The first stage will be called MISSIONSTAGE_1 or MISSIONSTAGE_1_EAST for example.
+ Stages need to be named according to the convention: MISSIONSTAGE_[OrderNumber]_[FreeForm]
+ The first stage will be called MISSIONSTAGE_1 or MISSIONSTAGE_1_EAST for example.


- Stages are divided in primary stages: MISSIONSTAGE_[number]
- or secondary stages: MISSIONSTAGE_x[number]
+ Stages are divided in primary stages: MISSIONSTAGE_[number]
+ or secondary stages: MISSIONSTAGE_x[number]
Note the x before the number. This marks it as "extra"

- For this mission we started with the three stages: MISSIONSTAGE_1_GROUND, - MISSIONSTAGE_2_WATER and MISSIONSTAGE_1_AIRBASE as you see in the image. + For this mission we started with the three stages: MISSIONSTAGE_1_GROUND, + MISSIONSTAGE_2_WATER and MISSIONSTAGE_1_AIRBASE as you see in the image.

- MISSIONSTAGE_1_GROUND will be activated when the mission starts
- MISSIONSTAGE_1_WATER will be also be activated
- MISSIONSTAGE_2_AIRBASE will be activated when the player enters the zone. + MISSIONSTAGE_1_GROUNDwill be activated when the mission starts
+ MISSIONSTAGE_1_WATER will be also be activated
+ MISSIONSTAGE_2_AIRBASE will be activated when the player enters the zone.

@@ -94,20 +93,19 @@ quite the challenge.
With the CAP managers we've tried to make this a lot easier.
- A CAP group needs to follow the following naming convention: CAP_[A|B][CONFIG]_[Free Form] + A CAP group needs to follow the following naming convention: CAP_[A|B][CONFIG]_[Free Form] - For details on config read this: CAP Group Config + For details on config read this: CAP Group Config - For now I set up 3 groups with the following names. CAP_A[1]1_Rota1, CAP_A[1]1_Rota1-1, - CAP_B[1]1_Rota1
- The first two are marked with A and will therefore be primary CAP units. They will be + For now I set up 3 groups with the following names. CAP_A[1]1_Rota1, CAP_A[1]1_Rota1-1, + CAP_B[1]1_Rota1
+ The first two are marked with A and will therefore be primary CAP units. They will be scheduled and make up for the total count.
Meaning that for this airbase there is 2 CAP units max at a time flying out.
- In this case all groups have [1]1 in the name, (This would be the same as [1]A) which means + In this case all groups have [1]1 in the name, (This would be the same as [1]A) which means that when stage 1 is active the groups will activate and fly out to stage 1. - I also set up a few groups further back. One example: CAP_A[1-3]3_Group1. This group will + I also set up a few groups further back. One example: CAP_A[1-3]3_Group1. This group will protect zone 3 when zones 1 through 3 are active. CAP units fly out, fly their CAP zone for x amount of minutes and will then RTB.
@@ -121,21 +119,21 @@

Creating CAP Routes

- Creating CAP routes is not needed per se, but with a multi-stage stage (we have 2 stages with _1_) it is recommended.
+ Creating CAP routes is not needed per se, but with a multi-stage stage (we have 2 stages with _1_) it is recommended.
Similarly with huge stages.
If there is multiple zones it will "round-robin" over them.

If no CAP route is present the unit will fly a route generated differently per zone:
- quad zone => race-track between the corner closest to the origin airbase to the center point of the zone
- circle zone => race-track between the closest point on circle to the origin airbase to the center
+ quad zone => race-track between the corner closest to the origin airbase to the center point of the zone
+ circle zone => race-track between the closest point on circle to the origin airbase to the center

If you want to create your own CAP Routes you can!
- For this example I created 2 CAP routes inside of the 2 _1_ stages.
+ For this example I created 2 CAP routes inside of the 2 _1_ stages.

- As you can see below there's a nice feature you can exploit. As long as the X of the zone is inside of the the CAPROUTE will be used for that stage! + As you can see below there's a nice feature you can exploit. As long as the X of the zone is inside of the the CAPROUTE will be used for that stage!

CAP Routes Image @@ -155,7 +153,7 @@ By default DCS will always keep 1 static object in 1 groups.

- For this example I'll set up two missions. The first one is DEAD mission and will consist of an SA-2 site with an additional "control center". + For this example I'll set up two missions. The first one is DEAD mission and will consist of an SA-2 site with an additional "control center".

Mission: DEAD

@@ -173,32 +171,32 @@

- Important to note. It's all inside the triggerzone MISSION_DEAD_BYRON. Which means it's a MISSION of type DEAD and with name BYRON.
+ Important to note. It's all inside the triggerzone MISSION_DEAD_BYRON. Which means it's a MISSION of type DEAD and with name BYRON.
At the start Spearhead will detect the triggerzone, take all units and despawn them and only spawn when needed for better performance.

The current list of mission types are: - DEAD - STRIKE - BAI - SAM
+ DEAD + STRIKE + BAI + SAM
For specific differences, please check the reference page.

Each type has some additional completion logic to it.
- DEAD and SAM missions will be marked complete when all air defences are destroyed. This includes Tracking Radars, Self tracking launchers and AAA guns if they are inside the zone.
- If you want to add the Search radar or another random unit like the command tent to the target list you can add a TGT_ prefix to the unit or group you want destroyed.
- Please be aware that adding TGT_ to a group will make the entire group a target and therefore each unit needs to be destroyed.
+ DEAD and SAM missions will be marked complete when all air defences are destroyed. This includes Tracking Radars, Self tracking launchers and AAA guns if they are inside the zone.
+ If you want to add the Search radar or another random unit like the command tent to the target list you can add a TGT_ prefix to the unit or group you want destroyed.
+ Please be aware that adding TGT_ to a group will make the entire group a target and therefore each unit needs to be destroyed.

Mission: STRIKE

- To show the power of TGT_ targets I'll create a strike mission next. + To show the power of TGT_ targets I'll create a strike mission next.

A nice supply strike mission will do. Add a ship, some containers and some additional units.
Even some SHORADS to spice the whole thing up.
- In the picture below all units that are selected and who show up as white (they are actually red) have the prefix TGT_ in front of their name.
+ In the picture below all units that are selected and who show up as white (they are actually red) have the prefix TGT_ in front of their name.

@@ -261,8 +259,8 @@ So now we've created some missions we also want to add briefings to them. This is pretty easy with Spearhead.

- To do so click on draw on the left hand pane in the mission editor. This opens up the drawing tools in the editor.
- On the right click TextBox and click somewhere inside the zone to which you want to add the briefing.
+ To do so click on draw on the left hand pane in the mission editor. This opens up the drawing tools in the editor.
+ On the right click TextBox and click somewhere inside the zone to which you want to add the briefing.
Give the briefing a name (It's not used, but can be nice to use to reference the briefing later) and add the briefing.
The text box is quite small, but can have a lot of text. Easiest is to edit the text in an editor of choice and paste it into the box afterwards.

@@ -270,7 +268,7 @@ Keep the binding layer to "Author" only. That way it doesn't show up for anyone other than in the mission editor.

- See the two images below. The left shows the Text Box drawing. The right shows the briefing as shown in the mission. + See the two images below. The left shows the Text Box drawing. The right shows the briefing as shown in the mission.

diff --git a/classes/capClasses/airGroups/InterceptGroup.lua b/classes/capClasses/airGroups/InterceptGroup.lua index ff4dfde..5c99313 100644 --- a/classes/capClasses/airGroups/InterceptGroup.lua +++ b/classes/capClasses/airGroups/InterceptGroup.lua @@ -80,7 +80,9 @@ function InterceptGroup:SetTargetUnits(unitNames) self:UpdateTask() if self._updateTaskID then - timer.removeFunction(self._updateTaskID) + pcall(function() + timer.removeFunction(self._updateTaskID) + end) end local updateContinous = function(selfA, time) diff --git a/logistics.excalidraw.json b/logistics.excalidraw.json index c0c6fc2..bd35053 100644 --- a/logistics.excalidraw.json +++ b/logistics.excalidraw.json @@ -11,7 +11,7 @@ "width": 554, "height": 485, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -25,11 +25,11 @@ "type": 3 }, "seed": 1480715984, - "version": 1017, - "versionNonce": 2014953008, + "version": 1024, + "versionNonce": 1212475525, "isDeleted": false, "boundElements": [], - "updated": 1765720175528, + "updated": 1766946988046, "link": null, "locked": false }, @@ -41,7 +41,7 @@ "width": 341.951904296875, "height": 64.99999999999996, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -53,11 +53,11 @@ "index": "a4", "roundness": null, "seed": 452506832, - "version": 424, - "versionNonce": 1296054361, + "version": 431, + "versionNonce": 2034208331, "isDeleted": false, "boundElements": [], - "updated": 1766420008704, + "updated": 1766946988046, "link": null, "locked": false, "text": "FARP/Airport", @@ -78,7 +78,7 @@ "width": 394, "height": 341.99999999999994, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -94,11 +94,11 @@ "type": 3 }, "seed": 1317450800, - "version": 1256, - "versionNonce": 344655191, + "version": 1263, + "versionNonce": 1728024549, "isDeleted": false, "boundElements": [], - "updated": 1766420012082, + "updated": 1766946988046, "link": null, "locked": false }, @@ -110,7 +110,7 @@ "width": 309.34393310546875, "height": 58.393233388830446, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -124,11 +124,11 @@ "index": "a4V", "roundness": null, "seed": 143418064, - "version": 778, - "versionNonce": 1775125111, + "version": 785, + "versionNonce": 913149163, "isDeleted": false, "boundElements": [], - "updated": 1766420012082, + "updated": 1766946988046, "link": null, "locked": false, "text": "Supply Source", @@ -149,7 +149,7 @@ "width": 300.519775390625, "height": 25, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -163,11 +163,11 @@ "index": "a5", "roundness": null, "seed": 35678256, - "version": 454, - "versionNonce": 1777130391, + "version": 461, + "versionNonce": 1521371973, "isDeleted": false, "boundElements": [], - "updated": 1766420012082, + "updated": 1766946988046, "link": null, "locked": false, "text": "zone: SupplySource_", @@ -188,7 +188,7 @@ "width": 149.69984436035156, "height": 25, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -202,11 +202,11 @@ "index": "a6", "roundness": null, "seed": 1659672624, - "version": 459, - "versionNonce": 1494962359, + "version": 466, + "versionNonce": 648198027, "isDeleted": false, "boundElements": [], - "updated": 1766420012082, + "updated": 1766946988046, "link": null, "locked": false, "text": "infinite supplies", @@ -227,7 +227,7 @@ "width": 423.6798095703125, "height": 250, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -239,11 +239,11 @@ "index": "aW", "roundness": null, "seed": 1391094320, - "version": 722, - "versionNonce": 1614171696, + "version": 729, + "versionNonce": 330209957, "isDeleted": false, "boundElements": [], - "updated": 1765721008728, + "updated": 1766946988046, "link": null, "locked": false, "text": "zone: FARP_\n\nparameters:\n - starting_supplies: [0, N, -1]\n 0 : 0 of everything.\n N: N of everything\n -1 : custom\n with custom, it will not edit the \n values at all\n - buildable: true", @@ -259,13 +259,13 @@ { "id": "DtDKPiLaL2Q0WeDDYfCp-", "type": "rectangle", - "x": 997.5804856856632, - "y": 356.68832461141733, + "x": 997.9266185508404, + "y": 357.0344574765945, "width": 381.9999999999999, "height": 271.3179487179487, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffffff", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -280,24 +280,24 @@ "type": 3 }, "seed": 1395091152, - "version": 673, - "versionNonce": 18046000, + "version": 683, + "versionNonce": 941198891, "isDeleted": false, "boundElements": [], - "updated": 1765721498062, + "updated": 1766946988046, "link": null, "locked": false }, { "id": "FMerdoUgOk5ZOgOWbZN5g", "type": "text", - "x": 1008.2676651728427, - "y": 370.23704256013525, + "x": 1008.6137980380199, + "y": 370.58317542531245, "width": 279.8335150928985, "height": 54.23134627794711, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#aaaaaa", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -310,11 +310,11 @@ "index": "aY", "roundness": null, "seed": 809733328, - "version": 701, - "versionNonce": 1220040240, + "version": 711, + "versionNonce": 447610373, "isDeleted": false, "boundElements": [], - "updated": 1765721498062, + "updated": 1766946988046, "link": null, "locked": false, "text": "Supply Config", @@ -335,7 +335,7 @@ "width": 442.6197204589844, "height": 100, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -347,11 +347,11 @@ "index": "ab", "roundness": null, "seed": 1323777584, - "version": 456, - "versionNonce": 1562782768, + "version": 463, + "versionNonce": 767332555, "isDeleted": false, "boundElements": [], - "updated": 1765720175528, + "updated": 1766946988046, "link": null, "locked": false, "text": "properties: \n personel: count of personel that's present\n supplies:\n warehouse data", @@ -367,12 +367,12 @@ { "id": "WSHKuarAUQAeV8PmQ39m7", "type": "text", - "x": 1014.5804856856632, - "y": 444.84729897039165, + "x": 1014.9266185508404, + "y": 445.19343183556884, "width": 132.0598907470703, "height": 25, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -386,11 +386,11 @@ "index": "ad", "roundness": null, "seed": 2031607504, - "version": 374, - "versionNonce": 95293488, + "version": 384, + "versionNonce": 111801701, "isDeleted": false, "boundElements": [], - "updated": 1765721498062, + "updated": 1766946988046, "link": null, "locked": false, "text": "enabled: false", @@ -411,8 +411,8 @@ "width": 397.5, "height": 85, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -425,8 +425,8 @@ "type": 3 }, "seed": 1024526896, - "version": 1070, - "versionNonce": 385848016, + "version": 1084, + "versionNonce": 153501547, "isDeleted": false, "boundElements": [ { @@ -442,7 +442,7 @@ "type": "arrow" } ], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false }, @@ -454,7 +454,7 @@ "width": 250.47979736328125, "height": 25, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -466,11 +466,11 @@ "index": "ai", "roundness": null, "seed": 1937723600, - "version": 905, - "versionNonce": 1294252240, + "version": 910, + "versionNonce": 854589637, "isDeleted": false, "boundElements": [], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false, "text": "Personel (non-parachute)", @@ -491,8 +491,8 @@ "width": 397.5, "height": 85, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -505,8 +505,8 @@ "type": 3 }, "seed": 1515529936, - "version": 1184, - "versionNonce": 695708368, + "version": 1200, + "versionNonce": 882973195, "isDeleted": false, "boundElements": [ { @@ -526,7 +526,7 @@ "type": "arrow" } ], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false }, @@ -538,7 +538,7 @@ "width": 158.0198516845703, "height": 25, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -550,11 +550,11 @@ "index": "ao", "roundness": null, "seed": 950521040, - "version": 1035, - "versionNonce": 148492496, + "version": 1040, + "versionNonce": 891133989, "isDeleted": false, "boundElements": [], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false, "text": "Building Supplies", @@ -575,8 +575,8 @@ "width": 152.5, "height": 85, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -589,8 +589,8 @@ "type": 3 }, "seed": 1197196496, - "version": 1389, - "versionNonce": 356706512, + "version": 1401, + "versionNonce": 1831313579, "isDeleted": false, "boundElements": [ { @@ -602,7 +602,7 @@ "type": "arrow" } ], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false }, @@ -614,7 +614,7 @@ "width": 74.15992736816406, "height": 25, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -626,11 +626,11 @@ "index": "aq", "roundness": null, "seed": 1193978576, - "version": 1262, - "versionNonce": 344805072, + "version": 1267, + "versionNonce": 320885637, "isDeleted": false, "boundElements": [], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false, "text": "Building", @@ -651,8 +651,8 @@ "width": 152.5, "height": 85, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -665,8 +665,8 @@ "type": 3 }, "seed": 1031329488, - "version": 1429, - "versionNonce": 436460240, + "version": 1441, + "versionNonce": 1818514251, "isDeleted": false, "boundElements": [ { @@ -678,7 +678,7 @@ "type": "arrow" } ], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false }, @@ -690,7 +690,7 @@ "width": 41.27996826171875, "height": 25, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -702,11 +702,11 @@ "index": "as", "roundness": null, "seed": 1540717776, - "version": 1305, - "versionNonce": 2019557584, + "version": 1310, + "versionNonce": 959814373, "isDeleted": false, "boundElements": [], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false, "text": "SAM", @@ -722,13 +722,13 @@ { "id": "wrB_6hD7EV4pSGq0qqfjQ", "type": "arrow", - "x": 1409.240567948737, - "y": 1324.3196729421625, - "width": 92.219084996839, - "height": 83.74363336037413, + "x": 1406.4048534026933, + "y": 1325.269571084001, + "width": 95.28701921130005, + "height": 86.600855285624, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -741,11 +741,11 @@ "type": 2 }, "seed": 798166736, - "version": 339, - "versionNonce": 1358406704, + "version": 359, + "versionNonce": 1387377131, "isDeleted": false, "boundElements": [], - "updated": 1765721488374, + "updated": 1766946988046, "link": null, "locked": false, "points": [ @@ -754,12 +754,12 @@ 0 ], [ - 85.51062563059008, - -28.64406239594541 + 88.3463401766337, + -29.593960537783914 ], [ - 92.219084996839, - -83.74363336037413 + 95.28701921130005, + -86.600855285624 ] ], "lastCommittedPoint": null, @@ -780,13 +780,13 @@ { "id": "LWI3DyrIZVDnfHpf-V6QQ", "type": "arrow", - "x": 1609.591994450547, - "y": 1340.0346411199139, - "width": 107.4085983347004, - "height": 98.7481736148759, + "x": 1611.655773091994, + "y": 1341.6290841617251, + "width": 111.85645031847594, + "height": 102.7975050252985, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -799,11 +799,11 @@ "type": 2 }, "seed": 1274830544, - "version": 339, - "versionNonce": 1327519792, + "version": 359, + "versionNonce": 803927621, "isDeleted": false, "boundElements": [], - "updated": 1765721488374, + "updated": 1766946988046, "link": null, "locked": false, "points": [ @@ -812,12 +812,12 @@ 0 ], [ - -46.090800871219926, - -35.60903057369683 + -48.15457951266694, + -37.203473615508074 ], [ - -107.4085983347004, - -98.7481736148759 + -111.85645031847594, + -102.7975050252985 ] ], "lastCommittedPoint": null, @@ -843,8 +843,8 @@ "width": 397.5, "height": 85, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -857,8 +857,8 @@ "type": 3 }, "seed": 1481531440, - "version": 1140, - "versionNonce": 942741712, + "version": 1152, + "versionNonce": 1360673931, "isDeleted": false, "boundElements": [ { @@ -870,7 +870,7 @@ "type": "arrow" } ], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false }, @@ -882,7 +882,7 @@ "width": 183.35986328125, "height": 50, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -894,11 +894,11 @@ "index": "ay", "roundness": null, "seed": 166910512, - "version": 1040, - "versionNonce": 439713488, + "version": 1045, + "versionNonce": 303949221, "isDeleted": false, "boundElements": [], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false, "text": "Vehicles (combat)\nAirdropped/hooked", @@ -919,8 +919,8 @@ "width": 467.66813284728687, "height": 94.76433218221348, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -933,8 +933,8 @@ "type": 3 }, "seed": 1272358448, - "version": 383, - "versionNonce": 392170192, + "version": 395, + "versionNonce": 324563755, "isDeleted": false, "boundElements": [ { @@ -946,7 +946,7 @@ "type": "arrow" } ], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false }, @@ -954,11 +954,11 @@ "id": "YoGO_zMl8gnmdbjNU8WRX", "type": "text", "x": 2004.9700858542255, - "y": 686.5203351350609, + "y": 686.520335135061, "width": 408.6596984863281, "height": 50, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -970,11 +970,11 @@ "index": "b01V", "roundness": null, "seed": 1099605040, - "version": 358, - "versionNonce": 1521889488, + "version": 364, + "versionNonce": 1123805445, "isDeleted": false, "boundElements": [], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false, "text": "- Need to be present before Building/SAM\nsupplies can be built", @@ -990,13 +990,13 @@ { "id": "friWeGi7sPIQahwgVX-At", "type": "arrow", - "x": 1704.6818063773258, - "y": 843.848762627295, - "width": 263.5830926984811, - "height": 109.48463994284509, + "x": 1704.653672752891, + "y": 843.8756730506669, + "width": 263.78905202728697, + "height": 109.53965287403707, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -1009,11 +1009,11 @@ "type": 2 }, "seed": 1215858384, - "version": 480, - "versionNonce": 967576624, + "version": 500, + "versionNonce": 1775687115, "isDeleted": false, "boundElements": [], - "updated": 1765721488374, + "updated": 1766946988046, "link": null, "locked": false, "points": [ @@ -1022,12 +1022,12 @@ 0 ], [ - 84.94751477026193, - -81.25414456285944 + 84.97564839469669, + -81.28105498623142 ], [ - 263.5830926984811, - -109.48463994284509 + 263.78905202728697, + -109.53965287403707 ] ], "lastCommittedPoint": null, @@ -1053,8 +1053,8 @@ "width": 467.66813284728687, "height": 94.76433218221348, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -1067,8 +1067,8 @@ "type": 3 }, "seed": 824426192, - "version": 533, - "versionNonce": 433857232, + "version": 545, + "versionNonce": 1677885541, "isDeleted": false, "boundElements": [ { @@ -1080,7 +1080,7 @@ "type": "arrow" } ], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false }, @@ -1088,11 +1088,11 @@ "id": "ufwkOxMNnAqwVSfGKKSfm", "type": "text", "x": 1995.7579342259187, - "y": 808.9755436042846, + "y": 808.9755436042844, "width": 419.69976806640625, "height": 50, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -1104,11 +1104,11 @@ "index": "b06", "roundness": null, "seed": 1776510160, - "version": 578, - "versionNonce": 1710180560, + "version": 584, + "versionNonce": 867165291, "isDeleted": false, "boundElements": [], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false, "text": "- Need to be near a combar vehicle to take\ncontrol", @@ -1124,13 +1124,13 @@ { "id": "SIAZ8tRfcPQfkxEtTJ2xC", "type": "arrow", - "x": 1705.847901982884, - "y": 860.1018875115827, - "width": 260.19318018001945, - "height": 36.639642343258515, + "x": 1705.7906379325166, + "y": 860.083956344296, + "width": 260.2756130298842, + "height": 36.65757351054526, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -1143,11 +1143,11 @@ "type": 2 }, "seed": 1250889264, - "version": 327, - "versionNonce": 333103152, + "version": 347, + "versionNonce": 184495045, "isDeleted": false, "boundElements": [], - "updated": 1765721488374, + "updated": 1766946988046, "link": null, "locked": false, "points": [ @@ -1156,12 +1156,12 @@ 0 ], [ - 117.01047070911613, - 36.639642343258515 + 117.06773475948353, + 36.65757351054526 ], [ - 260.19318018001945, - 3.7197992275551996 + 260.2756130298842, + 3.731705812781456 ] ], "lastCommittedPoint": null, @@ -1187,8 +1187,8 @@ "width": 467.66813284728687, "height": 94.76433218221348, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -1201,8 +1201,8 @@ "type": 3 }, "seed": 730740944, - "version": 634, - "versionNonce": 1368144592, + "version": 646, + "versionNonce": 145628939, "isDeleted": false, "boundElements": [ { @@ -1214,7 +1214,7 @@ "type": "arrow" } ], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false }, @@ -1222,11 +1222,11 @@ "id": "yBQwlGJFwzUDGvyPgUngu", "type": "text", "x": 2083.2142591059564, - "y": 990.0822125518592, + "y": 990.0822125518594, "width": 266.9398193359375, "height": 25, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -1238,11 +1238,11 @@ "index": "b0A", "roundness": null, "seed": 1693359824, - "version": 743, - "versionNonce": 1608278224, + "version": 749, + "versionNonce": 940534565, "isDeleted": false, "boundElements": [], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false, "text": "- Way to help combat areas", @@ -1258,13 +1258,13 @@ { "id": "Z4_dIyJNDD478UnIkY4j6", "type": "arrow", - "x": 1713.096802289363, - "y": 1011.1276234402895, - "width": 268.5480971859761, - "height": 38.22140239629812, + "x": 1712.8760228004337, + "y": 1011.0605237916935, + "width": 268.79383080249, + "height": 38.28850204489413, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -1277,11 +1277,11 @@ "type": 2 }, "seed": 1837444656, - "version": 322, - "versionNonce": 283931696, + "version": 342, + "versionNonce": 592184747, "isDeleted": false, "boundElements": [], - "updated": 1765721488374, + "updated": 1766946988046, "link": null, "locked": false, "points": [ @@ -1290,12 +1290,12 @@ 0 ], [ - 125.76074336846523, - 38.22140239629812 + 125.98152285739457, + 38.28850204489413 ], [ - 268.5480971859761, - 3.5087727384345726 + 268.79383080249, + 3.569805859665621 ] ], "lastCommittedPoint": null, @@ -1321,8 +1321,8 @@ "width": 467.66813284728687, "height": 94.76433218221348, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -1335,8 +1335,8 @@ "type": 3 }, "seed": 1510586064, - "version": 694, - "versionNonce": 74470096, + "version": 706, + "versionNonce": 195083909, "isDeleted": false, "boundElements": [ { @@ -1348,7 +1348,7 @@ "type": "arrow" } ], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false }, @@ -1360,7 +1360,7 @@ "width": 184.6798553466797, "height": 25, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -1372,11 +1372,11 @@ "index": "b0F", "roundness": null, "seed": 1934253264, - "version": 846, - "versionNonce": 1316845776, + "version": 851, + "versionNonce": 179574859, "isDeleted": false, "boundElements": [], - "updated": 1765721488258, + "updated": 1766946988046, "link": null, "locked": false, "text": "- Buildable Objects", @@ -1392,13 +1392,13 @@ { "id": "lH2yFDahZXKw9rAa3QUAw", "type": "arrow", - "x": 1691.172872127106, - "y": 1209.3407554948699, - "width": 273.2166460318358, - "height": 23.383406642364207, + "x": 1692.8079512709355, + "y": 1209.562659092961, + "width": 271.9282167137478, + "height": 23.161503044273104, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -1411,11 +1411,11 @@ "type": 2 }, "seed": 1867445296, - "version": 297, - "versionNonce": 337717296, + "version": 317, + "versionNonce": 1014015461, "isDeleted": false, "boundElements": [], - "updated": 1765721488374, + "updated": 1766946988046, "link": null, "locked": false, "points": [ @@ -1424,12 +1424,12 @@ 0 ], [ - 172.29878578584248, - 23.383406642364207 + 170.66370664201304, + 23.161503044273104 ], [ - 273.2166460318358, - 18.46058419134033 + 271.9282167137478, + 18.221770845652145 ] ], "lastCommittedPoint": null, @@ -1455,8 +1455,8 @@ "width": 152.5, "height": 85, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -1469,8 +1469,8 @@ "type": 3 }, "seed": 265134640, - "version": 1526, - "versionNonce": 1976361623, + "version": 1538, + "versionNonce": 2093012715, "isDeleted": false, "boundElements": [ { @@ -1482,7 +1482,7 @@ "type": "arrow" } ], - "updated": 1766426955530, + "updated": 1766946988046, "link": null, "locked": false }, @@ -1494,7 +1494,7 @@ "width": 39.41996765136719, "height": 25, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -1506,11 +1506,11 @@ "index": "b0J", "roundness": null, "seed": 1413078064, - "version": 1402, - "versionNonce": 425883575, + "version": 1407, + "versionNonce": 1301061957, "isDeleted": false, "boundElements": [], - "updated": 1766426955530, + "updated": 1766946988046, "link": null, "locked": false, "text": "Fuel", @@ -1531,8 +1531,8 @@ "width": 152.5, "height": 85, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -1545,8 +1545,8 @@ "type": 3 }, "seed": 166855888, - "version": 1481, - "versionNonce": 2056467959, + "version": 1493, + "versionNonce": 402440587, "isDeleted": false, "boundElements": [ { @@ -1558,7 +1558,7 @@ "type": "arrow" } ], - "updated": 1766426955530, + "updated": 1766946988046, "link": null, "locked": false }, @@ -1570,7 +1570,7 @@ "width": 104.85992431640625, "height": 25, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -1582,11 +1582,11 @@ "index": "b0L", "roundness": null, "seed": 1559816912, - "version": 1367, - "versionNonce": 214392599, + "version": 1372, + "versionNonce": 72495269, "isDeleted": false, "boundElements": [], - "updated": 1766426955530, + "updated": 1766946988046, "link": null, "locked": false, "text": "Ammunition", @@ -1607,8 +1607,8 @@ "width": 152.5, "height": 137.92034134850883, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -1621,8 +1621,8 @@ "type": 3 }, "seed": 2087552720, - "version": 1681, - "versionNonce": 313378135, + "version": 1695, + "versionNonce": 1168267307, "isDeleted": false, "boundElements": [ { @@ -1638,7 +1638,7 @@ "type": "arrow" } ], - "updated": 1766426955530, + "updated": 1766946988046, "link": null, "locked": false }, @@ -1650,7 +1650,7 @@ "width": 76.67991638183594, "height": 50, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -1662,11 +1662,11 @@ "index": "b0N", "roundness": null, "seed": 951332048, - "version": 1574, - "versionNonce": 362349175, + "version": 1579, + "versionNonce": 510579717, "isDeleted": false, "boundElements": [], - "updated": 1766426955530, + "updated": 1766946988046, "link": null, "locked": false, "text": "Aircraft\nSupplies", @@ -1682,13 +1682,13 @@ { "id": "xhjb3vIieswY23YvjCPkp", "type": "arrow", - "x": 1566.9870571973058, - "y": 1515.955841684358, - "width": 115.10424663379717, - "height": 52.21323313589551, + "x": 1567.1189914652791, + "y": 1515.8397395285408, + "width": 116.43038596474639, + "height": 52.32933529171282, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -1701,11 +1701,11 @@ "type": 2 }, "seed": 1304948432, - "version": 284, - "versionNonce": 606229945, + "version": 304, + "versionNonce": 1671174859, "isDeleted": false, "boundElements": [], - "updated": 1766426955849, + "updated": 1766946988046, "link": null, "locked": false, "points": [ @@ -1714,12 +1714,12 @@ 0 ], [ - -59.33321947260902, - 52.21323313589551 + -59.46515374058254, + 52.32933529171282 ], [ - -115.10424663379717, - 43.56056301782428 + -116.43038596474639, + 43.49138864227348 ] ], "lastCommittedPoint": null, @@ -1740,13 +1740,13 @@ { "id": "AJ_nwcqbc4EOpn7vi5Zxi", "type": "arrow", - "x": 1561.4369504095503, - "y": 1623.6786453700292, - "width": 107.04846767032836, - "height": 29.669113735771816, + "x": 1562.1710882575976, + "y": 1623.7874065327026, + "width": 109.08273568264872, + "height": 30.42244824098634, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -1759,11 +1759,11 @@ "type": 2 }, "seed": 2035722448, - "version": 288, - "versionNonce": 1481702265, + "version": 308, + "versionNonce": 1205865317, "isDeleted": false, "boundElements": [], - "updated": 1766426955849, + "updated": 1766946988046, "link": null, "locked": false, "points": [ @@ -1772,12 +1772,12 @@ 0 ], [ - -67.32087442516945, - -9.973462877803058 + -68.05501227321679, + -10.08222404047683 ], [ - -107.04846767032836, - -29.669113735771816 + -109.08273568264872, + -30.42244824098634 ] ], "lastCommittedPoint": null, @@ -1803,8 +1803,8 @@ "width": 152.5, "height": 137.92034134850883, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -1817,8 +1817,8 @@ "type": 3 }, "seed": 1525385264, - "version": 1857, - "versionNonce": 71463415, + "version": 1869, + "versionNonce": 780437867, "isDeleted": false, "boundElements": [ { @@ -1830,7 +1830,7 @@ "type": "arrow" } ], - "updated": 1766426945444, + "updated": 1766946988046, "link": null, "locked": false }, @@ -1842,7 +1842,7 @@ "width": 76.67991638183594, "height": 25, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -1854,11 +1854,11 @@ "index": "b0T", "roundness": null, "seed": 246088240, - "version": 1759, - "versionNonce": 355408663, + "version": 1764, + "versionNonce": 1358124741, "isDeleted": false, "boundElements": [], - "updated": 1766426945444, + "updated": 1766946988046, "link": null, "locked": false, "text": "Aircraft", @@ -1879,8 +1879,8 @@ "width": 467.66813284728687, "height": 94.76433218221348, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -1893,8 +1893,8 @@ "type": 3 }, "seed": 1545761840, - "version": 1317, - "versionNonce": 2127849623, + "version": 1329, + "versionNonce": 1806050315, "isDeleted": false, "boundElements": [ { @@ -1906,7 +1906,7 @@ "type": "arrow" } ], - "updated": 1766426947326, + "updated": 1766946988046, "link": null, "locked": false }, @@ -1918,7 +1918,7 @@ "width": 433.21966552734375, "height": 50, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -1930,11 +1930,11 @@ "index": "b0V", "roundness": null, "seed": 572812848, - "version": 1572, - "versionNonce": 1669938615, + "version": 1577, + "versionNonce": 608581157, "isDeleted": false, "boundElements": [], - "updated": 1766426947326, + "updated": 1766946988046, "link": null, "locked": false, "text": "- Aircraft themselves being a supply as well. \nCan be ferried, or need to be transported", @@ -1950,13 +1950,13 @@ { "id": "CbVvVoAj02v9gdNPAsIjd", "type": "arrow", - "x": 1790.8169109614512, - "y": 1832.2700527285067, - "width": 324.1795725755244, - "height": 25.16864349182174, + "x": 1790.8594982619086, + "y": 1832.2638488278365, + "width": 326.49225756042483, + "height": 25.174847392491984, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dotted", @@ -1969,11 +1969,11 @@ "type": 2 }, "seed": 1706006736, - "version": 901, - "versionNonce": 488437239, + "version": 921, + "versionNonce": 1392208555, "isDeleted": false, "boundElements": [], - "updated": 1766426950017, + "updated": 1766946988046, "link": null, "locked": false, "points": [ @@ -1982,12 +1982,12 @@ 0 ], [ - -172.77268600944308, - 25.16864349182174 + -172.81527330990048, + 25.174847392491984 ], [ - -324.1795725755244, - 4.382956202619425 + -326.49225756042483, + 4.077512808322126 ] ], "lastCommittedPoint": null, @@ -2013,8 +2013,8 @@ "width": 240.03826187159132, "height": 139.14797614727104, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -2027,8 +2027,8 @@ "type": 3 }, "seed": 785304345, - "version": 261, - "versionNonce": 1677916375, + "version": 316, + "versionNonce": 866049547, "isDeleted": false, "boundElements": [ { @@ -2048,19 +2048,19 @@ "type": "arrow" } ], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false }, { "id": "M8a6CqhTfztmTCBCvumbo", "type": "text", - "x": 26.331587243064405, + "x": 26.331587243064348, "y": 2314.586959105899, "width": 182.699951171875, "height": 45, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -2072,11 +2072,11 @@ "index": "b0Y", "roundness": null, "seed": 691753783, - "version": 190, - "versionNonce": 2096874999, + "version": 235, + "versionNonce": 13507621, "isDeleted": false, - "boundElements": null, - "updated": 1766427026580, + "boundElements": [], + "updated": 1766946963488, "link": null, "locked": false, "text": "Supply Hub", @@ -2097,8 +2097,8 @@ "width": 240.03826187159132, "height": 139.14797614727104, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -2111,14 +2111,18 @@ "type": 3 }, "seed": 1785093047, - "version": 626, - "versionNonce": 389649015, + "version": 687, + "versionNonce": 1338827947, "isDeleted": false, "boundElements": [ { "type": "text", "id": "G-p7eeV10m-vcn22SyqOV" }, + { + "id": "leg1QuHkxBlobyKunl-1Y", + "type": "arrow" + }, { "id": "r4eUfLBdxE3BDJYNxJSDD", "type": "arrow" @@ -2142,25 +2146,21 @@ { "id": "BI1-YoI6GBx0S2ywamnvj", "type": "arrow" - }, - { - "id": "leg1QuHkxBlobyKunl-1Y", - "type": "arrow" } ], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false }, { "id": "G-p7eeV10m-vcn22SyqOV", "type": "text", - "x": 596.8770725572696, + "x": 596.8770725572695, "y": 2648.907300782613, "width": 124.70394897460938, "height": 45, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -2172,11 +2172,11 @@ "index": "b0e", "roundness": null, "seed": 1789906135, - "version": 572, - "versionNonce": 1835936663, + "version": 617, + "versionNonce": 190926725, "isDeleted": false, "boundElements": [], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false, "text": "Airport", @@ -2192,13 +2192,13 @@ { "id": "r4eUfLBdxE3BDJYNxJSDD", "type": "arrow", - "x": 253.76765364340957, - "y": 2294.7391160065263, - "width": 338.45939410091904, - "height": 300.3830826961075, + "x": 251.4227591270251, + "y": 2294.2002825548047, + "width": 341.80011191482686, + "height": 304.1719554801407, "angle": 0, "strokeColor": "#e03131", - "backgroundColor": "#e9ecef", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -2211,8 +2211,8 @@ "type": 2 }, "seed": 786685751, - "version": 1183, - "versionNonce": 634228665, + "version": 1279, + "versionNonce": 1636420069, "isDeleted": false, "boundElements": [ { @@ -2220,7 +2220,7 @@ "id": "Xua1jBHt6MnsrJ95dUZI4" } ], - "updated": 1766427026623, + "updated": 1766946953432, "link": null, "locked": false, "points": [ @@ -2229,12 +2229,12 @@ 0 ], [ - 265.0852229969117, - 60.91394930976685 + 267.4301175132962, + 61.45278276148838 ], [ - 338.45939410091904, - 300.3830826961075 + 341.80011191482686, + 304.1719554801407 ] ], "lastCommittedPoint": null, @@ -2255,8 +2255,8 @@ { "id": "Xua1jBHt6MnsrJ95dUZI4", "type": "text", - "x": -539.8454860986211, - "y": 2332.511280829747, + "x": 337.1429462203994, + "y": 2310.653065316293, "width": 363.41986083984375, "height": 90, "angle": 0, @@ -2272,11 +2272,11 @@ "index": "b0hG", "roundness": null, "seed": 76795287, - "version": 40, - "versionNonce": 271909495, + "version": 43, + "versionNonce": 491067115, "isDeleted": false, - "boundElements": null, - "updated": 1766426519754, + "boundElements": [], + "updated": 1766946925213, "link": null, "locked": false, "text": "ground convoy (when\napplicable)", @@ -2292,13 +2292,13 @@ { "id": "pLy6cwouzkhVAGvdydItn", "type": "arrow", - "x": 201.4817994664886, - "y": 2403.657518078666, - "width": 322.8795759108036, - "height": 235.00062305381653, + "x": 205.61577905478612, + "y": 2408.187044337532, + "width": 321.00806504506374, + "height": 231.79570762913409, "angle": 0, "strokeColor": "#1971c2", - "backgroundColor": "#e9ecef", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -2311,8 +2311,8 @@ "type": 2 }, "seed": 856128343, - "version": 925, - "versionNonce": 1073067385, + "version": 1021, + "versionNonce": 742126315, "isDeleted": false, "boundElements": [ { @@ -2320,7 +2320,7 @@ "id": "ztphoE-7z-gD8pWOKDLJo" } ], - "updated": 1766427026623, + "updated": 1766946953432, "link": null, "locked": false, "points": [ @@ -2329,12 +2329,12 @@ 0 ], [ - 90.08803756432019, - 98.7078245167063 + 85.95405797602294, + 94.1782982578402 ], [ - 322.8795759108036, - 235.00062305381653 + 321.00806504506374, + 231.79570762913409 ] ], "lastCommittedPoint": null, @@ -2355,8 +2355,8 @@ { "id": "ztphoE-7z-gD8pWOKDLJo", "type": "text", - "x": -702.9160030537229, - "y": 2459.350950652917, + "x": 160.79987853471528, + "y": 2479.865342595372, "width": 261.5399169921875, "height": 45, "angle": 0, @@ -2372,11 +2372,11 @@ "index": "b0m", "roundness": null, "seed": 1752209561, - "version": 23, - "versionNonce": 1570273143, + "version": 27, + "versionNonce": 1470226789, "isDeleted": false, - "boundElements": null, - "updated": 1766426511827, + "boundElements": [], + "updated": 1766946925214, "link": null, "locked": false, "text": "air lift supplies", @@ -2397,8 +2397,8 @@ "width": 563.9873461120986, "height": 130.77910636725028, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -2411,8 +2411,8 @@ "type": 3 }, "seed": 1957398329, - "version": 622, - "versionNonce": 1179466167, + "version": 636, + "versionNonce": 1371608907, "isDeleted": false, "boundElements": [ { @@ -2428,7 +2428,7 @@ "type": "arrow" } ], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false }, @@ -2440,7 +2440,7 @@ "width": 435.41986083984375, "height": 90, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -2452,11 +2452,11 @@ "index": "b0s", "roundness": null, "seed": 1151673463, - "version": 418, - "versionNonce": 571140823, + "version": 423, + "versionNonce": 2012681957, "isDeleted": false, - "boundElements": null, - "updated": 1766427026580, + "boundElements": [], + "updated": 1766946963488, "link": null, "locked": false, "text": "Buildable (FARP/Airport)\nSAM Site", @@ -2472,13 +2472,13 @@ { "id": "pUhG-GWU0pNVRBYnBU9yz", "type": "arrow", - "x": 750.2751164619465, - "y": 2599.9875365767934, - "width": 630.8538438841103, - "height": 243.359030871095, + "x": 749.8481002263541, + "y": 2600.869905639259, + "width": 631.6540395639315, + "height": 244.24139993356084, "angle": 0, "strokeColor": "#2f9e44", - "backgroundColor": "#e9ecef", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -2491,8 +2491,8 @@ "type": 2 }, "seed": 546036217, - "version": 1033, - "versionNonce": 52390713, + "version": 1090, + "versionNonce": 1004619077, "isDeleted": false, "boundElements": [ { @@ -2500,7 +2500,7 @@ "id": "SAhFJqH44sxQdsCreNo8B" } ], - "updated": 1766427026623, + "updated": 1766946953432, "link": null, "locked": false, "points": [ @@ -2509,12 +2509,12 @@ 0 ], [ - 117.75916063927099, - -243.359030871095 + 118.18617687485204, + -244.24139993356084 ], [ - 630.8538438841103, - -236.88472103855202 + 631.6540395639315, + -237.76238126379076 ] ], "lastCommittedPoint": null, @@ -2535,8 +2535,8 @@ { "id": "SAhFJqH44sxQdsCreNo8B", "type": "text", - "x": 252.4838612355578, - "y": 2317.1194426376073, + "x": 797.5283032852881, + "y": 2334.128505705698, "width": 141.01194763183594, "height": 45, "angle": 0, @@ -2552,11 +2552,11 @@ "index": "b0vV", "roundness": null, "seed": 2057201721, - "version": 11, - "versionNonce": 1795167577, + "version": 15, + "versionNonce": 1425216363, "isDeleted": false, - "boundElements": null, - "updated": 1766426438626, + "boundElements": [], + "updated": 1766946925214, "link": null, "locked": false, "text": "personel", @@ -2572,13 +2572,13 @@ { "id": "ApDYGbwBfIIGwqv6Dmf0b", "type": "arrow", - "x": 775.3876575864397, - "y": 2600.6826988821663, - "width": 603.5493728468393, - "height": 206.33960553268753, + "x": 774.76065643821, + "y": 2601.2298092073565, + "width": 604.638874501149, + "height": 206.93510208802036, "angle": 0, "strokeColor": "#1971c2", - "backgroundColor": "#e9ecef", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -2591,8 +2591,8 @@ "type": 2 }, "seed": 1291560537, - "version": 1083, - "versionNonce": 673624313, + "version": 1140, + "versionNonce": 1639210379, "isDeleted": false, "boundElements": [ { @@ -2600,7 +2600,7 @@ "id": "wy3n-zlV9CNH7ilDG66fj" } ], - "updated": 1766427026623, + "updated": 1766946953432, "link": null, "locked": false, "points": [ @@ -2609,12 +2609,12 @@ 0 ], [ - 186.46526248325262, - -162.7047824674489 + 187.09226363145905, + -163.2518927926519 ], [ - 603.5493728468393, - -206.33960553268753 + 604.638874501149, + -206.93510208802036 ] ], "lastCommittedPoint": null, @@ -2635,8 +2635,8 @@ { "id": "wy3n-zlV9CNH7ilDG66fj", "type": "text", - "x": 355.64425234095904, - "y": 2405.368227171966, + "x": 824.7649684095129, + "y": 2415.477916414705, "width": 274.1759033203125, "height": 45, "angle": 0, @@ -2652,11 +2652,11 @@ "index": "b0y", "roundness": null, "seed": 1426799863, - "version": 12, - "versionNonce": 755133529, + "version": 16, + "versionNonce": 1980394693, "isDeleted": false, - "boundElements": null, - "updated": 1766426454527, + "boundElements": [], + "updated": 1766946925215, "link": null, "locked": false, "text": "building supplies", @@ -2677,8 +2677,8 @@ "width": 322.335414579567, "height": 139.14797614727104, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -2691,8 +2691,8 @@ "type": 3 }, "seed": 558580153, - "version": 1189, - "versionNonce": 403326071, + "version": 1205, + "versionNonce": 2010510827, "isDeleted": false, "boundElements": [ { @@ -2712,7 +2712,7 @@ "type": "arrow" } ], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false }, @@ -2724,7 +2724,7 @@ "width": 138.38392639160156, "height": 45, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -2736,11 +2736,11 @@ "index": "b12", "roundness": null, "seed": 365732505, - "version": 1139, - "versionNonce": 894999959, + "version": 1144, + "versionNonce": 287109701, "isDeleted": false, "boundElements": [], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false, "text": "Artillery", @@ -2756,13 +2756,13 @@ { "id": "QHaXZ1XeEczbne40qsKrG", "type": "arrow", - "x": 783.0208252496022, - "y": 2671.87012668229, - "width": 645.5185804051291, - "height": 125.52567675356295, + "x": 782.4938862055837, + "y": 2671.7887855276776, + "width": 646.4258987295113, + "height": 125.6536133251202, "angle": 0, "strokeColor": "#2f9e44", - "backgroundColor": "#e9ecef", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -2775,8 +2775,8 @@ "type": 2 }, "seed": 1461141401, - "version": 632, - "versionNonce": 1625786041, + "version": 689, + "versionNonce": 141710501, "isDeleted": false, "boundElements": [ { @@ -2784,7 +2784,7 @@ "id": "lUjq2uSFalKttY_GApViG" } ], - "updated": 1766427026624, + "updated": 1766946953432, "link": null, "locked": false, "points": [ @@ -2793,12 +2793,12 @@ 0 ], [ - 272.30733979474536, - 42.034830555446206 + 272.83427883876385, + 42.1161717100581 ], [ - 645.5185804051291, - -83.49084619811674 + 646.4258987295113, + -83.53744161506211 ] ], "lastCommittedPoint": null, @@ -2819,8 +2819,8 @@ { "id": "lUjq2uSFalKttY_GApViG", "type": "text", - "x": 320.1698123997951, - "y": 2452.138515037042, + "x": 1017.6541843314569, + "y": 2691.4049572377357, "width": 75.34796142578125, "height": 45, "angle": 0, @@ -2836,11 +2836,11 @@ "index": "b14V", "roundness": null, "seed": 336512601, - "version": 8, - "versionNonce": 232166585, + "version": 12, + "versionNonce": 397635083, "isDeleted": false, - "boundElements": null, - "updated": 1766426469124, + "boundElements": [], + "updated": 1766946925215, "link": null, "locked": false, "text": "sling", @@ -2856,13 +2856,13 @@ { "id": "BI1-YoI6GBx0S2ywamnvj", "type": "arrow", - "x": 784.9189237134592, - "y": 2656.0653405376215, - "width": 638.8664314742659, - "height": 111.6838800976202, + "x": 784.1136894330798, + "y": 2656.118658137938, + "width": 640.5137837544373, + "height": 111.94985180902813, "angle": 0, "strokeColor": "#1971c2", - "backgroundColor": "#e9ecef", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -2875,8 +2875,8 @@ "type": 2 }, "seed": 594872407, - "version": 553, - "versionNonce": 406711417, + "version": 610, + "versionNonce": 772174891, "isDeleted": false, "boundElements": [ { @@ -2884,7 +2884,7 @@ "id": "bh2jF6jEdRTQ4Ohh26mhX" } ], - "updated": 1766427026624, + "updated": 1766946953432, "link": null, "locked": false, "points": [ @@ -2893,12 +2893,12 @@ 0 ], [ - 266.46326369846656, - -17.643538208827977 + 267.26849797884597, + -17.69685580914438 ], [ - 638.8664314742659, - -111.6838800976202 + 640.5137837544373, + -111.94985180902813 ] ], "lastCommittedPoint": null, @@ -2919,8 +2919,8 @@ { "id": "bh2jF6jEdRTQ4Ohh26mhX", "type": "text", - "x": 308.86182762626004, - "y": 2376.6553601280993, + "x": 1006.3461995579219, + "y": 2615.9218023287935, "width": 90.07197570800781, "height": 45, "angle": 0, @@ -2936,11 +2936,11 @@ "index": "b19V", "roundness": null, "seed": 1961715737, - "version": 7, - "versionNonce": 1430921207, + "version": 10, + "versionNonce": 620436811, "isDeleted": false, - "boundElements": null, - "updated": 1766426466158, + "boundElements": [], + "updated": 1766946925213, "link": null, "locked": false, "text": "ammo", @@ -2961,8 +2961,8 @@ "width": 222.9804016095827, "height": 115.75956567695627, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -2975,8 +2975,8 @@ "type": 3 }, "seed": 1930064985, - "version": 1617, - "versionNonce": 1391542359, + "version": 1629, + "versionNonce": 1360396427, "isDeleted": false, "boundElements": [ { @@ -2988,7 +2988,7 @@ "type": "arrow" } ], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false }, @@ -3000,7 +3000,7 @@ "width": 92.55598449707031, "height": 45, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -3012,11 +3012,11 @@ "index": "b1C", "roundness": null, "seed": 1514963257, - "version": 1572, - "versionNonce": 364191095, + "version": 1577, + "versionNonce": 1940939173, "isDeleted": false, "boundElements": [], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false, "text": "JTAC", @@ -3032,13 +3032,13 @@ { "id": "leg1QuHkxBlobyKunl-1Y", "type": "arrow", - "x": 772.664557842945, - "y": 2744.40234119318, - "width": 660.4820279969272, - "height": 74.35809394747776, + "x": 769.9193614172583, + "y": 2743.585963370502, + "width": 663.6301411239612, + "height": 75.17447177015538, "angle": 0, "strokeColor": "#2f9e44", - "backgroundColor": "#e9ecef", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -3051,8 +3051,8 @@ "type": 2 }, "seed": 1819131257, - "version": 1089, - "versionNonce": 839246393, + "version": 1146, + "versionNonce": 1629271755, "isDeleted": false, "boundElements": [ { @@ -3060,7 +3060,7 @@ "id": "LDlkBCROpwA37Ci5B403u" } ], - "updated": 1766427026624, + "updated": 1766946953432, "link": null, "locked": false, "points": [ @@ -3069,12 +3069,12 @@ 0 ], [ - 250.0405670696619, - 74.35809394747776 + 252.7857634953486, + 75.17447177015538 ], [ - 660.4820279969272, - 24.78157377313937 + 663.6301411239612, + 25.54928397844742 ] ], "lastCommittedPoint": null, @@ -3095,8 +3095,8 @@ { "id": "LDlkBCROpwA37Ci5B403u", "type": "text", - "x": 233.5795966273804, - "y": 2583.1985436529567, + "x": 923.2731485942475, + "y": 2796.2604351406576, "width": 198.86395263671875, "height": 45, "angle": 0, @@ -3112,11 +3112,11 @@ "index": "b1DV", "roundness": null, "seed": 1960003671, - "version": 12, - "versionNonce": 412280631, + "version": 15, + "versionNonce": 906397323, "isDeleted": false, - "boundElements": null, - "updated": 1766426474140, + "boundElements": [], + "updated": 1766946925213, "link": null, "locked": false, "text": "JTAC Team", @@ -3137,8 +3137,8 @@ "width": 193.64898125204866, "height": 88.36863266966611, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -3151,8 +3151,8 @@ "type": 3 }, "seed": 169830231, - "version": 1676, - "versionNonce": 1492049111, + "version": 1727, + "versionNonce": 658224939, "isDeleted": false, "boundElements": [ { @@ -3164,19 +3164,19 @@ "type": "arrow" } ], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false }, { "id": "_ex49a_88TMqC3exjFhcq", "type": "text", - "x": 353.5631325635536, + "x": 353.56313256355367, "y": 3220.973861808029, "width": 73.22398376464844, "height": 45, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -3188,11 +3188,11 @@ "index": "b1H", "roundness": null, "seed": 847668343, - "version": 1630, - "versionNonce": 852175351, + "version": 1675, + "versionNonce": 1155458309, "isDeleted": false, "boundElements": [], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false, "text": "SAR", @@ -3208,13 +3208,13 @@ { "id": "RH7r8ZwT4fEi5DxOLjdYF", "type": "arrow", - "x": 437.2241680243269, - "y": 3197.6524370035436, - "width": 137.48606720548244, - "height": 332.8173193560715, + "x": 437.11282814450993, + "y": 3198.5988125553017, + "width": 139.20462164893905, + "height": 335.6640978197647, "angle": 0, "strokeColor": "#2f9e44", - "backgroundColor": "#e9ecef", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -3227,8 +3227,8 @@ "type": 2 }, "seed": 1074673049, - "version": 1570, - "versionNonce": 1260087289, + "version": 1666, + "versionNonce": 1430728549, "isDeleted": false, "boundElements": [ { @@ -3236,7 +3236,7 @@ "id": "6nGsnajqwTdOMhTUCEKzr" } ], - "updated": 1766427026624, + "updated": 1766946953432, "link": null, "locked": false, "points": [ @@ -3245,12 +3245,12 @@ 0 ], [ - 23.26641191409942, - -197.76169552920373 + 23.37775179391639, + -198.70807108096187 ], [ - 137.48606720548244, - -332.8173193560715 + 139.20462164893905, + -335.6640978197647 ] ], "lastCommittedPoint": null, @@ -3271,8 +3271,8 @@ { "id": "6nGsnajqwTdOMhTUCEKzr", "type": "text", - "x": -140.40994174327034, - "y": 2750.574120216737, + "x": 422.79859385444195, + "y": 2977.39074147434, "width": 75.38397216796875, "height": 45, "angle": 0, @@ -3288,11 +3288,11 @@ "index": "b1IV", "roundness": null, "seed": 1727968599, - "version": 7, - "versionNonce": 1935109431, + "version": 10, + "versionNonce": 62431179, "isDeleted": false, - "boundElements": null, - "updated": 1766426591291, + "boundElements": [], + "updated": 1766946925213, "link": null, "locked": false, "text": "exfil", @@ -3313,8 +3313,8 @@ "width": 240.03826187159132, "height": 139.14797614727104, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -3327,14 +3327,18 @@ "type": 3 }, "seed": 819713497, - "version": 986, - "versionNonce": 1593456983, + "version": 1045, + "versionNonce": 682727883, "isDeleted": false, "boundElements": [ { "type": "text", "id": "lv-kaKvlBtWcauxfduNFo" }, + { + "id": "leg1QuHkxBlobyKunl-1Y", + "type": "arrow" + }, { "id": "RH7r8ZwT4fEi5DxOLjdYF", "type": "arrow" @@ -3343,10 +3347,6 @@ "id": "4a2O6d-s_kfxavVqza9nT", "type": "arrow" }, - { - "id": "leg1QuHkxBlobyKunl-1Y", - "type": "arrow" - }, { "id": "-_QCeLghYWHOuqA_zCUCL", "type": "arrow" @@ -3356,7 +3356,7 @@ "type": "arrow" } ], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false }, @@ -3368,7 +3368,7 @@ "width": 95.43595886230469, "height": 45, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -3380,11 +3380,11 @@ "index": "b1L", "roundness": null, "seed": 1786098361, - "version": 931, - "versionNonce": 137819767, + "version": 975, + "versionNonce": 673578085, "isDeleted": false, "boundElements": [], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false, "text": "FARP", @@ -3400,13 +3400,13 @@ { "id": "4a2O6d-s_kfxavVqza9nT", "type": "arrow", - "x": 34.43794348069787, - "y": 2408.460247889612, - "width": 467.01212534184356, - "height": 385.21285233492017, + "x": 34.18533689783794, + "y": 2407.570532781306, + "width": 468.86883940244695, + "height": 386.8233985113411, "angle": 0, "strokeColor": "#f08c00", - "backgroundColor": "#e9ecef", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -3419,8 +3419,8 @@ "type": 2 }, "seed": 417375225, - "version": 1420, - "versionNonce": 2035973561, + "version": 1516, + "versionNonce": 1771152747, "isDeleted": false, "boundElements": [ { @@ -3428,7 +3428,7 @@ "id": "PzPpKOUQKNe-dfAxOumbp" } ], - "updated": 1766427026624, + "updated": 1766946953432, "link": null, "locked": false, "points": [ @@ -3437,12 +3437,12 @@ 0 ], [ - 57.06690480307236, - 200.99748317211788 + 57.31951138593223, + 201.88719828042395 ], [ - 467.01212534184356, - 385.21285233492017 + 468.86883940244695, + 386.8233985113411 ] ], "lastCommittedPoint": null, @@ -3463,8 +3463,8 @@ { "id": "PzPpKOUQKNe-dfAxOumbp", "type": "text", - "x": -657.4625821727179, - "y": 2414.220498070461, + "x": 18.40687922859439, + "y": 2586.95773106173, "width": 146.19593811035156, "height": 45, "angle": 0, @@ -3480,11 +3480,11 @@ "index": "b1N", "roundness": null, "seed": 821804249, - "version": 37, - "versionNonce": 1042141785, + "version": 41, + "versionNonce": 34245669, "isDeleted": false, "boundElements": [], - "updated": 1766426883465, + "updated": 1766946925215, "link": null, "locked": false, "text": "Air Drop", @@ -3505,8 +3505,8 @@ "width": 563.9873461120986, "height": 130.77910636725028, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", @@ -3519,8 +3519,8 @@ "type": 3 }, "seed": 462094201, - "version": 828, - "versionNonce": 129643095, + "version": 879, + "versionNonce": 1385310315, "isDeleted": false, "boundElements": [ { @@ -3532,19 +3532,19 @@ "type": "arrow" } ], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false }, { "id": "D2Tx1n7-c7dShdryFm1rR", "type": "text", - "x": 1567.3761882052943, + "x": 1567.3761882052945, "y": 3097.5016523810286, "width": 277.0198974609375, "height": 45, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -3556,11 +3556,11 @@ "index": "b1P", "roundness": null, "seed": 885958745, - "version": 643, - "versionNonce": 1754255223, + "version": 688, + "versionNonce": 1968383941, "isDeleted": false, "boundElements": [], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false, "text": "Combat Vehicles", @@ -3581,8 +3581,8 @@ "width": 158.8551654596534, "height": 55, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", @@ -3595,8 +3595,8 @@ "type": 3 }, "seed": 1447597305, - "version": 1282, - "versionNonce": 1419615671, + "version": 1333, + "versionNonce": 447210251, "isDeleted": false, "boundElements": [ { @@ -3608,7 +3608,7 @@ "type": "arrow" } ], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false }, @@ -3620,7 +3620,7 @@ "width": 81.03598022460938, "height": 45, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#ffffff", "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, @@ -3632,11 +3632,11 @@ "index": "b1R", "roundness": null, "seed": 1044221401, - "version": 1102, - "versionNonce": 1353633495, + "version": 1146, + "versionNonce": 780396325, "isDeleted": false, "boundElements": [], - "updated": 1766427026580, + "updated": 1766946963488, "link": null, "locked": false, "text": "Crew", @@ -3652,13 +3652,13 @@ { "id": "-_QCeLghYWHOuqA_zCUCL", "type": "arrow", - "x": 762.5786706449179, - "y": 2817.0330392831174, - "width": 648.3248084473307, - "height": 226.79194116505596, + "x": 761.0680264942605, + "y": 2816.5705500932745, + "width": 654.6086046249782, + "height": 229.2208908289881, "angle": 0, "strokeColor": "#2f9e44", - "backgroundColor": "#e9ecef", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", @@ -3671,8 +3671,8 @@ "type": 2 }, "seed": 1251964887, - "version": 720, - "versionNonce": 194978681, + "version": 816, + "versionNonce": 175212203, "isDeleted": false, "boundElements": [ { @@ -3680,7 +3680,7 @@ "id": "VuzadgqH4DvpIOLIzLuzL" } ], - "updated": 1766427026624, + "updated": 1766946953433, "link": null, "locked": false, "points": [ @@ -3689,12 +3689,12 @@ 0 ], [ - 380.8680462313765, - 116.60413477389056 + 382.37869038203394, + 117.06662396373349 ], [ - 648.3248084473307, - 226.79194116505596 + 654.6086046249782, + 229.2208908289881 ] ], "lastCommittedPoint": null, @@ -3715,8 +3715,8 @@ { "id": "VuzadgqH4DvpIOLIzLuzL", "type": "text", - "x": 220.95579729918109, - "y": 2725.8429260923867, + "x": 1024.9527517273686, + "y": 2911.137174057008, "width": 236.98793029785156, "height": 45, "angle": 0, @@ -3732,11 +3732,11 @@ "index": "b1T", "roundness": null, "seed": 1178385177, - "version": 15, - "versionNonce": 1027364921, + "version": 18, + "versionNonce": 2118410475, "isDeleted": false, - "boundElements": null, - "updated": 1766426847970, + "boundElements": [], + "updated": 1766946925213, "link": null, "locked": false, "text": "crew drop off", @@ -3752,13 +3752,13 @@ { "id": "0gOdWI0Qb4Wq1roj27t1C", "type": "arrow", - "x": 744.1719243059313, - "y": 2865.025337973955, - "width": 659.4883010272987, - "height": 277.66316524002605, + "x": 742.7390454747081, + "y": 2863.8785028014436, + "width": 661.6872909080034, + "height": 278.88227753864567, "angle": 0, "strokeColor": "#f08c00", - "backgroundColor": "#e9ecef", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", @@ -3771,8 +3771,8 @@ "type": 2 }, "seed": 1594909273, - "version": 498, - "versionNonce": 1237670201, + "version": 594, + "versionNonce": 1429494309, "isDeleted": false, "boundElements": [ { @@ -3780,7 +3780,7 @@ "id": "ggwAgvvBHHYD--i6Sfw4O" } ], - "updated": 1766427026624, + "updated": 1766946953433, "link": null, "locked": false, "points": [ @@ -3789,12 +3789,12 @@ 0 ], [ - 305.1507772853523, - 244.23394123169965 + 306.58365611657564, + 245.38077640421125 ], [ - 659.4883010272987, - 277.66316524002605 + 661.6872909080034, + 278.88227753864567 ] ], "lastCommittedPoint": null, @@ -3815,8 +3815,8 @@ { "id": "ggwAgvvBHHYD--i6Sfw4O", "type": "text", - "x": 309.9163478480983, - "y": 2847.4928370049606, + "x": 1007.4007197797603, + "y": 3086.759279205655, "width": 83.84396362304688, "height": 45, "angle": 0, @@ -3832,11 +3832,11 @@ "index": "b1V", "roundness": null, "seed": 1725527065, - "version": 25, - "versionNonce": 1950789113, + "version": 29, + "versionNonce": 1657762987, "isDeleted": false, - "boundElements": null, - "updated": 1766426875733, + "boundElements": [], + "updated": 1766946925215, "link": null, "locked": false, "text": "Drop", @@ -3857,8 +3857,8 @@ "width": 458.60382080078125, "height": 45, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", @@ -3869,8 +3869,8 @@ "index": "b1W", "roundness": null, "seed": 1175945111, - "version": 577, - "versionNonce": 1714498391, + "version": 587, + "versionNonce": 438658635, "isDeleted": false, "boundElements": [ { @@ -3878,7 +3878,7 @@ "type": "arrow" } ], - "updated": 1766427026580, + "updated": 1766946974512, "link": null, "locked": false, "text": "Integrate with Arti script", @@ -3894,13 +3894,13 @@ { "id": "6RPqsqx4nYBdljxD7zHDx", "type": "arrow", - "x": 2101.784317552928, - "y": 2616.3372654032187, - "width": 339.4415204576087, - "height": 65.38277937556722, + "x": 2101.7843175529274, + "y": 2616.337265403218, + "width": 340.08002199021894, + "height": 65.38277937556677, "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "strokeColor": "#ffffff", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", @@ -3913,11 +3913,11 @@ "type": 2 }, "seed": 1517066969, - "version": 831, - "versionNonce": 883498745, + "version": 851, + "versionNonce": 1500334507, "isDeleted": false, "boundElements": [], - "updated": 1766427026624, + "updated": 1766946963488, "link": null, "locked": false, "points": [ @@ -3926,12 +3926,12 @@ 0 ], [ - -108.93976161027717, - -65.38277937556722 + -108.93976161027672, + -65.38277937556677 ], [ - -339.4415204576087, - -52.09582606336289 + -340.08002199021894, + -52.059020530528414 ] ], "lastCommittedPoint": null, @@ -3952,13 +3952,13 @@ { "id": "Tch7sjut0PecYzWryfCmg", "type": "arrow", - "x": -465.1217107075172, + "x": -465.12171070751856, "y": 2633.4209227550487, - "width": 606.7329424899431, + "width": 606.7329424899442, "height": 163.85677954826906, "angle": 0, "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", @@ -3971,11 +3971,11 @@ "type": 2 }, "seed": 1234586809, - "version": 445, - "versionNonce": 646548985, + "version": 457, + "versionNonce": 993428069, "isDeleted": false, "boundElements": [], - "updated": 1766427311619, + "updated": 1766946925222, "link": null, "locked": false, "points": [ @@ -3984,11 +3984,11 @@ 0 ], [ - 325.8241841875679, + 325.824184187569, -163.85677954826906 ], [ - 606.7329424899431, + 606.7329424899442, -141.437807661212 ] ], @@ -4012,7 +4012,7 @@ "height": 225, "angle": 0, "strokeColor": "#1e1e1e", - "backgroundColor": "#e9ecef", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", @@ -4023,8 +4023,8 @@ "index": "b1b", "roundness": null, "seed": 299043063, - "version": 267, - "versionNonce": 1863981337, + "version": 276, + "versionNonce": 127132875, "isDeleted": false, "boundElements": [ { @@ -4032,7 +4032,7 @@ "type": "arrow" } ], - "updated": 1766427311618, + "updated": 1766946925213, "link": null, "locked": false, "text": "Options: \n 1. Fine grained supplies (Gray-Flag)\n 2. Broad \"all supplies\"\n\nIncluding personel? ", @@ -4050,7 +4050,7 @@ "gridSize": 20, "gridStep": 5, "gridModeEnabled": false, - "viewBackgroundColor": "#ffffff" + "viewBackgroundColor": "#1b1d27" }, "files": {} } \ No newline at end of file From d77279328ef2a518ba7f3a7705a07ded785e3ae6 Mon Sep 17 00:00:00 2001 From: dutchie031 Date: Fri, 6 Mar 2026 19:07:54 +0100 Subject: [PATCH 3/9] Work in progress refactor for toolkit --- .DS_Store | Bin 8196 -> 8196 bytes .gitignore | 4 +- .vscode/settings.json | 5 +- DcsTypes.lua | 1349 ---------- classes/fleetClasses/GlobalFleetManager.lua | 22 - main.lua | 82 - .../classes}/_baseClasses/Queue.lua | 5 +- {classes => src/classes}/api/SpearheadApi.lua | 9 +- .../classes}/api/SpearheadApiDoc.lua | 0 .../classes}/capClasses/CapAirbase.lua | 34 +- .../classes}/capClasses/GlobalCapManager.lua | 31 +- .../capClasses/airGroups/AirGroup.lua | 27 +- .../capClasses/airGroups/CapGroup.lua | 42 +- .../capClasses/airGroups/InterceptGroup.lua | 50 +- .../capClasses/airGroups/SweepGroup.lua | 39 +- .../capClasses/detection/DetectionManager.lua | 6 +- .../runwayBombing/RunwayBombingTracker.lua | 15 +- .../classes}/capClasses/taskings/CAP.lua | 33 +- .../capClasses/taskings/INTERCEPT.lua | 31 +- .../classes}/capClasses/taskings/RTB.lua | 21 +- .../classes}/capClasses/taskings/SWEEP.lua | 27 +- .../classes}/configuration/CapConfig.lua | 4 +- .../classes}/configuration/GlobalConfig.lua | 3 +- .../classes}/configuration/StageConfig.lua | 6 +- .../classes}/definitions/aliases.lua | 0 .../classes}/fleetClasses/FleetGroup.lua | 56 +- .../fleetClasses/GlobalFleetManager.lua | 26 + .../classes}/helpers/MizGroupsManager.lua | 13 +- .../classes}/helpers/SpawnManager.lua | 32 +- .../classes}/persistence/Persistence.lua | 21 +- {classes => src/classes}/spearhead_db.lua | 3 +- {classes => src/classes}/spearhead_events.lua | 5 +- .../classes}/spearhead_routeutil.lua | 4 +- .../stageClasses/GlobalStageManager.lua | 6 +- .../stageClasses/Groups/SpearheadGroup.lua | 9 +- .../Groups/SpearheadSceneryObject.lua | 11 +- .../stageClasses/SpecialZones/BlueSam.lua | 8 +- .../stageClasses/SpecialZones/FarpZone.lua | 6 +- .../stageClasses/SpecialZones/StageBase.lua | 8 +- .../stageClasses/SpecialZones/SupplyHub.lua | 7 +- .../SpecialZones/abstract/BuildableZone.lua | 7 +- .../stageClasses/Stages/BaseStage/Stage.lua | 6 +- .../stageClasses/Stages/ExtraStage.lua | 5 +- .../stageClasses/Stages/PrimaryStage.lua | 5 +- .../stageClasses/Stages/WaitingStage.lua | 5 +- .../stageClasses/drawings/CustomDrawing.lua | 18 +- .../drawings/helper/DrawingHelper.lua | 7 +- .../stageClasses/helpers/BattleManager.lua | 28 +- .../stageClasses/helpers/MaxLoadConfig.lua | 20 + .../helpers/MissionCommandsHelper.lua | 47 +- .../helpers/SupplyConfigHelper.lua | 31 +- .../helpers/SupplyUnitsTracker.lua | 45 +- .../missions/BuildableMission.lua | 48 +- .../missions/RunwayStrikeMission.lua | 7 +- .../stageClasses/missions/ZoneMission.lua | 5 +- .../missions/baseMissions/Mission.lua | 29 +- .../classes/util/DcsUtil.lua | 2289 ++++++----------- src/classes/util/Logger.lua | 75 + src/classes/util/MissionEditorWarnings.lua | 26 + src/classes/util/Util.lua | 503 ++++ src/main.lua | 96 + 61 files changed, 1987 insertions(+), 3375 deletions(-) delete mode 100644 DcsTypes.lua delete mode 100644 classes/fleetClasses/GlobalFleetManager.lua delete mode 100644 main.lua rename {classes => src/classes}/_baseClasses/Queue.lua (84%) rename {classes => src/classes}/api/SpearheadApi.lua (83%) rename {classes => src/classes}/api/SpearheadApiDoc.lua (100%) rename {classes => src/classes}/capClasses/CapAirbase.lua (92%) rename {classes => src/classes}/capClasses/GlobalCapManager.lua (62%) rename {classes => src/classes}/capClasses/airGroups/AirGroup.lua (92%) rename {classes => src/classes}/capClasses/airGroups/CapGroup.lua (66%) rename {classes => src/classes}/capClasses/airGroups/InterceptGroup.lua (83%) rename {classes => src/classes}/capClasses/airGroups/SweepGroup.lua (67%) rename {classes => src/classes}/capClasses/detection/DetectionManager.lua (93%) rename {classes => src/classes}/capClasses/runwayBombing/RunwayBombingTracker.lua (85%) rename {classes => src/classes}/capClasses/taskings/CAP.lua (88%) rename {classes => src/classes}/capClasses/taskings/INTERCEPT.lua (89%) rename {classes => src/classes}/capClasses/taskings/RTB.lua (82%) rename {classes => src/classes}/capClasses/taskings/SWEEP.lua (87%) rename {classes => src/classes}/configuration/CapConfig.lua (92%) rename {classes => src/classes}/configuration/GlobalConfig.lua (86%) rename {classes => src/classes}/configuration/StageConfig.lua (84%) rename {classes => src/classes}/definitions/aliases.lua (100%) rename {classes => src/classes}/fleetClasses/FleetGroup.lua (63%) create mode 100644 src/classes/fleetClasses/GlobalFleetManager.lua rename {classes => src/classes}/helpers/MizGroupsManager.lua (84%) rename {classes => src/classes}/helpers/SpawnManager.lua (90%) rename {classes => src/classes}/persistence/Persistence.lua (92%) rename {classes => src/classes}/spearhead_db.lua (99%) rename {classes => src/classes}/spearhead_events.lua (99%) rename {classes => src/classes}/spearhead_routeutil.lua (99%) rename {classes => src/classes}/stageClasses/GlobalStageManager.lua (98%) rename {classes => src/classes}/stageClasses/Groups/SpearheadGroup.lua (92%) rename {classes => src/classes}/stageClasses/Groups/SpearheadSceneryObject.lua (73%) rename {classes => src/classes}/stageClasses/SpecialZones/BlueSam.lua (92%) rename {classes => src/classes}/stageClasses/SpecialZones/FarpZone.lua (90%) rename {classes => src/classes}/stageClasses/SpecialZones/StageBase.lua (94%) rename {classes => src/classes}/stageClasses/SpecialZones/SupplyHub.lua (86%) rename {classes => src/classes}/stageClasses/SpecialZones/abstract/BuildableZone.lua (93%) rename {classes => src/classes}/stageClasses/Stages/BaseStage/Stage.lua (97%) rename {classes => src/classes}/stageClasses/Stages/ExtraStage.lua (84%) rename {classes => src/classes}/stageClasses/Stages/PrimaryStage.lua (68%) rename {classes => src/classes}/stageClasses/Stages/WaitingStage.lua (87%) rename {classes => src/classes}/stageClasses/drawings/CustomDrawing.lua (58%) rename {classes => src/classes}/stageClasses/drawings/helper/DrawingHelper.lua (93%) rename {classes => src/classes}/stageClasses/helpers/BattleManager.lua (86%) create mode 100644 src/classes/stageClasses/helpers/MaxLoadConfig.lua rename {classes => src/classes}/stageClasses/helpers/MissionCommandsHelper.lua (88%) rename classes/stageClasses/helpers/SupplyConfig.lua => src/classes/stageClasses/helpers/SupplyConfigHelper.lua (67%) rename {classes => src/classes}/stageClasses/helpers/SupplyUnitsTracker.lua (87%) rename {classes => src/classes}/stageClasses/missions/BuildableMission.lua (78%) rename {classes => src/classes}/stageClasses/missions/RunwayStrikeMission.lua (97%) rename {classes => src/classes}/stageClasses/missions/ZoneMission.lua (98%) rename {classes => src/classes}/stageClasses/missions/baseMissions/Mission.lua (78%) rename classes/spearhead_base.lua => src/classes/util/DcsUtil.lua (57%) create mode 100644 src/classes/util/Logger.lua create mode 100644 src/classes/util/MissionEditorWarnings.lua create mode 100644 src/classes/util/Util.lua create mode 100644 src/main.lua diff --git a/.DS_Store b/.DS_Store index 4b3a3c0283ea79121b114dc723b8e5e496a85858..dd3bcfbdc5a6f9b2baad3dc6000d463bdf36ffa8 100644 GIT binary patch delta 682 zcmZp1XmOa}&uF$WU^hRb*<>C8sn{h9K)}MF$B@pD$xxD;@8Xh_lb-|><4D@@um0N7 zDV7Q?Rst|*jRRk zWQH7uM22D@Ol2s}%|{pxQ8igYM2T_2W@`~H#`;X4qLQ3+g!v4t&DX(hO98qZD2MEJ zyKmKDw_$FF%Oi}(CXeiP2G$86FR(DAFysRrS?rm!*+: { [number]: T } - ----@class MGRS ----@field UTMZone string ----@field MGRSDigraph string ----@field Easting number ----@field Northing number - - -do -- env - ---@class env - ---@field mission table TODO: Mission - ---@field warehouses table - ---@field info fun(log:string, showMessageBox:boolean?) Prints passed log line with prefix 'info' - ---@field warning fun(log:string, showMessageBox:boolean?) Prints passed log line with prefix 'warning' - ---@field error fun(log:string, showMessageBox:boolean?) Prints passed log line with prefix 'error' - ---@field setErrorMessageBoxEnabled fun(toggle:boolean?) Enables or disables the lua error box to show up on a lua error - ---@field getValueDictByKey fun(value:string) : string Returns a string associated with the passed dictionary key value. - env = env -end - -do -- Mission table - - ---@class Mission - ---@field drawings Array - - ---@alias PrimitiveType - ---| "Polygon" - ---| "Line" - ---| "TextBox" - - ---@alias PolygonMode - ---| "circle" - ---| "free" - ---| "oval" - ---| "rect" - ---| "arrow" - - ---@alias LineMode - ---| "segment" - ---| "segments" - ---| "free" - - ---@class DrawingObject - ---@field primitiveType PrimitiveType - ---@field name string - ---@field visible boolean - ---@field layerName string? - ---@field mapX number - ---@field mapY number - ---@field colorString string Hex ARGB color - ---@field style string - ---@field thickness number - - ---@class Polygon : DrawingObject - ---@field polygonMode PolygonMode - ---@field fillColorString string? - - ---@class Circle : Polygon - ---@field radius number - - ---@class Free : Polygon - ---@field points Array - - ---@class Oval : Polygon - ---@field r1 number - ---@field r2 number - ---@field angle number? - - ---@class Rect : Polygon - ---@field width number - ---@field height number - ---@field angle number? - - ---@class Arrow : Polygon - ---@field length number - ---@field angle number? - - ---@class Line : DrawingObject - ---@field primitiveType PrimitiveType # PrimitiveType.Line - ---@field lineMode LineMode - ---@field closed boolean - ---@field points Array - - ---@class Segment : Line - - ---@class Segments : Line - - ---@class FreeLine : Line - - ---@class TextBox : DrawingObject - ---@field fontSize number - ---@field text string - ---@field fillColorString string - ---@field borderThickness number - -end - -do -- timer - ---@class timer - ---@field getTime fun() : number returns the time in the mission (in seconds. 3 decimals) - ---@field getAbsTime fun() : number returns the real time in seconds. 0 for midnight, 43200 for noon - ---@field getTime0 fun() : number returns the mission start time in seconds - ---@field scheduleFunction fun(functionToCall: function, arguments: any|nil, modelTime: number) : number schedule the function to be run at 'modelTime' and returns the functionID - ---@field removeFunction fun(functionID:number) removes a scheduled function from the scheduler - ---@field setFunctionTime fun(functionID:number, modelTime:number) reschedules an scheduled function - timer = timer -end - - -do -- land - ---@class land - ---@field getHeight fun(position:Vec2) : number returns the distance from the sea-level to the ground alt at point - ---@field getSurfaceHeightWithSeabed fun(position:Vec2) : sufaceHeight: number, depth: number returns the surface height and the depth of the seabed respectively. - ---@field getSurfaceType fun(position: Vec2) : SurfaceType returns the surface type at a certain position - ---@field isVisible fun(origin: Vec3, target: Vec3) returns whether or not there's line between the two points that's not interrupted by terrain. - ---@field getIP fun(origin:Vec3, direction:number, distance:number) TODO: Describe - ---@field profile fun(start:Vec3, end:Vec3) : Array Returns a list of terrain points between two points. Amount is not directly known. - ---@field getClosestPointOnRoads fun(roadType: RoadType, x : number, y : number) : x: number, y: number return x and y coordinate of the closest point on a road type. - ---@field findPathOnRoads fun(roadType: RoadType, startX: number, startY: number, endX: number, endY: number) : Array Returns a list of Vec2 points of a road. Can be quite long! - land = land - - ---@enum SurfaceType - land.SurfaceType = { - LAND = 1, - SHALLOW_WATER = 2, - WATER = 3, - ROAD = 4, - RUNWAY = 5 - } - - ---@alias RoadType - ---| "roads" - ---| "railroads" -end - -do -- atmosphere - ---@class atmosphere - ---@field getWind fun(point:Vec3): Vec3 returns a velocity vector for the wind at a point - ---@field getWindWithTurbulence fun(point:Vec3): Vec3 return a velocity vector for the wind including turbulent air - ---@field getTemperatureAndPressure fun(point:Vec3) : temp: number, pressure: number returns temperature and pressure. Kelvins and Pascals. - atmosphere = atmosphere -end - -do -- world - ---@class Event - ---@field id world.event - ---@field time number - - ---@class EventHandler - ---@field onEvent fun(event: Event) - - ---@class world - ---@field addEventHandler fun(handler: EventHandler) Adds an event handler that will be called on DCS events. - ---@field removeEventHandler fun(handler: EventHandler) Removes an event handler from the "to call table" - ---@field getPlayer fun() : Unit returns the unit object that is specified as "Player" in the mission editor - ---@field getAirbases fun(side: CoalitionSide?) : Array returns airbases (ships, bases, farps) of a side, or all if side is not passed - ---@field searchObjects fun(category: ObjectCategory, searchVolume: Volume, handler: fun(item:Object, value : any?), value: any?) : table searches a volume or space for objects of a specific type and can call the handler function on each found object - ---@field getMarkPanels fun() : table TODO: describe - ---@field removeJunk fun(searchVolume: Volume) : number searches a volume for any wrecks, craters or debris and removes it - ---@field getFogThickness fun() : number returns the global fog thickness. 0 if no fog is present - ---@field setFogThickness fun(thickness: number) sets the fog thickness. 100 to 5000. 0 to remove fog - ---@field getFogVisibilityDistance fun() : number returns the visibility distance in mist - ---@field setFogVisibilityDistance fun(visibility:number) sets the visibility distance. 100 to 100000. If 0 the fog will be removed. - ---@field setFogAnimation fun(dataTable: Array>) {relative time (seconds), visibility (meters), thickness (meters)} eg. {5, 1000, 500}, - ---@field weather table TODO: describe weather - world = world - - - do -- Event Types - ---@enum world.event - world.event = { - S_EVENT_INVALID = 0, - S_EVENT_SHOT = 1, - S_EVENT_HIT = 2, - S_EVENT_TAKEOFF = 3, - S_EVENT_LAND = 4, - S_EVENT_CRASH = 5, - S_EVENT_EJECTION = 6, - S_EVENT_REFUELING = 7, - S_EVENT_DEAD = 8, - S_EVENT_PILOT_DEAD = 9, - S_EVENT_BASE_CAPTURED = 10, - S_EVENT_MISSION_START = 11, - S_EVENT_MISSION_END = 12, - S_EVENT_TOOK_CONTROL = 13, - S_EVENT_REFUELING_STOP = 14, - S_EVENT_BIRTH = 15, - S_EVENT_HUMAN_FAILURE = 16, - S_EVENT_DETAILED_FAILURE = 17, - S_EVENT_ENGINE_STARTUP = 18, - S_EVENT_ENGINE_SHUTDOWN = 19, - S_EVENT_PLAYER_ENTER_UNIT = 20, - S_EVENT_PLAYER_LEAVE_UNIT = 21, - S_EVENT_PLAYER_COMMENT = 22, - S_EVENT_SHOOTING_START = 23, - S_EVENT_SHOOTING_END = 24, - S_EVENT_MARK_ADDED = 25, - S_EVENT_MARK_CHANGE = 26, - S_EVENT_MARK_REMOVED = 27, - S_EVENT_KILL = 28, - S_EVENT_SCORE = 29, - S_EVENT_UNIT_LOST = 30, - S_EVENT_LANDING_AFTER_EJECTION = 31, - S_EVENT_PARATROOPER_LENDING = 32, - S_EVENT_DISCARD_CHAIR_AFTER_EJECTION = 33, - S_EVENT_WEAPON_ADD = 34, - S_EVENT_TRIGGER_ZONE = 35, - S_EVENT_LANDING_QUALITY_MARK = 36, - S_EVENT_BDA = 37, - S_EVENT_AI_ABORT_MISSION = 38, - S_EVENT_DAYNIGHT = 39, - S_EVENT_FLIGHT_TIME = 40, - S_EVENT_PLAYER_SELF_KILL_PILOT = 41, - S_EVENT_PLAYER_CAPTURE_AIRFIELD = 42, - S_EVENT_EMERGENCY_LANDING = 43, - S_EVENT_UNIT_CREATE_TASK = 44, - S_EVENT_UNIT_DELETE_TASK = 45, - S_EVENT_SIMULATION_START = 46, - S_EVENT_WEAPON_REARM = 47, - S_EVENT_WEAPON_DROP = 48, - S_EVENT_UNIT_TASK_COMPLETE = 49, - S_EVENT_UNIT_TASK_STAGE = 50, - S_EVENT_MAC_EXTRA_SCORE = 51, - S_EVENT_MISSION_RESTART = 52, - S_EVENT_MISSION_WINNER = 53, - S_EVENT_RUNWAY_TAKEOFF = 54, - S_EVENT_RUNWAY_TOUCH = 55, - S_EVENT_MAC_LMS_RESTART = 56, - S_EVENT_SIMULATION_FREEZE = 57, - S_EVENT_SIMULATION_UNFREEZE = 58, - S_EVENT_HUMAN_AIRCRAFT_REPAIR_START = 59, - S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH = 60, - S_EVENT_MAX = 61, - } - end - - ---@enum BirthPlace - world.BirthPlace = { - wsBirthPlace_Air = 1, - wsBirthPlace_RunWay = 2, - wsBirthPlace_Park = 3, - wsBirthPlace_Heliport_Hot = 4, - wsBirthPlace_Heliport_Cold = 5, - } - - do --Volumes - ---@class Volume - ---@field id VolumeType - - ---@class Segment - ---@field params SegmentParams - - ---@class SegmentParams - ---@field from Vec3 - ---@field to Vec3 - - ---@class Box : Volume - ---@field params BoxParams - - ---@class BoxParams - ---@field min Vec3 - ---@field max Vec3 - - ---@class Sphere : Volume - ---@field params SphereParams - - ---@class SphereParams - ---@field point Vec3 - ---@field radius number - - ---@class VolumePyramid : Volume - ---@field params VolumePyramidParams - - ---@class VolumePyramidParams - ---@field pos Vec3 - ---@field length number - ---@field halfAngleHor number - ---@field halfAngleVer number - end - - ---@enum VolumeType - world.VolumeType = { - SEGMENT = 1, - BOX = 2, - SPHERE = 3, - PYRAMID = 4 - } -end - -do -- coalition - ---@class coalition - ---@field addGroup fun(countryID: CountryID, groupCategory: GroupCategory, groupData: MissionGroupTable) : Group Spawn a group. If the name of the group or unit is already present the unit will be replaced. - ---@field addStaticObject fun(countryID: CountryID, data: MissionStaticObjectTable) : StaticObject Dynamically adds a static object to the mission. Name needs to be unique. UnitID and groupID can be ommitted and will be created automatically - ---@field getGroups fun(coalitionID : CoalitionSide, category: GroupCategory?) : Array returns group objects of a specific category (or all if no category is specified) - ---@field getStaticObjects fun(coalitionID: CoalitionSide) : Array returns static objects in a coalition - ---@field getAirbases fun(coalitionID: CoalitionSide) : Array returns airbases belonging to a coalition - ---@field getPlayers fun(coalitionID: CoalitionSide) : Array returns units currently occupied by players - ---@field getServiceProviders fun(coalitionID: CoalitionSide, serviceType: CoalitionService) : Array Returns all units that provide a specific service. (AWACS, Tankers etc.) - ---@field addRefPoint fun(coalitionID: CoalitionSide, refPoint: RefPoint) adds a new reference point to the coalition - ---@field getRefPoints fun(coalitionID: CoalitionSide) : table TODO: Ref point return table - ---@field getMainRefPoint fun(coalitionID: CoalitionSide) : Vec3 returns bullseye location for coalition - ---@field getCountryCoalition fun(countryID: CountryID) : CoalitionSide returns coalitionID for a specific countryID - coalition = coalition - - ---@class RefPoint - ---@field callsign number - ---@field type number - ---@field point Vec3 - - - do -- group tables - ---@class MissionGroupTable - end - - do -- static object tables - ---@class MissionStaticObjectTable - ---@field heading number - ---@field groupId number - ---@field shape_name string - ---@field type string - ---@field unitId number - ---@field rate number - ---@field name string - ---@field category string - ---@field x number - ---@field y number - ---@field dead boolean - end - - ---@enum CoalitionSide - coalition.side = { - NEUTRAL = 0, - RED = 1, - BLUE = 2, - } - - ---@enum CoalitionService - coalition.service = { - ATC = 0, - AWACS = 1, - TANKER = 2, - FAC = 3 - } -end - -do -- trigger - ---@class Trigger - ---@field action TriggerActions - ---@field misc TriggerMisc - trigger = trigger - - ---@class TriggerMisc - ---@field getUserFlag fun(flagName: string) : number returns the value of a user flag. - ---@field getZone fun(zoneName: string) : Array returns triggerzones. Only works for cilinders really. For more detailed zone data see: env.mission.triggers.zones - trigger.misc = trigger.misc - - ---@class TriggerActions : OutText, OutSound, OtherCommands, MarkCommands, AITriggerCommands - ---@field ctfColorTag fun(unitName: string, smokeColor: SmokePlumeColor, minAlt: number?) Created a smoke plume behind a specified aircraft - ---@field setUserFlag fun(flagName: string, userFlagValue: boolean|number) Sets a user flag to a specified value - ---@field explosion fun(point: Vec3, power: number) Creates an explosion at a given point at the specified power. - ---@field smoke fun(point: Vec3, color: SmokeColor) Creates colored smoke marker at a given point - ---@field effectSmokeBig fun(point: Vec3, effect: SmokeEffect, density: number, name: string?) Creates a large smoke effect on a vec3 point of a specified type and density. - ---@field effectSmokeStop fun(name: string) Stop a smoke effect effect of the passsed name - ---@field illuminationBomb fun(point: Vec3, power: number) Creates an ilumination bomb that will burn for 300 seconds - ---@field signalFlare fun(point: Vec3, flareColor: FlareColor, azimuth: number) Creates a signal flare at the given point in the specified color. The flare will be launched in the direction of the azimuth variable. - ---@field radioTransmission fun(fileName: string, point:Vec3, modulation: Modulation, loop: boolean, frequency: number, power: number, name: string?) Transmits an audio file to be broadcast over a specific frequency eneminating from the specified point. - ---@field stopRadioTransmission fun(name: string) Stops a radio transmission of the passed name - ---@field setUnitInternalCargo fun(unitName: string, mass: number) Sets the internal cargo for the specified unit at the specified mass - trigger.action = trigger.action - - ---@class OutSound - ---@field outSound fun(fileName: string) Plays a sound file to all players. - ---@field outSoundForCoalition fun(coalitionSide: CoalitionSide, soundFile: string) Plays a sound file to all players on the specified coalition. - ---@field outSoundForCountry fun(country: CountryID, soundfile: string) Plays a sound file to all players on the specified country. - ---@field outSoundForGroup fun(groupId: number, soundFile: string) Plays a sound file to all players in the specified group. - ---@field outSoundForUnit fun(unitId: number, soundFile: string) Plays a sound file to all players in the specified unit. - - ---@class OutText - ---@field outText fun(text: string, displayTime: number, clearview: boolean?) Displays the passed string of text for the specified time to all players. - ---@field outTextForCoalition fun(coalitionId: CoalitionSide, text: string, displayTime: number, clearview: boolean?) Displays the passed string of text for the specified time to all players belonging to the specified coalition. - ---@field outTextForCountry fun(country: CountryID, text: string, displayTime: number, clearview: boolean?) Displays the passed string of text for the specified time to all players belonging to the specified country. - ---@field outTextForGroup fun(groupId: number, text: string, displayTime: number, clearView: boolean?) Displays the passed string of text for the specified time to all players in the specified group. - ---@field outTextForUnit fun(unitId: number, text: string, displayTime: number, clearView: boolean?) Displays the passed string of text for the specified time to all players in the specified unit. - - ---@class OtherCommands - ---@field addOtherCommand fun(name: string, userFlagName: string, userFlagValue: string) Adds a command to the "F10 Other" radio menu allowing players to call commands and set flags within the mission. - ---@field removeOtherCommand fun(name: string) Removes the command that matches the specified name input variable from the "F10 Other" radio menu. - ---@field addOtherCommandForCoalition fun(coalitionId: CoalitionSide, name: string, userFlagName: string, userFlagValue: number) Adds a command to the "F10 Other" radio menu allowing players to call commands and set flags within the mission. - ---@field removeOtherCommandForCoalition fun(coalitionId: CoalitionSide, name: string) Removes the command that matches the specified name input variable from the "F10 Other" radio menu if the command was added for coalition. - ---@field addOtherCommandForGroup fun(groupId: number, name: string, userFlagName: string, userFlagValue: number) Adds a command to the "F10 Other" radio menu allowing players to call commands and set flags within the mission. - ---@field removeOtherCommandForGroup fun(groupId: number, name: string) Removes the command that matches the specified name input variable from the "F10 Other" radio menu if the command exists for the specified group. - - ---@class MarkCommands - ---@field markToAll fun(id: number, text:string, point:Vec3, readOnly: boolean?, message: string?) Adds a mark point to all on the F10 map with attached text. - ---@field markToCoalition fun(id: number, text: string, point: Vec3, coalitionID: CoalitionSide, readOnly: boolean?, message: string) Adds a mark point to a coalition on the F10 map with attached text. - ---@field markToGroup fun(id: number, text: string, point: Vec3, groupID: number, readOnly: boolean?, message: string?) Adds a mark point to a group on the F10 map with attached text. - ---@field removeMark fun(id: number) Removes a mark panel from the f10 map - ---@field markupToAll fun(shapeID: ShapeId, coalition: DrawCoalition, id: number, ... : any) Complex parameters.
See: https://wiki.hoggitworld.com/view/DCS_func_markupToAll - ---@field lineToAll fun(coalition: DrawCoalition, id: number, startPoint:Vec3, endPoint: Vec3, color:table, lineType: LineType, readonly: boolean?, message: string?) Creates a line on the F10 map from one point to another. - ---@field circleToAll fun(coalition: DrawCoalition, id: number, center: Vec3 , radius: number, color: table , fillColor: table , lineType: LineType , readOnly: boolean?, message: string?) Creates a circle on the map with a given radius, color, fill color, and outline. - ---@field rectToAll fun(coalition:DrawCoalition, id:number, startPoint: Vec3, endPoint:Vec3, color: table, fillColor: table, lineType: LineType , readOnly: boolean?, message: string?) Creates a rectangle on the map from the startpoint in one corner to the endPoint in the opposite corner. - ---@field quadToAll fun(coalition:DrawCoalition, id:number, point1: Vec3, point2:Vec3, point3:Vec3, point4:Vec3,color: table, fillColor: table, lineType: LineType , readOnly: boolean?, message: string?) Creates a shape defined by the 4 points on the F10 map. - ---@field textToAll fun(coalition:DrawCoalition, id: number, point:Vec3, color: table, fillColor: table, fontSize:number, readOnly:boolean, text:string) Creates a text imposed on the map at a given point. Text scales with the map. - ---@field arrowToAll fun(coalition:DrawCoalition, id: number, startPoint:Vec3, endPoint:Vec3, color:table, fillColor:table, lineType:LineType, readonly:boolean?, message: string?) Creates an arrow from the startPoint to the endPoint on the F10 map. The arrow will be "pointing at" the startPoint - ---@field setMarkupRadius fun(id:number, radius:number) Updates the radius of the specified mark to be the new value. - ---@field setMarkupText fun(id:number, text: string) Updates the text value of the passed mark to the passed text value. - ---@field setMarkupFontSize fun(id:number, fontSize:number) Updates the font size of the specified mark to be the new value. - ---@field setMarkupColor fun(id:number, color:table) Updates the color of the specified mark to be the new value. - ---@field setMarkupColorFill fun(id: number, color: table) Updates the fill color of the specified mark to be the new value. - ---@field setMarkupTypeLine fun(id:number, lineType: LineType) Updates the type line of the specified mark to be the new value. - ---@field setMarkupPositionEnd fun(id: number, end:Vec3) Updates the position of a mark that was defined at the last point given to create it. - ---@field setMarkupPositionStart fun(id: number, end:Vec3) Updates the position of a mark that was defined at the last point given to create it. - - ---@class AITriggerCommands - ---@field setAITask fun(group:Group, taskIndex:number) Sets the task of the specified index to be the one and only active task. - ---@field pushAITask fun(group:Group, taskIndex:number) Pushes the task of the specified index to the front of the tasking queue. - ---@field activateGroup fun(group:Group) Activates the specified group if it is setup for "late activation." Calls the Group.activate function. - ---@field deactivateGroup fun(group:Group) Deactivates the specified group. Calls the Group.destroy function. - ---@field setGroupAIOn fun(group:Group) Turns the specified groups AI on. Calls the Group.getController(setOnOff(true)) function. - ---@field setGroupAIOff fun(group:Group) Turns the specified groups AI off. Calls the Group.getController(setOnOff(false)) function. - ---@field groupStopMoving fun(group:Group) Orders the specified group to stop moving. Calls Group.getController(setCommand()) function and sets the stopRoute command to true. - ---@field groupContinueMoving fun(group:Group) Orders the specified group to resume moving. Calls Group.getController(setCommand()) function and sets the stopRoute command to false. - - ---@enum SmokeColor - trigger.smokeColor = { - Green = 0, - Red = 1, - White = 2, - Orange = 3, - Blue = 4 - } - - ---@enum FlareColor - trigger.flareColor = { - Green = 0, - Red = 1, - White = 2, - Yellow = 3 - } - - ---@alias SmokePlumeColor - ---| 0 Disabled - ---| 1 Green - ---| 2 Red - ---| 3 White - ---| 4 Orange - ---| 5 Blue - - ---@alias SmokeEffect - ---| 1 small smoke and fire - ---| 2 medium smoke and fire - ---| 3 large smoke and fire - ---| 4 huge smoke and fire - ---| 5 small smoke - ---| 6 medium smoke - ---| 7 large smoke - ---| 8 huge smoke - - ---@alias Modulation - ---| 0 AM - ---| 1 FM - - ---@alias ShapeId - ---| 1 Line - ---| 2 Circle - ---| 3 Rect - ---| 4 Arrow - ---| 5 Text - ---| 6 Quad - ---| 7 Freeform - - ---@alias LineType - ---| 0 No Line - ---| 1 Solid - ---| 2 Dashed - ---| 3 Dotted - ---| 4 Dot Dash - ---| 5 Long Dash - ---| 6 Two Dash - - ---@alias DrawCoalition - ---| -1 All - ---| 0 Neutral - ---| 1 Red - ---| 2 Blue - - ---@class TriggerZone - ---@field point Vec3 - ---@field radius number -end - -do --coord - ---@class Coord - ---@field LLtoLO fun(lattitude: number, longitude:number, altitude: number?):Vec3 Returns a point from latitude and longitude in the vec3 format. - ---@field LOtoLL fun(point:Vec3): lattitude:number, longitude:number, altitude:number Returns multiple values of a given vec3 point in latitude, longitude, and altitude - ---@field LLtoMGRS fun(lattitude: number, longitude:number):MGRS Returns an MGRS table from the latitude and longitude coordinates provided - ---@field MGRStoLL fun(mgrs:MGRS): lattitude:number, longitude:number, altitude:number Returns multiple values of a given in MGRS coordinates and converts it to latitude, longitude, and altitude - coord = coord -end - -do -- missioncommands - ---@class MissionCommands - ---@field addCommand fun(name:string, path: table?, functionToRun: function, argument: any?):table Adds a command to the "F10 Other" radio menu allowing players to run specified scripting functions. - ---@field addSubMenu fun(name: string, path: table?): table Creates a submenu of a specified name for all players. - ---@field removeItem fun(path:table?) Removes the item of the specified path from the F10 radio menu for all. - ---@field addCommandForCoalition fun(coalition: CoalitionSide, name: string, path: table?, functionToRun:function, argument:any?):table Adds a command to the "F10 Other" radio menu allowing players to run specified scripting functions. - ---@field addSubMenuForCoalition fun(coalition:CoalitionSide, name: string, path:table?):table Creates a submenu of a specified name for the specified coalition. Can be used to create nested sub menues. If the path is not specified, submenu is added to the root menu. - ---@field removeItemForCoalition fun(coalition:CoalitionSide, path:table?) Removes the item of the specified path from the F10 radio menu for the specified coalition. - ---@field addCommandForGroup fun(groupId: number, name: string, path: table?, functionToRun:function, argument:any?):table Adds a command to the "F10 Other" radio menu allowing players to run specified scripting functions. - ---@field addSubMenuForGroup fun(groupId: number, name:string, path:table?): table Creates a submenu of a specified name for the specified group. - ---@field removeItemForGroup fun(groupId: number, path:table?) Removes the item of the specified path from the F10 radio menu for the specified group. - missionCommands = missionCommands -end - -do -- net - ---@class Net - ---@field send_chat fun(message:string, all:boolean?) Sends a chat message. - ---@field send_chat_to fun(message: string, playerID: number, fromID: number?) Sends a chat message to the player with the passed id. - ---@field load_mission fun(name:string):boolean Loads the specified mission. - ---@field load_next_mission fun():boolean Load the next mission from the server mission list. - ---@field get_player_list fun():table? Returns players currently connected TODO: Document table - ---@field get_my_player_id fun():number returns playerID - ---@field get_server_id fun():number returns the playerID for the server (currently always 1) - ---@field get_player_info fun(playerID: number, attribute? : string) : PlayerInfo Returns a table of attributes for a given playerId. If optional attribute present only that value is returned - ---@field kick fun(playerID:number, message:string?):boolean Kicks a player from the server. Can display a message to the user. - ---@field get_stat fun(playeID:number, statID:number?):number - ---@field get_name fun(playerID:number):string get players name - ---@field get_slot fun(playerID:number):coalitionID:CoalitionSide,slotID:number gets coalitionID and slotID of a player - ---@field force_player_slot fun(playerID: number, sideID: CoalitionSide, slotID:number) forces a - ---@field lua2json fun(luaObject:any):string converts a lua object to a json string - ---@field json2lua fun(json: string):table converts a json string to a lua object - ---@field dostring_in fun(state:string, dostring:string):string Executes a lua string in a given lua environment in the game. - ---@field log fun(text:string) writes INFO log to, recommend using "env" singleton to log as it supports all levels. - net = net - - ---@class PlayerInfo - ---@field id number - ---@field name string - ---@field side CoalitionSide - ---@field slotID number - ---@field ping number - ---@field ipaddr string - ---@field ucid string - - -- undocumented - --@field set_slot - --@field recv_chat -end - -do -- Country - ---@class CountrySingleton - ---@field name table - country = country - - - ---@enum CountryID - country.id = { - RUSSIA = 1, - UKRAINE = 2, - USA = 3, - TURKEY = 4, - UK = 5, - FRANCE = 6, - GERMANY = 7, - CANADA = 8, - SPAIN = 9, - THE_NETHERLANDS = 10, - BELGIUM = 11, - NORWAY = 12, - DENMARK = 13, - ISRAEL = 14, - GEORGIA = 15, - INSURGENTS = 16, - ABKHAZIA = 17, - SOUTH_OSETIA = 18, - ITALY = 19 - } -end - ---======================== --- Classes ---======================== - - -do -- Controller - ---@class Controller - ---@field setTask fun(self:Controller, task: Task) Sets the task of the controller to the passed task - ---@field setCommand function - ---@field getDetectedTargets fun(self:Controller, type1: DetectionType?, type2: DetectionType?, type3: DetectionType?) : Array Returns a list of units that are detected by the controller.
Only works for unit controllers, not group controllers. - Controller = Controller - - ---@enum DetectionType - Controller.Detection = { - VISUAL = 1, - OPTIC = 2, - RADAR = 4, - IRST = 8, - RWR = 16, - DLINK = 32 - } - - ---@class DetectedObject - ---@field object Object - ---@field visible boolean - ---@field type boolean - ---@field distance boolean - - ---@alias TaskType - ---| "NoTask" - ---| "AttackGroup" attack group - ---| - - ---@class Task - ---@field id TaskType - - do -- Task Aliases - AI = AI - - ---@enum WeaponExpend - AI.Task.WeaponExpend = { - - } - end - - do --TaskWrapper - - ---@class WrapperTask : Task - - end - - do -- Main Tasks - ---@class MainTask : Task - - ---@class AttackGroup : MainTask - ---@field params AttackGroupParams - - ---@class AttackGroupParams - ---@field groupId number - ---@field weaponType WeaponFlag? - ---@field expend AI.Task.WeaponExpend? - ---@field directionEnabled boolean? - ---@field altitude number? - ---@field attackQtyLimit boolean? - ---@field attackQty number? - - ---@class AttackUnit : Task - ---@field params AttackUnitParams - - ---@class AttackUnitParams - ---@field unitId number - ---@field weaponType WeaponFlag? - ---@field expend AI.Task.WeaponExpend? - ---@field direction number? - ---@field attackQtyLimit boolean? - ---@field attackQty number? - ---@field groupAttack number? - - ---@class Bombing : Task - ---@field params BombingTaskParams - - ---@class BombingTaskParams - ---@field point Vec2 - ---@field attackQty number - ---@field weaponType WeaponFlag? - ---@field expend AI.Task.WeaponExpend? - ---@field attackQtyLimit boolean? - ---@field direction number? - ---@field groupAttack boolean? - ---@field altitude number? - ---@field altitudeEnabled boolean? - ---@field attackType string "DIVE" - - ---@class Strafing : Task - --[[ - TODO - ]] - - ---@class CarpetBombing : Task - --[[ - TODO - ]] - - ---@class AttackMapObject : Task - --[[ - TODO - ]] - - ---@class BombingRunway : Task - --[[ - TODO - ]] - - ---@class orbit : Task - --[[ - TODO - ]] - - ---@class refueling : Task - --[[ - TODO - ]] - - ---@class land : Task - --[[ - TODO - ]] - - ---@class follow : Task - --[[ - TODO - ]] - - ---@class followBigFormation : Task - --[[ - TODO - ]] - - ---@class escort : Task - --[[ - TODO - ]] - - ---@class Embarking : Task - --[[ - TODO - ]] - - ---@class fireAtPoint : Task - --[[ - TODO - ]] - - ---@class hold : Task - --[[ - TODO - ]] - - ---@class FAC_AttackGroup : Task - --[[ - TODO - ]] - - ---@class EmbarkToTransport : Task - --[[ - TODO - ]] - - ---@class DisembarkFromTransport : Task - --[[ - TODO - ]] - - ---@class CargoTransportation : Task - --[[ - TODO - ]] - - ---@class goToWaypoint : Task - --[[ - TODO - ]] - - ---@class groundEscort : Task - --[[ - TODO - ]] - - ---@class RecoveryTanker : Task - --[[ - TODO - ]] - - - end - - ---@class EnrouteTask : Task - - do -- AI - ---@class AI - AI = AI - AI = { - -- AI.Option - Option = { - -- AI.Option.Air - Air = { - - ---@enum AI.Option.Air.id - id = { - JETT_TANKS_IF_EMPTY = "25", - SILENCE = "7", - ROE = "0", - RADAR_USING = "3", - OPTION_RADIO_USAGE_ENGAGE = "22", - PROHIBIT_AG = "17", - ECM_USING = "13", - PROHIBIT_WP_PASS_REPORT = "19", - PROHIBIT_AA = "14", - REACTION_ON_THREAT = "1", - PREFER_VERTICAL = "32", - FORCED_ATTACK = "26", - PROHIBIT_AB = "16", - RTB_ON_OUT_OF_AMMO = "10", - MISSILE_ATTACK = "18", - PROHIBIT_JETT = "15", - OPTION_RADIO_USAGE_CONTACT = "21", - FLARE_USING = "4", - OPTION_RADIO_USAGE_KILL = "23", - FORMATION = "5", - RTB_ON_BINGO = "6", - NO_OPTION = "-1", - }, - - val = { - ---@enum AI.Option.Air.val.FLARE_USING - FLARE_USING = { - WHEN_FLYING_NEAR_ENEMIES = "3", - WHEN_FLYING_IN_SAM_WEZ = "2", - AGAINST_FIRED_MISSILE = "1", - NEVER = "0", - }, - - ---@enum AI.Option.Air.val.RADAR_USING - RADAR_USING = { - FOR_ATTACK_ONLY = "1", - FOR_SEARCH_IF_REQUIRED = "2", - NEVER = "0", - FOR_CONTINUOUS_SEARCH = "3", - }, - - ---@enum AI.Option.Air.val.REACTION_ON_THREAT - REACTION_ON_THREAT = { - BYPASS_AND_ESCAPE = "3", - EVADE_FIRE = "2", - NO_REACTION = "0", - PASSIVE_DEFENCE = "1", - ALLOW_ABORT_MISSION = "4", - }, - - ---@enum AI.Option.Air.val.MISSILE_ATTACK - MISSILE_ATTACK = { - HALF_WAY_RMAX_NEZ = "2", - MAX_RANGE = "0", - TARGET_THREAT_EST = "3", - RANDOM_RANGE = "4", - NEZ_RANGE = "1", - }, - - ---@enum AI.Option.Air.val.ECM_USING - ECM_USING = { - ALWAYS_USE = "3", - NEVER_USE = "0", - USE_IF_DETECTED_LOCK_BY_RADAR = "2", - USE_IF_ONLY_LOCK_BY_RADAR = "1", - }, - - ---@enum AI.Option.Air.val.ROE - ROE = { - WEAPON_FREE = "0", - RETURN_FIRE = "3", - OPEN_FIRE = "2", - WEAPON_HOLD = "4", - OPEN_FIRE_WEAPON_FREE = "1", - }, - } - }, - Ground = { - ---@enum AI.Option.Ground.id - id = { - EVASION_OF_ARM = "31", - ALARM_STATE = "9", - DISPERSE_ON_ATTACK = "8", - ENGAGE_AIR_WEAPONS = "20", - AC_ENGAGEMENT_RANGE_RESTRICTION = "24", - FORMATION = "5", - ROE = "0", - NO_OPTION = "-1", - }, - - val = { - ---@enum AI.Option.Ground.val.ALARM_STATE - ALARM_STATE = { - AUTO = "0", - GREEN = "1", - RED = "2", - }, - ---@enum AI.Option.Ground.val.ROE - ROE = { - OPEN_FIRE = "2", - WEAPON_HOLD = "4", - RETURN_FIRE = "3" - } - } - }, - Naval = { - ---@enum AI.Option.Naval.id - id = { - ROE = "0", - NO_OPTION = "-1", - }, - val = { - - ---@enum AI.Option.Naval.val.ROE - ROE = { - OPEN_FIRE = "2", - WEAPON_HOLD = "4", - RETURN_FIRE = "3", - }, - } - } - }, - Task = { - ---@enum AI.Task.OrbitPattern - OrbitPattern = { - RACE_TRACK = "Race-Track", - CIRCLE = "Circle", - }, - - ---@enum AI.Task.Designation - Designation = { - WP = "WP", - NO = "No", - LASER = "Laser", - IR_POINTER = "IR-Pointer", - AUTO = "Auto", - }, - - ---@enum AI.Task.TurnMethod - TurnMethod = { - FLY_OVER_POINT = "Fly Over Point", - FIN_POINT = "Fin Point", - }, - - ---@enum AI.Task.VehicleFormation - VehicleFormation = { - VEE = "Vee", - ECHELON_RIGHT = "EchelonR", - OFF_ROAD = "Off Road", - RANK = "Rank", - ECHELON_LEFT = "EchelonL", - ON_ROAD = "On Road", - CONE = "Cone", - DIAMOND = "Diamond", - }, - - ---@enum AI.Task.AltitudeType - AltitudeType = { - RADIO = "RADIO", - BARO = "BARO", - }, - - ---@enum AI.Task.WaypointType - WaypointType = { - TAKEOFF = "TakeOff", - TAKEOFF_PARKING = "TakeOffParking", - TURNING_POINT = "Turning Point", - TAKEOFF_PARKING_HOT = "TakeOffParkingHot", - LAND = "Land", - }, - - ---@enum AI.Task.WeaponExpend - WeaponExpend = { - QUARTER = "Quarter", - TWO = "Two", - ONE = "One", - FOUR = "Four", - HALF = "Half", - ALL = "All", - }, - }, - ---@enum AI.Skill - Skill = { - PLAYER = "Player", - AVERAGE = "Average", - HIGH = "High", - EXCELLENT = "Excellent", - GOOD = "Good", - CLIENT = "Client" - } - } - end -end - -do -- Object - ---@class Object - ---@field getName fun(self:Object): string returns the name of the object in the mission environment - ---@field isExist fun(self:Object): boolean Return a boolean value based on whether the object currently exists in the mission. - ---@field destroy fun(self:Object) Destroys the object, physically removing it from the game world without creating an event. - ---@field getCategory fun(self:Object): category: ObjectCategory, subCategory:number Returns an enumerator of the category for the specific object.
!!Dependent on how called. Check docs - ---@field getTypeName fun(self:Object): string returns the typename of the object - ---@field getDesc fun(self:Object):table returns description table, which is different for each type and subtype and even object - ---@field hasAttribute fun(self:Object, attribute:string):boolean Returns a boolean value if the object in question has the passed attribute. - ---@field getPoint fun(self:Object):Vec3 Returns a vec3 table of the x, y, and z coordinates for the position of the given object in 3D space. - ---@field getPosition fun(self:Object):Position Returns a Position3 table of the objects current position and orientation in 3D space. - ---@field getVelocity fun(self:Object):Vec3 Returns a vec3 table of the objects velocity vectors. - ---@field inAir fun(self:Object):boolean Returns a boolean value if the object in question is in the air. - Object = Object - - ---@enum ObjectCategory - Object.Category = { - UNIT = 1, - WEAPON = 2, - STATIC = 3, - BASE = 4, - SCENERY = 5, - Cargo = 6 - } -end - -do --SceneryObject - ---@class SceneryObject : Object - ---@field getLife fun(self:SceneryObject):number gets the life of a sceneryObject - ---@field getDescByName fun(typeName:string):table gets the description based on the typename - SceneryObject = SceneryObject -end - -do --CoalitionObject - ---@class CoalitionObject : Object - ---@field getCoalition fun(self:CoalitionObject):CoalitionSide Returns an enumerator that defines the coalition that an object currently belongs to. - ---@field getCountry fun(self:CoalitionObject):CountryID Returns an enumerator that defines the country that an object currently belongs to. - CoalitionObject = CoalitionObject -end - -do --Unit - ---@class Unit : CoalitionObject - ---@field Category UnitCategory - ---@field getByName fun(name: string): Unit? - ---@field getDescByName fun(typeName: string): table? - ---@field isActive fun(self:Unit): boolean Returns a boolean value if the unit is activated - ---@field getPlayerName fun(self:Unit): string? Returns a string value of the name of the player if the unit is currently controlled by a player - ---@field getID fun(self:Unit): number Returns a number which defines the unique mission id of a given object. - ---@field getNumber fun(self:Unit):number Returns a numerical value of the default index of the specified unit within the group as defined within the mission editor or addGroup scripting function. - ---@field getCategoryEx fun(self:Unit):number Returns an enumerator of the category for the specific object. - ---@field getObjectID fun(self:Unit):number Returns the runtime object Id associated with the unit. - ---@field getController fun(self:Unit):Controller Returns the controller of the specified object. - ---@field getGroup fun(self:Unit):Group Gets the group of the unit - ---@field getCallsign fun(self:Unit): string Returns a localized string of the units callsign - ---@field getLife fun(self:Unit): number Returns the current "life" of a unit. - ---@field getLife0 fun(self:Unit):number Returns the initial life value of a unit. - ---@field getFuel fun(self:Unit):number Returns a percentage of the current fuel remaining in an aircraft's inventory based on the maximum possible fuel load. - ---@field getAmmo fun(self:Unit):table returns ammo table TODO: specify table - ---@field getSensors fun(self:Unit):table returns sensors table TODO: Specify table - ---@field hasSensors fun(self:Unit, sensorType: SensorType?, subType:OpticType|RadarType|nil) : boolean Returns true if the unit has the specified sensors. - ---@field getRadar fun(self:Unit) : isOperational:boolean, mostInteresting: Object Returns two values. The first value is a boolean indicating if any radar on the Unit is operational. - ---@field getDrawArgumentValue fun(self:Unit, arg:number): number Returns the current value for an animation argument on the external model of the given object - ---@field getNearestCargos fun(self:Unit): table Returns a table of friendly cargo objects indexed numerically and sorted by distance from the helicopter. - ---@field enableEmission fun(self:Unit, setting:boolean) Sets the passed group or unit objects radar emitters on or off. - ---@field getDescentCapacity fun(self:Unit):number Returns the number of infantry that can be embark onto the aircraft. - Unit = Unit - - ---@enum UnitCategory - Unit.Category = { - AIRPLANE = 0, - HELICOPTER = 1, - GROUND_UNIT = 2, - SHIP = 3, - STRUCTURE = 4 - } - - ---@enum SensorType - Unit.SensorType = { - OPTIC = 0, - RADAR = 1, - IRST = 2, - RWR = 3 - } - - ---@enum OpticType - Unit.OpticType = { - TV = 0, --TV-sensor - LLTV = 1, --Low-level TV-sensor - IR = 2 --Infra-Red optic sensor - } - - ---@enum RadarType - Unit.RadarType = { - AS = 0, --air search radar - SS = 1 --surface/land search radar - } -end - -do --StaticObject - ---@class StaticObject : CoalitionObject - ---@field getByName fun(name:string): StaticObject? Returns a static object by name - ---@field getDescByName fun(typeName: string): table? returns the description of this static object type - ---@field getID fun(self:StaticObject):number Returns a number which defines the unique mission id of a given object. - ---@field getLife fun(self:StaticObject) number Returns the current "life" of a static object. - ---@field getCargoDisplayName fun(self:StaticObject): string Returns a string of a cargo objects mass in the format ' mass kg' - ---@field getCargoWeight fun(self:StaticObject): number Returns the mass of a cargo object measured in kg. - ---@field getDrawArgumentValue fun(self:Unit, arg:number): number Returns the current value for an animation argument on the external model of the given object - StaticObject = StaticObject -end - -do -- Airbase - ---@class Airbase: CoalitionObject - ---@field getByName fun(name:string): Airbase? Gets an Airfield by name - ---@field getDescByName fun(typeName: string): table? Gets airfield description - ---@field getCallsign fun(self:Airbase): string - ---@field getUnit fun(self:Airbase): Unit gets the unit associated with the airbase. (eg. Aircraft Carrier) - ---@field getID fun(self:Airbase): number Returns a number which defines the unique mission id of a given object. - ---@field getCategoryEx fun(self:Airbase):number Get category type - ---@field getParking fun(self:Airbase, available:boolean?): Array Returns a table of parking data for a given airbase. - ---@field getRunways fun(self:Airbase): Array Returns a table with runway information like length, width, course, and Name. - ---@field getTechObjectPos fun(self:Airbase, objectType:number|string): Vec3 Returns a table of vec3 objects corresponding to the passed value. - ---@field getDispatcherTowerPos fun(self:Airbase): Vec3 Returns the tower position - ---@field getRadioSilentMode fun(self:Airbase): boolean Returns a boolean value for whether or not the ATC for the passed airbase object has been silenced. - ---@field setRadioSilentMode fun(self:Airbase, silent:boolean) Sets the ATC belonging to an airbase object to be silent and unresponsive. - ---@field autoCapture fun(self:Airbase, setting:boolean) Enables or disables the airbase and FARP auto capture game mechanic where ownership of a base can change based on the presence of ground forces or the default setting assigned in the editor. - ---@field autoCaptureIsOn fun(self:Airbase): boolean Returns the current autoCapture setting for the passed base. - ---@field setCoalition fun(self:Airbase, side:CoalitionSide) Changes airbase's coalition to the set value. - ---@field getWarehouse fun(self:Airbase): Warehouse Returns the warehouse object associated with the airbase object. - Airbase = Airbase - - ---@class ParkingSpot - ---@field Term_Index number index - ---@field vTerminalPos Vec3 Position - ---@field TO_AC boolean - ---@field Term_Index_0 number - ---@field Term_Type TerminalType terminal type, see TerminalType - ---@field fDistToRW number distance to runway - - ---@class Runway - ---@field course number - ---@field Name string - ---@field position Vec3 - ---@field length number - ---@field width number - - ---@enum AirbaseCategory - Airbase.Category = { - AIRDROME = 0, - HELIPAD = 1, - SHIP = 2, - } - - ---@alias TerminalType - ---| 16 Valid spawn points on runway - ---| 40 Helicopter only spawn - ---| 68 Hardened Air Shelter - ---| 72 Open/Shelter air airplane only - ---| 100 Small shelter - ---| 104 Open air spawn -end - -do --Warehouse - ---@class Warehouse - ---@field getByName fun(name:string):Warehouse Returns an instance of the calling class for the object of a specified name. - ---@field getCargoAsWarehouse fun(object:StaticObject):Warehouse Returns a warehouse object that exists within the passed Static Object cargo crate. - ---@field addItem fun(self:Warehouse, itemType:string|table, count:number) Adds the passed amount of a given item to the warehouse. - ---@field getItemCount fun(self:Warehouse, itemType:string|table): number Returns the number of the passed type of item currently in a warehouse object. - ---@field setItem fun(self:Warehouse, itemType: string|table, count:number) Sets the passed amount of a given item to the warehouse. - ---@field removeItem fun(self:Warehouse, itemType:string|table, count:number) Removes the amount of the passed item from the warehouse. - ---@field addLiquid fun(self:Warehouse, liquidType:LiquidType, count:number) Adds the passed amount of a liquid fuel into the warehouse inventory - ---@field getLiquidAmount fun(self:Warehouse, liquidType:LiquidType): number Returns the amount of the passed liquid type within a given warehouse. - ---@field setLiquidAmount fun(self:Warehouse, liquidType:LiquidType, count:number) Sets the passed amount of a liquid fuel into the warehouse inventory - ---@field removeLiquid fun(self:Warehouse, liquidType:LiquidType, count:number) Removes the set amount of liquid from the inventory in a warehouse. - ---@field getOwner fun(self:Warehouse): Airbase Returns the airbase object associated with the warehouse object - ---@field getInventory fun(self:Warehouse, itemType: string|table): table Returns a full itemized list of everything currently in a warehouse. - Warehouse = Warehouse - - ---@alias LiquidType - ---|0 jetfuel - ---|1 Aviation gasoline - ---|2 MW50 - ---|3 Diesel -end - -do -- Weapon - ---@class Weapon : CoalitionObject - ---@field getLauncher fun(self:Weapon): Unit Returns the Unit object that had launched the weapon. - ---@field getTarget fun(self:Weapon): Object? Returns the target object that the weapon is guiding to. - ---@field getCategoryEx fun(self:Weapon): ObjectCategory Returns an enumerator of the category for the specific object. - Weapon = Weapon - - ---@enum WeaponCategory - Weapon.Category = { - SHELL = 0, - MISSILE = 1, - ROCKET = 2, - BOMB = 3 - } - - do -- WeaponFlag - ---@alias WeaponFlag - ---| 0 No Weapon - ---| 2 LGB - ---| 4 TvGB - ---| 8 SNSGB - ---| 16 HEBomb - ---| 32 Penetrator - ---| 64 NapalmBomb - ---| 128 FAEBomb - ---| 256 ClusterBomb - ---| 512 Dispencer - ---| 1024 CandleBomb - ---| 2147483648 ParachuteBomb - ---| 14 GuidedBomb (LGB + TvGB + SNSGB) - ---| 2147485680 AnyUnguidedBomb (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb) - ---| 2147485694 AnyBomb (GuidedBomb + AnyUnguidedBomb) - ---| 2048 LightRocket - ---| 4096 MarkerRocket - ---| 8192 CandleRocket - ---| 16384 HeavyRocket - ---| 30720 AnyRocket (LightRocket + MarkerRocket + CandleRocket + HeavyRocket) - ---| 32768 AntiRadarMissile - ---| 65536 AntiShipMissile - ---| 131072 AntiTankMissile - ---| 262144 FireAndForgetASM - ---| 524288 LaserASM - ---| 1048576 TeleASM - ---| 2097152 CruiseMissile - ---| 1073741824 AntiRadarMissile2 - ---| 8589934592 Decoys - ---| 1572864 GuidedASM (LaserASM + TeleASM) - ---| 1835008 TacticalASM (GuidedASM + FireAndForgetASM) - ---| 4161536 AnyASM (AntiRadarMissile + AntiShipMissile + AntiTankMissile + FireAndForgetASM + GuidedASM + CruiseMissile) - ---| 4194304 SRAAM - ---| 8388608 MRAAM - ---| 16777216 LRAAM - ---| 33554432 IR_AAM - ---| 67108864 SAR_AAM - ---| 134217728 AR_AAM - ---| 264241152 AnyAMM(IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM) - ---| 268402688 AnyMissile (ASM + AnyAAM) - ---| 36012032 AnyAutonomousMissile (IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile) - ---| 268435456 GUN_POD - ---| 536870912 BuiltInCannon - ---| 805306368 Cannons (GUN_POD + BuiltInCannon) - ---| 17179869184 SmokeShell - ---| 34359738368 Illumination Shell - ---| 51539607552 MarkerShell - ---| 68719476736 SubmunitionDispenserShell - ---| 137438953472 GuidedShell - ---| 206963736576 ConventionalShell - ---| 258503344128 AnyShell - ---| 4294967296 Torpedo - ---| 2956984318 AnyAGWeapon (BuiltInCannon + GUN_POD + AnyBomb + AnyRocket + AnyASM) - ---| 264241152 AnyAAWeapon (BuiltInCannon + GUN_POD + AnyAAM) - ---| 2952822768 UnguidedWeapon (Cannons + BuiltInCannon + GUN_POD + AnyUnguidedBomb + AnyRocket) - ---| 268402702 GuidedWeapon (GuidedBomb + AnyASM + AnyAAM) - ---| 3221225470 AnyWeapon (AnyBomb + AnyRocket + AnyMissile + Cannons) - ---| 13312 MarkerWeapon (MarkerRocket + CandleRocket + CandleBomb) - ---| 209379642366 ArmWeapon (AnyWeapon - MarkerWeapon) - end - - ---@enum GuidanceType - Weapon.GuidanceType = { - INS = 1, - IR = 2, - RADAR_ACTIVE = 3, - RADAR_SEMI_ACTIVE = 4, - RADAR_PASSIVE = 5, - TV = 6, - LASER = 7, - TELE = 8 - } - - ---@enum MissileCategory - Weapon.MissileCategory = { - AAM = 1, - SAM = 2, - BM = 3, - ANTI_SHIP = 4, - CRUISE = 5, - OTHER = 6 - } - - ---@enum WarheadType - Weapon.WarheadType = { - AP = 0, - HE = 1, - SHAPED_EXPLOSIVE = 2 - } -end - -do --Group - ---@class Group - ---@field getByName fun(name:string): Group? gets group by name - ---@field isExist fun(self:Group) : boolean checks if the Group currently exists - ---@field activate fun(self:Group) activates the group - ---@field destroy fun(self:Group) destroys a group - ---@field getCategory fun(self:Group): objectCategory: ObjectCategory, subCategory:number - ---@field getCoalition fun(self:Group): CoalitionSide - ---@field getName fun(self:Group):string - ---@field getID fun(self:Group):number - ---@field getUnit fun(self:Group, index:number): Unit? - ---@field getUnits fun(self:Group): Array - ---@field getSize fun(self:Group): number - ---@field getInitialSize fun(self:Group): number - ---@field getController fun(self:Group):Controller - ---@field enableEmission fun(self:Group, enabled:boolean) - Group = Group - - ---@enum GroupCategory - Group.Category = { - AIRPLANE = 0, - HELICOPTER = 1, - GROUND = 2, - SHIP = 3, - TRAIN = 4, - } -end - -do --Spot - ---@class Spot - ---@field createInfraRed fun(source:Object, localPoint: Vec3?, targetPoint:Vec3, lasercode:number?):Spot Creates an infrared ray emanating from the given object to a point in 3d space. Can be seen with night vision goggles. - ---@field createLaser fun(source:Object, localRef: Vec3, targetPoint:Vec3, lasercode:number?):Spot Creates a laser ray emanating from the given object to a point in 3d space. - ---@field destroy fun(self:Spot) destroys the laser/ir beam - ---@field getCategory fun(self:Spot) : ObjectCategory - ---@field getPoint fun(self:Spot): Vec3 - ---@field setPoint fun(self:Spot, point:Vec3) sets the target spot - ---@field getCode fun(self:Spot): number Returns the number that is used to define the laser code for which laser designation can track. - ---@field setCode fun(self:Spot, code:number) sets the laster code. - Spot = Spot -end diff --git a/classes/fleetClasses/GlobalFleetManager.lua b/classes/fleetClasses/GlobalFleetManager.lua deleted file mode 100644 index 51f5e7d..0000000 --- a/classes/fleetClasses/GlobalFleetManager.lua +++ /dev/null @@ -1,22 +0,0 @@ - - -local GlobalFleetManager = {} - -local fleetGroups = {} - -GlobalFleetManager.start = function(database) - - local logger = Spearhead.LoggerTemplate.new("CARRIERFLEET", "INFO") - - local all_groups = Spearhead.classes.helpers.MizGroupsManager.getAllGroupNames() - for _, groupName in pairs(all_groups) do - if Spearhead.Util.startswith(string.lower(groupName), "carriergroup" ) == true then - logger:info("Registering " .. groupName .. " as a managed fleet") - local carrierGroup = Spearhead.internal.FleetGroup:new(groupName, database, logger) - table.insert(fleetGroups, carrierGroup) - end - end -end - -if not Spearhead.internal then Spearhead.internal = {} end -Spearhead.internal.GlobalFleetManager = GlobalFleetManager \ No newline at end of file diff --git a/main.lua b/main.lua deleted file mode 100644 index 3f851b5..0000000 --- a/main.lua +++ /dev/null @@ -1,82 +0,0 @@ ---Single player purpose - -local defaultLogLevel = "INFO" - -if SpearheadConfig and SpearheadConfig.debugEnabled == true then - defaultLogLevel = "DEBUG" -end - -local startTime = timer.getTime() * 1000 - -Spearhead.Events.Init(defaultLogLevel) - -local dbLogger = Spearhead.LoggerTemplate.new("database", defaultLogLevel) -local standardLogger = Spearhead.LoggerTemplate.new("", defaultLogLevel) -local databaseManager = Spearhead.DB.New(dbLogger) -Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(defaultLogLevel) -- initiate - -local capConfig = Spearhead.internal.configuration.CapConfig:new(); -local stageConfig = Spearhead.internal.configuration.StageConfig:new(); - -local startingStage = stageConfig.startingStage or 1 -if SpearheadConfig and SpearheadConfig.Persistence and SpearheadConfig.Persistence.enabled == true then - standardLogger:info("Persistence enabled") - local persistenceLogger = Spearhead.LoggerTemplate.new("Persistence", defaultLogLevel) - Spearhead.classes.persistence.Persistence.Init(persistenceLogger) - - local persistanceStage = Spearhead.classes.persistence.Persistence.GetActiveStage() - if persistanceStage then - standardLogger:info("Persistance activated and using persistant active stage: " .. persistanceStage) - startingStage = persistanceStage - end -else - standardLogger:info("Persistence disabled") -end - -local spawnLogger = Spearhead.LoggerTemplate.new("SpawnManager", defaultLogLevel) -local spawnManager = Spearhead.classes.helpers.SpawnManager.new(spawnLogger) -local detectionLogger = Spearhead.LoggerTemplate.new("DetectionManager", defaultLogLevel) -local detectionManager = Spearhead.classes.capClasses.detection.DetectionManager.New(detectionLogger) - -Spearhead.classes.capClasses.GlobalCapManager.start(databaseManager, capConfig, detectionManager, stageConfig, defaultLogLevel, spawnManager) -Spearhead.internal.GlobalStageManager.NewAndStart(databaseManager, stageConfig, defaultLogLevel, spawnManager) - -Spearhead.internal.GlobalFleetManager.start(databaseManager) - -local SetStageDelayed = function(number, time) - Spearhead.Events.PublishStageNumberChanged(number) - return nil -end - -timer.scheduleFunction(SetStageDelayed, startingStage, timer.getTime() + 3) - -env.info(startTime .. "ms / " .. timer.getTime() * 1000 .. "ms") -local duration = (timer.getTime() * 1000) - startTime -standardLogger:info("Spearhead Initialisation duration: " .. tostring(duration) .. "ms") - -Spearhead.LoadingDone() - -Spearhead.internal.GlobalStageManager:printFullOverview() - ---Check lines of code in directory per file: --- Get-ChildItem . -Include *.lua -Recurse | foreach {""+(Get-Content $_).Count + " => " + $_.name }; GCI . -Include *.lua* -Recurse | foreach{(GC $_).Count} | measure-object -sum | % Sum --- find . -name '*.lua' | xargs wc -l - ---- ==================== DEBUG ORDER OR ZONE VEC =========================== --- local zone = Spearhead.DcsUtil.getZoneByName("MISSIONSTAGE_99") - --- local count = Spearhead.Util.tableLength(zone.verts) - --- for i = 1, count - 1 do - --- local a = zone.verts[i] --- local b = zone.verts[i+1] - --- local color = {0,0,0,1} - --- color[i] = 1 - --- trigger.action.textToAll(-1, 46+i , { x= a.x, y = 0, z = a.z } , color, {0,0,0}, 24 , true , "" .. i ) --- trigger.action.lineToAll(-1 , 56+i , { x= a.x, y = 0, z = a.z } , { x = b.x, y = 0, z = b.z } , color , 1, true) - --- end diff --git a/classes/_baseClasses/Queue.lua b/src/classes/_baseClasses/Queue.lua similarity index 84% rename from classes/_baseClasses/Queue.lua rename to src/classes/_baseClasses/Queue.lua index 5d50c5c..44b0a92 100644 --- a/classes/_baseClasses/Queue.lua +++ b/src/classes/_baseClasses/Queue.lua @@ -44,7 +44,4 @@ function Queue:toList() return items end - -if Spearhead == nil then Spearhead = {} end -if Spearhead._baseClasses == nil then Spearhead._baseClasses = {} end -Spearhead._baseClasses.Queue = Queue \ No newline at end of file +return Queue \ No newline at end of file diff --git a/classes/api/SpearheadApi.lua b/src/classes/api/SpearheadApi.lua similarity index 83% rename from classes/api/SpearheadApi.lua rename to src/classes/api/SpearheadApi.lua index 5fba5e4..4cbc90b 100644 --- a/classes/api/SpearheadApi.lua +++ b/src/classes/api/SpearheadApi.lua @@ -1,3 +1,6 @@ +local SpearheadEvents = require("classes.spearhead_events") +local GlobalStageManager = require("classes.stageClasses.GlobalStageManager") + ---@type Array local MissionCompleteListeners = {} @@ -16,18 +19,18 @@ SpearheadAPI = { return false, "stageNumber " .. stageNumber .. " is not a valid number" end - Spearhead.Events.PublishStageNumberChanged(stageNumber) + SpearheadEvents.PublishStageNumberChanged(stageNumber) return true, "" end, getCurrentStage = function() - return Spearhead.StageNumber or nil + return GlobalStageManager.getCurrentStage() or nil end, isStageComplete = function(stageNumber) if type(stageNumber) ~= "number" then return false, "stageNumber " .. stageNumber .. " is not a valid number" end - local isComplete = Spearhead.internal.GlobalStageManager.isStageComplete(stageNumber) + local isComplete = GlobalStageManager.isStageComplete(stageNumber) if isComplete == nil then return nil, "no stage found with number " .. stageNumber end diff --git a/classes/api/SpearheadApiDoc.lua b/src/classes/api/SpearheadApiDoc.lua similarity index 100% rename from classes/api/SpearheadApiDoc.lua rename to src/classes/api/SpearheadApiDoc.lua diff --git a/classes/capClasses/CapAirbase.lua b/src/classes/capClasses/CapAirbase.lua similarity index 92% rename from classes/capClasses/CapAirbase.lua rename to src/classes/capClasses/CapAirbase.lua index 345429f..db14a11 100644 --- a/classes/capClasses/CapAirbase.lua +++ b/src/classes/capClasses/CapAirbase.lua @@ -1,3 +1,11 @@ +local Util = require("classes.util.Util") +local CapGroup = require("classes.capClasses.airGroups.CapGroup") +local SweepGroup = require("classes.capClasses.airGroups.SweepGroup") +local InterceptGroup = require("classes.capClasses.airGroups.InterceptGroup") +local RunwayBombingTracker = require("classes.capClasses.runwayBombing.RunwayBombingTracker") +local SpearheadEvents = require("classes.util.SpearheadEvents") +local RunwayStrikeMission = require("classes.stageClasses.missions.RunwayStrikeMission") + ---@class CapBase : OnStageChangedListener ---@field private airbaseName string ---@field private logger table @@ -54,7 +62,7 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw local baseData = database:getAirbaseDataForZone(airbaseName) if baseData and baseData.CapGroups then for key, name in pairs(baseData.CapGroups) do - local capGroup = Spearhead.classes.capClasses.airGroups.CapGroup.New(name, capConfig, logger, spawnManager) + local capGroup = CapGroup.New(name, capConfig, logger, spawnManager) if capGroup then self.capGroupsByName[name] = capGroup end @@ -63,7 +71,7 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw if baseData and baseData.SweepGroups then for key, name in pairs(baseData.SweepGroups) do - local sweepGroup = Spearhead.classes.capClasses.airGroups.SweepGroup.New(name, capConfig, logger, spawnManager) + local sweepGroup = SweepGroup.New(name, capConfig, logger, spawnManager) if sweepGroup then self.sweepGroupsByName[name] = sweepGroup end @@ -72,22 +80,21 @@ function CapBase.new(airbaseName, database, logger, capConfig, stageConfig, runw if baseData and baseData.InterceptGroups then for key, name in pairs(baseData.InterceptGroups) do - local interceptGroup = Spearhead.classes.capClasses.airGroups.InterceptGroup.New(name, capConfig, logger, detectionManager, spawnManager) + local interceptGroup = InterceptGroup.New(name, capConfig, logger, detectionManager, spawnManager) if interceptGroup then self.interceptGroupsByName[name] = interceptGroup end end end - local capFlights = Spearhead.Util.tableLength(self.capGroupsByName) - local sweepFlights = Spearhead.Util.tableLength(self.sweepGroupsByName) - local interceptFlights = Spearhead.Util.tableLength(self.interceptGroupsByName) + local capFlights = Util.tableLength(self.capGroupsByName) + local sweepFlights = Util.tableLength(self.sweepGroupsByName) + local interceptFlights = Util.tableLength(self.interceptGroupsByName) logger:info(airbaseName .. " : " .. capFlights .." CAP | " .. sweepFlights .. " SWEEP | " .. interceptFlights .. " INTERCEPT") self:CreateRunwayStrikeMission(database) - - Spearhead.Events.AddStageNumberChangedListener(self) + SpearheadEvents.AddStageNumberChangedListener(self) timer.scheduleFunction(CheckStateContinuous, self, timer.getTime() + 15) @@ -111,7 +118,7 @@ function CapBase:CreateRunwayStrikeMission(database) " at airbase " .. self.airbaseName .. " with heading " .. runway.course .. " and length " .. runway.length .. " and width " .. runway.width) - local mission = Spearhead.classes.stageClasses.missions.RunwayStrikeMission.new(runway, self.airbaseName, + local mission = RunwayStrikeMission.new(runway, self.airbaseName, database, self.logger, self.runwayBombingTracker) self.runwayStrikeMissions[runway.Name] = mission end @@ -338,7 +345,7 @@ function CapBase:CheckAndScheduleIntercept() local unit = Unit.getByName(unitName) if unit and unit:isExist() then local unitPos = unit:getPoint() - if Spearhead.Util.is3dPointInZone(unitPos, zone) == true then + if Util.is3dPointInZone(unitPos, zone) == true then if not unitsToInterceptPerZone[zone.name] then unitsToInterceptPerZone[zone.name] = {} end @@ -356,7 +363,7 @@ function CapBase:CheckAndScheduleIntercept() for name, targets in pairs(unitsToInterceptPerZone) do local required = 0 - local nrTargets = Spearhead.Util.tableLength(targets) + local nrTargets = Util.tableLength(targets) if targets and nrTargets > 0 then required = math.ceil(nrTargets / ratio) end @@ -424,7 +431,4 @@ function CapBase:IsBaseActiveWhenStageIsActive(stageNumber) return false end -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end -Spearhead.classes.capClasses.CapAirbase = CapBase +return CapBase diff --git a/classes/capClasses/GlobalCapManager.lua b/src/classes/capClasses/GlobalCapManager.lua similarity index 62% rename from classes/capClasses/GlobalCapManager.lua rename to src/classes/capClasses/GlobalCapManager.lua index 8b25ca1..88f3edc 100644 --- a/classes/capClasses/GlobalCapManager.lua +++ b/src/classes/capClasses/GlobalCapManager.lua @@ -1,3 +1,9 @@ +local Logger = require("classes.util.Logger") +local Util = require("classes.util.Util") +local RunwayBombingTracker = require("classes.capClasses.runwayBombing.RunwayBombingTracker") +local CapAirbase = require("classes.capClasses.CapAirbase") + +---@class GlobalCapManager local GlobalCapManager = {} do local airbasesPerStage = {} @@ -17,9 +23,9 @@ do function GlobalCapManager.start(database, capConfig, detectionManager, stageConfig, logLevel, spawnManager) if initiated == true then return end - local logger = Spearhead.LoggerTemplate.new("AirbaseManager", logLevel) - local bombTrackLogger = Spearhead.LoggerTemplate.new("RunwayBombingTracker", logLevel) - local runwayBombingTracker = Spearhead.classes.capClasses.runwayBombing.RunwayBombingTracker.new(bombTrackLogger) + local logger = Logger.new("AirbaseManager", logLevel) + local bombTrackLogger = Logger.new("RunwayBombingTracker", logLevel) + local runwayBombingTracker = RunwayBombingTracker.new(bombTrackLogger) local zones = database:getStagezoneNames() if zones then @@ -32,9 +38,9 @@ do if airbaseNames then for _, airbaseName in pairs(airbaseNames) do if airbaseName then - local airbaseSpecificLogger = Spearhead.LoggerTemplate.new("CAP_" .. airbaseName, logLevel) + local airbaseSpecificLogger = Logger.new("CAP_" .. airbaseName, logLevel) - local airbase = Spearhead.classes.capClasses.CapAirbase.new(airbaseName, database, airbaseSpecificLogger, capConfig, stageConfig, runwayBombingTracker, detectionManager, spawnManager) + local airbase = CapAirbase.new(airbaseName, database, airbaseSpecificLogger, capConfig, stageConfig, runwayBombingTracker, detectionManager, spawnManager) if airbase then table.insert(airbasesPerStage[stageName], airbase) @@ -46,16 +52,14 @@ do end end - logger:info("Initiated " .. Spearhead.Util.tableLength(allAirbasesByName) .. " airbases for cap") + logger:info("Initiated " .. Util.tableLength(allAirbasesByName) .. " airbases for cap") initiated = true - local InfoFunctions = {} - ---returns if there is CAP active ---@param zoneName any ---@param activeZoneNumber number ---@return boolean - InfoFunctions.IsCapActiveWhenZoneIsActive = function(zoneName, activeZoneNumber) + GlobalCapManager.IsCapActiveWhenZoneIsActive = function(zoneName, activeZoneNumber) for _, airbase in pairs(airbasesPerStage[zoneName]) do if airbase:IsBaseActiveWhenStageIsActive(activeZoneNumber) == true then return true @@ -63,14 +67,7 @@ do end return false end - - Spearhead.capInfo = InfoFunctions end end - - -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end -Spearhead.classes.capClasses.GlobalCapManager = GlobalCapManager +return GlobalCapManager diff --git a/classes/capClasses/airGroups/AirGroup.lua b/src/classes/capClasses/airGroups/AirGroup.lua similarity index 92% rename from classes/capClasses/airGroups/AirGroup.lua rename to src/classes/capClasses/airGroups/AirGroup.lua index a70db61..ea5a5ce 100644 --- a/classes/capClasses/airGroups/AirGroup.lua +++ b/src/classes/capClasses/airGroups/AirGroup.lua @@ -1,3 +1,8 @@ +local SpearheadEvents = require("classes.spearhead_events") +local RTBMission = require("classes.capClasses.taskings.RTB") +local Util = require("classes.util.Util") +local DcsUtil = require("classes.util.DcsUtil") + ---@class AirGroup : OnUnitLostListener ---@field protected _logger Logger ---@field protected _groupName string @@ -26,13 +31,13 @@ function AirGroup:New(groupName, groupType, config, logger, spawnManager) local group = Group.getByName(self._groupName) if group then - Spearhead.Events.addOnGroupRTBListener(self._groupName, self) - Spearhead.Events.addOnGroupRTBInTenListener(self._groupName, self) - Spearhead.Events.addOnGroupOnStationListener(self._groupName, self) + SpearheadEvents.addOnGroupRTBListener(self._groupName, self) + SpearheadEvents.addOnGroupRTBInTenListener(self._groupName, self) + SpearheadEvents.addOnGroupOnStationListener(self._groupName, self) for _, unit in pairs(group:getUnits()) do - Spearhead.Events.addOnUnitLostEventListener(unit:getName(), self) - Spearhead.Events.addOnUnitLandEventListener(unit:getName(), self) + SpearheadEvents.addOnUnitLostEventListener(unit:getName(), self) + SpearheadEvents.addOnUnitLandEventListener(unit:getName(), self) end spawnManager:DestroyGroup(groupName) @@ -105,7 +110,7 @@ function AirGroup:SendRTB(airbase) end end if location then - local mission = Spearhead.classes.capClasses.taskings.RTB.getAsMission(airbase, { x= location.x, y= location.z }, self._config) + local mission = RTBMission.getAsMission(airbase, { x= location.x, y= location.z }, self._config) group:getController():setTask(mission) self._logger:debug("AirGroup:SendRTB - Task set for group: " .. self._groupName) end @@ -248,7 +253,7 @@ function AirGroup:OnLastUnitLanded() return time + 5 end - if pos and Spearhead.Util.VectorDistance3d(pos, data.lastLocations[unit:getName()]) > 10 then + if pos and Util.VectorDistance3d(pos, data.lastLocations[unit:getName()]) > 10 then data.lastChangeTime = time end data.lastLocations[unit:getName()] = pos @@ -426,14 +431,10 @@ do --EVENT LISTENERS end function AirGroup:Destroy() - Spearhead.DcsUtil.DestroyGroup(self._groupName) + self._spawnManager:DestroyGroup(self._groupName) end -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end -if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end -Spearhead.classes.capClasses.airGroups.AirGroup = AirGroup +return AirGroup ---@alias AirGroupState ---| "UnSpawned" diff --git a/classes/capClasses/airGroups/CapGroup.lua b/src/classes/capClasses/airGroups/CapGroup.lua similarity index 66% rename from classes/capClasses/airGroups/CapGroup.lua rename to src/classes/capClasses/airGroups/CapGroup.lua index 65a2e88..40319bb 100644 --- a/classes/capClasses/airGroups/CapGroup.lua +++ b/src/classes/capClasses/airGroups/CapGroup.lua @@ -1,3 +1,7 @@ +local AirGroup = require("classes.capClasses.airGroups.AirGroup") +local CAP = require("classes.capClasses.taskings.CAP") +local Util = require("classes.util.Util") +local MissionEditorWarner = require("classes.util.MissionEditorWarnings") ---@class CapGroup : AirGroup ---@field private _targetZoneIdPerStage table @@ -14,9 +18,9 @@ CapGroup.__index = CapGroup ---@return CapGroup function CapGroup.New(groupName, config, logger, spawnManager) - setmetatable(CapGroup, Spearhead.classes.capClasses.airGroups.AirGroup) + setmetatable(CapGroup, AirGroup) local self = setmetatable({}, CapGroup) --[[@as CapGroup]] - Spearhead.classes.capClasses.airGroups.AirGroup.New(self, groupName, "CAP", config, logger, spawnManager) + AirGroup.New(self, groupName, "CAP", config, logger, spawnManager) self._targetZoneIdPerStage = {} @@ -65,10 +69,10 @@ function CapGroup:SendToZone(zone, targetZoneID, airbase) end if isInAir == true then - local mission = Spearhead.classes.capClasses.taskings.CAP.getAsMission(self._groupName, airbase, zone, self._config) + local mission = CAP.getAsMission(self._groupName, airbase, zone, self._config) self:SetMission(mission) else - local mission = Spearhead.classes.capClasses.taskings.CAP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config) + local mission = CAP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config) if mission then self:SetMission(mission) else @@ -80,8 +84,8 @@ end ---@private function CapGroup:InitWithName(groupName) - local split_string = Spearhead.Util.split_string(groupName, "_") - local partCount = Spearhead.Util.tableLength(split_string) + local split_string = Util.split_string(groupName, "_") + local partCount = Util.tableLength(split_string) if partCount >= 3 then local configPart = split_string[2] @@ -95,34 +99,34 @@ function CapGroup:InitWithName(groupName) elseif first == "[" then self._isBackup = false else - Spearhead.AddMissionEditorWarning("Could not parse the CAP config for group: " .. groupName) + MissionEditorWarner.Add("Could not parse the CAP config for group: " .. groupName) return end - local subsplit = Spearhead.Util.split_string(configPart, "|") + local subsplit = Util.split_string(configPart, "|") if subsplit then for key, value in pairs(subsplit) do - local keySplit = Spearhead.Util.split_string(value, "]") + local keySplit = Util.split_string(value, "]") local targetZone = keySplit[2] local allActives = string.sub(keySplit[1], 2, #keySplit[1]) - local commaSeperated = Spearhead.Util.split_string(allActives, ",") + local commaSeperated = Util.split_string(allActives, ",") for _, value in pairs(commaSeperated) do - local dashSeperated = Spearhead.Util.split_string(value, "-") - if Spearhead.Util.tableLength(dashSeperated) > 1 then + local dashSeperated = Util.split_string(value, "-") + if Util.tableLength(dashSeperated) > 1 then local from = tonumber(dashSeperated[1]) local till = tonumber(dashSeperated[2]) for i = from, till do - if Spearhead.Util.strContains(targetZone, "A") == true then + if Util.strContains(targetZone, "A") == true then self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i)) else self._targetZoneIdPerStage[tostring(i)] = targetZone end end else - if Spearhead.Util.strContains(targetZone, "A") == true then + if Util.strContains(targetZone, "A") == true then self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A", tostring(dashSeperated[1])) else self._targetZoneIdPerStage[tostring(dashSeperated[1])] = targetZone @@ -132,15 +136,11 @@ function CapGroup:InitWithName(groupName) end end - env.info("Capgroup parsed with table: " .. Spearhead.Util.toString(self._targetZoneIdPerStage)) + env.info("Capgroup parsed with table: " .. Util.toString(self._targetZoneIdPerStage)) else - Spearhead.AddMissionEditorWarning("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount) + MissionEditorWarner.Add("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount) end end -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end -if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end -Spearhead.classes.capClasses.airGroups.CapGroup = CapGroup \ No newline at end of file +return CapGroup \ No newline at end of file diff --git a/classes/capClasses/airGroups/InterceptGroup.lua b/src/classes/capClasses/airGroups/InterceptGroup.lua similarity index 83% rename from classes/capClasses/airGroups/InterceptGroup.lua rename to src/classes/capClasses/airGroups/InterceptGroup.lua index 5c99313..bfe98fa 100644 --- a/classes/capClasses/airGroups/InterceptGroup.lua +++ b/src/classes/capClasses/airGroups/InterceptGroup.lua @@ -1,3 +1,9 @@ +local AirGroup = require("classes.capClasses.airGroups.AirGroup") +local INTERCEPT = require("classes.capClasses.taskings.INTERCEPT") +local Util = require("classes.util.Util") +local DcsUtil = require("classes.util.DcsUtil") +local MissionEditorWarner = require("classes.util.MissionEditorWarnings") + ---@class InterceptGroup : AirGroup ---@field private _targetNames Array ---@field private _targetZoneName string @@ -22,9 +28,9 @@ InterceptGroup.__index = InterceptGroup ---@return InterceptGroup? function InterceptGroup.New(groupName, config, logger, detectionManager, spawnManager) - setmetatable(InterceptGroup, Spearhead.classes.capClasses.airGroups.AirGroup) + setmetatable(InterceptGroup, AirGroup) local self = setmetatable({}, InterceptGroup) - Spearhead.classes.capClasses.airGroups.AirGroup.New(self, groupName, "INTERCEPT", config, logger, spawnManager) + AirGroup.New(self, groupName, "INTERCEPT", config, logger, spawnManager) local group = Group.getByName(groupName) if not group then @@ -135,7 +141,7 @@ function InterceptGroup:GetClosestTarget() local targetUnit = Unit.getByName(targetName) if targetUnit and targetUnit:isExist() then local pos = targetUnit:getPoint() - local distance = Spearhead.Util.VectorDistance3d(groupPoint, pos) + local distance = Util.VectorDistance3d(groupPoint, pos) if distance < closestDistance then closestDistance = distance closestUnit = targetUnit @@ -179,7 +185,7 @@ function InterceptGroup:UpdateTask() end end - if Spearhead.DcsUtil.IsBingoFuel(self._groupName) then + if DcsUtil.IsBingoFuel(self._groupName) then self._logger:debug("InterceptGroup: " .. self._groupName .. " is at bingo fuel, returning to base") self:SendRTB(self._airbase) return nil -- Return to base if bingo fuel @@ -187,7 +193,7 @@ function InterceptGroup:UpdateTask() if selfDetected and self:IsInAir() == true then if self._currentTargetName and self._currentTargetName == closestUnit:getName() then - local distance = Spearhead.Util.VectorDistance3d(closestUnit:getPoint(), groupPoint) + local distance = Util.VectorDistance3d(closestUnit:getPoint(), groupPoint) if distance < 30 * 1852 then -- If the target is within 10 nautical miles, continue attacking self._logger:debug("InterceptGroup: " .. self._groupName .. " continues attacking target " .. closestUnit:getName()) @@ -199,7 +205,7 @@ function InterceptGroup:UpdateTask() local vec3 = closestUnit:getPoint() local vec2 = { x = vec3.x, y = vec3.z } local groupPointVec2 = { x = groupPoint.x, y = groupPoint.z } - local mission = Spearhead.classes.capClasses.taskings.INTERCEPT.getUnitInterceptMissionFromAir(self._groupName, groupPointVec2, vec2, closestUnit, self._airbase, self._config, self._maxSpeed, alt) + local mission = INTERCEPT.getUnitInterceptMissionFromAir(self._groupName, groupPointVec2, vec2, closestUnit, self._airbase, self._config, self._maxSpeed, alt) self:SetMission(mission) self._currentTargetName = closestUnit:getName() return 15 -- Just continue attacking the detected target @@ -219,7 +225,7 @@ function InterceptGroup:UpdateTask() local mission = nil if self:IsInAir() == true then -- If the group is in the air, create an intercept mission - mission = Spearhead.classes.capClasses.taskings.INTERCEPT.getMissionFromInAir( + mission = INTERCEPT.getMissionFromInAir( self._groupName, { x = groupPoint.x, y = groupPoint.z }, interceptPoint, @@ -229,7 +235,7 @@ function InterceptGroup:UpdateTask() alt ) else - mission = Spearhead.classes.capClasses.taskings.INTERCEPT.getMissionFromAirbase( + mission = INTERCEPT.getMissionFromAirbase( self._groupName, interceptPoint, self._airbase, @@ -310,33 +316,33 @@ end ---@private function InterceptGroup:InitWithName(groupName) - local split_string = Spearhead.Util.split_string(groupName, "_") - local partCount = Spearhead.Util.tableLength(split_string) + local split_string = Util.split_string(groupName, "_") + local partCount = Util.tableLength(split_string) if partCount >= 3 then local configPart = split_string[2] configPart = string.sub(configPart, 2, #configPart) - local subsplit = Spearhead.Util.split_string(configPart, "|") + local subsplit = Util.split_string(configPart, "|") if subsplit then for key, value in pairs(subsplit) do - local keySplit = Spearhead.Util.split_string(value, "]") + local keySplit = Util.split_string(value, "]") local targetZone = keySplit[2] local allActives = string.sub(keySplit[1], 2, #keySplit[1]) - local commaSeperated = Spearhead.Util.split_string(allActives, ",") + local commaSeperated = Util.split_string(allActives, ",") for _, value in pairs(commaSeperated) do - local dashSeperated = Spearhead.Util.split_string(value, "-") - if Spearhead.Util.tableLength(dashSeperated) > 1 then + local dashSeperated = Util.split_string(value, "-") + if Util.tableLength(dashSeperated) > 1 then local from = tonumber(dashSeperated[1]) local till = tonumber(dashSeperated[2]) for i = from, till do - if Spearhead.Util.strContains(targetZone, "A") == true then + if Util.strContains(targetZone, "A") == true then self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i)) else self._targetZoneIdPerStage[tostring(i)] = targetZone end end else - if Spearhead.Util.strContains(targetZone, "A") == true then + if Util.strContains(targetZone, "A") == true then self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A", tostring(dashSeperated[1])) else self._targetZoneIdPerStage[tostring(dashSeperated[1])] = targetZone @@ -346,16 +352,12 @@ function InterceptGroup:InitWithName(groupName) end end - env.info("interceptGroup parsed with table: " .. Spearhead.Util.toString(self._targetZoneIdPerStage)) + env.info("interceptGroup parsed with table: " .. Util.toString(self._targetZoneIdPerStage)) else - Spearhead.AddMissionEditorWarning("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount) + MissionEditorWarner.Add("CAP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount) end end -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end -if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end -Spearhead.classes.capClasses.airGroups.InterceptGroup = InterceptGroup \ No newline at end of file +return InterceptGroup \ No newline at end of file diff --git a/classes/capClasses/airGroups/SweepGroup.lua b/src/classes/capClasses/airGroups/SweepGroup.lua similarity index 67% rename from classes/capClasses/airGroups/SweepGroup.lua rename to src/classes/capClasses/airGroups/SweepGroup.lua index af77f6f..4ad1812 100644 --- a/classes/capClasses/airGroups/SweepGroup.lua +++ b/src/classes/capClasses/airGroups/SweepGroup.lua @@ -1,3 +1,8 @@ +local AirGroup = require("classes.capClasses.airGroups.AirGroup") +local SWEEP = require("classes.capClasses.taskings.SWEEP") +local Util = require("classes.util.Util") +local MissionEditorWarner = require("classes.util.MissionEditorWarnings") + ---@class SweepGroup : AirGroup ---@field _targetZoneIdPerStage table ---@field _currentTargetZoneID string? @@ -11,9 +16,9 @@ SweepGroup.__index = SweepGroup ---@param spawnManager SpawnManager ---@return SweepGroup function SweepGroup.New(groupName, config, logger, spawnManager) - setmetatable(SweepGroup, Spearhead.classes.capClasses.airGroups.AirGroup) + setmetatable(SweepGroup, AirGroup) local self = setmetatable({}, SweepGroup) --[[@as SweepGroup]] - Spearhead.classes.capClasses.airGroups.AirGroup.New(self, groupName, "SWEEP", config, logger, spawnManager) + AirGroup.New(self, groupName, "SWEEP", config, logger, spawnManager) self._targetZoneIdPerStage = {} self:InitWithName(groupName) @@ -47,7 +52,7 @@ function SweepGroup:SendToZone(zone, targetZoneID, airbase) local group = Group.getByName(self._groupName) - local mission = Spearhead.classes.capClasses.taskings.SWEEP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config) + local mission = SWEEP.getAsMissionFromAirbase(self._groupName, airbase, zone, self._config) if mission then ---@type SetTaskParams local params = { @@ -68,35 +73,35 @@ end ---@private function SweepGroup:InitWithName(groupName) - local split_string = Spearhead.Util.split_string(groupName, "_") - local partCount = Spearhead.Util.tableLength(split_string) + local split_string = Util.split_string(groupName, "_") + local partCount = Util.tableLength(split_string) if partCount >= 3 then local configPart = split_string[2] configPart = string.sub(configPart, 2, #configPart) - local subsplit = Spearhead.Util.split_string(configPart, "|") + local subsplit = Util.split_string(configPart, "|") if subsplit then for key, value in pairs(subsplit) do - local keySplit = Spearhead.Util.split_string(value, "]") + local keySplit = Util.split_string(value, "]") local targetZone = keySplit[2] local allActives = string.sub(keySplit[1], 2, #keySplit[1]) - local commaSeperated = Spearhead.Util.split_string(allActives, ",") + local commaSeperated = Util.split_string(allActives, ",") for _, value in pairs(commaSeperated) do - local dashSeperated = Spearhead.Util.split_string(value, "-") - if Spearhead.Util.tableLength(dashSeperated) > 1 then + local dashSeperated = Util.split_string(value, "-") + if Util.tableLength(dashSeperated) > 1 then local from = tonumber(dashSeperated[1]) local till = tonumber(dashSeperated[2]) for i = from, till do - if Spearhead.Util.strContains(targetZone, "A") == true then + if Util.strContains(targetZone, "A") == true then self._targetZoneIdPerStage[tostring(i)] = string.gsub(targetZone, "A", tostring(i)) else self._targetZoneIdPerStage[tostring(i)] = targetZone end end else - if Spearhead.Util.strContains(targetZone, "A") == true then + if Util.strContains(targetZone, "A") == true then self._targetZoneIdPerStage[tostring(dashSeperated[1])] = string.gsub(targetZone, "A", tostring(dashSeperated[1])) else @@ -107,15 +112,11 @@ function SweepGroup:InitWithName(groupName) end end - env.info("SweepGroup parsed with table: " .. Spearhead.Util.toString(self._targetZoneIdPerStage)) + env.info("SweepGroup parsed with table: " .. Util.toString(self._targetZoneIdPerStage)) else - Spearhead.AddMissionEditorWarning("SWEEP Group with name: " .. + MissionEditorWarner.Add("SWEEP Group with name: " .. groupName .. "should have at least 3 parts, but has " .. partCount) end end -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end -if not Spearhead.classes.capClasses.airGroups then Spearhead.classes.capClasses.airGroups = {} end -Spearhead.classes.capClasses.airGroups.SweepGroup = SweepGroup \ No newline at end of file +return SweepGroup \ No newline at end of file diff --git a/classes/capClasses/detection/DetectionManager.lua b/src/classes/capClasses/detection/DetectionManager.lua similarity index 93% rename from classes/capClasses/detection/DetectionManager.lua rename to src/classes/capClasses/detection/DetectionManager.lua index e0025cb..50b16b4 100644 --- a/classes/capClasses/detection/DetectionManager.lua +++ b/src/classes/capClasses/detection/DetectionManager.lua @@ -161,8 +161,4 @@ function DetectionManager:UpdateDetectedUnitsByCoalition(coalitionSide) end end -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end -if not Spearhead.classes.capClasses.detection then Spearhead.classes.capClasses.detection = {} end -Spearhead.classes.capClasses.detection.DetectionManager = DetectionManager +return DetectionManager diff --git a/classes/capClasses/runwayBombing/RunwayBombingTracker.lua b/src/classes/capClasses/runwayBombing/RunwayBombingTracker.lua similarity index 85% rename from classes/capClasses/runwayBombing/RunwayBombingTracker.lua rename to src/classes/capClasses/runwayBombing/RunwayBombingTracker.lua index b6a945c..a8366ae 100644 --- a/classes/capClasses/runwayBombing/RunwayBombingTracker.lua +++ b/src/classes/capClasses/runwayBombing/RunwayBombingTracker.lua @@ -1,3 +1,6 @@ +local SpearheadEvents = require("classes.spearhead_events") +local Util = require("classes.util.Util") + ---@class RunwayBombingTracker : OnWeaponFiredListener ---@field trackedRunways table ---@field private _logger Logger @@ -11,8 +14,7 @@ function RunwayBombingTracker.new(logger) local self = setmetatable({}, RunwayBombingTracker) self._logger = logger - - Spearhead.Events.AddWeaponFiredListener(self) + SpearheadEvents.AddWeaponFiredListener(self) return self end @@ -111,15 +113,10 @@ function RunwayBombingTracker:OnWeaponImpact(weaponDesc, impactPoint) local zone= strikeMission:GetRunwayZone() - if Spearhead.Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then + if Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then strikeMission:RunwayHit(impactPoint, explosiveMass) end end end - -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end -if not Spearhead.classes.capClasses.runwayBombing then Spearhead.classes.capClasses.runwayBombing = {} end -Spearhead.classes.capClasses.runwayBombing.RunwayBombingTracker = RunwayBombingTracker \ No newline at end of file +return RunwayBombingTracker \ No newline at end of file diff --git a/classes/capClasses/taskings/CAP.lua b/src/classes/capClasses/taskings/CAP.lua similarity index 88% rename from classes/capClasses/taskings/CAP.lua rename to src/classes/capClasses/taskings/CAP.lua index d198e39..ecc8f2b 100644 --- a/classes/capClasses/taskings/CAP.lua +++ b/src/classes/capClasses/taskings/CAP.lua @@ -1,3 +1,6 @@ +local Util = require("classes.util.Util") +local RTB = require("classes.capClasses.taskings.RTB") + ---@class CAPTasking local CAP = {} @@ -38,7 +41,7 @@ local function GetCAPPointFromTriggerZone(airBase, capZone) for indexA, pointA in ipairs(capZone.verts) do for indexB, pointB in ipairs(capZone.verts) do if pointA ~= pointB then - local distance = Spearhead.Util.VectorDistance2d(pointA, pointB) + local distance = Util.VectorDistance2d(pointA, pointB) if distance > furthestDistance then furthestDistance = distance furthestA = indexA @@ -58,12 +61,12 @@ local function GetCAPPointFromTriggerZone(airBase, capZone) local furthest = pointA local closest = pointB - local heading = Spearhead.Util.vectorHeadingFromTo(pointA, pointB) + local heading = Util.vectorHeadingFromTo(pointA, pointB) - if Spearhead.Util.VectorDistance2d(baseVec2, pointB) > Spearhead.Util.VectorDistance2d(baseVec2, pointA) then + if Util.VectorDistance2d(baseVec2, pointB) > Util.VectorDistance2d(baseVec2, pointA) then furthest = pointB closest = pointA - heading = Spearhead.Util.vectorHeadingFromTo(pointB, pointA) + heading = Util.vectorHeadingFromTo(pointB, pointA) end local distance = furthestDistance @@ -87,8 +90,8 @@ end local GetOutboundTask = function(airbase, capZone, capConfig) local airbaseVec3 = airbase:getPoint() local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z } - local heading = Spearhead.Util.vectorHeadingFromTo(airbaseVec2, capZone.location) - local point = Spearhead.Util.vectorMove(airbaseVec2, heading, 18520) + local heading = Util.vectorHeadingFromTo(airbaseVec2, capZone.location) + local point = Util.vectorMove(airbaseVec2, heading, 18520) return { alt = 2000, @@ -130,9 +133,9 @@ function CAP.getAsMissionFromAirbase(groupName, airbase, capZone, capConfig) [1] = GetOutboundTask(airbase, capZone, capConfig), [2] = GetOutboundTask(airbase, capZone, capConfig), [3] = CAP.getAsTasking(groupName, airbase, capZone, capConfig), - [4] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, capZone.location, capConfig), - [5] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase), - [6] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase) + [4] = RTB.getApproachPoint(airbase, capZone.location, capConfig), + [5] = RTB.getInitialPoint(airbase), + [6] = RTB.getLandingPoint(airbase) } local mission = { @@ -155,9 +158,9 @@ end function CAP.getAsMission(groupName, airbase, capZone, capConfig) local points = { [1] = CAP.getAsTasking(groupName, airbase, capZone, capConfig), - [2] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, capZone.location, capConfig), - [3] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase), - [4] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase) + [2] = RTB.getApproachPoint(airbase, capZone.location, capConfig), + [3] = RTB.getInitialPoint(airbase), + [4] = RTB.getLandingPoint(airbase) } local mission = { @@ -320,8 +323,4 @@ function CAP.getAsTasking(groupName, airbase, capZone, capConfig) } end -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end -if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end -Spearhead.classes.capClasses.taskings.CAP = CAP +return CAP diff --git a/classes/capClasses/taskings/INTERCEPT.lua b/src/classes/capClasses/taskings/INTERCEPT.lua similarity index 89% rename from classes/capClasses/taskings/INTERCEPT.lua rename to src/classes/capClasses/taskings/INTERCEPT.lua index a649be2..8ed2600 100644 --- a/classes/capClasses/taskings/INTERCEPT.lua +++ b/src/classes/capClasses/taskings/INTERCEPT.lua @@ -1,4 +1,5 @@ - +local Util = require("classes.util.Util") +local RTB = require("classes.capClasses.taskings.RTB") ---@class InterceptTasking local INTERCEPT = {} @@ -14,11 +15,11 @@ function INTERCEPT.getMissionFromAirbase(groupName, interceptPoint, airbase, con local airbaseVec3 = airbase:getPoint() local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z } - local heading = Spearhead.Util.vectorHeadingFromTo(airbaseVec2, interceptPoint) + local heading = Util.vectorHeadingFromTo(airbaseVec2, interceptPoint) env.info("BLAAT: heading from runway to first point: " .. heading) - local point = Spearhead.Util.vectorMove(airbaseVec2, heading, 3*1852) + local point = Util.vectorMove(airbaseVec2, heading, 3*1852) local pointA, pointB, pointC, pointD = INTERCEPT.getInterceptTaskPoint(groupName, point, interceptPoint, airbaseVec2, config, speed, alt) @@ -28,9 +29,9 @@ function INTERCEPT.getMissionFromAirbase(groupName, interceptPoint, airbase, con [3] = pointB, [4] = pointC, [5] = pointD, - [6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, interceptPoint, config), - [7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase), - [8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase) + [6] = RTB.getApproachPoint(airbase, interceptPoint, config), + [7] = RTB.getInitialPoint(airbase), + [8] = RTB.getLandingPoint(airbase) } local mission = { @@ -67,9 +68,9 @@ function INTERCEPT.getMissionFromInAir(groupName, currentPosition, interceptPoin [3] = pointB, [4] = pointC, [5] = pointD, - [6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, interceptPoint, config), - [7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase), - [8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase) + [6] = RTB.getApproachPoint(airbase, interceptPoint, config), + [7] = RTB.getInitialPoint(airbase), + [8] = RTB.getLandingPoint(airbase) } local mission = { @@ -109,9 +110,9 @@ function INTERCEPT.getUnitInterceptMissionFromAir(groupName, currentPoint, targe [3] = pointB, [4] = pointC, [5] = pointD, - [6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, currentPoint, config), - [7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase), - [8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase) + [6] = RTB.getApproachPoint(airbase, currentPoint, config), + [7] = RTB.getInitialPoint(airbase), + [8] = RTB.getLandingPoint(airbase) } local mission = { @@ -392,8 +393,4 @@ function INTERCEPT.getUnitInterceptTaskPoint(groupName, currentPoint, targetPosi end -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end -if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end -Spearhead.classes.capClasses.taskings.INTERCEPT = INTERCEPT \ No newline at end of file +return INTERCEPT \ No newline at end of file diff --git a/classes/capClasses/taskings/RTB.lua b/src/classes/capClasses/taskings/RTB.lua similarity index 82% rename from classes/capClasses/taskings/RTB.lua rename to src/classes/capClasses/taskings/RTB.lua index c00108c..7ebfa5a 100644 --- a/classes/capClasses/taskings/RTB.lua +++ b/src/classes/capClasses/taskings/RTB.lua @@ -1,3 +1,4 @@ +local Util = require("classes.util.Util") ---@class RTBTasking local RTB = {} @@ -33,7 +34,7 @@ local getRunwayIntoWindCourseRad = function(airbase) local runways = airbase:getRunways() local windVec = atmosphere.getWind(airbase:getPoint()) - local mPerS = Spearhead.Util.vectorMagnitude(windVec) + local mPerS = Util.vectorMagnitude(windVec) if mPerS > 0 then for i = 1, #runways do @@ -48,7 +49,7 @@ local getRunwayIntoWindCourseRad = function(airbase) end local runwayVec = {x = math.cos(rad), z = math.sin(rad), y = 0} - local alignment = Spearhead.Util.vectorAlignment(windVec, runwayVec) + local alignment = Util.vectorAlignment(windVec, runwayVec) if minAlignment == nil or alignment < minAlignment then activeCourse = i @@ -68,7 +69,7 @@ local getRunwayIntoWindCourseRad = function(airbase) end local runwayVec = {x = math.cos(rad), z = math.sin(rad), y = 0} - local alignment = Spearhead.Util.vectorAlignment(windVec, runwayVec) + local alignment = Util.vectorAlignment(windVec, runwayVec) if minAlignment == nil or alignment < minAlignment then activeCourse = i @@ -102,7 +103,7 @@ local function calcInitialPoint(airbase) local flipped = (heading + 180) % 360 local basePoint = airbase:getPoint() local basePointVec2 = {x = basePoint.x, y = basePoint.z} - local point = Spearhead.Util.vectorMove(basePointVec2, flipped, 22000) + local point = Util.vectorMove(basePointVec2, flipped, 22000) return point, flipped @@ -116,10 +117,10 @@ end function RTB.getApproachPoint(airbase, missionPoint, capConfig) local initialPoint, headingFromRunway = calcInitialPoint(airbase) - local pointA = Spearhead.Util.vectorMove(initialPoint, headingFromRunway - 45, 9000) - local pointB = Spearhead.Util.vectorMove(initialPoint, headingFromRunway + 45, 9000) + local pointA = Util.vectorMove(initialPoint, headingFromRunway - 45, 9000) + local pointB = Util.vectorMove(initialPoint, headingFromRunway + 45, 9000) - if Spearhead.Util.VectorDistance2d(missionPoint, pointA) > Spearhead.Util.VectorDistance2d(missionPoint, pointB) then + if Util.VectorDistance2d(missionPoint, pointA) > Util.VectorDistance2d(missionPoint, pointB) then pointA = pointB end @@ -194,8 +195,4 @@ function RTB.getLandingPoint(airbase) end -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end -if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end -Spearhead.classes.capClasses.taskings.RTB = RTB \ No newline at end of file +return RTB \ No newline at end of file diff --git a/classes/capClasses/taskings/SWEEP.lua b/src/classes/capClasses/taskings/SWEEP.lua similarity index 87% rename from classes/capClasses/taskings/SWEEP.lua rename to src/classes/capClasses/taskings/SWEEP.lua index 63bb99f..ed2ee40 100644 --- a/classes/capClasses/taskings/SWEEP.lua +++ b/src/classes/capClasses/taskings/SWEEP.lua @@ -1,3 +1,6 @@ +local Util = require("classes.util.Util") +local RTB = require("classes.capClasses.taskings.RTB") + ---@class SWEEPTasking local SWEEP = {} @@ -31,7 +34,7 @@ local function GetCAPPointFromTriggerZone(airBase, capZone) for indexA, pointA in ipairs(capZone.verts) do for indexB, pointB in ipairs(capZone.verts) do if pointA ~= pointB then - local distance = Spearhead.Util.VectorDistance2d(pointA, pointB) + local distance = Util.VectorDistance2d(pointA, pointB) if distance > furthestDistance then furthestDistance = distance furthestA = indexA @@ -51,12 +54,12 @@ local function GetCAPPointFromTriggerZone(airBase, capZone) local furthest = pointA local closest = pointB - local heading = Spearhead.Util.vectorHeadingFromTo(pointA, pointB) + local heading = Util.vectorHeadingFromTo(pointA, pointB) - if Spearhead.Util.VectorDistance2d(baseVec2, pointB) > Spearhead.Util.VectorDistance2d(baseVec2, pointA) then + if Util.VectorDistance2d(baseVec2, pointB) > Util.VectorDistance2d(baseVec2, pointA) then furthest = pointB closest = pointA - heading = Spearhead.Util.vectorHeadingFromTo(pointB, pointA) + heading = Util.vectorHeadingFromTo(pointB, pointA) end local distance = furthestDistance @@ -80,8 +83,8 @@ end local GetOutboundTask = function(airbase, capZone, capConfig) local airbaseVec3 = airbase:getPoint() local airbaseVec2 = { x = airbaseVec3.x, y = airbaseVec3.z } - local heading = Spearhead.Util.vectorHeadingFromTo(airbaseVec2, capZone.location) - local point = Spearhead.Util.vectorMove(airbaseVec2, heading, 18520) + local heading = Util.vectorHeadingFromTo(airbaseVec2, capZone.location) + local point = Util.vectorMove(airbaseVec2, heading, 18520) return { alt = 2000, @@ -128,9 +131,9 @@ function SWEEP.getAsMissionFromAirbase(groupName, airbase, capZone, capConfig) [3] = pointA, [4] = pointB, [5] = pointC, - [6] = Spearhead.classes.capClasses.taskings.RTB.getApproachPoint(airbase, capZone.location, capConfig), - [7] = Spearhead.classes.capClasses.taskings.RTB.getInitialPoint(airbase), - [8] = Spearhead.classes.capClasses.taskings.RTB.getLandingPoint(airbase) + [6] = RTB.getApproachPoint(airbase, capZone.location, capConfig), + [7] = RTB.getInitialPoint(airbase), + [8] = RTB.getLandingPoint(airbase) } local mission = { @@ -280,8 +283,4 @@ function SWEEP.getAsTasking(groupName, airbase, capZone, capConfig) return pointA, pointB, pointC end -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.capClasses then Spearhead.classes.capClasses = {} end -if not Spearhead.classes.capClasses.taskings then Spearhead.classes.capClasses.taskings = {} end -Spearhead.classes.capClasses.taskings.SWEEP = SWEEP +return SWEEP diff --git a/classes/configuration/CapConfig.lua b/src/classes/configuration/CapConfig.lua similarity index 92% rename from classes/configuration/CapConfig.lua rename to src/classes/configuration/CapConfig.lua index bac55f3..076cbe9 100644 --- a/classes/configuration/CapConfig.lua +++ b/src/classes/configuration/CapConfig.lua @@ -95,6 +95,4 @@ function CapConfig:getDeathDelay() return self._deathDelay end -if not Spearhead.internal then Spearhead.internal = {} end -if not Spearhead.internal.configuration then Spearhead.internal.configuration = {} end -Spearhead.internal.configuration.CapConfig = CapConfig; \ No newline at end of file +return CapConfig \ No newline at end of file diff --git a/classes/configuration/GlobalConfig.lua b/src/classes/configuration/GlobalConfig.lua similarity index 86% rename from classes/configuration/GlobalConfig.lua rename to src/classes/configuration/GlobalConfig.lua index 011efe2..102703c 100644 --- a/classes/configuration/GlobalConfig.lua +++ b/src/classes/configuration/GlobalConfig.lua @@ -29,6 +29,5 @@ function GlobalConfig:getBriefingTime() end -if Spearhead == nil then Spearhead = {} end -Spearhead.GlobalConfig = GlobalConfig.New() +return GlobalConfig diff --git a/classes/configuration/StageConfig.lua b/src/classes/configuration/StageConfig.lua similarity index 84% rename from classes/configuration/StageConfig.lua rename to src/classes/configuration/StageConfig.lua index 956f21d..6b617f5 100644 --- a/classes/configuration/StageConfig.lua +++ b/src/classes/configuration/StageConfig.lua @@ -7,8 +7,6 @@ --- @field startingStage integer --- @field maxMissionsPerStage integer --- @field AmountPreactivateStage integer - - local StageConfig = {}; ---comment @@ -34,6 +32,4 @@ function StageConfig:new() return o; end -if not Spearhead.internal then Spearhead.internal = {} end -if not Spearhead.internal.configuration then Spearhead.internal.configuration = {} end -Spearhead.internal.configuration.StageConfig = StageConfig; \ No newline at end of file +return StageConfig \ No newline at end of file diff --git a/classes/definitions/aliases.lua b/src/classes/definitions/aliases.lua similarity index 100% rename from classes/definitions/aliases.lua rename to src/classes/definitions/aliases.lua diff --git a/classes/fleetClasses/FleetGroup.lua b/src/classes/fleetClasses/FleetGroup.lua similarity index 63% rename from classes/fleetClasses/FleetGroup.lua rename to src/classes/fleetClasses/FleetGroup.lua index e792f82..2e703dc 100644 --- a/classes/fleetClasses/FleetGroup.lua +++ b/src/classes/fleetClasses/FleetGroup.lua @@ -1,3 +1,10 @@ +local Util = require("classes.util.Util") +local DcsUtil = require("classes.util.DcsUtil") +local MissionEditorWarning = require("classes.util.MissionEditorWarnings") +local RouteUtil = require("classes.spearhead_routeutil") +local SpearheadEvents = require("classes.spearhead_events") + +---@class FleetGroup local FleetGroup = {} ---comment @@ -13,9 +20,9 @@ function FleetGroup:new(fleetGroupName, database, logger) o.fleetGroupName = fleetGroupName o.logger = logger - local split_name = Spearhead.Util.split_string(fleetGroupName, "_") - if Spearhead.Util.tableLength(split_name) < 2 then - Spearhead.AddMissionEditorWarning("CARRIERGROUP should have at least 2 parts. CARRIERGROUP_") + local split_name = Util.split_string(fleetGroupName, "_") + if Util.tableLength(split_name) < 2 then + MissionEditorWarning.Add("CARRIERGROUP should have at least 2 parts. CARRIERGROUP_") return nil end o.fleetNameIdentifier = split_name[2] @@ -27,12 +34,12 @@ function FleetGroup:new(fleetGroupName, database, logger) do --INIT local carrierRouteZones = database:getCarrierRouteZones() for _, zoneName in pairs(carrierRouteZones) do - if Spearhead.Util.strContains(string.lower(zoneName), "_".. string.lower(o.fleetNameIdentifier) .. "_" ) == true then - local zone = Spearhead.DcsUtil.getZoneByName(zoneName) - if zone and zone.zone_type == Spearhead.DcsUtil.ZoneType.Polygon then - local split_string = Spearhead.Util.split_string(zoneName, "_") - if Spearhead.Util.tableLength(split_string) < 3 then - Spearhead.AddMissionEditorWarning( + if Util.strContains(string.lower(zoneName), "_".. string.lower(o.fleetNameIdentifier) .. "_" ) == true then + local zone = DcsUtil.getZoneByName(zoneName) + if zone and zone.zone_type == DcsUtil.ZoneType.Polygon then + local split_string = Util.split_string(zoneName, "_") + if Util.tableLength(split_string) < 3 then + MissionEditorWarning.Add( "CARRIERROUTE should at least have 3 parts. Check the documentation for: " .. zoneName) else @@ -48,7 +55,7 @@ function FleetGroup:new(fleetGroupName, database, logger) for ii = i + 1, 4 do local a = zone.verts[i] local b = zone.verts[ii] - local dist = Spearhead.Util.VectorDistance2d(a, b) + local dist = Util.VectorDistance2d(a, b) if biggest == nil or dist > biggest then biggestA = a @@ -65,17 +72,17 @@ function FleetGroup:new(fleetGroupName, database, logger) return nil, nil end - if Spearhead.Util.startswith(namePart, "%[") == true then - namePart = Spearhead.Util.split_string(namePart, "[")[1] + if Util.startswith(namePart, "%[") == true then + namePart = Util.split_string(namePart, "[")[1] end - if Spearhead.Util.strContains(namePart, "%]") == true then - namePart = Spearhead.Util.split_string(namePart, "]")[1] + if Util.strContains(namePart, "%]") == true then + namePart = Util.split_string(namePart, "]")[1] end - local split_numbers = Spearhead.Util.split_string(namePart, "-") - if Spearhead.Util.tableLength(split_numbers) < 2 then - Spearhead.AddMissionEditorWarning("CARRIERROUTE zone stage numbers not in the format _[-]: " .. zoneName) + local split_numbers = Util.split_string(namePart, "-") + if Util.tableLength(split_numbers) < 2 then + MissionEditorWarning.Add("CARRIERROUTE zone stage numbers not in the format _[-]: " .. zoneName) return nil, nil end @@ -83,7 +90,7 @@ function FleetGroup:new(fleetGroupName, database, logger) local second = tonumber(split_numbers[2]) if first == nil or second == nil then - Spearhead.AddMissionEditorWarning("CARRIERROUTE zone stage numbers not in the format _[-]: " .. zoneName) + MissionEditorWarning.Add("CARRIERROUTE zone stage numbers not in the format _[-]: " .. zoneName) return nil, nil end return first, second @@ -97,11 +104,11 @@ function FleetGroup:new(fleetGroupName, database, logger) end o.pointsPerZone[zoneName] = { pointA = { x = pointA.x, z = pointA.y, y = 0 }, pointB = { x = pointB.x, z = pointB.y, y = 0} } else - Spearhead.AddMissionEditorWarning("CARRIERROUTE zone stage numbers not in the format _[-]: " .. zoneName) + MissionEditorWarning.Add("CARRIERROUTE zone stage numbers not in the format _[-]: " .. zoneName) end end else - Spearhead.AddMissionEditorWarning("CARRIERROUTE cannot be a cilinder: " .. zoneName) + MissionEditorWarning.Add("CARRIERROUTE cannot be a cilinder: " .. zoneName) end end end @@ -115,7 +122,7 @@ function FleetGroup:new(fleetGroupName, database, logger) local group = Group.getByName(groupName) if group then - logger:info("Sending " .. fleetGroupName .. " to " .. targetZone) + logger:info("Sending " .. groupName .. " to " .. targetZone) group:getController():setTask(task) end end @@ -124,14 +131,13 @@ function FleetGroup:new(fleetGroupName, database, logger) local targetZone = self.targetZonePerStage[tostring(number)] if targetZone and targetZone ~= self.currentTargetZone then local points = self.pointsPerZone[targetZone] - local task = Spearhead.RouteUtil.CreateCarrierRacetrack(points.pointA, points.pointB) + local task = RouteUtil.CreateCarrierRacetrack(points.pointA, points.pointB) timer.scheduleFunction(SetTaskAsync, { task = task, targetZone = targetZone, groupName = self.fleetGroupName, logger = self.logger }, timer.getTime() + 5) end end - Spearhead.Events.AddStageNumberChangedListener(o) + SpearheadEvents.AddStageNumberChangedListener(o) return o end -if not Spearhead.internal then Spearhead.internal = {} end -Spearhead.internal.FleetGroup = FleetGroup +return FleetGroup diff --git a/src/classes/fleetClasses/GlobalFleetManager.lua b/src/classes/fleetClasses/GlobalFleetManager.lua new file mode 100644 index 0000000..11aa78c --- /dev/null +++ b/src/classes/fleetClasses/GlobalFleetManager.lua @@ -0,0 +1,26 @@ +local Logger = require("classes.util.Logger") +local Util = require("classes.util.Util") +local FleetGroup = require("classes.fleetClasses.FleetGroup") +local MizGroupsManager = require("classes.helpers.MizGroupsManager") + + +---@class GlobalFleetManager +local GlobalFleetManager = {} + +local fleetGroups = {} + +GlobalFleetManager.start = function(database) + + local logger = Logger.new("CARRIERFLEET", "INFO") + + local all_groups = MizGroupsManager.getAllGroupNames() + for _, groupName in pairs(all_groups) do + if Util.startswith(string.lower(groupName), "carriergroup" ) == true then + logger:info("Registering " .. groupName .. " as a managed fleet") + local carrierGroup = FleetGroup:new(groupName, database, logger) + table.insert(fleetGroups, carrierGroup) + end + end +end + +return GlobalFleetManager \ No newline at end of file diff --git a/classes/helpers/MizGroupsManager.lua b/src/classes/helpers/MizGroupsManager.lua similarity index 84% rename from classes/helpers/MizGroupsManager.lua rename to src/classes/helpers/MizGroupsManager.lua index 68ad5f8..1e03b9a 100644 --- a/classes/helpers/MizGroupsManager.lua +++ b/src/classes/helpers/MizGroupsManager.lua @@ -1,3 +1,5 @@ +local DcsUtil = require("classes.util.DcsUtil") + ---@class SpawnData ---@field groupTemplate table ---@field isStatic boolean @@ -14,17 +16,17 @@ MizGroupsManager._spawnTemplateData = {} do --init for coalition_name, coalition_data in pairs(env.mission.coalition) do - local coalition_nr = Spearhead.DcsUtil.stringToCoalition(coalition_name) + local coalition_nr = DcsUtil.stringToCoalition(coalition_name) if coalition_data.country then for country_index, country_data in pairs(coalition_data.country) do for category_name, categorydata in pairs(country_data) do - local category_id = Spearhead.DcsUtil.stringToGroupCategory(category_name) + local category_id = DcsUtil.stringToGroupCategory(category_name) if category_id ~= nil and type(categorydata) == "table" and categorydata.group ~= nil and type(categorydata.group) == "table" then for group_index, group in pairs(categorydata.group) do local name = group.name local skippable = false local isStatic = false - if category_id == Spearhead.DcsUtil.GroupCategory.STATIC then + if category_id == DcsUtil.GroupCategory.STATIC then isStatic = true local unit = group.units[1] if unit and unit.category == "Heliports" then @@ -75,7 +77,4 @@ function MizGroupsManager.getSpawnTemplateData(groupName) return MizGroupsManager._spawnTemplateData[groupName] end -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.helpers then Spearhead.classes.helpers = {} end -Spearhead.classes.helpers.MizGroupsManager = MizGroupsManager +return MizGroupsManager diff --git a/classes/helpers/SpawnManager.lua b/src/classes/helpers/SpawnManager.lua similarity index 90% rename from classes/helpers/SpawnManager.lua rename to src/classes/helpers/SpawnManager.lua index 15bdf08..14f4686 100644 --- a/classes/helpers/SpawnManager.lua +++ b/src/classes/helpers/SpawnManager.lua @@ -1,3 +1,8 @@ +local MizGroupsManager = require("classes.helpers.MizGroupsManager") +local Persistence = require("classes.persistence.Persistence") +local SpearheadEvents = require("classes.spearhead_events") +local Util = require("classes.util.Util") + ---@class SpawnManager : OnUnitLostListener ---@field private _persistedUnits table ---@field private _logger Logger @@ -29,7 +34,7 @@ end ---@return boolean isStatic function SpawnManager:SpawnGroup(groupName, overrides, isGroupPersistant) - local spawnData = Spearhead.classes.helpers.MizGroupsManager.getSpawnTemplateData(groupName) + local spawnData = MizGroupsManager.getSpawnTemplateData(groupName) if spawnData == nil then env.error("SpawnManager:SpawnGroup - No spawn template found for group: " .. groupName) @@ -67,7 +72,7 @@ end ---@param groupName string ---@return boolean function SpawnManager:IsGroupStatic(groupName) - local isStatic = Spearhead.classes.helpers.MizGroupsManager.IsGroupStatic(groupName) + local isStatic = MizGroupsManager.IsGroupStatic(groupName) if isStatic ~= nil then return isStatic end return StaticObject.getByName(groupName) ~= nil @@ -89,7 +94,7 @@ function SpawnManager:OnUnitLost(object) heading = heading end - Spearhead.classes.persistence.Persistence.UnitKilled( + Persistence.UnitKilled( name, object:getPoint(), heading, @@ -119,7 +124,7 @@ do --- privates country = override.countryID or spawnData.country end - local spawnTemplate = Spearhead.Util.deepCopyTable(spawnData.groupTemplate) + local spawnTemplate = Util.deepCopyTable(spawnData.groupTemplate) ---@type Array local removeableUnitNames = {} --[[ @@ -131,8 +136,8 @@ do --- privates for _, unit in pairs(spawnTemplate["units"]) do local name = unit["name"] - Spearhead.Events.addOnUnitLostEventListener(name, self) - local state = Spearhead.classes.persistence.Persistence.UnitState(name) + SpearheadEvents.addOnUnitLostEventListener(name, self) + local state = Persistence.UnitState(name) if state then if state.isDead == true then removeableUnitNames[#removeableUnitNames+1] = name @@ -170,7 +175,7 @@ do --- privates if isPersistent == true then self._persistedUnits[unit:getName()] = true end - Spearhead.Events.addOnUnitLostEventListener(unit:getName(), self) + SpearheadEvents.addOnUnitLostEventListener(unit:getName(), self) end return group @@ -195,10 +200,10 @@ do --- privates country = overrides.countryID or spawnData.country end - local spawnTemplate = Spearhead.Util.deepCopyTable(spawnData.groupTemplate) + local spawnTemplate = Util.deepCopyTable(spawnData.groupTemplate) --for static Objecst groupNames and unit names are the same and is always 1:1 - local persistentState = Spearhead.classes.persistence.Persistence.UnitState(groupName) + local persistentState = Persistence.UnitState(groupName) if persistentState then if persistentState.isDead == true then spawnTemplate["dead"] = true @@ -226,7 +231,7 @@ do --- privates if isPersistent == true then self._persistedUnits[groupName] = true - Spearhead.Events.addOnUnitLostEventListener(groupName, self) + SpearheadEvents.addOnUnitLostEventListener(groupName, self) end return object @@ -238,7 +243,7 @@ do --- privates if not unit then return end - local deadState = Spearhead.classes.persistence.Persistence.UnitState(unit:getName()) + local deadState = Persistence.UnitState(unit:getName()) if deadState and deadState.isDead == true then unit:destroy() -- Destroy the unit if it is dead local staticObject = { @@ -255,10 +260,7 @@ do --- privates end -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.helpers then Spearhead.classes.helpers = {} end -Spearhead.classes.helpers.SpawnManager = SpawnManager +return SpawnManager --Old Spawn Method diff --git a/classes/persistence/Persistence.lua b/src/classes/persistence/Persistence.lua similarity index 92% rename from classes/persistence/Persistence.lua rename to src/classes/persistence/Persistence.lua index 2c267a7..02e179e 100644 --- a/classes/persistence/Persistence.lua +++ b/src/classes/persistence/Persistence.lua @@ -1,4 +1,6 @@ +local Util = require("classes.util.Util") + ---@class Perstistence ---@field private _path string ---@field private _updateRequired boolean @@ -81,7 +83,7 @@ do if lua then tables = lua - logger:debug("Loaded persistence data: " .. Spearhead.Util.toString(lua)) + logger:debug("Loaded persistence data: " .. Util.toString(lua)) else logger:error("Could not load persistence file, using default tables") end @@ -143,8 +145,8 @@ do local latestFile, lastNumber = default, 0 for file in lfs.dir(dir) do - local split = Spearhead.Util.split_string(file, ".") - local doesStartWith = Spearhead.Util.startswith(file, startsWith, true) + local split = Util.split_string(file, ".") + local doesStartWith = Util.startswith(file, startsWith, true) if split and #split > 0 and split[#split] == EXTENSION and doesStartWith == true then local numberString = split[#split-1] local number = tonumber(numberString) @@ -179,7 +181,7 @@ do if SpearheadConfig.Persistence.fileName then local userFileName = SpearheadConfig.Persistence.fileName - local split = Spearhead.Util.split_string(userFileName, ".") + local split = Util.split_string(userFileName, ".") if not split and #split < 3 then split[#split+1] = "0" @@ -199,12 +201,12 @@ do end end - local split = Spearhead.Util.split_string(fileName, ".") - local matchingPart = table.concat(Spearhead.Util.sublist(split, 1, #split-2), ".") + local split = Util.split_string(fileName, ".") + local matchingPart = table.concat(Util.sublist(split, 1, #split-2), ".") local lastFile = getLastFileOrDefault(dir--[[@as string]], matchingPart, fileName) - local fileSplit = Spearhead.Util.split_string(lastFile, ".") + local fileSplit = Util.split_string(lastFile, ".") fileSplit[#fileSplit-1] = tostring(tonumber(fileSplit[#fileSplit-1]) + 1) fileName = table.concat(fileSplit, ".") @@ -329,7 +331,4 @@ do end end -if Spearhead == nil then Spearhead = {} end -if Spearhead.classes == nil then Spearhead.classes = {} end -if Spearhead.classes.persistence == nil then Spearhead.classes.persistence = {} end -Spearhead.classes.persistence.Persistence = Persistence \ No newline at end of file +return Persistence \ No newline at end of file diff --git a/classes/spearhead_db.lua b/src/classes/spearhead_db.lua similarity index 99% rename from classes/spearhead_db.lua rename to src/classes/spearhead_db.lua index d29b4a6..8e3e8df 100644 --- a/classes/spearhead_db.lua +++ b/src/classes/spearhead_db.lua @@ -1009,5 +1009,4 @@ function Database:GetNewMissionCode() ]] end -if not Spearhead then Spearhead = {} end -Spearhead.DB = Database +return Database diff --git a/classes/spearhead_events.lua b/src/classes/spearhead_events.lua similarity index 99% rename from classes/spearhead_events.lua rename to src/classes/spearhead_events.lua index 3efa807..2b2a09d 100644 --- a/classes/spearhead_events.lua +++ b/src/classes/spearhead_events.lua @@ -1,4 +1,4 @@ - +---@class SpearheadEvents local SpearheadEvents = {} do @@ -398,5 +398,4 @@ do world.addEventHandler(e) end -if Spearhead == nil then Spearhead = {} end -Spearhead.Events = SpearheadEvents \ No newline at end of file +return SpearheadEvents \ No newline at end of file diff --git a/classes/spearhead_routeutil.lua b/src/classes/spearhead_routeutil.lua similarity index 99% rename from classes/spearhead_routeutil.lua rename to src/classes/spearhead_routeutil.lua index 4d27da9..74cc5f5 100644 --- a/classes/spearhead_routeutil.lua +++ b/src/classes/spearhead_routeutil.lua @@ -1,3 +1,4 @@ +---@class SpearheadRouteUtil local ROUTE_UTIL = {} do --setup route util ---comment @@ -461,5 +462,4 @@ do --setup route util end end -if Spearhead == nil then Spearhead = {} end -Spearhead.RouteUtil = ROUTE_UTIL \ No newline at end of file +return ROUTE_UTIL \ No newline at end of file diff --git a/classes/stageClasses/GlobalStageManager.lua b/src/classes/stageClasses/GlobalStageManager.lua similarity index 98% rename from classes/stageClasses/GlobalStageManager.lua rename to src/classes/stageClasses/GlobalStageManager.lua index 54f9c80..d1f43f7 100644 --- a/classes/stageClasses/GlobalStageManager.lua +++ b/src/classes/stageClasses/GlobalStageManager.lua @@ -20,6 +20,8 @@ local currentStage = -99 local GlobalStageManager = {} GlobalStageManager.__index = GlobalStageManager +GlobalStageManager.getCurrentStage = function() return currentStage end + ---comment ---@param database Database ---@param stageConfig StageConfig @@ -45,6 +47,7 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa end } + Spearhead.Events.AddStageNumberChangedListener(OnStageNumberChangedListener) for _, stageName in pairs(database:getStagezoneNames()) do @@ -282,5 +285,4 @@ GlobalStageManager.isStageComplete = function (stageNumber) return true end -if not Spearhead.internal then Spearhead.internal = {} end -Spearhead.internal.GlobalStageManager = GlobalStageManager +return GlobalStageManager diff --git a/classes/stageClasses/Groups/SpearheadGroup.lua b/src/classes/stageClasses/Groups/SpearheadGroup.lua similarity index 92% rename from classes/stageClasses/Groups/SpearheadGroup.lua rename to src/classes/stageClasses/Groups/SpearheadGroup.lua index 52ba6fb..efccb30 100644 --- a/classes/stageClasses/Groups/SpearheadGroup.lua +++ b/src/classes/stageClasses/Groups/SpearheadGroup.lua @@ -1,5 +1,5 @@ - +local DcsUtil = require("classes.util.DcsUtil") ---@class SpearheadGroup : OnUnitLostListener ---@field private _groupName string @@ -149,7 +149,7 @@ end function SpearheadGroup:SetInvisible() if self._isStatic == true then - local country = Spearhead.DcsUtil.GetNeutralCountry() + local country = DcsUtil.GetNeutralCountry() ---@type SpawnOverrides local overrides = { @@ -187,7 +187,4 @@ function SpearheadGroup:SetVisible() end end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end -if not Spearhead.classes.stageClasses.Groups then Spearhead.classes.stageClasses.Groups = {} end -Spearhead.classes.stageClasses.Groups.SpearheadGroup = SpearheadGroup +return SpearheadGroup diff --git a/classes/stageClasses/Groups/SpearheadSceneryObject.lua b/src/classes/stageClasses/Groups/SpearheadSceneryObject.lua similarity index 73% rename from classes/stageClasses/Groups/SpearheadSceneryObject.lua rename to src/classes/stageClasses/Groups/SpearheadSceneryObject.lua index 6d26f56..873d674 100644 --- a/classes/stageClasses/Groups/SpearheadSceneryObject.lua +++ b/src/classes/stageClasses/Groups/SpearheadSceneryObject.lua @@ -1,3 +1,5 @@ +local Persistence = require("classes.persistence.Persistence") + ---@class SpearheadSceneryObject ---@field private persistentName string The persistent name of the scenery object ---@field private objectID number The ID of the scenery object @@ -47,7 +49,7 @@ end function SpearheadSceneryObject:MarkDead() if self.isDead == true then return end self.isDead = true - Spearhead.classes.persistence.Persistence.UnitKilled(self.persistentName, self:GetPoint(), 0, "Scenery") + Persistence.UnitKilled(self.persistentName, self:GetPoint(), 0, "Scenery") end function SpearheadSceneryObject:UpdateStatePersistently() @@ -55,7 +57,7 @@ function SpearheadSceneryObject:UpdateStatePersistently() return end - local state = Spearhead.classes.persistence.Persistence.UnitState(self.persistentName) + local state = Persistence.UnitState(self.persistentName) if state and state.isDead == true then trigger.action.explosion(self:GetPoint(), 1000) self.isDead = true @@ -71,7 +73,4 @@ function SpearheadSceneryObject:GetPoint() return Object.getPoint(self.internalObj) end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end -if not Spearhead.classes.stageClasses.Groups then Spearhead.classes.stageClasses.Groups = {} end -Spearhead.classes.stageClasses.Groups.SpearheadSceneryObject = SpearheadSceneryObject +return SpearheadSceneryObject diff --git a/classes/stageClasses/SpecialZones/BlueSam.lua b/src/classes/stageClasses/SpecialZones/BlueSam.lua similarity index 92% rename from classes/stageClasses/SpecialZones/BlueSam.lua rename to src/classes/stageClasses/SpecialZones/BlueSam.lua index 970a5d1..61243d4 100644 --- a/classes/stageClasses/SpecialZones/BlueSam.lua +++ b/src/classes/stageClasses/SpecialZones/BlueSam.lua @@ -148,10 +148,4 @@ function BlueSam:OnBuildingComplete() self:SpawnGroups() end - - -if Spearhead == nil then Spearhead = {} end -if Spearhead.classes == nil then Spearhead.classes = {} end -if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end -if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end -Spearhead.classes.stageClasses.SpecialZones.BlueSam = BlueSam +return BlueSam diff --git a/classes/stageClasses/SpecialZones/FarpZone.lua b/src/classes/stageClasses/SpecialZones/FarpZone.lua similarity index 90% rename from classes/stageClasses/SpecialZones/FarpZone.lua rename to src/classes/stageClasses/SpecialZones/FarpZone.lua index 5faf715..a871cbd 100644 --- a/classes/stageClasses/SpecialZones/FarpZone.lua +++ b/src/classes/stageClasses/SpecialZones/FarpZone.lua @@ -132,8 +132,4 @@ function FarpZone:SetPadsBlue() end end -if Spearhead == nil then Spearhead = {} end -if Spearhead.classes == nil then Spearhead.classes = {} end -if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end -if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end -Spearhead.classes.stageClasses.SpecialZones.FarpZone = FarpZone +return FarpZone diff --git a/classes/stageClasses/SpecialZones/StageBase.lua b/src/classes/stageClasses/SpecialZones/StageBase.lua similarity index 94% rename from classes/stageClasses/SpecialZones/StageBase.lua rename to src/classes/stageClasses/SpecialZones/StageBase.lua index 476194f..c808d2d 100644 --- a/classes/stageClasses/SpecialZones/StageBase.lua +++ b/src/classes/stageClasses/SpecialZones/StageBase.lua @@ -187,10 +187,4 @@ function StageBase:OnBuildingComplete() self:FinaliseBlueStage() end - - -if Spearhead == nil then Spearhead = {} end -if Spearhead.classes == nil then Spearhead.classes = {} end -if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end -if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end -Spearhead.classes.stageClasses.SpecialZones.StageBase = StageBase +return StageBase diff --git a/classes/stageClasses/SpecialZones/SupplyHub.lua b/src/classes/stageClasses/SpecialZones/SupplyHub.lua similarity index 86% rename from classes/stageClasses/SpecialZones/SupplyHub.lua rename to src/classes/stageClasses/SpecialZones/SupplyHub.lua index 8d578b7..3c5fb11 100644 --- a/classes/stageClasses/SpecialZones/SupplyHub.lua +++ b/src/classes/stageClasses/SpecialZones/SupplyHub.lua @@ -80,9 +80,4 @@ function SupplyHub:Activate() end - -if not Spearhead then Spearhead = {} end -if Spearhead.classes == nil then Spearhead.classes = {} end -if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end -if Spearhead.classes.stageClasses.SpecialZones == nil then Spearhead.classes.stageClasses.SpecialZones = {} end -Spearhead.classes.stageClasses.SpecialZones.SupplyHub = SupplyHub \ No newline at end of file +return SupplyHub \ No newline at end of file diff --git a/classes/stageClasses/SpecialZones/abstract/BuildableZone.lua b/src/classes/stageClasses/SpecialZones/abstract/BuildableZone.lua similarity index 93% rename from classes/stageClasses/SpecialZones/abstract/BuildableZone.lua rename to src/classes/stageClasses/SpecialZones/abstract/BuildableZone.lua index 42e3749..9294973 100644 --- a/classes/stageClasses/SpecialZones/abstract/BuildableZone.lua +++ b/src/classes/stageClasses/SpecialZones/abstract/BuildableZone.lua @@ -197,9 +197,4 @@ function BuildableZone:SpawnAmount(amount) return true end -if not Spearhead then Spearhead = {} end -Spearhead.classes = Spearhead.classes or {} -Spearhead.classes.stageClasses = Spearhead.classes.stageClasses or {} -Spearhead.classes.stageClasses.SpecialZones = Spearhead.classes.stageClasses.SpecialZones or {} -Spearhead.classes.stageClasses.SpecialZones.abstract = Spearhead.classes.stageClasses.SpecialZones.abstract or {} -Spearhead.classes.stageClasses.SpecialZones.abstract.BuildableZone = BuildableZone \ No newline at end of file +return BuildableZone \ No newline at end of file diff --git a/classes/stageClasses/Stages/BaseStage/Stage.lua b/src/classes/stageClasses/Stages/BaseStage/Stage.lua similarity index 97% rename from classes/stageClasses/Stages/BaseStage/Stage.lua rename to src/classes/stageClasses/Stages/BaseStage/Stage.lua index fc63302..912727c 100644 --- a/classes/stageClasses/Stages/BaseStage/Stage.lua +++ b/src/classes/stageClasses/Stages/BaseStage/Stage.lua @@ -575,11 +575,7 @@ function Stage:ActivateBlueStage() timer.scheduleFunction(ActivateBlueAsync, self, timer.getTime() + 3) end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end -if not Spearhead.classes.stageClasses.Stages then Spearhead.classes.stageClasses.Stages = {} end -if not Spearhead.classes.stageClasses.Stages.BaseStage then Spearhead.classes.stageClasses.Stages.BaseStage = {} end -Spearhead.classes.stageClasses.Stages.BaseStage.Stage = Stage +return Stage diff --git a/classes/stageClasses/Stages/ExtraStage.lua b/src/classes/stageClasses/Stages/ExtraStage.lua similarity index 84% rename from classes/stageClasses/Stages/ExtraStage.lua rename to src/classes/stageClasses/Stages/ExtraStage.lua index 2d10263..240be3d 100644 --- a/classes/stageClasses/Stages/ExtraStage.lua +++ b/src/classes/stageClasses/Stages/ExtraStage.lua @@ -61,9 +61,6 @@ function ExtraStage:OnStageNumberChanged(number) end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end -if not Spearhead.classes.stageClasses.Stages then Spearhead.classes.stageClasses.Stages = {} end -Spearhead.classes.stageClasses.Stages.ExtraStage = ExtraStage +return ExtraStage diff --git a/classes/stageClasses/Stages/PrimaryStage.lua b/src/classes/stageClasses/Stages/PrimaryStage.lua similarity index 68% rename from classes/stageClasses/Stages/PrimaryStage.lua rename to src/classes/stageClasses/Stages/PrimaryStage.lua index a7560d7..d305377 100644 --- a/classes/stageClasses/Stages/PrimaryStage.lua +++ b/src/classes/stageClasses/Stages/PrimaryStage.lua @@ -22,9 +22,6 @@ function PrimaryStage.New(database, stageConfig, logger, initData, spawnManager) end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end -if not Spearhead.classes.stageClasses.Stages then Spearhead.classes.stageClasses.Stages = {} end -Spearhead.classes.stageClasses.Stages.PrimaryStage = PrimaryStage +return PrimaryStage diff --git a/classes/stageClasses/Stages/WaitingStage.lua b/src/classes/stageClasses/Stages/WaitingStage.lua similarity index 87% rename from classes/stageClasses/Stages/WaitingStage.lua rename to src/classes/stageClasses/Stages/WaitingStage.lua index adfa12c..8c3e073 100644 --- a/classes/stageClasses/Stages/WaitingStage.lua +++ b/src/classes/stageClasses/Stages/WaitingStage.lua @@ -69,9 +69,6 @@ function WaitingStage:GetExpectedTime() return self._startTime + self._waitTimeSeconds end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end -if not Spearhead.classes.stageClasses.Stages then Spearhead.classes.stageClasses.Stages = {} end -Spearhead.classes.stageClasses.Stages.WaitingStage = WaitingStage +return WaitingStage diff --git a/classes/stageClasses/drawings/CustomDrawing.lua b/src/classes/stageClasses/drawings/CustomDrawing.lua similarity index 58% rename from classes/stageClasses/drawings/CustomDrawing.lua rename to src/classes/stageClasses/drawings/CustomDrawing.lua index 4b97dda..fa97160 100644 --- a/classes/stageClasses/drawings/CustomDrawing.lua +++ b/src/classes/stageClasses/drawings/CustomDrawing.lua @@ -1,9 +1,10 @@ +local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelper") +local Util = require("classes.util.Util") ---@class CustomDrawing ---@field private _id integer? ---@field private _drawingObject DrawingObject ----@field private _helper DrawingHelper ---@field private _startingStage number ---@field private _removeAtStage number local CustomDrawing = {} @@ -17,12 +18,11 @@ function CustomDrawing.New(drawingObject, id) local self = setmetatable({}, CustomDrawing) self._drawingObject = drawingObject self._id = id - self._helper = Spearhead.classes.stageClasses.drawings.helper.DrawingHelper local name = drawingObject.name - local split = Spearhead.Util.split_string(name or "", "_") + local split = Util.split_string(name or "", "_") local secondPart = split[2] or "1" - local splitPart = Spearhead.Util.split_string(secondPart, ":") + local splitPart = Util.split_string(secondPart, ":") self._startingStage = tonumber(splitPart[1]) or 1 self._removeAtStage = tonumber(splitPart[2]) or math.huge return self @@ -35,18 +35,14 @@ function CustomDrawing:GetStartAndStop() end function CustomDrawing:Draw() - self._id = self._helper.Draw(self._drawingObject) + self._id = DrawingHelper.Draw(self._drawingObject) end function CustomDrawing:Remove() if self._id ~= nil then - self._helper.Remove(self._id) + DrawingHelper.Remove(self._id) self._id = nil end end -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end -if not Spearhead.classes.stageClasses.drawings then Spearhead.classes.stageClasses.drawings = {} end -Spearhead.classes.stageClasses.drawings.CustomDrawing = CustomDrawing \ No newline at end of file +return CustomDrawing \ No newline at end of file diff --git a/classes/stageClasses/drawings/helper/DrawingHelper.lua b/src/classes/stageClasses/drawings/helper/DrawingHelper.lua similarity index 93% rename from classes/stageClasses/drawings/helper/DrawingHelper.lua rename to src/classes/stageClasses/drawings/helper/DrawingHelper.lua index c05262d..99d6c93 100644 --- a/classes/stageClasses/drawings/helper/DrawingHelper.lua +++ b/src/classes/stageClasses/drawings/helper/DrawingHelper.lua @@ -220,9 +220,4 @@ end ---@field public b number -if not Spearhead then Spearhead = {} end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end -if not Spearhead.classes.stageClasses.drawings then Spearhead.classes.stageClasses.drawings = {} end -if not Spearhead.classes.stageClasses.drawings.helper then Spearhead.classes.stageClasses.drawings.helper = {} end -Spearhead.classes.stageClasses.drawings.helper.DrawingHelper = DrawingHelper \ No newline at end of file +return DrawingHelper \ No newline at end of file diff --git a/classes/stageClasses/helpers/BattleManager.lua b/src/classes/stageClasses/helpers/BattleManager.lua similarity index 86% rename from classes/stageClasses/helpers/BattleManager.lua rename to src/classes/stageClasses/helpers/BattleManager.lua index 707c5ee..3dec4f3 100644 --- a/classes/stageClasses/helpers/BattleManager.lua +++ b/src/classes/stageClasses/helpers/BattleManager.lua @@ -1,3 +1,7 @@ +local Logger = require("classes.util.Logger") +local Util = require("classes.util.Util") +local DcsUtil = require("classes.util.DcsUtil") + ---@class BattleManager ---@field private _name string ---@field private _logger Logger @@ -21,7 +25,7 @@ function BattleManager.New(redGroups, blueGroups, name, logLevel) self._isActive = false self._name = name - self._logger = Spearhead.LoggerTemplate.new("BattleManager_" .. name, logLevel) + self._logger = Logger.new("BattleManager_" .. name, logLevel) self._redGroups = redGroups self._blueGroups = blueGroups @@ -167,7 +171,7 @@ function BattleManager:getBestAmmo(unit) end end - local entry = Spearhead.Util.randomFromList(shells) + local entry = Util.randomFromList(shells) if entry and entry.desc and entry.desc.warhead then local caliber = entry.desc.warhead.caliber if caliber > 50 then @@ -213,10 +217,10 @@ function BattleManager:ToShootingHulls(groups) end end - local hulls = Spearhead.Util.getSeparatedConvexHulls(points, 50) + local hulls = Util.getSeparatedConvexHulls(points, 50) local enlargedHulls = {} for _, hull in pairs(hulls) do - local enlarged = Spearhead.Util.enlargeConvexHull(hull, 25) + local enlarged = Util.enlargeConvexHull(hull, 25) if enlarged then table.insert(enlargedHulls, enlarged) end @@ -237,15 +241,15 @@ end ---@return Vec2? function BattleManager:GetRandomPoint(origin, groupHulls) - local hull = Spearhead.Util.randomFromList(groupHulls) --[[@as Array]] + local hull = Util.randomFromList(groupHulls) --[[@as Array]] if not hull then return nil end - local shootPoints = Spearhead.Util.GetTangentHullPointsFromOrigin(hull, origin) + local shootPoints = Util.GetTangentHullPointsFromOrigin(hull, origin) if debugDrawing == true then self:DrawDebugZone({ hull }) end - return Spearhead.Util.randomFromList(shootPoints) --[[@as Vec2]] + return Util.randomFromList(shootPoints) --[[@as Vec2]] end do --DEBUG @@ -258,7 +262,7 @@ do --DEBUG color = {r = 0, g = 0, b = 1, a = 1} end - Spearhead.DcsUtil.DrawLine(unit:getPoint(), {x = target.x, y = 0, z = target.y}, color, 1) + DcsUtil.DrawLine(unit:getPoint(), {x = target.x, y = 0, z = target.y}, color, 1) end ---@param hulls Array> @@ -274,13 +278,9 @@ do --DEBUG location = { x=drawHull[1].x, y=drawHull[1].y }, } - Spearhead.DcsUtil.DrawZone(zone, {r =0, g=0, b =1, a = 0.5} ,{r =0, g= 0, b =1, a = 0}, 1) + DcsUtil.DrawZone(zone, {r =0, g=0, b =1, a = 0.5} ,{r =0, g= 0, b =1, a = 0}, 1) end end end --DEBUG -if Spearhead == nil then Spearhead = {} end -if Spearhead.classes == nil then Spearhead.classes = {} end -if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end -if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end -Spearhead.classes.stageClasses.helpers.BattleManager = BattleManager +return BattleManager diff --git a/src/classes/stageClasses/helpers/MaxLoadConfig.lua b/src/classes/stageClasses/helpers/MaxLoadConfig.lua new file mode 100644 index 0000000..0d7f407 --- /dev/null +++ b/src/classes/stageClasses/helpers/MaxLoadConfig.lua @@ -0,0 +1,20 @@ +---@class MaxLoadConfig +---@field maxInternalLoad number + +---@type table +local MaxLoadConfig = { + ["Mi-8MT"] = { + maxInternalLoad = 4000, + }, + ["CH-47Fbl1"] = { + maxInternalLoad = 10000 + }, + ["Mi-24P"] = { + maxInternalLoad = 2000 + }, + ["UH-1H"] = { + maxInternalLoad = 2000 + } +} + +return MaxLoadConfig \ No newline at end of file diff --git a/classes/stageClasses/helpers/MissionCommandsHelper.lua b/src/classes/stageClasses/helpers/MissionCommandsHelper.lua similarity index 88% rename from classes/stageClasses/helpers/MissionCommandsHelper.lua rename to src/classes/stageClasses/helpers/MissionCommandsHelper.lua index 5c78a58..0b19dd3 100644 --- a/classes/stageClasses/helpers/MissionCommandsHelper.lua +++ b/src/classes/stageClasses/helpers/MissionCommandsHelper.lua @@ -1,3 +1,10 @@ +local Util = require("classes.util.Util") +local DcsUtil = require("classes.util.DcsUtil") +local Logger = require("classes.util.Logger") +local SpearheadEvents = require("classes.spearhead_events") +local SupplyUnitsTracker = require("classes.stageClasses.helpers.SupplyUnitsTracker") +local SupplyConfigHelper = require("classes.stageClasses.helpers.SupplyConfigHelper") + ---@class MissionCommandsHelper ---@field missionsByCode table @table of missions by their code @@ -17,8 +24,8 @@ MissionCommandsHelper.__index = MissionCommandsHelper ---@param groupPos Vec2 local function sortMissions(list, groupPos) table.sort(list, function(a, b) - local distA = Spearhead.Util.VectorDistance2d(groupPos, a.location or {x=0, y=0}) - local distB = Spearhead.Util.VectorDistance2d(groupPos, b.location or {x=0, y=0}) + local distA = Util.VectorDistance2d(groupPos, a.location or {x=0, y=0}) + local distB = Util.VectorDistance2d(groupPos, b.location or {x=0, y=0}) return distA < distB; end) end @@ -33,7 +40,7 @@ function MissionCommandsHelper.getOrCreate(logLevel) if instance == nil then instance = setmetatable({}, MissionCommandsHelper) - instance._logger = Spearhead.LoggerTemplate.new("MissionCommandsHelper", logLevel) + instance._logger = Logger.new("MissionCommandsHelper", logLevel) instance._logger:info("Creating MissionCommandsHelper instance") @@ -45,7 +52,7 @@ function MissionCommandsHelper.getOrCreate(logLevel) instance._supplyHubGroups = {} instance._stageBriefings = {} - instance._supplyUnitsTracker = Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker.getOrCreate(logLevel) + instance._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate(logLevel) ---comment ---@param selfA MissionCommandsHelper @@ -56,7 +63,7 @@ function MissionCommandsHelper.getOrCreate(logLevel) return time + 10 end - for _, unit in pairs(Spearhead.DcsUtil.getAllPlayerUnits()) do + for _, unit in pairs(DcsUtil.getAllPlayerUnits()) do if unit and unit:isExist() then local group = unit:getGroup() if group then @@ -71,7 +78,7 @@ function MissionCommandsHelper.getOrCreate(logLevel) end timer.scheduleFunction(instance.updateContinuous, instance, timer.getTime() + 5) - Spearhead.Events.AddOnPlayerEnterUnitListener(instance) + SpearheadEvents.AddOnPlayerEnterUnitListener(instance) end @@ -174,7 +181,7 @@ function MissionCommandsHelper:AddOverviewCommand(groupID) local text = "Missions Overview\n\n" - local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(id) + local group = DcsUtil.GetPlayerGroupByGroupID(id) ---@type Vec2 local groupPos = { x=0, y=0 } if group then @@ -193,7 +200,7 @@ function MissionCommandsHelper:AddOverviewCommand(groupID) if lead and lead:isExist() == true then local pos = lead:getPoint() local Vec2Pos = { x= pos.x, y=pos.z } - local distance = Spearhead.Util.VectorDistance2d(Vec2Pos, mission.location) / 1852 + local distance = Util.VectorDistance2d(Vec2Pos, mission.location) / 1852 distanceText = string.format("~%d", math.floor(distance)) end end @@ -323,7 +330,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID) local perFolder = 9 - local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID) + local group = DcsUtil.GetPlayerGroupByGroupID(groupID) ---@type Vec2 local groupPos = { x=0, y=0 } if group then @@ -351,7 +358,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID) for _, mission in pairs(primaryMissions) do count = count + 1 if count <= perFolder then - local copied = Spearhead.Util.deepCopyTable(path) + local copied = Util.deepCopyTable(path) self:addMissionCommands(groupID, copied, mission) else local name = "Next Menu ..." @@ -380,7 +387,7 @@ function MissionCommandsHelper:AddAllMissionCommandsToGroup(groupID) for _, mission in pairs(secondaryMissions) do count = count + 1 if count <= perFolder then - local copied = Spearhead.Util.deepCopyTable(path) + local copied = Util.deepCopyTable(path) self:addMissionCommands(groupID, copied, mission) else local name = "Next Menu ..." @@ -401,14 +408,14 @@ function MissionCommandsHelper:addMissionCommands(groupId, path, mission) if path then - local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupId) + local group = DcsUtil.GetPlayerGroupByGroupID(groupId) local distance = "[?]" if group then local lead = group:getUnit(1) if lead and lead:isExist() == true then local pos = lead:getPoint() local Vec2Pos = { x= pos.x, y=pos.z } - local dist = Spearhead.Util.VectorDistance2d(Vec2Pos, mission.location) / 1852 + local dist = Util.VectorDistance2d(Vec2Pos, mission.location) / 1852 distance = "[" .. string.format("~%dnM", math.floor(dist)) .. "]" end end @@ -435,7 +442,7 @@ function MissionCommandsHelper:AddSupplyHubCommandsIfApplicable(groupID) self._logger:debug("Adding supply hub commands for group: " .. tostring(groupID)) - local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID) + local group = DcsUtil.GetPlayerGroupByGroupID(groupID) if group == nil then return end local unit = group:getUnit(1) @@ -482,7 +489,7 @@ end function MissionCommandsHelper:AddCargoCommands(groupID) - local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID) + local group = DcsUtil.GetPlayerGroupByGroupID(groupID) if group == nil then return end local unit = group:getUnit(1) @@ -504,7 +511,7 @@ function MissionCommandsHelper:AddCargoCommands(groupID) local cargo = self._supplyUnitsTracker:GetCargoInUnit(unit:getID()) if cargo then for cargoType, amount in pairs(cargo) do - local cargoConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(cargoType) + local cargoConfig = SupplyConfigHelper.getSupplyConfig(cargoType) if cargoConfig then for i = 1, amount do local path = { [1] = folderNames.cargo } @@ -531,7 +538,7 @@ function MissionCommandsHelper:addMissionFolders(groupId) missionCommands.addSubMenuForGroup(groupId, folderNames.supplyHub) end - local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupId) + local group = DcsUtil.GetPlayerGroupByGroupID(groupId) if group == nil then return end local unit = group:getUnit(1) @@ -561,8 +568,4 @@ function MissionCommandsHelper:ResetFolders(groupID) self:addMissionFolders(groupID) end -if Spearhead == nil then Spearhead = {} end -if Spearhead.classes == nil then Spearhead.classes = {} end -if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end -if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end -Spearhead.classes.stageClasses.helpers.MissionCommandsHelper = MissionCommandsHelper +return MissionCommandsHelper diff --git a/classes/stageClasses/helpers/SupplyConfig.lua b/src/classes/stageClasses/helpers/SupplyConfigHelper.lua similarity index 67% rename from classes/stageClasses/helpers/SupplyConfig.lua rename to src/classes/stageClasses/helpers/SupplyConfigHelper.lua index baccfac..885bd87 100644 --- a/classes/stageClasses/helpers/SupplyConfig.lua +++ b/src/classes/stageClasses/helpers/SupplyConfigHelper.lua @@ -1,4 +1,6 @@ +local Util = require("classes.util.Util") + ---@alias SupplyType ---| "FARP_CRATE" ---| "SAM_CRATE" @@ -65,24 +67,6 @@ local SupplyConfig = { }, } ----@class MaxLoadConfig ----@field maxInternalLoad number - ----@type table -MaxLoadConfig = { - ["Mi-8MT"] = { - maxInternalLoad = 4000, - }, - ["CH-47Fbl1"] = { - maxInternalLoad = 10000 - }, - ["Mi-24P"] = { - maxInternalLoad = 2000 - }, - ["UH-1H"] = { - maxInternalLoad = 2000 - } -} ---@class SupplyConfigHelper local SupplyConfigHelper = {} @@ -92,7 +76,7 @@ local SupplyConfigHelper = {} ---@return SupplyConfig? function SupplyConfigHelper.fromObjectName(name) for configName, config in pairs(SupplyConfig) do - if Spearhead.Util.startswith(name, configName, true) == true then + if Util.startswith(name, configName, true) == true then return config end end @@ -104,11 +88,6 @@ function SupplyConfigHelper.getSupplyConfig(type) return SupplyConfig[type] end -if Spearhead == nil then Spearhead = {} end -if Spearhead.classes == nil then Spearhead.classes = {} end -if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end -if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end -if Spearhead.classes.stageClasses.helpers.supplies == nil then Spearhead.classes.stageClasses.helpers.supplies = {} end -Spearhead.classes.stageClasses.helpers.supplies.MaxLoadConfig = MaxLoadConfig -Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper = SupplyConfigHelper + +return SupplyConfigHelper diff --git a/classes/stageClasses/helpers/SupplyUnitsTracker.lua b/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua similarity index 87% rename from classes/stageClasses/helpers/SupplyUnitsTracker.lua rename to src/classes/stageClasses/helpers/SupplyUnitsTracker.lua index 72c7797..cc033bb 100644 --- a/classes/stageClasses/helpers/SupplyUnitsTracker.lua +++ b/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua @@ -1,4 +1,12 @@ +local Logger = require("classes.util.Logger") +local Util = require("classes.util.Util") +local DcsUtil = require("classes.util.DcsUtil") +local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper") +local SpearheadEvents = require("classes.spearhead_events") +local SupplyConfigHelper = require("classes.stageClasses.helpers.SupplyConfigHelper") +local MaxLoadConfig = require("classes.stageClasses.helpers.MaxLoadConfig") + env.info("Spearhead SupplyUnitsTracker loaded") ---@class SupplyUnitsTracker @@ -23,7 +31,7 @@ function SupplyUnitsTracker.getOrCreate(logLevel) if singleton == nil then singleton = setmetatable({}, SupplyUnitsTracker) - singleton._logger = Spearhead.LoggerTemplate.new("SupplyUnitsTracker", logLevel) + singleton._logger = Logger.new("SupplyUnitsTracker", logLevel) singleton._unitPositions = {} singleton._cargoInUnits = {} singleton._supplyUnitsByName = {} @@ -31,9 +39,9 @@ function SupplyUnitsTracker.getOrCreate(logLevel) singleton._registeredHubs = {} singleton._supplyUnitSpawnedListener = {} - singleton._commandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(singleton._logger.LogLevel) + singleton._commandsHelper = MissionCommandsHelper.getOrCreate(singleton._logger.LogLevel) - Spearhead.Events.AddOnPlayerEnterUnitListener(singleton) + SpearheadEvents.AddOnPlayerEnterUnitListener(singleton) ---@param selfA SupplyUnitsTracker local function updateTask(selfA, time) @@ -96,7 +104,7 @@ function SupplyUnitsTracker:AddOnSupplyUnitSpawnedListener(listener) end function SupplyUnitsTracker:Update() - local players = Spearhead.DcsUtil.getAllPlayerUnits() + local players = DcsUtil.getAllPlayerUnits() for _, player in pairs(players) do if player ~= nil and player:isExist() and self:IsSupplyUnit(player) == true then self._supplyUnitsByName[player:getName()] = player @@ -127,7 +135,7 @@ function SupplyUnitsTracker:AddCargoToUnit(unitID, crateType) if unitID == nil or crateType == nil then return end - local unit = Spearhead.DcsUtil.GetPLayerUnitByID(unitID) + local unit = DcsUtil.GetPlayerUnitByID(unitID) if unit == nil then return end if self._cargoInUnits[unitID] == nil then @@ -174,7 +182,7 @@ function SupplyUnitsTracker:UpdateWeightForUnit(unit) local weight = 0 if self._cargoInUnits[tostring(unit:getID())] then for crateType, count in pairs(self._cargoInUnits[tostring(unit:getID())]) do - local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType) + local crateConfig = SupplyConfigHelper.getSupplyConfig(crateType) if crateConfig and count then weight = weight + (crateConfig.weight * count) end @@ -197,7 +205,7 @@ function SupplyUnitsTracker:CheckUnitsInZones() if enabled == true then local zone = hub:GetZone() if zone ~= nil then - if Spearhead.Util.is3dPointInZone(pos, zone) then + if Util.is3dPointInZone(pos, zone) then self._commandsHelper:MarkUnitInSupplyHub(group:getID()) else self._commandsHelper:MarkUnitOutsideSupplyHub(group:getID()) @@ -241,7 +249,7 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType) self._logger:debug("Unload requested for unit: " .. unitID .. " crateType: " .. crateType) - local unit = Spearhead.DcsUtil.GetPLayerUnitByID(unitID) + local unit = DcsUtil.GetPlayerUnitByID(unitID) if unit == nil or unit:isExist() == false then return end local group = unit:getGroup() if group == nil then @@ -251,7 +259,7 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType) self:RemoveCargoFromUnit(unitID, crateType) self:UpdateWeightForUnit(unit) - local cargoConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType) + local cargoConfig = SupplyConfigHelper.getSupplyConfig(crateType) if cargoConfig == nil then self._logger:error("Invalid crate type: " .. crateType) @@ -285,10 +293,10 @@ function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType) self._logger:debug("UnitRequestCrateLoading called with groupID: " .. groupID .. " and crateType: " .. crateType) - local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID) + local group = DcsUtil.GetPlayerGroupByGroupID(groupID) if group ~= nil then - local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType) + local crateConfig = SupplyConfigHelper.getSupplyConfig(crateType) if crateConfig == nil then self._logger:error("Invalid crate type: " .. crateType) return @@ -341,7 +349,7 @@ end ---@return boolean function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType) - local crateConfigA = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType) + local crateConfigA = SupplyConfigHelper.getSupplyConfig(crateType) if crateConfigA == nil then trigger.action.outTextForUnit(unit:getID(), "Invalid crate type: " .. crateType, 5) return false @@ -354,7 +362,7 @@ function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType) end end - local unitConfig = Spearhead.classes.stageClasses.helpers.supplies.MaxLoadConfig[unit:getTypeName()] + local unitConfig = MaxLoadConfig[unit:getTypeName()] if unitConfig == nil then trigger.action.outTextForUnit(unit:getID(), "Your unit type is not configured for logistics: " .. crateType, 5) self._logger:error("Invalid unit type: " .. unit:getTypeName()) @@ -383,10 +391,10 @@ end ---@param crateType CrateType function SupplyUnitsTracker:UnitRequestCrateSpawn(groupID, crateType) - local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID) + local group = DcsUtil.GetPlayerGroupByGroupID(groupID) if group == nil then - local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.getSupplyConfig(crateType) + local crateConfig = SupplyConfigHelper.getSupplyConfig(crateType) if crateConfig == nil then self._logger:error("Invalid crate type: " .. crateType) return @@ -446,9 +454,4 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit) end - -if Spearhead == nil then Spearhead = {} end -if Spearhead.classes == nil then Spearhead.classes = {} end -if Spearhead.classes.stageClasses == nil then Spearhead.classes.stageClasses = {} end -if Spearhead.classes.stageClasses.helpers == nil then Spearhead.classes.stageClasses.helpers = {} end -Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker = SupplyUnitsTracker \ No newline at end of file +return SupplyUnitsTracker \ No newline at end of file diff --git a/classes/stageClasses/missions/BuildableMission.lua b/src/classes/stageClasses/missions/BuildableMission.lua similarity index 78% rename from classes/stageClasses/missions/BuildableMission.lua rename to src/classes/stageClasses/missions/BuildableMission.lua index 745d855..cf42f58 100644 --- a/classes/stageClasses/missions/BuildableMission.lua +++ b/src/classes/stageClasses/missions/BuildableMission.lua @@ -1,4 +1,10 @@ - +local Mission = require("classes.stageClasses.missions.baseMissions.Mission") +local Util = require("classes.util.Util") +local DcsUtil = require("classes.util.DcsUtil") +local SupplyUnitsTracker = require("classes.stageClasses.helpers.SupplyUnitsTracker") +local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper") +local GlobalConfig = require("classes.configuration.GlobalConfig") +local SupplyConfigHelper = require("classes.stageClasses.helpers.SupplyConfigHelper") ---@class BuildableMission : Mission, SupplyUnitSpawnedListener ---@field private _requiredKilos number @@ -27,7 +33,6 @@ BuildableMission.__index = BuildableMission ---@param logger Logger function BuildableMission.new(database, logger, targetZone, noLandingZone, requiredKilos, requiredCrateType) - local Mission = Spearhead.classes.stageClasses.missions.baseMissions.Mission setmetatable(BuildableMission, Mission) local self = setmetatable({}, { __index = BuildableMission }) @@ -42,7 +47,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi if noLandingZone then local verts = noLandingZone.verts - local enlarged = Spearhead.Util.enlargeConvexHull(verts, 300) + local enlarged = Util.enlargeConvexHull(verts, 300) ---@type SpearheadTriggerZone local dropOfZone = { @@ -71,7 +76,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi self._onCrateDroppedOfListeners = {} self._completeListeners = {} self._markIDsPerGroup = {} - self._supplyUnitsTracker = Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker.getOrCreate(logger.LogLevel) + self._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate(logger.LogLevel) self._state = "NEW" @@ -82,7 +87,7 @@ function BuildableMission.new(database, logger, targetZone, noLandingZone, requi self.priority = "secondary" - self._missionCommandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(self._logger.LogLevel) + self._missionCommandsHelper = MissionCommandsHelper.getOrCreate(self._logger.LogLevel) self._crateType = requiredCrateType @@ -96,11 +101,11 @@ end function BuildableMission:ShowBriefing(groupID) - local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupID) + local group = DcsUtil.GetPlayerGroupByGroupID(groupID) if group == nil then return end - local unitType = Spearhead.DcsUtil.getUnitTypeFromGroup(group) - local coords = Spearhead.DcsUtil.convertVec2ToUnitUsableType(self.location, unitType) + local unitType = DcsUtil.getUnitTypeFromGroup(group) + local coords = DcsUtil.convertVec2ToUnitUsableType(self.location, unitType) local siteType = "FARP" if self._crateType == "SAM_CRATE" then @@ -119,18 +124,18 @@ function BuildableMission:ShowBriefing(groupID) "\n\n" .. "NOTE: Do not land in the orange construction zone!" - trigger.action.outTextForGroup(groupID, briefing, Spearhead.GlobalConfig:getBriefingTime()) + trigger.action.outTextForGroup(groupID, briefing, GlobalConfig:getBriefingTime()) end function BuildableMission:MarkMissionAreaToGroup(groupID) if self._markIDsPerGroup[groupID] then - Spearhead.DcsUtil.RemoveMark(self._markIDsPerGroup[groupID]) + DcsUtil.RemoveMark(self._markIDsPerGroup[groupID]) end local text = "[" .. self.code .. "] " .. self.name .. " | " .. self._crateType local location = { x= self.location.x, y=land.getHeight(self.location), z=self.location.y } - local markID = Spearhead.DcsUtil.AddMarkToGroup(groupID, text, location) + local markID = DcsUtil.AddMarkToGroup(groupID, text, location) self._markIDsPerGroup[groupID] = markID end @@ -163,7 +168,7 @@ function BuildableMission:SpawnActive() local lineColor = { r=230/255, g=93/255, b=49/255, a=1} ---@type DrawColor local fillColor = { r=230/255, g=93/255, b=49/255, a=0.2} - self._noLandingZoneId = Spearhead.DcsUtil.DrawZone(self._noLandingZone, lineColor, fillColor, 6) + self._noLandingZoneId = DcsUtil.DrawZone(self._noLandingZone, lineColor, fillColor, 6) if self._dropOffZone == nil then self._logger:error("No drop off zone found for mission: " .. self.code) @@ -172,7 +177,7 @@ function BuildableMission:SpawnActive() local lineColor2 = { r=0, g=0, b=1, a=1} local fillColor2 = { r=0, g=0, b=1, a=0} - self._dropOffZoneId = Spearhead.DcsUtil.DrawZone(self._dropOffZone, lineColor2, fillColor2, 6) + self._dropOffZoneId = DcsUtil.DrawZone(self._dropOffZone, lineColor2, fillColor2, 6) ---@param selfA BuildableMission ---@param time number @@ -230,17 +235,17 @@ function BuildableMission:CheckCratesInZone() local crates = self._supplyUnitsTracker:GetCargoCratesDropped() for _, staticObject in pairs(crates) do - if staticObject and staticObject:isExist() and Spearhead.Util.startswith(staticObject:getName(), self._crateType, true) then + if staticObject and staticObject:isExist() and Util.startswith(staticObject:getName(), self._crateType, true) then local pos = staticObject:getPoint() - if Spearhead.Util.is3dPointInZone(pos, self._dropOffZone) then + if Util.is3dPointInZone(pos, self._dropOffZone) then table.insert(foundCrates, staticObject) end end end for _, foundCrate in pairs(foundCrates) do - local crateConfig = Spearhead.classes.stageClasses.helpers.supplies.SupplyConfigHelper.fromObjectName(foundCrate:getName()) + local crateConfig = SupplyConfigHelper.fromObjectName(foundCrate:getName()) if crateConfig then self._droppedKilos = self._droppedKilos + crateConfig.weight foundCrate:destroy() @@ -249,8 +254,8 @@ function BuildableMission:CheckCratesInZone() end if self._droppedKilos >= self._requiredKilos then - Spearhead.DcsUtil.RemoveMark(self._noLandingZoneId) - Spearhead.DcsUtil.RemoveMark(self._dropOffZoneId) + DcsUtil.RemoveMark(self._noLandingZoneId) + DcsUtil.RemoveMark(self._dropOffZoneId) self:NotifyMissionComplete() self._state = "COMPLETED" end @@ -258,7 +263,7 @@ function BuildableMission:CheckCratesInZone() if self._state == "COMPLETED" then for groupID, markID in pairs(self._markIDsPerGroup) do if markID then - Spearhead.DcsUtil.RemoveMark(markID) + DcsUtil.RemoveMark(markID) self._markIDsPerGroup[groupID] = nil end end @@ -267,7 +272,4 @@ function BuildableMission:CheckCratesInZone() end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end -if not Spearhead.classes.stageClasses.missions then Spearhead.classes.stageClasses.missions = {} end -Spearhead.classes.stageClasses.missions.BuildableMission = BuildableMission \ No newline at end of file +return BuildableMission \ No newline at end of file diff --git a/classes/stageClasses/missions/RunwayStrikeMission.lua b/src/classes/stageClasses/missions/RunwayStrikeMission.lua similarity index 97% rename from classes/stageClasses/missions/RunwayStrikeMission.lua rename to src/classes/stageClasses/missions/RunwayStrikeMission.lua index a07155e..a130e33 100644 --- a/classes/stageClasses/missions/RunwayStrikeMission.lua +++ b/src/classes/stageClasses/missions/RunwayStrikeMission.lua @@ -537,9 +537,4 @@ function RunwayStrikeMission:RunwayToSpearheadZone(runway) } end - - -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end -if not Spearhead.classes.stageClasses.missions then Spearhead.classes.stageClasses.missions = {} end -Spearhead.classes.stageClasses.missions.RunwayStrikeMission = RunwayStrikeMission \ No newline at end of file +return RunwayStrikeMission \ No newline at end of file diff --git a/classes/stageClasses/missions/ZoneMission.lua b/src/classes/stageClasses/missions/ZoneMission.lua similarity index 98% rename from classes/stageClasses/missions/ZoneMission.lua rename to src/classes/stageClasses/missions/ZoneMission.lua index 1f20207..49609f7 100644 --- a/classes/stageClasses/missions/ZoneMission.lua +++ b/src/classes/stageClasses/missions/ZoneMission.lua @@ -517,7 +517,4 @@ function ZoneMission:MarkLastContact(unit) self._lastContactMarkerID = Spearhead.DcsUtil.AddMarkToAll("Last Contact: " .. self.name .. " [" .. self.code .. "]", point) end -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end -if not Spearhead.classes.stageClasses.missions then Spearhead.classes.stageClasses.missions = {} end -Spearhead.classes.stageClasses.missions.ZoneMission = ZoneMission +return ZoneMission diff --git a/classes/stageClasses/missions/baseMissions/Mission.lua b/src/classes/stageClasses/missions/baseMissions/Mission.lua similarity index 78% rename from classes/stageClasses/missions/baseMissions/Mission.lua rename to src/classes/stageClasses/missions/baseMissions/Mission.lua index 0acaac9..22e183c 100644 --- a/classes/stageClasses/missions/baseMissions/Mission.lua +++ b/src/classes/stageClasses/missions/baseMissions/Mission.lua @@ -1,5 +1,8 @@ - +local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper") +local DcsUtil = require("classes.util.DcsUtil") +local Util = require("classes.util.Util") +local GlobalConfig = require("classes.configuration.GlobalConfig") ---@class Mission ---@field name string @@ -49,7 +52,7 @@ function Mission.newSuper(self, zoneName, missionName, missionType, missionBrief self.location = database:GetLocationForMissionZone(zoneName) self.missionTypeDisplay = self.missionType - self._missionCommandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(logger.LogLevel) + self._missionCommandsHelper = MissionCommandsHelper.getOrCreate(logger.LogLevel) return true, "success" end @@ -77,11 +80,11 @@ end ---@param groupId number function Mission:ShowBriefing(groupId) - local group = Spearhead.DcsUtil.GetPlayerGroupByGroupID(groupId) + local group = DcsUtil.GetPlayerGroupByGroupID(groupId) if group == nil then return end - local unitType = Spearhead.DcsUtil.getUnitTypeFromGroup(group) - local coords = Spearhead.DcsUtil.convertVec2ToUnitUsableType(self.location, unitType) + local unitType = DcsUtil.getUnitTypeFromGroup(group) + local coords = DcsUtil.convertVec2ToUnitUsableType(self.location, unitType) self._logger:debug("Coords converted: " .. coords) local stateString = self:ToStateString() @@ -89,12 +92,12 @@ function Mission:ShowBriefing(groupId) local briefing = self._missionBriefing - briefing = Spearhead.Util.replaceString(briefing, "{{coords}}", coords) - briefing = Spearhead.Util.replaceString(briefing, "{{ coords }}", coords) + briefing = Util.replaceString(briefing, "{{coords}}", coords) + briefing = Util.replaceString(briefing, "{{ coords }}", coords) local text = "Mission [" .. self.code .. "] " .. self.name .. "\n \n" .. briefing .. " \n \n" .. stateString - trigger.action.outTextForGroup(groupId, text, Spearhead.GlobalConfig:getBriefingTime()); + trigger.action.outTextForGroup(groupId, text, GlobalConfig:getBriefingTime()); end @@ -135,14 +138,6 @@ function Mission:ToStateString() return "status: in progress" end --endregion - - -if not Spearhead.classes then Spearhead.classes = {} end -if not Spearhead.classes.stageClasses then Spearhead.classes.stageClasses = {} end -if not Spearhead.classes.stageClasses.missions then Spearhead.classes.stageClasses.missions = {} end -if not Spearhead.classes.stageClasses.missions.baseMissions then Spearhead.classes.stageClasses.missions.baseMissions = {} end -Spearhead.classes.stageClasses.missions.baseMissions.Mission = Mission - do --aliases --- @alias MissionPriority @@ -168,4 +163,4 @@ do --aliases end - +return Mission \ No newline at end of file diff --git a/classes/spearhead_base.lua b/src/classes/util/DcsUtil.lua similarity index 57% rename from classes/spearhead_base.lua rename to src/classes/util/DcsUtil.lua index 311b0b0..0366be0 100644 --- a/classes/spearhead_base.lua +++ b/src/classes/util/DcsUtil.lua @@ -1,1443 +1,846 @@ ---- DEFAULT Values -if Spearhead == nil then Spearhead = {} end - -local UTIL = {} -do -- INIT UTIL - ---splits a string in sub parts by seperator - ---@param input string - ---@param seperator string - ---@return table result list of strings - function UTIL.split_string(input, seperator) - if seperator == nil then - seperator = " " - end - - local result = {} - if input == nil then - return result - end - - for str in string.gmatch(input, "[^" .. seperator .. "]+") do - table.insert(result, str) - end - return result - end - - ---comment - ---@param table any - ---@return number - function UTIL.tableLength(table) - if table == nil then return 0 end - - local count = 0 - for _ in pairs(table) do count = count + 1 end - return count - end - - ---@param orig table - ---@return table copy - function UTIL.deepCopyTable(orig) - local orig_type = type(orig) - local copy - if orig_type == 'table' then - copy = {} - for orig_key, orig_value in next, orig, nil do - copy[UTIL.deepCopyTable(orig_key)] = UTIL.deepCopyTable(orig_value) - end - setmetatable(copy, UTIL.deepCopyTable(getmetatable(orig))) - else -- number, string, boolean, etc - copy = orig - end - return copy - end - - ---Gets a random from the list - ---@param list Array - ---@return any @random element from the list - function UTIL.randomFromList(list) - local max = #list - - if max == 0 or max == nil then - return nil - end - - local random = math.random(0, max) - if random == 0 then random = 1 end - - return list[random] - end - - ---@param list Array - ---@param start number start - ---@param n number length - ---@return Array - function UTIL.sublist(list, start, n) - local result = {} - for i = start, n do - result[#result + 1] = list[i] - end - return result - end - - ---@param str string - ---@param find string - ---@param replace string - ---@return string - function UTIL.replaceString(str, find, replace) - if str == nil then return "" end - if find == nil or replace == nil then return str end - - local result = str:gsub(find, replace) - return result - end - - local function table_print(tt, indent, done) - done = done or {} - indent = indent or 0 - if type(tt) == "table" then - local sb = {} - for key, value in pairs(tt) do - table.insert(sb, string.rep(" ", indent)) -- indent it - if type(value) == "table" and not done[value] then - done[value] = true - table.insert(sb, "[\"" ..key .. "\"]" .. " = {\n"); - table.insert(sb, table_print(value, indent + 2, done)) - table.insert(sb, string.rep(" ", indent)) -- indent it - table.insert(sb, "},\n"); - elseif "number" == type(key) then - table.insert(sb, string.format("\"%s\",\n", tostring(value))) - else - table.insert(sb, string.format( - "[\"%s\"] = \"%s\",\n", tostring(key), tostring(value))) - end - end - return table.concat(sb) - else - return tt .. "\n" - end - end - - ---comment - ---@param str string - ---@param findable string - ---@param ignoreCase boolean? - ---@return boolean - UTIL.startswith = function(str, findable, ignoreCase) - if ignoreCase == true then - return string.lower(str):find('^' .. string.lower(findable)) ~= nil - end - - return str:find('^' .. findable) ~= nil - end - - ---comment - ---@param str string - ---@param findable string - ---@return boolean - UTIL.strContains = function(str, findable) - return str:find(findable) ~= nil - end - - ---comment - ---@param str string - ---@param findableTable table - ---@return boolean - UTIL.startswithAny = function(str, findableTable) - for key, value in pairs(findableTable) do - if type(value) == "string" and UTIL.startswith(str, value) then return true end - end - return false - end - - function UTIL.toString(something) - if something == nil then - return "nil" - elseif "table" == type(something) then - return table_print(something) - elseif "string" == type(something) then - return something - else - return tostring(something) - end - end - - ---comment - ---@param a Vec2 - ---@param b Vec2 - ---@return number - function UTIL.VectorDistance2d(a, b) - return math.sqrt((b.x - a.x) ^ 2 + (b.y - a.y) ^ 2) - end - - ---comment - ---@param a Vec3 - ---@param b Vec3 - ---@return number - function UTIL.VectorDistance3d(a, b) - return UTIL.vectorMagnitude({ x = a.x - b.x, y = a.y - b.y, z = a.z - b.z }) - end - - ---comment - ---@param vec Vec3 - ---@return number - function UTIL.vectorMagnitude(vec) - return (vec.x ^ 2 + vec.y ^ 2 + vec.z ^ 2) ^ 0.5 - end - - ---@param vec Vec3 - ---@return Vec3 - function UTIL.vectorNormalize(vec) - local magnitude = UTIL.vectorMagnitude(vec) - if magnitude == 0 then - return { x = 0, y = 0, z = 0 } - end - return { x = vec.x / magnitude, y = vec.y / magnitude, z = vec.z / magnitude } - end - - ---@param vec Vec2 - ---@param direction number @in degrees - ---@param distance number - ---@return Vec2 - function UTIL.vectorMove(vec, direction, distance) - local rad = math.rad(direction) - local x = vec.x + (math.cos(rad) * distance) - local y = vec.y + (math.sin(rad) * distance) - - return { x = x, y = y} - end - - ---comment - ---@param vec1 Vec2 - ---@param vec2 Vec2 - ---@return number in degrees - function UTIL.vectorHeadingFromTo(vec1, vec2) - local dx = vec2.x - vec1.x - local dy = vec2.y - vec1.y - local heading = math.deg(math.atan2(dy, dx)) - if heading < 0 then - heading = heading + 360 - end - return heading - end - - - - ---@param vec1 Vec3 - ---@param vec2 Vec3 - ---@return number alignment a number in range [-1,1]. > 0 same direction. < 0 opposite direction - function UTIL.vectorAlignment(vec1, vec2) - local vec1Norm = UTIL.vectorNormalize(vec1) - local vec2Norm = UTIL.vectorNormalize(vec2) - - return ((vec1Norm.x * vec2Norm.x) + (vec1Norm.y * vec2Norm.y) + (vec1Norm.z * vec2Norm.z)) - end - - local function isInComplexPolygon(polygon, x, y) - local function getEdges(poly) - local result = {} - for i = 1, #poly do - local point1 = poly[i] - local point2Index = i + 1 - if point2Index > #poly then point2Index = 1 end - local point2 = poly[point2Index] - local edge = { x1 = point1.x, z1 = point1.y, x2 = point2.x, z2 = point2.y } - table.insert(result, edge) - end - return result - end - - local edges = getEdges(polygon) - local count = 0; - for _, edge in pairs(edges) do - if (x < edge.x1) ~= (x < edge.x2) and y < edge.z1 + ((x - edge.x1) / (edge.x2 - edge.x1)) * (edge.z2 - edge.z1) then - count = count + 1 - -- if (yp < y1) != (yp < y2) and xp < x1 + ((yp-y1)/(y2-y1))*(x2-x1) then - -- count = count + 1 - end - end - return count % 2 == 1 - end - - ---comment - ---@param polygon Array of pairs { x, y } - ---@param x number X location - ---@param y number Y location - ---@return boolean - function UTIL.IsPointInPolygon(polygon, x, y) - return isInComplexPolygon(polygon, x, y) - end - - ---@param point Vec3 - ---@param zone SpearheadTriggerZone - function UTIL.is3dPointInZone(point, zone) - if zone.zone_type == "Polygon" and zone.verts then - if UTIL.IsPointInPolygon(zone.verts, point.x, point.z) == true then - return true - end - else - if (((point.x - zone.location.x) ^ 2 + (point.z - zone.location.y) ^ 2) ^ 0.5 <= zone.radius) then - return true - end - end - - return false - end - - ---@param point Vec2 - ---@param zone SpearheadTriggerZone - function UTIL.is2dPointInZone(point, zone) - if zone.zone_type == "Polygon" and zone.verts then - if UTIL.IsPointInPolygon(zone.verts, point.x, point.y) == true then - return true - end - else - if (((point.x - zone.location.x) ^ 2 + (point.y - zone.location.y) ^ 2) ^ 0.5 <= zone.radius) then - return true - end - end - - return false - end - - ---comment - ---@param points Array points - ---@return Array hullPoints - function UTIL.getConvexHull(points) - if #points == 0 then - return {} - end - - ---comment - ---@param a Vec2 - ---@param b Vec2 - ---@param c Vec2 - ---@return boolean - local function ccw(a, b, c) - return (b.y - a.y) * (c.x - a.x) > (b.x - a.x) * (c.y - a.y) - end - - table.sort(points, function(left, right) - return left.y < right.y - end) - - local hull = {} - -- lower hull - for _, point in pairs(points) do - while #hull >= 2 and not ccw(hull[#hull - 1], hull[#hull], point) do - table.remove(hull, #hull) - end - table.insert(hull, point) - end - - -- upper hull - local t = #hull + 1 - for i = #points, 1, -1 do - local point = points[i] - while #hull >= t and not ccw(hull[#hull - 1], hull[#hull], point) do - table.remove(hull, #hull) - end - table.insert(hull, point) - end - table.remove(hull, #hull) - return hull - end - - ---Splits points into clusters with at least minSeparation between clusters, returns convex hull for each cluster - ---@param points Array - ---@param minSeparation number - ---@return Array> hulls - function UTIL.getSeparatedConvexHulls(points, minSeparation) - if #points == 0 then return {} end - - -- Simple clustering: group points that are within minSeparation of each other - local clusters = {} - local assigned = {} - - for i, p in ipairs(points) do - if not assigned[i] then - local cluster = { p } - assigned[i] = true - -- Find all points close to p (BFS) - local queue = { i } - while #queue > 0 do - local idx = table.remove(queue) - local base = points[idx] - for j, q in ipairs(points) do - if not assigned[j] then - local dx = base.x - q.x - local dy = base.y - q.y - if (dx * dx + dy * dy) <= (minSeparation * minSeparation) then - table.insert(cluster, q) - assigned[j] = true - table.insert(queue, j) - end - end - end - end - table.insert(clusters, cluster) - end - end - - -- Compute convex hull for each cluster - local hulls = {} - for _, cluster in ipairs(clusters) do - local hull = UTIL.getConvexHull(cluster) - if #hull > 0 then - table.insert(hulls, hull) - end - end - - return hulls - end - - ---@param points Array - function UTIL.enlargeConvexHull(points, meters) - if points == nil or #points == 0 then - return {} - end - - ---@type Array - local allpoints = {} - - for _, point in pairs(points) do - table.insert(allpoints, point) - - allpoints[#allpoints + 1] = point - allpoints[#allpoints+1] = { x = point.x + meters, y = point.y, } - allpoints[#allpoints+1] = { x = point.x - meters, y = point.y, } - allpoints[#allpoints+1] = { x = point.x, y = point.y + meters, } - allpoints[#allpoints+1] = { x = point.x, y = point.y - meters, } - - allpoints[#allpoints+1] = { x = point.x + math.cos(math.rad(45)) * meters, y = point.y + math.sin(math.rad(45)) * meters, } - allpoints[#allpoints+1] = { x = point.x - math.cos(math.rad(45)) * meters, y = point.y - math.sin(math.rad(45)) * meters, } - allpoints[#allpoints+1] = { x = point.x - math.cos(math.rad(45)) * meters, y = point.y + math.sin(math.rad(45)) * meters, } - allpoints[#allpoints+1] = { x = point.x + math.cos(math.rad(45)) * meters, y = point.y - math.sin(math.rad(45)) * meters, } - end - - return UTIL.getConvexHull(allpoints) - end - - ---Returns hull points visible from origin (not blocked by hull edges) ----@param hull Array ----@param origin Vec2 ----@return Array -function UTIL.GetVisibleHullPointsFromOrigin(hull, origin) - local function segmentsIntersect(a, b, c, d) - -- Helper: returns true if segment ab intersects cd (excluding endpoints) - local function ccw(p1, p2, p3) - return (p3.y - p1.y) * (p2.x - p1.x) > (p2.y - p1.y) * (p3.x - p1.x) - end - return (ccw(a, c, d) ~= ccw(b, c, d)) and (ccw(a, b, c) ~= ccw(a, b, d)) - end - - local n = #hull - local visible = {} - - for i = 1, n do - local p = hull[i] - local isVisible = true - - -- Check against all hull edges except those incident to p - for j = 1, n do - local a = hull[j] - local b = hull[(j % n) + 1] - -- skip edges incident to p - if (a ~= p and b ~= p) then - if segmentsIntersect(origin, p, a, b) then - isVisible = false - break - end - end - end - - if isVisible then - table.insert(visible, p) - end - end - - return visible -end - ----Returns the two tangent points ("scratch points") from origin to the convex hull ----@param hull Array ----@param origin Vec2 ----@return Array -function UTIL.GetTangentHullPointsFromOrigin(hull, origin) - - if hull == nil or #hull <= 0 then - return {} - end - - local function orientation(a, b, c) - -- Returns >0 if c is to the left of ab, <0 if to the right, 0 if colinear - return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x) - end - - local n = #hull - if n == 0 then return {} end - if n == 1 then return { hull[1] } end - - -- Find left tangent: the hull point where all other points are to the right of the line from origin to that point - local function findTangent(isLeft) - local best = 1 - for i = 2, n do - local o = orientation(origin, hull[best], hull[i]) - if (isLeft and o < 0) or (not isLeft and o > 0) then - best = i - end - end - return hull[best] - end - - local leftTangent = findTangent(true) - local rightTangent = findTangent(false) - - -- If tangents are the same (can happen if origin is colinear), only return one - if leftTangent.x == rightTangent.x and leftTangent.y == rightTangent.y then - return { leftTangent } - else - return { leftTangent, rightTangent } - end -end - -end -Spearhead.Util = UTIL - ----DCS UTIL Takes inspiration from MIST but only takes the things it needs, changes for DCS updates and different vision for advanced mission scripting stuff. ----It also adds functions that make the other TDCS scripts easier without taking too much "control" away like MOOSE can sometimes. -local DCS_UTIL = {} -do -- INIT DCS_UTIL - do -- local databases - --[[ - groupdata = { - category, - country_id, - group_template - } - ]] -- - - --[[ - zone = { - name, - - zone_type, - x, - y, - radius - verts, - - } - ]] -- - - ---@alias SpearheadTriggerZoneType - ---| "Cilinder" - ---| "Polygon" - - ---@class SpearheadTriggerZone - ---@field name string - ---@field location Vec2 - ---@field radius number - ---@field verts Array - ---@field zone_type SpearheadTriggerZoneType - ---@field properties Array? - - ---@type Array - DCS_UTIL.__trigger_zones = {} - end - - DCS_UTIL.Coalition = - { - NEUTRAL = 0, - RED = 1, - BLUE = 2 - } - - DCS_UTIL.ZoneType = { - Cilinder = 0, - Polygon = 2 - } - - ---@enum SpearheadGroupCategory - DCS_UTIL.GroupCategory = { - AIRPLANE = 0, - HELICOPTER = 1, - GROUND = 2, - SHIP = 3, - TRAIN = 4, - STATIC = 5 --CUSTOM CATEGORY - } - - DCS_UTIL.__airbaseNamesById = {} - - ---@type table - DCS_UTIL.__airbaseZonesByName = {} - - DCS_UTIL.__airportsStartingCoalition = {} - DCS_UTIL.__warehouseStartingCoalition = {} - function DCS_UTIL.__INIT() - do -- INITS ALL TABLES WITH DATA THAT's from the MIZ environment - - do --init trigger zones - for i, trigger_zone in pairs(env.mission.triggers.zones) do - -- reorder verts as they are not ordered correctly in the ME - local verts = {} - if Spearhead.Util.tableLength(trigger_zone.verticies) >= 4 then - table.insert(verts, { x = trigger_zone.verticies[4].x, y = trigger_zone.verticies[4].y }) - table.insert(verts, { x = trigger_zone.verticies[3].x, y = trigger_zone.verticies[3].y }) - table.insert(verts, { x = trigger_zone.verticies[2].x, y = trigger_zone.verticies[2].y }) - table.insert(verts, { x = trigger_zone.verticies[1].x, y = trigger_zone.verticies[1].y }) - end - - local zoneType = "Cilinder" - if trigger_zone.type == DCS_UTIL.ZoneType.Polygon then - zoneType = "Polygon" - end - - ---@type SpearheadTriggerZone - local zone = { - name = trigger_zone.name, - zone_type = zoneType, - location = { x = trigger_zone.x, y = trigger_zone.y }, - radius = trigger_zone.radius, - verts = verts, - properties = {} - } - - if trigger_zone.properties then - for _, kvPair in pairs(trigger_zone.properties) do - local key = kvPair["key"] - local value = kvPair["value"] - zone.properties[#zone.properties + 1] = { key = key, value = value } - end - end - - DCS_UTIL.__trigger_zones[zone.name] = zone - end - end - - do -- init airports and warehouses - if env.warehouses.airports then - for warehouse_id, value in pairs(env.warehouses.airports) do - if warehouse_id ~= nil then - warehouse_id = tostring(warehouse_id) or "nil" - local coalitionNumber = DCS_UTIL.stringToCoalition(value.coalition) - DCS_UTIL.__airportsStartingCoalition[warehouse_id] = coalitionNumber - end - end - end - - if env.warehouses.warehouses then - DCS_UTIL.__warehouseStartingCoalition[-1] = "placeholder" - for warehouse_id, value in pairs(env.warehouses.warehouses) do - if warehouse_id ~= nil then - warehouse_id = tostring(warehouse_id) or "nil" - local coalitionNumber = DCS_UTIL.stringToCoalition(value.coalition) - DCS_UTIL.__warehouseStartingCoalition[warehouse_id] = coalitionNumber - end - end - end - end - - do -- fill airbaseNames and zones - local airbases = world.getAirbases() - if airbases then - for _, airbase in pairs(airbases) do - local name = airbase:getName() - - airbase:autoCapture(false) - - DCS_UTIL.__airbaseNamesById[tostring(airbase:getID())] = name - - if name then - ---@type Array - local relevantPoints = {} - for _, x in pairs(airbase:getRunways()) do - if x.position and x.position.x and x.position.z then - table.insert(relevantPoints, { x = x.position.x, y = x.position.z }) - end - end - - for _, x in pairs(airbase:getParking()) do - if x.vTerminalPos and x.vTerminalPos.x and x.vTerminalPos.z then - table.insert(relevantPoints, { x = x.vTerminalPos.x, y = x.vTerminalPos.z }) - end - end - - local points = UTIL.getConvexHull(relevantPoints) - local enlargedPoints = UTIL.enlargeConvexHull(points, 750) - - local triggerZone = { - name = name, - location = { x = airbase:getPoint().x, y = airbase:getPoint().z }, - zone_type = "Polygon", - radius = 0, - verts = enlargedPoints - } - - if SpearheadConfig and SpearheadConfig.debugEnabled == true then - DCS_UTIL.DrawZone(triggerZone, { r = 0, g = 1, b = 0, a = 1 }, { a = 0, r = 0, g = 1, b = 0 }, 1) - end - - DCS_UTIL.__airbaseZonesByName[name] = triggerZone - end - end - end - end - end - end - - ---maps the coalition name to the DCS coalition integer - ---@param input string the name - ---@return integer - function DCS_UTIL.stringToCoalition(input) - --[[ - coalition.side = { - NEUTRAL = 0 - RED = 1 - BLUE = 2 - } - ]] -- - local input = string.lower(input) - if input == 'neutrals' or input == "neutral" or input == "0" then - return DCS_UTIL.Coalition.NEUTRAL - end - - if input == 'red' or input == "1" then - return DCS_UTIL.Coalition.RED - end - - if input == 'blue' or input == "2" then - return DCS_UTIL.Coalition.BLUE - end - - return -1 - end - - - ---destroy the given unit - ---@param unitName string - function DCS_UTIL.DestroyUnit(unitName) - local unit = Unit.getByName(unitName) - if unit and unit:isExist() then - unit:destroy() - end - end - - --- takes a list of units and returns all the units that are in any of the zones - ---@param unit_names table unit names - ---@param zone_names table zone names - ---@return table unit list of objects { unit = UNIT, zone_name = zoneName} - function DCS_UTIL.getUnitsInZones(unit_names, zone_names) - local units = {} - - ---@type Array - local zones = {} - - for k = 1, #unit_names do - local unit = Unit.getByName(unit_names[k]) or StaticObject.getByName(unit_names[k]) - if unit and unit:isExist() == true then - units[#units + 1] = unit - end - end - - for index, zone_name in pairs(zone_names) do - local zone = DCS_UTIL.__trigger_zones[zone_name] - if zone then - zones[#zones + 1] = zone - end - end - - local in_zone_units = {} - for units_ind = 1, #units do - local lUnit = units[units_ind] - local unit_pos = lUnit:getPosition().p - local lCat = Object.getCategory(lUnit) - for zone_name, zone in pairs(zones) do - if unit_pos and ((lCat == 1 and lUnit:isActive() == true) or lCat ~= 1) then -- it is a unit and is active or it is not a unit - local isInZone = UTIL.is3dPointInZone(unit_pos, zone) - if isInZone == true then - in_zone_units[#in_zone_units + 1] = { unit = lUnit, zone_name = zone.name } - end - end - end - end - return in_zone_units - end - - --- takes a list of groups and returns all the group leaders that are in any of the zones - ---@param group_names table unit names - ---@param zone_name string zone names - ---@return table groupnames list of group names - function DCS_UTIL.getGroupsInZone(group_names, zone_name) - local zone = DCS_UTIL.__trigger_zones[zone_name] - if zone == nil then - return {} - end - - return DCS_UTIL.areGroupsInCustomZone(group_names, zone) - end - - --- takes a x, y poistion and checks if it is inside any of the zones - ---@param group_names Array North South position - ---@param zone SpearheadTriggerZone - ---@return Array groupnames list of groups that are in the zone - function DCS_UTIL.areGroupsInCustomZone(group_names, zone) - local units = {} - if Spearhead.Util.tableLength(group_names) < 1 then return {} end - - for k = 1, #group_names do - local entry = nil - local group = Group.getByName(group_names[k]) - if group ~= nil then - entry = { unit = group:getUnit(1), groupname = group_names[k] } - else - entry = { unit = StaticObject.getByName(group_names[k]), groupname = group_names[k] } - end - - if entry and entry.unit and entry.unit:isExist() == true then - units[#units + 1] = entry - end - end - - local result_groups = {} - for _, entry in pairs(units) do - local pos = entry.unit:getPoint() - local isInZone = UTIL.is3dPointInZone(pos, zone) - if isInZone == true then - table.insert(result_groups, entry.groupname) - end - end - return result_groups - end - - --- takes a x, y poistion and checks if it is inside any of the zones - ---@param x number North South position - ---@param z number West East position - ---@param zone_names table zone names - ---@return table zones list of objects { zone_name = zoneName} - function DCS_UTIL.isPositionInZones(x, z, zone_names) - ---@type Array - local zones = {} - for index, zone_name in pairs(zone_names) do - local zone = DCS_UTIL.__trigger_zones[zone_name] - if zone then - zones[#zones + 1] = zone - end - end - - local result_zones = {} - for zone_name, zone in pairs(zones) do - if UTIL.is3dPointInZone({ x = x, z = z, y = 0 }, zone) == true then - result_zones[#result_zones + 1] = zone.name - end - end - return result_zones - end - - --- takes a x, y poistion and checks if it is inside any of the zones - ---@param x number North South position - ---@param z number West East position - ---@param zone_name string zone name - ---@return boolean result - function DCS_UTIL.isPositionInZone(x, z, zone_name) - local zone = DCS_UTIL.__trigger_zones[zone_name] - if UTIL.is3dPointInZone({ x = x, y = 0, z = z }, zone) then - return true - end - return false - end - - --- takes a x, y poistion and checks if it is inside any of the zones - ---@param zone_name string - ---@param parent_zone_name string - ---@return boolean result - function DCS_UTIL.isZoneInZone(zone_name, parent_zone_name) - local zoneA = DCS_UTIL.__trigger_zones[zone_name] --[[@as SpearheadTriggerZone]] - if zoneA == nil then return false end - local zoneB = DCS_UTIL.__trigger_zones[parent_zone_name] --[[@as SpearheadTriggerZone]] - if zoneB == nil then return false end - return UTIL.is3dPointInZone({ x = zoneA.location.x, y = 0, z = zoneA.location.y }, zoneB) - end - - ---comment - ---@param zone_name any - ---@return SpearheadTriggerZone? zone { name,b zone_type, x, z, radius, verts } - function DCS_UTIL.getZoneByName(zone_name) - if zone_name == nil then return nil end - return DCS_UTIL.__trigger_zones[zone_name] - end - - ---comment - ---@param airbaseName string - ---@return SpearheadTriggerZone? zone - function DCS_UTIL.getAirbaseZoneByName(airbaseName) - if airbaseName == nil then return nil end - return DCS_UTIL.__airbaseZonesByName[airbaseName] - end - - ---maps the category name to the DCS group category - ---@param input string the name - ---@return integer? - function DCS_UTIL.stringToGroupCategory(input) - input = string.lower(input) - if input == 'airplane' or input == 'plane' then - return DCS_UTIL.GroupCategory.AIRPLANE - end - if input == 'helicopter' then - return DCS_UTIL.GroupCategory.HELICOPTER - end - if input == 'ground' or input == 'vehicle' then - return DCS_UTIL.GroupCategory.GROUND - end - if input == 'ship' then - return DCS_UTIL.GroupCategory.SHIP - end - if input == 'train' then - return DCS_UTIL.GroupCategory.TRAIN - end - if input == "static" then - return DCS_UTIL.GroupCategory.STATIC - end - return nil; - end - - ---@type table - local config = - { - ["ah-64d_blk_ii"] = "MGRS", - ["fa-18c_hornet"] = "DMS", - ["av8bna"] = "DMS", - ["f-14b"] = "DMS", - ["f-14a-135-gr"] = "DMS" - } - - - ---@param location Vec2 - ---@param unitType string - function DCS_UTIL.convertVec2ToUnitUsableType(location, unitType) - - local height = land.getHeight(location) - local vec3 = { x = location.x, y = height, z = location.y } - - local unitType = string.lower(unitType or "") - local conversionType = config[unitType] - - if not conversionType then conversionType = "DDM" end - return DCS_UTIL.convertToDisplayCoord(vec3, conversionType) - end - - ---@alias CoordType - ---| "MGRS" MGRS - ---| "DMS" DECIMAL SECONDS - ---| "DDM" DECIMAL MINUTES - - ---@private - ---@param location Vec3 - ---@param coordType CoordType - function DCS_UTIL.convertToDisplayCoord(location, coordType) - local lattitude, longitude, altitude = coord.LOtoLL(location) - - if coordType == "MGRS" then - local mgrs = coord.LLtoMGRS(lattitude, longitude) - return string.format("%s %s %s %s", mgrs.UTMZone, mgrs.MGRSDigraph, mgrs.Easting, mgrs.Northing) - end - - -- Convert DD to DDM (Degrees Decimal Minutes) - local function dd_to_ddm(dd) - local degrees = math.floor(math.abs(dd)) - local minutes = (math.abs(dd) - degrees) * 60 - local sign = dd >= 0 and 1 or -1 - return degrees * sign, minutes - end - - local lat_deg, lat_min = dd_to_ddm(lattitude) - local lon_deg, lon_min = dd_to_ddm(longitude) - - local lat_hemisphere = lattitude >= 0 and "N" or "S" - local lon_hemisphere = longitude >= 0 and "E" or "W" - - if coordType == "DDM" then - return string.format("%s%02d°%06.3f' %s%03d°%06.3f' %dft", - lat_hemisphere, math.abs(lat_deg), lat_min, - lon_hemisphere, math.abs(lon_deg), lon_min, - altitude * 3,28084) - end - - if coordType == "DMS" then - - local lat_min_display = math.floor(lat_min) - local lon_min_display = math.floor(lon_min) - - local lat_sec_display = math.floor((lat_min - lat_min_display) * 60) - local lon_sec_display = math.floor((lon_min - lon_min_display) * 60) - - return string.format("%s%02d°%02d'%02d %s%03d°%02d'%02d %dft", - lat_hemisphere, math.abs(lat_deg), lat_min_display, lat_sec_display, - lon_hemisphere, math.abs(lon_deg), lon_min_display, lon_sec_display, - altitude * 3,28084) - end - - end - - ---@param zone SpearheadTriggerZone - ---@return Array sceneryObjects - function DCS_UTIL.getSceneryObjectsInZone(zone) - ---@type Volume - local volume - - if(zone.zone_type == "Cilinder") then - local y = land.getHeight({ x = zone.location.x, y = zone.location.y }) - ---@type Sphere - local sphere = { - id = world.VolumeType.SPHERE, - params = { - point = { x = zone.location.x, y = y, z = zone.location.y }, - radius = zone.radius - } - } - volume = sphere - else - local minX = nil - local maxX = nil - local minZ = nil - local maxZ = nil - - for _, point in pairs(zone.verts) do - if minX == nil or point.x < minX then - minX = point.x - end - if maxX == nil or point.x > maxX then - maxX = point.x - end - if minZ == nil or point.y < minZ then - minZ = point.y - end - if maxZ == nil or point.y > maxZ then - maxZ = point.y - end - end - - if(minX == nil or maxX == nil or minZ == nil or maxZ == nil) then - return {} - end - - ---@type Vec3 - local min = { - x = minX, - y = land.getHeight({ x = minX, y = minZ }) - 100, - z = minZ - } - - ---@type Vec3 - local max = { - x = maxX, - y = land.getHeight({ x = maxX, y = maxZ }) + 500, - z = maxZ - } - - ---@type Box - local box = { - id = world.VolumeType.BOX, - params = { - min = min, - max = max - } - } - volume = box - end - - ---@type Array - local sceneryObjects = {} - - ---@param object SceneryObject - local onFound = function(object) - if object and object:isExist() and - object:hasAttribute("Buildings") - then - local obj = Spearhead.classes.stageClasses.Groups.SpearheadSceneryObject.New(object["id_"]) - table.insert(sceneryObjects, obj) - end - end - - world.searchObjects(Object.Category.SCENERY, volume, onFound) - return sceneryObjects - end - - ---@param group Group - function DCS_UTIL.getUnitTypeFromGroup(group) - for _, unit in pairs(group:getUnits()) do - if unit and unit:isExist() then - return unit:getTypeName() - end - end - end - - - ---comment Get all units that are players - ---@return Array units - function DCS_UTIL.getAllPlayerUnits() - local units = {} - for i = 0, 2 do - local players = coalition.getPlayers(i) - for key, unit in pairs(players) do - units[#units + 1] = unit - end - end - return units - end - - ---get base name from ID - ---@param baseId number - ---@return string? name - function DCS_UTIL.getAirbaseName(baseId) - local stringified = tostring(baseId) - return DCS_UTIL.__airbaseNamesById[stringified] - end - - ---get base from id - ---@param baseId number - ---@return Airbase? table - function DCS_UTIL.getAirbaseById(baseId) - local name = DCS_UTIL.getAirbaseName(baseId) - if name == nil then return nil end - return Airbase.getByName(name) - end - - ---Get the starting coalition of a farp or airbase - ---@param airbase Airbase - ---@return number? coalition - function DCS_UTIL.getStartingCoalition(airbase) - if airbase == nil then - return nil - end - - --STRING based dictionary otherwise it'll be a string/collapsed array - local baseId = tostring(airbase:getID()) - - local result = DCS_UTIL.__airportsStartingCoalition[baseId] - if result == nil then - result = DCS_UTIL.__warehouseStartingCoalition[baseId] - end - return result - end - - function DCS_UTIL.CleanCorpse(unitName) - local unitName = "dead_" .. unitName - - local object = StaticObject.getByName(unitName) - - if object then - object:destroy() - end - end - - ---@class DrawColor - ---@field r number - ---@field g number - ---@field b number - ---@field a number - - local drawID = 400 - ---@param zone SpearheadTriggerZone - ---@param lineColor DrawColor - ---@param fillColor DrawColor - ---@param lineStyle LineType - ---@return number drawID - function DCS_UTIL.DrawZone(zone, lineColor, fillColor, lineStyle) - if lineStyle == nil then lineStyle = 4 end - drawID = drawID + 1 - if zone.zone_type == "Cilinder" then - trigger.action.circleToAll(-1, drawID, { x = zone.location.x, y = 0, z = zone.location.y }, zone.radius, - { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, lineStyle, true) - else - local functionString = "trigger.action.markupToAll(7, -1, " .. drawID .. "," - for _, vecpoint in pairs(zone.verts) do - functionString = functionString .. " { x=" .. vecpoint.x .. ", y=0,z=" .. vecpoint.y .. "}," - end - functionString = functionString .. "{0,1,0,1}, {0,1,0,1}, " .. lineStyle .. ")" - - ---@diagnostic disable-next-line: deprecated - local f, err = loadstring(functionString) - if f then - f() - else - env.error("Something failed when drawing complex drawing" .. err) - end - end - local fillColorMapped = { - fillColor.r or 0, - fillColor.g or 0, - fillColor.b or 0, - fillColor.a or 0.5 - } - - local lineColorMapped = { - lineColor.r or 0, - lineColor.g or 0, - lineColor.b or 0, - lineColor.a or 1 - } - - trigger.action.setMarkupColorFill(drawID, fillColorMapped) - trigger.action.setMarkupColor(drawID, lineColorMapped) - - return drawID - end - - ---@param start Vec3 - ---@param finish Vec3 - ---@param lineColor DrawColor - ---@param lineStyle LineType - function DCS_UTIL.DrawLine(start, finish, lineColor, lineStyle) - if lineStyle == nil then lineStyle = 4 end - drawID = drawID + 1 - - local lineColorMapped = { - lineColor.r or 0, - lineColor.g or 0, - lineColor.b or 0, - lineColor.a or 1 - } - - trigger.action.lineToAll(-1, drawID, start, finish, lineColorMapped, lineStyle) - return drawID - end - - ---@param groupID number - ---@param text string - ---@param location Vec3 - ---@return number markID - function DCS_UTIL.AddMarkToGroup(groupID, text, location) - drawID = drawID + 1 - trigger.action.markToGroup(drawID, text, location, groupID, true, nil) - return drawID - end - - ---comment - ---@param text any - ---@param location Vec3 - ---@return integer - function DCS_UTIL.AddMarkToAll(text, location) - drawID = drawID + 1 - trigger.action.markToAll(drawID, text, location, true, nil) - return drawID - end - - ---@param markId number - function DCS_UTIL.RemoveMark(markId) - if markId ~= nil then - trigger.action.removeMark(markId) - end - end - - ---comment - ---@param drawID number - ---@param lineColor DrawColor - function DCS_UTIL.SetLineColor(drawID, lineColor) - local lineColorMapped = { - lineColor.r or 0, - lineColor.g or 0, - lineColor.b or 0, - lineColor.a or 1 - } - trigger.action.setMarkupColor(drawID, lineColorMapped) - end - - ---comment - ---@param drawID number - ---@param fillColor DrawColor - function DCS_UTIL.SetFillColor(drawID, fillColor) - local lineColorMapped = { - fillColor.r or 0, - fillColor.g or 0, - fillColor.b or 0, - fillColor.a or 1 - } - trigger.action.setMarkupColorFill(drawID, lineColorMapped) - end - - function DCS_UTIL.RemoveZoneDraw(drawID) - if drawID ~= nil then - trigger.action.removeMark(drawID) - end - end - - - ---@return number? id - function DCS_UTIL.GetNeutralCountry() - for name, id in pairs(country.id) do - if coalition.getCountryCoalition(id) == DCS_UTIL.Coalition.NEUTRAL then - return id - end - end - end - - - function DCS_UTIL.NeedsRTBInTen(groupName, fuelOffset) - - local isBingo = DCS_UTIL.IsBingoFuel(groupName, fuelOffset) - if isBingo then return true end - - local aliveUnits = 0 - local group = Group.getByName(groupName) - if group then - for _ , unit in pairs(group:getUnits()) do - if unit and unit:isExist() == true and unit:inAir() == true then - aliveUnits = aliveUnits + 1 - end - end - - if aliveUnits / group:getInitialSize() <= 0.5 then - return true - end - end - - return false - end - - ---@return boolean - function DCS_UTIL.IsBingoFuel(groupName, offset) - if offset == nil then offset = 0 end - local bingoSetting = 0.20 - bingoSetting = bingoSetting + offset - - local group = Group.getByName(groupName) - if group then - for _, unit in pairs(group:getUnits()) do - if unit and unit:isExist() == true and unit:inAir() == true and unit:getFuel() < bingoSetting then - return true - end - end - end - - return false - end - - ---comment - ---@param groupId number - ---@return Group? - function DCS_UTIL.GetPlayerGroupByGroupID(groupId) - for i = 0, 2 do - local players = coalition.getPlayers(i) - for key, unit in pairs(players) do - if unit and unit:isExist() == true then - local group = unit:getGroup() - if group and group:getID() == groupId then - return group - end - end - end - end - end - - ---@param unitID number - ---@return Unit? - function DCS_UTIL.GetPLayerUnitByID(unitID) - for i = 0, 2 do - local players = coalition.getPlayers(i) - for key, unit in pairs(players) do - if unit and unit:getID() == unitID then - return unit - end - end - end - end - - DCS_UTIL.__INIT(); -end -Spearhead.DcsUtil = DCS_UTIL - ---- @class Logger ---- @field LoggerName string the name of the logger ---- @field LogLevel string the log level of the logger -local LOGGER = {} -do - local PreFix = "Spearhead" - - ---comment - ---@param logger_name any - ---@param logLevel LogLevel - ---@return Logger - function LOGGER.new(logger_name, logLevel) - LOGGER.__index = LOGGER - local self = setmetatable({}, LOGGER) - self.LoggerName = logger_name or "(loggername not set)" - self.LogLevel = logLevel or "INFO" - - return self - end - - ---@param message any the message - function LOGGER:info(message) - if message == nil then - return - end - message = UTIL.toString(message) - - if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" then - env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message) - end - end - - ---comment - ---@param message string - function LOGGER:warn(message) - if message == nil then - return - end - message = UTIL.toString(message) - - if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" then - env.warning("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message) - end - end - - ---@param message any -- the message - function LOGGER:error(message) - if message == nil then - return - end - - message = UTIL.toString(message) - - if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" or self.LogLevel == "ERROR" then - env.error("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message) - end - end - - ---@param message any the message - function LOGGER:debug(message) - if message == nil then - return - end - - message = UTIL.toString(message) - if self.LogLevel == "DEBUG" then - env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "][DEBUG] " .. message) - end - end -end -Spearhead.LoggerTemplate = LOGGER - -Spearhead.MissionEditingWarnings = {} -function Spearhead.AddMissionEditorWarning(warningMessage) - table.insert(Spearhead.MissionEditingWarnings, warningMessage or "skip") -end - -local loadDone = false -Spearhead.LoadingDone = function() - if loadDone == true then - return - end - - local warningLogger = Spearhead.LoggerTemplate.new("MISSIONPARSER", "INFO") - if Spearhead.Util.tableLength(Spearhead.MissionEditingWarnings) > 0 then - for key, message in pairs(Spearhead.MissionEditingWarnings) do - warningLogger:warn(message) - end - else - warningLogger:info("No issues detected") - end - - loadDone = true -end +local Util = require("classes.util.Util") + +---DCS UTIL Takes inspiration from MIST but only takes the things it needs, changes for DCS updates and different vision for advanced mission scripting stuff. +---It also adds functions that make the other TDCS scripts easier without taking too much "control" away like MOOSE can sometimes. +---@class DcsUtil +local DCS_UTIL = {} +do -- INIT DCS_UTIL + do -- local databases + --[=[ + groupdata = { + category, + country_id, + group_template + } + --]=] + + --[[ + zone = { + name, + + zone_type, + x, + y, + radius + verts, + + } + something ]] + + ---@alias SpearheadTriggerZoneType + ---| "Cilinder" + ---| "Polygon" + + ---@class SpearheadTriggerZone + ---@field name string + ---@field location Vec2 + ---@field radius number + ---@field verts Array + ---@field zone_type SpearheadTriggerZoneType + ---@field properties Array? + + ---@type Array + DCS_UTIL.__trigger_zones = {} + end + + DCS_UTIL.Coalition = + { + NEUTRAL = 0, + RED = 1, + BLUE = 2 + } + + DCS_UTIL.ZoneType = { + Cilinder = 0, + Polygon = 2 + } + + ---@enum SpearheadGroupCategory + DCS_UTIL.GroupCategory = { + AIRPLANE = 0, + HELICOPTER = 1, + GROUND = 2, + SHIP = 3, + TRAIN = 4, + STATIC = 5 --CUSTOM CATEGORY + } + + DCS_UTIL.__airbaseNamesById = {} + + ---@type table + DCS_UTIL.__airbaseZonesByName = {} + + DCS_UTIL.__airportsStartingCoalition = {} + DCS_UTIL.__warehouseStartingCoalition = {} + function DCS_UTIL.__INIT() + do -- INITS ALL TABLES WITH DATA THAT's from the MIZ environment + + do --init trigger zones + for i, trigger_zone in pairs(env.mission.triggers.zones) do + -- reorder verts as they are not ordered correctly in the ME + local verts = {} + if Util.tableLength(trigger_zone.verticies) >= 4 then + table.insert(verts, { x = trigger_zone.verticies[4].x, y = trigger_zone.verticies[4].y }) + table.insert(verts, { x = trigger_zone.verticies[3].x, y = trigger_zone.verticies[3].y }) + table.insert(verts, { x = trigger_zone.verticies[2].x, y = trigger_zone.verticies[2].y }) + table.insert(verts, { x = trigger_zone.verticies[1].x, y = trigger_zone.verticies[1].y }) + end + + local zoneType = "Cilinder" + if trigger_zone.type == DCS_UTIL.ZoneType.Polygon then + zoneType = "Polygon" + end + + ---@type SpearheadTriggerZone + local zone = { + name = trigger_zone.name, + zone_type = zoneType, + location = { x = trigger_zone.x, y = trigger_zone.y }, + radius = trigger_zone.radius, + verts = verts, + properties = {} + } + + if trigger_zone.properties then + for _, kvPair in pairs(trigger_zone.properties) do + local key = kvPair["key"] + local value = kvPair["value"] + zone.properties[#zone.properties + 1] = { key = key, value = value } + end + end + + DCS_UTIL.__trigger_zones[zone.name] = zone + end + end + + do -- init airports and warehouses + if env.warehouses.airports then + for warehouse_id, value in pairs(env.warehouses.airports) do + if warehouse_id ~= nil then + warehouse_id = tostring(warehouse_id) or "nil" + local coalitionNumber = DCS_UTIL.stringToCoalition(value.coalition) + DCS_UTIL.__airportsStartingCoalition[warehouse_id] = coalitionNumber + end + end + end + + if env.warehouses.warehouses then + DCS_UTIL.__warehouseStartingCoalition[-1] = "placeholder" + for warehouse_id, value in pairs(env.warehouses.warehouses) do + if warehouse_id ~= nil then + warehouse_id = tostring(warehouse_id) or "nil" + local coalitionNumber = DCS_UTIL.stringToCoalition(value.coalition) + DCS_UTIL.__warehouseStartingCoalition[warehouse_id] = coalitionNumber + end + end + end + end + + do -- fill airbaseNames and zones + local airbases = world.getAirbases() + if airbases then + for _, airbase in pairs(airbases) do + local name = airbase:getName() + + airbase:autoCapture(false) + + DCS_UTIL.__airbaseNamesById[tostring(airbase:getID())] = name + + if name then + ---@type Array + local relevantPoints = {} + for _, x in pairs(airbase:getRunways()) do + if x.position and x.position.x and x.position.z then + table.insert(relevantPoints, { x = x.position.x, y = x.position.z }) + end + end + + for _, x in pairs(airbase:getParking()) do + if x.vTerminalPos and x.vTerminalPos.x and x.vTerminalPos.z then + table.insert(relevantPoints, { x = x.vTerminalPos.x, y = x.vTerminalPos.z }) + end + end + + local points = Util.getConvexHull(relevantPoints) + local enlargedPoints = Util.enlargeConvexHull(points, 750) + + local triggerZone = { + name = name, + location = { x = airbase:getPoint().x, y = airbase:getPoint().z }, + zone_type = "Polygon", + radius = 0, + verts = enlargedPoints + } + + if SpearheadConfig and SpearheadConfig.debugEnabled == true then + DCS_UTIL.DrawZone(triggerZone, { r = 0, g = 1, b = 0, a = 1 }, { a = 0, r = 0, g = 1, b = 0 }, 1) + end + + DCS_UTIL.__airbaseZonesByName[name] = triggerZone + end + end + end + end + end + end + + ---maps the coalition name to the DCS coalition integer + ---@param input string the name + ---@return integer + function DCS_UTIL.stringToCoalition(input) + --[[ + coalition.side = { + NEUTRAL = 0 + RED = 1 + BLUE = 2 + } + ]] -- + local input = string.lower(input) + if input == 'neutrals' or input == "neutral" or input == "0" then + return DCS_UTIL.Coalition.NEUTRAL + end + + if input == 'red' or input == "1" then + return DCS_UTIL.Coalition.RED + end + + if input == 'blue' or input == "2" then + return DCS_UTIL.Coalition.BLUE + end + + return -1 + end + + + ---destroy the given unit + ---@param unitName string + function DCS_UTIL.DestroyUnit(unitName) + local unit = Unit.getByName(unitName) + if unit and unit:isExist() then + unit:destroy() + end + end + + --- takes a list of units and returns all the units that are in any of the zones + ---@param unit_names table unit names + ---@param zone_names table zone names + ---@return table unit list of objects { unit = UNIT, zone_name = zoneName} + function DCS_UTIL.getUnitsInZones(unit_names, zone_names) + local units = {} + + ---@type Array + local zones = {} + + for k = 1, #unit_names do + local unit = Unit.getByName(unit_names[k]) or StaticObject.getByName(unit_names[k]) + if unit and unit:isExist() == true then + units[#units + 1] = unit + end + end + + for index, zone_name in pairs(zone_names) do + local zone = DCS_UTIL.__trigger_zones[zone_name] + if zone then + zones[#zones + 1] = zone + end + end + + local in_zone_units = {} + for units_ind = 1, #units do + local lUnit = units[units_ind] + local unit_pos = lUnit:getPosition().p + local lCat = Object.getCategory(lUnit) + for zone_name, zone in pairs(zones) do + if unit_pos and ((lCat == 1 and lUnit:isActive() == true) or lCat ~= 1) then -- it is a unit and is active or it is not a unit + local isInZone = Util.is3dPointInZone(unit_pos, zone) + if isInZone == true then + in_zone_units[#in_zone_units + 1] = { unit = lUnit, zone_name = zone.name } + end + end + end + end + return in_zone_units + end + + --- takes a list of groups and returns all the group leaders that are in any of the zones + ---@param group_names table unit names + ---@param zone_name string zone names + ---@return table groupnames list of group names + function DCS_UTIL.getGroupsInZone(group_names, zone_name) + local zone = DCS_UTIL.__trigger_zones[zone_name] + if zone == nil then + return {} + end + + return DCS_UTIL.areGroupsInCustomZone(group_names, zone) + end + + --- takes a x, y poistion and checks if it is inside any of the zones + ---@param group_names Array North South position + ---@param zone SpearheadTriggerZone + ---@return Array groupnames list of groups that are in the zone + function DCS_UTIL.areGroupsInCustomZone(group_names, zone) + local units = {} + if Util.tableLength(group_names) < 1 then return {} end + + for k = 1, #group_names do + local entry = nil + local group = Group.getByName(group_names[k]) + if group ~= nil then + entry = { unit = group:getUnit(1), groupname = group_names[k] } + else + entry = { unit = StaticObject.getByName(group_names[k]), groupname = group_names[k] } + end + + if entry and entry.unit and entry.unit:isExist() == true then + units[#units + 1] = entry + end + end + + local result_groups = {} + for _, entry in pairs(units) do + local pos = entry.unit:getPoint() + local isInZone = Util.is3dPointInZone(pos, zone) + if isInZone == true then + table.insert(result_groups, entry.groupname) + end + end + return result_groups + end + + --- takes a x, y poistion and checks if it is inside any of the zones + ---@param x number North South position + ---@param z number West East position + ---@param zone_names table zone names + ---@return table zones list of objects { zone_name = zoneName} + function DCS_UTIL.isPositionInZones(x, z, zone_names) + ---@type Array + local zones = {} + for index, zone_name in pairs(zone_names) do + local zone = DCS_UTIL.__trigger_zones[zone_name] + if zone then + zones[#zones + 1] = zone + end + end + + local result_zones = {} + for zone_name, zone in pairs(zones) do + if Util.is3dPointInZone({ x = x, z = z, y = 0 }, zone) == true then + result_zones[#result_zones + 1] = zone.name + end + end + return result_zones + end + + --- takes a x, y poistion and checks if it is inside any of the zones + ---@param x number North South position + ---@param z number West East position + ---@param zone_name string zone name + ---@return boolean result + function DCS_UTIL.isPositionInZone(x, z, zone_name) + local zone = DCS_UTIL.__trigger_zones[zone_name] + if Util.is3dPointInZone({ x = x, y = 0, z = z }, zone) then + return true + end + return false + end + + --- takes a x, y poistion and checks if it is inside any of the zones + ---@param zone_name string + ---@param parent_zone_name string + ---@return boolean result + function DCS_UTIL.isZoneInZone(zone_name, parent_zone_name) + local zoneA = DCS_UTIL.__trigger_zones[zone_name] --[[@as SpearheadTriggerZone]] + if zoneA == nil then return false end + local zoneB = DCS_UTIL.__trigger_zones[parent_zone_name] --[[@as SpearheadTriggerZone]] + if zoneB == nil then return false end + return Util.is3dPointInZone({ x = zoneA.location.x, y = 0, z = zoneA.location.y }, zoneB) + end + + ---comment + ---@param zone_name any + ---@return SpearheadTriggerZone? zone { name,b zone_type, x, z, radius, verts } + function DCS_UTIL.getZoneByName(zone_name) + if zone_name == nil then return nil end + return DCS_UTIL.__trigger_zones[zone_name] + end + + ---comment + ---@param airbaseName string + ---@return SpearheadTriggerZone? zone + function DCS_UTIL.getAirbaseZoneByName(airbaseName) + if airbaseName == nil then return nil end + return DCS_UTIL.__airbaseZonesByName[airbaseName] + end + + ---maps the category name to the DCS group category + ---@param input string the name + ---@return integer? + function DCS_UTIL.stringToGroupCategory(input) + input = string.lower(input) + if input == 'airplane' or input == 'plane' then + return DCS_UTIL.GroupCategory.AIRPLANE + end + if input == 'helicopter' then + return DCS_UTIL.GroupCategory.HELICOPTER + end + if input == 'ground' or input == 'vehicle' then + return DCS_UTIL.GroupCategory.GROUND + end + if input == 'ship' then + return DCS_UTIL.GroupCategory.SHIP + end + if input == 'train' then + return DCS_UTIL.GroupCategory.TRAIN + end + if input == "static" then + return DCS_UTIL.GroupCategory.STATIC + end + return nil; + end + + ---@type table + local config = + { + ["ah-64d_blk_ii"] = "MGRS", + ["fa-18c_hornet"] = "DMS", + ["av8bna"] = "DMS", + ["f-14b"] = "DMS", + ["f-14a-135-gr"] = "DMS" + } + + + ---@param location Vec2 + ---@param unitType string + function DCS_UTIL.convertVec2ToUnitUsableType(location, unitType) + + local height = land.getHeight(location) + local vec3 = { x = location.x, y = height, z = location.y } + + local unitType = string.lower(unitType or "") + local conversionType = config[unitType] + + if not conversionType then conversionType = "DDM" end + return DCS_UTIL.convertToDisplayCoord(vec3, conversionType) + end + + ---@alias CoordType + ---| "MGRS" MGRS + ---| "DMS" DECIMAL SECONDS + ---| "DDM" DECIMAL MINUTES + + ---@private + ---@param location Vec3 + ---@param coordType CoordType + function DCS_UTIL.convertToDisplayCoord(location, coordType) + local lattitude, longitude, altitude = coord.LOtoLL(location) + + if coordType == "MGRS" then + local mgrs = coord.LLtoMGRS(lattitude, longitude) + return string.format("%s %s %s %s", mgrs.UTMZone, mgrs.MGRSDigraph, mgrs.Easting, mgrs.Northing) + end + + -- Convert DD to DDM (Degrees Decimal Minutes) + local function dd_to_ddm(dd) + local degrees = math.floor(math.abs(dd)) + local minutes = (math.abs(dd) - degrees) * 60 + local sign = dd >= 0 and 1 or -1 + return degrees * sign, minutes + end + + local lat_deg, lat_min = dd_to_ddm(lattitude) + local lon_deg, lon_min = dd_to_ddm(longitude) + + local lat_hemisphere = lattitude >= 0 and "N" or "S" + local lon_hemisphere = longitude >= 0 and "E" or "W" + + if coordType == "DDM" then + return string.format("%s%02d°%06.3f' %s%03d°%06.3f' %dft", + lat_hemisphere, math.abs(lat_deg), lat_min, + lon_hemisphere, math.abs(lon_deg), lon_min, + altitude * 3,28084) + end + + if coordType == "DMS" then + + local lat_min_display = math.floor(lat_min) + local lon_min_display = math.floor(lon_min) + + local lat_sec_display = math.floor((lat_min - lat_min_display) * 60) + local lon_sec_display = math.floor((lon_min - lon_min_display) * 60) + + return string.format("%s%02d°%02d'%02d %s%03d°%02d'%02d %dft", + lat_hemisphere, math.abs(lat_deg), lat_min_display, lat_sec_display, + lon_hemisphere, math.abs(lon_deg), lon_min_display, lon_sec_display, + altitude * 3,28084) + end + + end + + ---@param zone SpearheadTriggerZone + ---@return Array sceneryObjects + function DCS_UTIL.getSceneryObjectsInZone(zone) + ---@type Volume + local volume + + if(zone.zone_type == "Cilinder") then + local y = land.getHeight({ x = zone.location.x, y = zone.location.y }) + ---@type Sphere + local sphere = { + id = world.VolumeType.SPHERE, + params = { + point = { x = zone.location.x, y = y, z = zone.location.y }, + radius = zone.radius + } + } + volume = sphere + else + local minX = nil + local maxX = nil + local minZ = nil + local maxZ = nil + + for _, point in pairs(zone.verts) do + if minX == nil or point.x < minX then + minX = point.x + end + if maxX == nil or point.x > maxX then + maxX = point.x + end + if minZ == nil or point.y < minZ then + minZ = point.y + end + if maxZ == nil or point.y > maxZ then + maxZ = point.y + end + end + + if(minX == nil or maxX == nil or minZ == nil or maxZ == nil) then + return {} + end + + ---@type Vec3 + local min = { + x = minX, + y = land.getHeight({ x = minX, y = minZ }) - 100, + z = minZ + } + + ---@type Vec3 + local max = { + x = maxX, + y = land.getHeight({ x = maxX, y = maxZ }) + 500, + z = maxZ + } + + ---@type Box + local box = { + id = world.VolumeType.BOX, + params = { + min = min, + max = max + } + } + volume = box + end + + ---@type Array + local sceneryObjects = {} + + ---@param object SceneryObject + local onFound = function(object) + if object and object:isExist() and + object:hasAttribute("Buildings") + then + local obj = Spearhead.classes.stageClasses.Groups.SpearheadSceneryObject.New(object["id_"]) + table.insert(sceneryObjects, obj) + end + end + + world.searchObjects(Object.Category.SCENERY, volume, onFound) + return sceneryObjects + end + + ---@param group Group + function DCS_UTIL.getUnitTypeFromGroup(group) + for _, unit in pairs(group:getUnits()) do + if unit and unit:isExist() then + return unit:getTypeName() + end + end + end + + + ---comment Get all units that are players + ---@return Array units + function DCS_UTIL.getAllPlayerUnits() + local units = {} + for i = 0, 2 do + local players = coalition.getPlayers(i) + for key, unit in pairs(players) do + units[#units + 1] = unit + end + end + return units + end + + ---get base name from ID + ---@param baseId number + ---@return string? name + function DCS_UTIL.getAirbaseName(baseId) + local stringified = tostring(baseId) + return DCS_UTIL.__airbaseNamesById[stringified] + end + + ---get base from id + ---@param baseId number + ---@return Airbase? table + function DCS_UTIL.getAirbaseById(baseId) + local name = DCS_UTIL.getAirbaseName(baseId) + if name == nil then return nil end + return Airbase.getByName(name) + end + + ---Get the starting coalition of a farp or airbase + ---@param airbase Airbase + ---@return number? coalition + function DCS_UTIL.getStartingCoalition(airbase) + if airbase == nil then + return nil + end + + --STRING based dictionary otherwise it'll be a string/collapsed array + local baseId = tostring(airbase:getID()) + + local result = DCS_UTIL.__airportsStartingCoalition[baseId] + if result == nil then + result = DCS_UTIL.__warehouseStartingCoalition[baseId] + end + return result + end + + function DCS_UTIL.CleanCorpse(unitName) + local unitName = "dead_" .. unitName + + local object = StaticObject.getByName(unitName) + + if object then + object:destroy() + end + end + + ---@class DrawColor + ---@field r number + ---@field g number + ---@field b number + ---@field a number + + local drawID = 400 + ---@param zone SpearheadTriggerZone + ---@param lineColor DrawColor + ---@param fillColor DrawColor + ---@param lineStyle LineType + ---@return number drawID + function DCS_UTIL.DrawZone(zone, lineColor, fillColor, lineStyle) + if lineStyle == nil then lineStyle = 4 end + drawID = drawID + 1 + if zone.zone_type == "Cilinder" then + trigger.action.circleToAll(-1, drawID, { x = zone.location.x, y = 0, z = zone.location.y }, zone.radius, + { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, lineStyle, true) + else + local functionString = "trigger.action.markupToAll(7, -1, " .. drawID .. "," + for _, vecpoint in pairs(zone.verts) do + functionString = functionString .. " { x=" .. vecpoint.x .. ", y=0,z=" .. vecpoint.y .. "}," + end + functionString = functionString .. "{0,1,0,1}, {0,1,0,1}, " .. lineStyle .. ")" + + ---@diagnostic disable-next-line: deprecated + local f, err = loadstring(functionString) + if f then + f() + else + env.error("Something failed when drawing complex drawing" .. err) + end + end + local fillColorMapped = { + fillColor.r or 0, + fillColor.g or 0, + fillColor.b or 0, + fillColor.a or 0.5 + } + + local lineColorMapped = { + lineColor.r or 0, + lineColor.g or 0, + lineColor.b or 0, + lineColor.a or 1 + } + + trigger.action.setMarkupColorFill(drawID, fillColorMapped) + trigger.action.setMarkupColor(drawID, lineColorMapped) + + return drawID + end + + ---@param start Vec3 + ---@param finish Vec3 + ---@param lineColor DrawColor + ---@param lineStyle LineType + function DCS_UTIL.DrawLine(start, finish, lineColor, lineStyle) + if lineStyle == nil then lineStyle = 4 end + drawID = drawID + 1 + + local lineColorMapped = { + lineColor.r or 0, + lineColor.g or 0, + lineColor.b or 0, + lineColor.a or 1 + } + + trigger.action.lineToAll(-1, drawID, start, finish, lineColorMapped, lineStyle) + return drawID + end + + ---@param groupID number + ---@param text string + ---@param location Vec3 + ---@return number markID + function DCS_UTIL.AddMarkToGroup(groupID, text, location) + drawID = drawID + 1 + trigger.action.markToGroup(drawID, text, location, groupID, true, nil) + return drawID + end + + ---comment + ---@param text any + ---@param location Vec3 + ---@return integer + function DCS_UTIL.AddMarkToAll(text, location) + drawID = drawID + 1 + trigger.action.markToAll(drawID, text, location, true, nil) + return drawID + end + + ---@param markId number + function DCS_UTIL.RemoveMark(markId) + if markId ~= nil then + trigger.action.removeMark(markId) + end + end + + ---comment + ---@param drawID number + ---@param lineColor DrawColor + function DCS_UTIL.SetLineColor(drawID, lineColor) + local lineColorMapped = { + lineColor.r or 0, + lineColor.g or 0, + lineColor.b or 0, + lineColor.a or 1 + } + trigger.action.setMarkupColor(drawID, lineColorMapped) + end + + ---comment + ---@param drawID number + ---@param fillColor DrawColor + function DCS_UTIL.SetFillColor(drawID, fillColor) + local lineColorMapped = { + fillColor.r or 0, + fillColor.g or 0, + fillColor.b or 0, + fillColor.a or 1 + } + trigger.action.setMarkupColorFill(drawID, lineColorMapped) + end + + function DCS_UTIL.RemoveZoneDraw(drawID) + if drawID ~= nil then + trigger.action.removeMark(drawID) + end + end + + + ---@return number? id + function DCS_UTIL.GetNeutralCountry() + for name, id in pairs(country.id) do + if coalition.getCountryCoalition(id) == DCS_UTIL.Coalition.NEUTRAL then + return id + end + end + end + + + function DCS_UTIL.NeedsRTBInTen(groupName, fuelOffset) + + local isBingo = DCS_UTIL.IsBingoFuel(groupName, fuelOffset) + if isBingo then return true end + + local aliveUnits = 0 + local group = Group.getByName(groupName) + if group then + for _ , unit in pairs(group:getUnits()) do + if unit and unit:isExist() == true and unit:inAir() == true then + aliveUnits = aliveUnits + 1 + end + end + + if aliveUnits / group:getInitialSize() <= 0.5 then + return true + end + end + + return false + end + + ---@return boolean + function DCS_UTIL.IsBingoFuel(groupName, offset) + if offset == nil then offset = 0 end + local bingoSetting = 0.20 + bingoSetting = bingoSetting + offset + + local group = Group.getByName(groupName) + if group then + for _, unit in pairs(group:getUnits()) do + if unit and unit:isExist() == true and unit:inAir() == true and unit:getFuel() < bingoSetting then + return true + end + end + end + + return false + end + + ---comment + ---@param groupId number + ---@return Group? + function DCS_UTIL.GetPlayerGroupByGroupID(groupId) + for i = 0, 2 do + local players = coalition.getPlayers(i) + for key, unit in pairs(players) do + if unit and unit:isExist() == true then + local group = unit:getGroup() + if group and group:getID() == groupId then + return group + end + end + end + end + end + + ---@param unitID number + ---@return Unit? + function DCS_UTIL.GetPlayerUnitByID(unitID) + for i = 0, 2 do + local players = coalition.getPlayers(i) + for key, unit in pairs(players) do + if unit and unit:getID() == unitID then + return unit + end + end + end + end + + DCS_UTIL.__INIT(); +end +return DCS_UTIL diff --git a/src/classes/util/Logger.lua b/src/classes/util/Logger.lua new file mode 100644 index 0000000..29fa232 --- /dev/null +++ b/src/classes/util/Logger.lua @@ -0,0 +1,75 @@ + +local Util = require("classes.util.Util") + +--- @class Logger +--- @field LoggerName string the name of the logger +--- @field LogLevel string the log level of the logger +local LOGGER = {} +do + local PreFix = "Spearhead" + + ---comment + ---@param logger_name any + ---@param logLevel LogLevel + ---@return Logger + function LOGGER.new(logger_name, logLevel) + LOGGER.__index = LOGGER + local self = setmetatable({}, LOGGER) + self.LoggerName = logger_name or "(loggername not set)" + self.LogLevel = logLevel or "INFO" + + return self + end + + ---@param message any the message + function LOGGER:info(message) + if message == nil then + return + end + message = Util.toString(message) + + if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" then + env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message) + end + end + + ---comment + ---@param message string + function LOGGER:warn(message) + if message == nil then + return + end + message = Util.toString(message) + + if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" then + env.warning("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message) + end + end + + ---@param message any -- the message + function LOGGER:error(message) + if message == nil then + return + end + + message = Util.toString(message) + + if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" or self.LogLevel == "ERROR" then + env.error("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message) + end + end + + ---@param message any the message + function LOGGER:debug(message) + if message == nil then + return + end + + message = Util.toString(message) + if self.LogLevel == "DEBUG" then + env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "][DEBUG] " .. message) + end + end +end + +return LOGGER \ No newline at end of file diff --git a/src/classes/util/MissionEditorWarnings.lua b/src/classes/util/MissionEditorWarnings.lua new file mode 100644 index 0000000..a5ff7ea --- /dev/null +++ b/src/classes/util/MissionEditorWarnings.lua @@ -0,0 +1,26 @@ + +---@class MissionEditingWarnings +local MissionEditingWarnings = {} +function MissionEditingWarnings.Add(warningMessage) + table.insert(MissionEditingWarnings, warningMessage or "skip") +end + +---@param logger Logger +function MissionEditingWarnings.WriteAll(logger) + + if not logger then + return + end + + if not MissionEditingWarnings or #MissionEditingWarnings == 0 then + return + end + + logger:warn("Mission Editor Warnings:") + for _, warning in ipairs(MissionEditingWarnings) do + logger:warn("- " .. warning) + end + +end + +return MissionEditingWarnings \ No newline at end of file diff --git a/src/classes/util/Util.lua b/src/classes/util/Util.lua new file mode 100644 index 0000000..41dc0ff --- /dev/null +++ b/src/classes/util/Util.lua @@ -0,0 +1,503 @@ +---@class Util +local UTIL = {} +do -- INIT UTIL + ---splits a string in sub parts by seperator + ---@param input string + ---@param seperator string + ---@return table result list of strings + function UTIL.split_string(input, seperator) + if seperator == nil then + seperator = " " + end + + local result = {} + if input == nil then + return result + end + + for str in string.gmatch(input, "[^" .. seperator .. "]+") do + table.insert(result, str) + end + return result + end + + ---comment + ---@param table any + ---@return number + function UTIL.tableLength(table) + if table == nil then return 0 end + + local count = 0 + for _ in pairs(table) do count = count + 1 end + return count + end + + ---@param orig table + ---@return table copy + function UTIL.deepCopyTable(orig) + local orig_type = type(orig) + local copy + if orig_type == 'table' then + copy = {} + for orig_key, orig_value in next, orig, nil do + copy[UTIL.deepCopyTable(orig_key)] = UTIL.deepCopyTable(orig_value) + end + setmetatable(copy, UTIL.deepCopyTable(getmetatable(orig))) + else -- number, string, boolean, etc + copy = orig + end + return copy + end + + ---Gets a random from the list + ---@param list Array + ---@return any @random element from the list + function UTIL.randomFromList(list) + local max = #list + + if max == 0 or max == nil then + return nil + end + + local random = math.random(0, max) + if random == 0 then random = 1 end + + return list[random] + end + + ---@param list Array + ---@param start number start + ---@param n number length + ---@return Array + function UTIL.sublist(list, start, n) + local result = {} + for i = start, n do + result[#result + 1] = list[i] + end + return result + end + + ---@param str string + ---@param find string + ---@param replace string + ---@return string + function UTIL.replaceString(str, find, replace) + if str == nil then return "" end + if find == nil or replace == nil then return str end + + local result = str:gsub(find, replace) + return result + end + + local function table_print(tt, indent, done) + done = done or {} + indent = indent or 0 + if type(tt) == "table" then + local sb = {} + for key, value in pairs(tt) do + table.insert(sb, string.rep(" ", indent)) -- indent it + if type(value) == "table" and not done[value] then + done[value] = true + table.insert(sb, "[\"" ..key .. "\"]" .. " = {\n"); + table.insert(sb, table_print(value, indent + 2, done)) + table.insert(sb, string.rep(" ", indent)) -- indent it + table.insert(sb, "},\n"); + elseif "number" == type(key) then + table.insert(sb, string.format("\"%s\",\n", tostring(value))) + else + table.insert(sb, string.format( + "[\"%s\"] = \"%s\",\n", tostring(key), tostring(value))) + end + end + return table.concat(sb) + else + return tt .. "\n" + end + end + + ---comment + ---@param str string + ---@param findable string + ---@param ignoreCase boolean? + ---@return boolean + UTIL.startswith = function(str, findable, ignoreCase) + if ignoreCase == true then + return string.lower(str):find('^' .. string.lower(findable)) ~= nil + end + + return str:find('^' .. findable) ~= nil + end + + ---comment + ---@param str string + ---@param findable string + ---@return boolean + UTIL.strContains = function(str, findable) + return str:find(findable) ~= nil + end + + ---comment + ---@param str string + ---@param findableTable table + ---@return boolean + UTIL.startswithAny = function(str, findableTable) + for key, value in pairs(findableTable) do + if type(value) == "string" and UTIL.startswith(str, value) then return true end + end + return false + end + + function UTIL.toString(something) + if something == nil then + return "nil" + elseif "table" == type(something) then + return table_print(something) + elseif "string" == type(something) then + return something + else + return tostring(something) + end + end + + ---comment + ---@param a Vec2 + ---@param b Vec2 + ---@return number + function UTIL.VectorDistance2d(a, b) + return math.sqrt((b.x - a.x) ^ 2 + (b.y - a.y) ^ 2) + end + + ---comment + ---@param a Vec3 + ---@param b Vec3 + ---@return number + function UTIL.VectorDistance3d(a, b) + return UTIL.vectorMagnitude({ x = a.x - b.x, y = a.y - b.y, z = a.z - b.z }) + end + + ---comment + ---@param vec Vec3 + ---@return number + function UTIL.vectorMagnitude(vec) + return (vec.x ^ 2 + vec.y ^ 2 + vec.z ^ 2) ^ 0.5 + end + + ---@param vec Vec3 + ---@return Vec3 + function UTIL.vectorNormalize(vec) + local magnitude = UTIL.vectorMagnitude(vec) + if magnitude == 0 then + return { x = 0, y = 0, z = 0 } + end + return { x = vec.x / magnitude, y = vec.y / magnitude, z = vec.z / magnitude } + end + + ---@param vec Vec2 + ---@param direction number @in degrees + ---@param distance number + ---@return Vec2 + function UTIL.vectorMove(vec, direction, distance) + local rad = math.rad(direction) + local x = vec.x + (math.cos(rad) * distance) + local y = vec.y + (math.sin(rad) * distance) + + return { x = x, y = y} + end + + ---comment + ---@param vec1 Vec2 + ---@param vec2 Vec2 + ---@return number in degrees + function UTIL.vectorHeadingFromTo(vec1, vec2) + local dx = vec2.x - vec1.x + local dy = vec2.y - vec1.y + local heading = math.deg(math.atan2(dy, dx)) + if heading < 0 then + heading = heading + 360 + end + return heading + end + + + + ---@param vec1 Vec3 + ---@param vec2 Vec3 + ---@return number alignment a number in range [-1,1]. > 0 same direction. < 0 opposite direction + function UTIL.vectorAlignment(vec1, vec2) + local vec1Norm = UTIL.vectorNormalize(vec1) + local vec2Norm = UTIL.vectorNormalize(vec2) + + return ((vec1Norm.x * vec2Norm.x) + (vec1Norm.y * vec2Norm.y) + (vec1Norm.z * vec2Norm.z)) + end + + local function isInComplexPolygon(polygon, x, y) + local function getEdges(poly) + local result = {} + for i = 1, #poly do + local point1 = poly[i] + local point2Index = i + 1 + if point2Index > #poly then point2Index = 1 end + local point2 = poly[point2Index] + local edge = { x1 = point1.x, z1 = point1.y, x2 = point2.x, z2 = point2.y } + table.insert(result, edge) + end + return result + end + + local edges = getEdges(polygon) + local count = 0; + for _, edge in pairs(edges) do + if (x < edge.x1) ~= (x < edge.x2) and y < edge.z1 + ((x - edge.x1) / (edge.x2 - edge.x1)) * (edge.z2 - edge.z1) then + count = count + 1 + -- if (yp < y1) != (yp < y2) and xp < x1 + ((yp-y1)/(y2-y1))*(x2-x1) then + -- count = count + 1 + end + end + return count % 2 == 1 + end + + ---comment + ---@param polygon Array of pairs { x, y } + ---@param x number X location + ---@param y number Y location + ---@return boolean + function UTIL.IsPointInPolygon(polygon, x, y) + return isInComplexPolygon(polygon, x, y) + end + + ---@param point Vec3 + ---@param zone SpearheadTriggerZone + function UTIL.is3dPointInZone(point, zone) + if zone.zone_type == "Polygon" and zone.verts then + if UTIL.IsPointInPolygon(zone.verts, point.x, point.z) == true then + return true + end + else + if (((point.x - zone.location.x) ^ 2 + (point.z - zone.location.y) ^ 2) ^ 0.5 <= zone.radius) then + return true + end + end + + return false + end + + ---@param point Vec2 + ---@param zone SpearheadTriggerZone + function UTIL.is2dPointInZone(point, zone) + if zone.zone_type == "Polygon" and zone.verts then + if UTIL.IsPointInPolygon(zone.verts, point.x, point.y) == true then + return true + end + else + if (((point.x - zone.location.x) ^ 2 + (point.y - zone.location.y) ^ 2) ^ 0.5 <= zone.radius) then + return true + end + end + + return false + end + + ---comment + ---@param points Array points + ---@return Array hullPoints + function UTIL.getConvexHull(points) + if #points == 0 then + return {} + end + + ---comment + ---@param a Vec2 + ---@param b Vec2 + ---@param c Vec2 + ---@return boolean + local function ccw(a, b, c) + return (b.y - a.y) * (c.x - a.x) > (b.x - a.x) * (c.y - a.y) + end + + table.sort(points, function(left, right) + return left.y < right.y + end) + + local hull = {} + -- lower hull + for _, point in pairs(points) do + while #hull >= 2 and not ccw(hull[#hull - 1], hull[#hull], point) do + table.remove(hull, #hull) + end + table.insert(hull, point) + end + + -- upper hull + local t = #hull + 1 + for i = #points, 1, -1 do + local point = points[i] + while #hull >= t and not ccw(hull[#hull - 1], hull[#hull], point) do + table.remove(hull, #hull) + end + table.insert(hull, point) + end + table.remove(hull, #hull) + return hull + end + + ---Splits points into clusters with at least minSeparation between clusters, returns convex hull for each cluster + ---@param points Array + ---@param minSeparation number + ---@return Array> hulls + function UTIL.getSeparatedConvexHulls(points, minSeparation) + if #points == 0 then return {} end + + -- Simple clustering: group points that are within minSeparation of each other + local clusters = {} + local assigned = {} + + for i, p in ipairs(points) do + if not assigned[i] then + local cluster = { p } + assigned[i] = true + -- Find all points close to p (BFS) + local queue = { i } + while #queue > 0 do + local idx = table.remove(queue) + local base = points[idx] + for j, q in ipairs(points) do + if not assigned[j] then + local dx = base.x - q.x + local dy = base.y - q.y + if (dx * dx + dy * dy) <= (minSeparation * minSeparation) then + table.insert(cluster, q) + assigned[j] = true + table.insert(queue, j) + end + end + end + end + table.insert(clusters, cluster) + end + end + + -- Compute convex hull for each cluster + local hulls = {} + for _, cluster in ipairs(clusters) do + local hull = UTIL.getConvexHull(cluster) + if #hull > 0 then + table.insert(hulls, hull) + end + end + + return hulls + end + + ---@param points Array + function UTIL.enlargeConvexHull(points, meters) + if points == nil or #points == 0 then + return {} + end + + ---@type Array + local allpoints = {} + + for _, point in pairs(points) do + table.insert(allpoints, point) + + allpoints[#allpoints + 1] = point + allpoints[#allpoints+1] = { x = point.x + meters, y = point.y, } + allpoints[#allpoints+1] = { x = point.x - meters, y = point.y, } + allpoints[#allpoints+1] = { x = point.x, y = point.y + meters, } + allpoints[#allpoints+1] = { x = point.x, y = point.y - meters, } + + allpoints[#allpoints+1] = { x = point.x + math.cos(math.rad(45)) * meters, y = point.y + math.sin(math.rad(45)) * meters, } + allpoints[#allpoints+1] = { x = point.x - math.cos(math.rad(45)) * meters, y = point.y - math.sin(math.rad(45)) * meters, } + allpoints[#allpoints+1] = { x = point.x - math.cos(math.rad(45)) * meters, y = point.y + math.sin(math.rad(45)) * meters, } + allpoints[#allpoints+1] = { x = point.x + math.cos(math.rad(45)) * meters, y = point.y - math.sin(math.rad(45)) * meters, } + end + + return UTIL.getConvexHull(allpoints) + end + + ---Returns hull points visible from origin (not blocked by hull edges) +---@param hull Array +---@param origin Vec2 +---@return Array +function UTIL.GetVisibleHullPointsFromOrigin(hull, origin) + local function segmentsIntersect(a, b, c, d) + -- Helper: returns true if segment ab intersects cd (excluding endpoints) + local function ccw(p1, p2, p3) + return (p3.y - p1.y) * (p2.x - p1.x) > (p2.y - p1.y) * (p3.x - p1.x) + end + return (ccw(a, c, d) ~= ccw(b, c, d)) and (ccw(a, b, c) ~= ccw(a, b, d)) + end + + local n = #hull + local visible = {} + + for i = 1, n do + local p = hull[i] + local isVisible = true + + -- Check against all hull edges except those incident to p + for j = 1, n do + local a = hull[j] + local b = hull[(j % n) + 1] + -- skip edges incident to p + if (a ~= p and b ~= p) then + if segmentsIntersect(origin, p, a, b) then + isVisible = false + break + end + end + end + + if isVisible then + table.insert(visible, p) + end + end + + return visible +end + +---Returns the two tangent points ("scratch points") from origin to the convex hull +---@param hull Array +---@param origin Vec2 +---@return Array +function UTIL.GetTangentHullPointsFromOrigin(hull, origin) + + if hull == nil or #hull <= 0 then + return {} + end + + local function orientation(a, b, c) + -- Returns >0 if c is to the left of ab, <0 if to the right, 0 if colinear + return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x) + end + + local n = #hull + if n == 0 then return {} end + if n == 1 then return { hull[1] } end + + -- Find left tangent: the hull point where all other points are to the right of the line from origin to that point + local function findTangent(isLeft) + local best = 1 + for i = 2, n do + local o = orientation(origin, hull[best], hull[i]) + if (isLeft and o < 0) or (not isLeft and o > 0) then + best = i + end + end + return hull[best] + end + + local leftTangent = findTangent(true) + local rightTangent = findTangent(false) + + -- If tangents are the same (can happen if origin is colinear), only return one + if leftTangent.x == rightTangent.x and leftTangent.y == rightTangent.y then + return { leftTangent } + else + return { leftTangent, rightTangent } + end +end + +end + +return UTIL \ No newline at end of file diff --git a/src/main.lua b/src/main.lua new file mode 100644 index 0000000..763ac58 --- /dev/null +++ b/src/main.lua @@ -0,0 +1,96 @@ +--Single player purpose + +local Logger = require("classes.util.Logger") +local DcsUtil = require("classes.util.DcsUtil") +local Database = require("classes.spearhead_db") +local SpearheadEvents = require("classes.spearhead_events") +local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper") +local CapConfig = require("classes.configuration.CapConfig") +local StageConfig = require("classes.configuration.StageConfig") +local Persistence = require("classes.persistence.Persistence") +local SpawnManager = require("classes.helpers.SpawnManager") +local DetectionManager = require("classes.capClasses.detection.DetectionManager") +local GlobalCapManager = require("classes.capClasses.GlobalCapManager") +local GlobalStageManager = require("classes.stageClasses.GlobalStageManager") +local GlobalFleetManager = require("classes.fleetClasses.GlobalFleetManager") +local MissionEditorWarnings = require("classes.util.MissionEditorWarnings") + +local defaultLogLevel = "INFO" + +if SpearheadConfig and SpearheadConfig.debugEnabled == true then + defaultLogLevel = "DEBUG" +end + +local startTime = timer.getTime() * 1000 + +SpearheadEvents.Init(defaultLogLevel) + +local dbLogger = Logger.new("database", defaultLogLevel) +local standardLogger = Logger.new("", defaultLogLevel) +local databaseManager = Database.New(dbLogger) +MissionCommandsHelper.getOrCreate(defaultLogLevel) -- initiate + +local capConfig = CapConfig:new(); +local stageConfig = StageConfig:new(); + +local startingStage = stageConfig.startingStage or 1 +if SpearheadConfig and SpearheadConfig.Persistence and SpearheadConfig.Persistence.enabled == true then + standardLogger:info("Persistence enabled") + local persistenceLogger = Logger.new("Persistence", defaultLogLevel) + Persistence.Init(persistenceLogger) + + local persistanceStage = Persistence.GetActiveStage() + if persistanceStage then + standardLogger:info("Persistance activated and using persistant active stage: " .. persistanceStage) + startingStage = persistanceStage + end +else + standardLogger:info("Persistence disabled") +end + +local spawnLogger = Logger.new("SpawnManager", defaultLogLevel) +local spawnManager = SpawnManager.new(spawnLogger) +local detectionLogger = Logger.new("DetectionManager", defaultLogLevel) +local detectionManager = DetectionManager.New(detectionLogger) + +GlobalCapManager.start(databaseManager, capConfig, detectionManager, stageConfig, defaultLogLevel, spawnManager) +GlobalStageManager.NewAndStart(databaseManager, stageConfig, defaultLogLevel, spawnManager) +GlobalFleetManager.start(databaseManager) + +local SetStageDelayed = function(number, time) + SpearheadEvents.PublishStageNumberChanged(number) + return nil +end + +timer.scheduleFunction(SetStageDelayed, startingStage, timer.getTime() + 3) + +env.info(startTime .. "ms / " .. timer.getTime() * 1000 .. "ms") +local duration = (timer.getTime() * 1000) - startTime +standardLogger:info("Spearhead Initialisation duration: " .. tostring(duration) .. "ms") + +local missionEditorWarningsLogger = Logger.new("MissionEditorWarnings", defaultLogLevel) +MissionEditorWarnings.WriteAll(missionEditorWarningsLogger) +GlobalStageManager:printFullOverview() + +--Check lines of code in directory per file: +-- Get-ChildItem . -Include *.lua -Recurse | foreach {""+(Get-Content $_).Count + " => " + $_.name }; GCI . -Include *.lua* -Recurse | foreach{(GC $_).Count} | measure-object -sum | % Sum +-- find . -name '*.lua' | xargs wc -l + +--- ==================== DEBUG ORDER OR ZONE VEC =========================== +-- local zone = Spearhead.DcsUtil.getZoneByName("MISSIONSTAGE_99") + +-- local count = Spearhead.Util.tableLength(zone.verts) + +-- for i = 1, count - 1 do + +-- local a = zone.verts[i] +-- local b = zone.verts[i+1] + +-- local color = {0,0,0,1} + +-- color[i] = 1 + +-- trigger.action.textToAll(-1, 46+i , { x= a.x, y = 0, z = a.z } , color, {0,0,0}, 24 , true , "" .. i ) +-- trigger.action.lineToAll(-1 , 56+i , { x= a.x, y = 0, z = a.z } , { x = b.x, y = 0, z = b.z } , color , 1, true) + +-- end From 62fbb35f968f237e80ca3232a976707c5726326e Mon Sep 17 00:00:00 2001 From: dutchie031 Date: Sat, 4 Apr 2026 16:52:51 +0200 Subject: [PATCH 4/9] wip --- .DS_Store | Bin 8196 -> 0 bytes .../helpers/MissionCommandsHelper.lua | 21 +++-- .../helpers/SupplyUnitsTracker.lua | 88 ++++++++++++------ .../missions/BuildableMission.lua | 2 +- 4 files changed, 74 insertions(+), 37 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index dd3bcfbdc5a6f9b2baad3dc6000d463bdf36ffa8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHM&1(}u6n~SXHEIt9h4v!kBow94i${&^=Fo$pws_Shsc9r>LSrgb!4;2QJP9HQ z73)pZqfk%~Eux61DAa?B|A2^yUaG(O$mY#{coGRX1G{gT_x0P~yqU>th)9gL%VR{t zM3luP<=|G_Vw%*;E7DS?XAh)+Kh-K@Q>8||8L@O7jseGjW56-s7;p^q3Io`)Wy>b) z`&#SnjseHOf60JcA9A>)%p2)zsg@4h)DZwOgKpLEJSZO^b4KQk^tFTrO=wq0?JCR> zLuhyO=Zsz6NMB3sPQn~Mgvl(-2}P*vh@VsDB=TCiyJNsH5M@A~-7_>w6{^xHets7p zZc6JwS8sS;vDPS}FI&93asKVf+J0MK{-eG**Znq-Dtv@1ZR+HuIcigpXD1rD7Pa6Q zzEvJ9dxxJ$452vnMi)u46~{w#sDoz_Jd|mc8q~7zi1js-iCJ(w_eX9_rhUbU#A8`? z@g!SWc*K`3o)(pW2AC{7l)4q*2`o6CPbb$$9&H5tEE2Qo;z_o$@aV<3kB$LH19)1K zwMNNQgIsXsD2)D6O#&xo$x(}4-gaBd5wAS;HwK0jgUp|gX1oPQDt4=p%m8T}BY7r- z^A56eoPixjN&S;oU>E2(#vk|ItiC#DD&OSb2;=k|ANBhPjfyQ%jb<<=E!0|wO>qep z9A(Jb#`?M)jmHu1d-$yI;;N}jD=WvHY-OR(UY6*$sEVAY6JWT4TCZXD&V-nj6>*(a z!~l7+ZfKg8s43q$-cS4t`K8rtT(qMrE6$LyvY4TvuZU$doRQDZFLdfG&~|4;RBJJy zXF>+3s(1%$#iYKA?~pOj@tk|VpFR4%vtk-w&VQWV%G;-ym$75!U#fzo_jiq@Ay&a=}fS$bNMkhYbEO#BJ3Qa%qFQS`*Dn+7@zxIF R_J6nk<5z`MFFm{e{|0tPe}@17 diff --git a/src/classes/stageClasses/helpers/MissionCommandsHelper.lua b/src/classes/stageClasses/helpers/MissionCommandsHelper.lua index 0b19dd3..a519d0f 100644 --- a/src/classes/stageClasses/helpers/MissionCommandsHelper.lua +++ b/src/classes/stageClasses/helpers/MissionCommandsHelper.lua @@ -453,6 +453,7 @@ function MissionCommandsHelper:AddSupplyHubCommandsIfApplicable(groupID) ---@field groupID number ---@field crateType CrateType ---@field supplyUnitsTracker SupplyUnitsTracker + ---@field commandHelper MissionCommandsHelper ---comment ---@param params LoadCargoCommandParams @@ -460,31 +461,31 @@ function MissionCommandsHelper:AddSupplyHubCommandsIfApplicable(groupID) local crateType = params.crateType local supplyUnitsTracker = params.supplyUnitsTracker if supplyUnitsTracker then - supplyUnitsTracker:UnitRequestCrateLoading(params.groupID, crateType) + supplyUnitsTracker:UnitRequestCrateLoading(params.groupID, crateType, params.commandHelper) end end local path = { [1] = folderNames.supplyHub } ---@type LoadCargoCommandParams - local farpParams1000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "FARP_CRATE_1000", supplyUnitsTracker = self._supplyUnitsTracker } + local farpParams1000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "FARP_CRATE_1000", supplyUnitsTracker = self._supplyUnitsTracker, commandHelper = self } missionCommands.addCommandForGroup(groupID, "Load FARP Crate (1000)", path, loadCargoCommand, farpParams1000) ---@type LoadCargoCommandParams - local farpParams2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "FARP_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker } + local farpParams2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "FARP_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker, commandHelper = self } missionCommands.addCommandForGroup(groupID, "Load FARP Crate (2000)", path, loadCargoCommand, farpParams2000) ---@type LoadCargoCommandParams - local samParms1000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "SAM_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker } + local samParms1000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "SAM_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker, commandHelper = self } missionCommands.addCommandForGroup(groupID, "Load SAM Crate (1000)", path, loadCargoCommand, samParms1000) ---@type LoadCargoCommandParams - local samParms2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "SAM_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker } + local samParms2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "SAM_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker, commandHelper = self } missionCommands.addCommandForGroup(groupID, "Load SAM Crate (2000)", path, loadCargoCommand, samParms2000) ---@type LoadCargoCommandParams - local samParms2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "AIRBASE_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker } - missionCommands.addCommandForGroup(groupID, "Airbase Crate (2000)", path, loadCargoCommand, samParms2000) + local airbaseParms2000 = { unitID = unit:getID(), groupID = group:getID(), crateType = "AIRBASE_CRATE_2000", supplyUnitsTracker = self._supplyUnitsTracker, commandHelper = self } + missionCommands.addCommandForGroup(groupID, "Airbase Crate (2000)", path, loadCargoCommand, airbaseParms2000) end function MissionCommandsHelper:AddCargoCommands(groupID) @@ -499,13 +500,15 @@ function MissionCommandsHelper:AddCargoCommands(groupID) ---@field unitID number ---@field crateType CrateType ---@field supplyUnitsTracker SupplyUnitsTracker + ---@field commandHelper MissionCommandsHelper ---comment ---@param params UnloadCargoCommandParams local unloadCargoCommand = function(params) local unitID = params.unitID local crateType = params.crateType - params.supplyUnitsTracker:UnloadRequested(unitID, crateType) + local supplyUnitsTracker = params.supplyUnitsTracker + params.supplyUnitsTracker:UnloadRequested(unitID, crateType, params.commandHelper) end local cargo = self._supplyUnitsTracker:GetCargoInUnit(unit:getID()) @@ -516,7 +519,7 @@ function MissionCommandsHelper:AddCargoCommands(groupID) for i = 1, amount do local path = { [1] = folderNames.cargo } ---@type UnloadCargoCommandParams - local params = { unitID = unit:getID(), crateType = cargoType, supplyUnitsTracker = self._supplyUnitsTracker } + local params = { unitID = unit:getID(), crateType = cargoType, supplyUnitsTracker = self._supplyUnitsTracker, commandHelper = self } missionCommands.addCommandForGroup(groupID, "Unload " .. cargoConfig.displayName, path, unloadCargoCommand, params) end end diff --git a/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua b/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua index cc033bb..f9bbdb4 100644 --- a/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua +++ b/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua @@ -1,23 +1,26 @@ - local Logger = require("classes.util.Logger") local Util = require("classes.util.Util") local DcsUtil = require("classes.util.DcsUtil") -local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper") local SpearheadEvents = require("classes.spearhead_events") local SupplyConfigHelper = require("classes.stageClasses.helpers.SupplyConfigHelper") local MaxLoadConfig = require("classes.stageClasses.helpers.MaxLoadConfig") env.info("Spearhead SupplyUnitsTracker loaded") +---@class SupplyUnitEventListener +---@field supplyUnitSpawned fun(self:SupplyUnitEventListener, unit:Unit) | nil +---@field enteredSupplyHub fun(self:SupplyUnitEventListener, unit:Unit, hub:SupplyHub) | nil +---@field exitedSupplyHub fun(self:SupplyUnitEventListener, unit:Unit, hub:SupplyHub) | nil + ---@class SupplyUnitsTracker ---@field private _supplyUnitsByName table ---@field private _cargoInUnits table> ---@field private _logger Logger ---@field private _unitPositions table ----@field private _commandsHelper MissionCommandsHelper +---@field private _unitInSupplyHub table ---@field private _droppedCrates table ---@field private _registeredHubs table ----@field private _supplyUnitSpawnedListener Array +---@field private _supplyUnitEventsListeners Array local SupplyUnitsTracker = {} SupplyUnitsTracker.__index = SupplyUnitsTracker @@ -37,10 +40,9 @@ function SupplyUnitsTracker.getOrCreate(logLevel) singleton._supplyUnitsByName = {} singleton._droppedCrates = {} singleton._registeredHubs = {} - singleton._supplyUnitSpawnedListener = {} + singleton._supplyUnitEventsListeners = {} + singleton._unitInSupplyHub = {} - singleton._commandsHelper = MissionCommandsHelper.getOrCreate(singleton._logger.LogLevel) - SpearheadEvents.AddOnPlayerEnterUnitListener(singleton) ---@param selfA SupplyUnitsTracker @@ -78,29 +80,30 @@ function SupplyUnitsTracker:OnPlayerEntersUnit(unit) if self:IsSupplyUnit(unit) == true then self._supplyUnitsByName[unit:getName()] = unit self._cargoInUnits[tostring(unit:getID())] = nil + self._unitInSupplyHub[tostring(unit:getID())] = false self._unitPositions[tostring(unit:getID())] = unit:getPoint() end - for _, listener in pairs(self._supplyUnitSpawnedListener) do + for _, listener in pairs(self._supplyUnitEventsListeners) do pcall(function() - listener:SupplyUnitSpawned(unit) + if listener.supplyUnitSpawned then + listener:supplyUnitSpawned(unit) + end end) end end ----@class SupplyUnitSpawnedListener ----@field SupplyUnitSpawned fun(self:SupplyUnitSpawnedListener, unit:Unit) ----@param listener SupplyUnitSpawnedListener +---@param listener SupplyUnitEventListener function SupplyUnitsTracker:AddOnSupplyUnitSpawnedListener(listener) if listener == nil then return end - if self._supplyUnitSpawnedListener == nil then - self._supplyUnitSpawnedListener = {} + if self._supplyUnitEventsListeners == nil then + self._supplyUnitEventsListeners = {} end - table.insert(self._supplyUnitSpawnedListener, listener) + table.insert(self._supplyUnitEventsListeners, listener) end function SupplyUnitsTracker:Update() @@ -150,6 +153,7 @@ function SupplyUnitsTracker:AddCargoToUnit(unitID, crateType) end +---@private ---@param unitID number ---@param crateType CrateType function SupplyUnitsTracker:RemoveCargoFromUnit(unitID, crateType) @@ -176,6 +180,7 @@ function SupplyUnitsTracker:RemoveCargoFromUnit(unitID, crateType) end +---@private ---@param unit Unit function SupplyUnitsTracker:UpdateWeightForUnit(unit) @@ -206,9 +211,29 @@ function SupplyUnitsTracker:CheckUnitsInZones() local zone = hub:GetZone() if zone ~= nil then if Util.is3dPointInZone(pos, zone) then - self._commandsHelper:MarkUnitInSupplyHub(group:getID()) + if self._unitInSupplyHub[tostring(unit:getID())] ~= true then + self._unitInSupplyHub[tostring(unit:getID())] = true + + for _, listener in pairs(self._supplyUnitEventsListeners) do + pcall(function() + if listener.enteredSupplyHub then + listener:enteredSupplyHub(unit, hub) + end + end) + end + end + else - self._commandsHelper:MarkUnitOutsideSupplyHub(group:getID()) + if self._unitInSupplyHub[tostring(unit:getID())] == true then + self._unitInSupplyHub[tostring(unit:getID())] = false + for _, listener in pairs(self._supplyUnitEventsListeners) do + pcall(function() + if listener.exitedSupplyHub then + listener:exitedSupplyHub(unit, hub) + end + end) + end + end end end end @@ -245,7 +270,12 @@ function SupplyUnitsTracker:GetUnits() end local cargoCount = 0 -function SupplyUnitsTracker:UnloadRequested(unitID, crateType) + +---comment +---@param unitID number +---@param crateType CrateType +---@param missionCommandsHelper MissionCommandsHelper +function SupplyUnitsTracker:UnloadRequested(unitID, crateType, missionCommandsHelper) self._logger:debug("Unload requested for unit: " .. unitID .. " crateType: " .. crateType) @@ -278,7 +308,7 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType) local spawned = coalition.addStaticObject(unit:getCoalition(), cargoSpawnObject) self._droppedCrates[cargoSpawnObject.name] = spawned - self._commandsHelper:updateCommandsForGroup(group:getID()) + missionCommandsHelper:updateCommandsForGroup(group:getID()) end ---@return table @@ -289,7 +319,8 @@ end ---Loads a crate directly into the unit ---@param groupID number ---@param crateType CrateType -function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType) +---@param missionCommandsHelper MissionCommandsHelper +function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType, missionCommandsHelper) self._logger:debug("UnitRequestCrateLoading called with groupID: " .. groupID .. " and crateType: " .. crateType) @@ -320,11 +351,12 @@ function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType) ---@field unit Unit ---@field groupID number ---@field crateType CrateType + ---@field commandHelper MissionCommandsHelper ---@param params LoadCargoParams local LoadCrateTask = function(params) - local loaded = params.self:TryLoadCrateInUnit(params.unit, params.crateType) + local loaded = params.self:TryLoadCrateInUnit(params.unit, params.crateType, params.commandHelper) if loaded ~= false then trigger.action.outTextForUnit(unit:getID(), "Loaded crate :" .. params.crateType, 10) end @@ -335,7 +367,8 @@ function SupplyUnitsTracker:UnitRequestCrateLoading(groupID, crateType) self = self, unit = unit, crateType = crateType, - groupID = groupID + groupID = groupID, + commandHelper = missionCommandsHelper } timer.scheduleFunction(LoadCrateTask, params, timer.getTime() + 15) @@ -346,8 +379,9 @@ end ---comment ---@param unit Unit ---@param crateType CrateType +---@param commandHelper MissionCommandsHelper ---@return boolean -function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType) +function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType, commandHelper) local crateConfigA = SupplyConfigHelper.getSupplyConfig(crateType) if crateConfigA == nil then @@ -381,7 +415,7 @@ function SupplyUnitsTracker:TryLoadCrateInUnit(unit, crateType) local group = unit:getGroup() if group == nil then return false end local groupID = group:getID() - self._commandsHelper:updateCommandsForGroup(groupID) + commandHelper:updateCommandsForGroup(groupID) return true end @@ -412,19 +446,19 @@ end function SupplyUnitsTracker:GetCargoPlacePosition(unit) local pos = unit:getPosition() - local preferedPos = { + local preferredPos = { x = pos.p.x - 10 * pos.x.x, y = pos.p.y - 10 * pos.x.y, z = pos.p.z - 10 * pos.x.z } - return preferedPos + return preferredPos -- local volume = { -- id = world.VolumeType.SPHERE, -- params = { - -- point = preferedPos, + -- point = preferredPos, -- radius = 10 -- } -- } diff --git a/src/classes/stageClasses/missions/BuildableMission.lua b/src/classes/stageClasses/missions/BuildableMission.lua index cf42f58..c270945 100644 --- a/src/classes/stageClasses/missions/BuildableMission.lua +++ b/src/classes/stageClasses/missions/BuildableMission.lua @@ -6,7 +6,7 @@ local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionComma local GlobalConfig = require("classes.configuration.GlobalConfig") local SupplyConfigHelper = require("classes.stageClasses.helpers.SupplyConfigHelper") ----@class BuildableMission : Mission, SupplyUnitSpawnedListener +---@class BuildableMission : Mission, SupplyUnitEventListener ---@field private _requiredKilos number ---@field private _droppedKilos number ---@field private _crateType SupplyType From a4b87bee3143faeec636424b70e12d657940a73d Mon Sep 17 00:00:00 2001 From: dutchie031 Date: Tue, 7 Apr 2026 13:05:58 +0200 Subject: [PATCH 5/9] Refactored everything for Lua Compile Tool --- .vscode/settings.json | 2 +- src/classes/capClasses/CapAirbase.lua | 2 +- src/classes/spearhead_db.lua | 135 +++++++++--------- src/classes/spearhead_events.lua | 16 ++- src/classes/spearhead_routeutil.lua | 11 +- .../stageClasses/GlobalStageManager.lua | 53 ++++--- .../stageClasses/SpecialZones/BlueSam.lua | 34 +++-- .../stageClasses/SpecialZones/FarpZone.lua | 18 ++- .../stageClasses/SpecialZones/StageBase.lua | 26 ++-- .../stageClasses/SpecialZones/SupplyHub.lua | 17 ++- .../SpecialZones/abstract/BuildableZone.lua | 13 +- .../stageClasses/Stages/BaseStage/Stage.lua | 61 ++++---- .../stageClasses/Stages/ExtraStage.lua | 6 +- .../stageClasses/Stages/PrimaryStage.lua | 3 +- .../stageClasses/Stages/WaitingStage.lua | 3 +- .../helpers/SupplyUnitsTracker.lua | 2 - .../missions/RunwayStrikeMission.lua | 21 +-- .../stageClasses/missions/ZoneMission.lua | 41 +++--- src/classes/util/DcsUtil.lua | 3 +- 19 files changed, 264 insertions(+), 203 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5506a02..3f5d3b0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,5 +19,5 @@ "livePreview.serverRoot": "/_docs", "Lua.workspace.library": [ "/Users/ex61wi/Developer/personal/DcsMissionScriptingTools/dutchies-dcs-scripting-tools/lua-addons" - ] + ], } \ No newline at end of file diff --git a/src/classes/capClasses/CapAirbase.lua b/src/classes/capClasses/CapAirbase.lua index db14a11..a906f28 100644 --- a/src/classes/capClasses/CapAirbase.lua +++ b/src/classes/capClasses/CapAirbase.lua @@ -3,7 +3,7 @@ local CapGroup = require("classes.capClasses.airGroups.CapGroup") local SweepGroup = require("classes.capClasses.airGroups.SweepGroup") local InterceptGroup = require("classes.capClasses.airGroups.InterceptGroup") local RunwayBombingTracker = require("classes.capClasses.runwayBombing.RunwayBombingTracker") -local SpearheadEvents = require("classes.util.SpearheadEvents") +local SpearheadEvents = require("classes.spearhead_events") local RunwayStrikeMission = require("classes.stageClasses.missions.RunwayStrikeMission") ---@class CapBase : OnStageChangedListener diff --git a/src/classes/spearhead_db.lua b/src/classes/spearhead_db.lua index 8e3e8df..85ed3d9 100644 --- a/src/classes/spearhead_db.lua +++ b/src/classes/spearhead_db.lua @@ -1,3 +1,10 @@ +local Util = require("classes.util.Util") +local DcsUtil = require("classes.util.DcsUtil") +local MissionEditorWarnings = require("classes.util.MissionEditorWarnings") +local MizGroupsManager = require("classes.helpers.MizGroupsManager") +local CustomDrawing = require("classes.stageClasses.drawings.CustomDrawing") + + ---@class DatabaseTables ---@field AllZoneNames Array All Zone Names ---@field StageZoneNames Array All Stage Zone Names @@ -112,13 +119,13 @@ function Database.New(Logger) self._logger:debug("Initiating tables") do -- INIT ZONE TABLES - for zone_ind, zone_data in pairs(Spearhead.DcsUtil.__trigger_zones) do + for zone_ind, zone_data in pairs(DcsUtil.__trigger_zones) do local zone_name = zone_data.name ---@type Vec2 local zoneLocation = { x = zone_data.location.x, y = zone_data.location.y } - local split_string = Spearhead.Util.split_string(zone_name, "_") + local split_string = Util.split_string(zone_name, "_") table.insert(self._tables.AllZoneNames, zone_name) if string.lower(split_string[1]) == "missionstage" then @@ -188,7 +195,7 @@ function Database.New(Logger) end if lowered == "scenerytarget" or lowered == "scenerytargets" then - local sceneryObjects = Spearhead.DcsUtil.getSceneryObjectsInZone(zone_data) + local sceneryObjects = DcsUtil.getSceneryObjectsInZone(zone_data) for _, sceneryObject in pairs(sceneryObjects) do table.insert(self._tables.AllSceneryObjects, sceneryObject) end @@ -202,7 +209,7 @@ function Database.New(Logger) for i, layer in pairs(env.mission.drawings.layers) do if string.lower(layer.name) == "author" then for key, layer_object in pairs(layer.objects) do - if Spearhead.Util.startswith(string.lower(layer_object.name), "buildable", true) == true then + if Util.startswith(string.lower(layer_object.name), "buildable", true) == true then local airbaseData = self:getAirbaseDataForDrawLayer(layer_object) if airbaseData then self._logger:debug("found airbase data for " .. layer_object.name) @@ -222,9 +229,9 @@ function Database.New(Logger) for i, layer in pairs(env.mission.drawings.layers) do if string.lower(layer.name) == "author" then for key, layer_object in pairs(layer.objects) do - if Spearhead.Util.startswith(layer_object.name, "drawing_", true) then + if Util.startswith(layer_object.name, "drawing_", true) then local object = layer_object --[[@as DrawingObject]] - local customDrawing = Spearhead.classes.stageClasses.drawings.CustomDrawing.New(object) + local customDrawing = CustomDrawing.New(object) table.insert(self._tables.CustomDrawings, customDrawing) end end @@ -248,10 +255,10 @@ function Database.New(Logger) for i, layer in pairs(env.mission.drawings.layers) do if string.lower(layer.name) == "author" then for key, layer_object in pairs(layer.objects) do - if Spearhead.Util.startswith(string.lower(layer_object.name), "stagebriefing_", true) == true then - local zone = Spearhead.DcsUtil.getZoneByName(stageZoneName) + if Util.startswith(string.lower(layer_object.name), "stagebriefing_", true) == true then + local zone = DcsUtil.getZoneByName(stageZoneName) local vec2 = { x = layer_object.mapX, y = layer_object.mapY } - if zone and Spearhead.Util.is2dPointInZone(vec2, zone) == true then + if zone and Util.is2dPointInZone(vec2, zone) == true then local description = layer_object.text if description and description ~= "" then stageData.StageBriefing = description @@ -265,18 +272,18 @@ function Database.New(Logger) -- fill blue sams for _, blueSamStageName in pairs(self._tables.BlueSams) do - if Spearhead.DcsUtil.isZoneInZone(blueSamStageName, stageZoneName) == true then + if DcsUtil.isZoneInZone(blueSamStageName, stageZoneName) == true then table.insert(stageData.BlueSamZones, blueSamStageName) end end --- fill farp zones for _, farpZoneName in pairs(self._tables.AllFarpZones) do - if Spearhead.DcsUtil.isZoneInZone(farpZoneName, stageZoneName) then + if DcsUtil.isZoneInZone(farpZoneName, stageZoneName) then table.insert(stageData.FarpZones, farpZoneName) for hubZoneName, available in pairs(availableSupplyHubs) do - if available == true and Spearhead.DcsUtil.isZoneInZone(hubZoneName, farpZoneName) == true then + if available == true and DcsUtil.isZoneInZone(hubZoneName, farpZoneName) == true then local farpZoneData = self:getOrCreateFarpDataForZone(farpZoneName) if farpZoneData then table.insert(farpZoneData.supplyHubNames, hubZoneName) @@ -291,15 +298,15 @@ function Database.New(Logger) for _, airbase in pairs(world.getAirbases()) do local point = airbase:getPoint() - if Spearhead.DcsUtil.isPositionInZone(point.x, point.z, stageZoneName) == true then + if DcsUtil.isPositionInZone(point.x, point.z, stageZoneName) == true then if airbase:getDesc().category == 0 then table.insert(stageData.AirbaseNames, airbase:getName()) - local airbaseZone = Spearhead.DcsUtil.getAirbaseZoneByName(airbase:getName()) + local airbaseZone = DcsUtil.getAirbaseZoneByName(airbase:getName()) for hubZoneName, available in pairs(availableSupplyHubs) do - local zone = Spearhead.DcsUtil.getZoneByName(hubZoneName) + local zone = DcsUtil.getZoneByName(hubZoneName) if zone and airbaseZone then - if available == true and Spearhead.Util.is2dPointInZone(zone.location, airbaseZone) == true then + if available == true and Util.is2dPointInZone(zone.location, airbaseZone) == true then local airbaseData = self:getOrCreateAirbaseData(airbase:getName()) if airbaseData then table.insert(airbaseData.supplyHubNames, hubZoneName) @@ -314,14 +321,14 @@ function Database.New(Logger) -- fill supply hubs for supplyHubZone, available in pairs(availableSupplyHubs) do - if available == true and Spearhead.DcsUtil.isZoneInZone(supplyHubZone, stageZoneName) == true then + if available == true and DcsUtil.isZoneInZone(supplyHubZone, stageZoneName) == true then table.insert(stageData.SupplyHubZones, supplyHubZone) end end for _, farpZoneName in pairs(stageData.FarpZones) do for _, supplyHubZone in pairs(self._tables.SupplyHubZones) do - if Spearhead.DcsUtil.isZoneInZone(supplyHubZone, farpZoneName) == true then + if DcsUtil.isZoneInZone(supplyHubZone, farpZoneName) == true then stageData.SupplyHubZonesInFarp[supplyHubZone] = farpZoneName end end @@ -329,14 +336,14 @@ function Database.New(Logger) -- fill missions for key, missionZone in pairs(self._tables.MissionZones) do - if Spearhead.DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then + if DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then table.insert(stageData.MissionZones, missionZone) end end -- fill random missions for key, missionZone in pairs(self._tables.RandomMissionZones) do - if Spearhead.DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then + if DcsUtil.isZoneInZone(missionZone, stageZoneName) == true then table.insert(stageData.RandomMissionZones, missionZone) end end @@ -345,13 +352,13 @@ function Database.New(Logger) for _, missionZone in pairs(self._tables.MissionZones) do if self._tables.MissionZoneData[missionZone] == nil or self._tables.MissionZoneData[missionZone].description == nil then - Spearhead.AddMissionEditorWarning("Mission with zonename: " .. missionZone .. " does not have a briefing") + MissionEditorWarnings.Add("Mission with zonename: " .. missionZone .. " does not have a briefing") end end for _, missionZone in pairs(self._tables.RandomMissionZones) do if self._tables.MissionZoneData[missionZone] == nil or self._tables.MissionZoneData[missionZone].description == nil then - Spearhead.AddMissionEditorWarning("Mission with zonename: " .. missionZone .. " does not have a briefing") + MissionEditorWarnings.Add("Mission with zonename: " .. missionZone .. " does not have a briefing") end end @@ -368,7 +375,7 @@ function Database.New(Logger) } end local position = airbase:getPoint() - if Spearhead.DcsUtil.isPositionInZone(position.x, position.z, farpZoneName) == true then + if DcsUtil.isPositionInZone(position.x, position.z, farpZoneName) == true then table.insert(self._tables.FarpZoneData[farpZoneName].padNames, name) end end @@ -387,7 +394,7 @@ function Database.New(Logger) for _, cap_route_zone in pairs(self._tables.AllCapRoutes) do - local split = Spearhead.Util.split_string(cap_route_zone, "_") + local split = Util.split_string(cap_route_zone, "_") local zoneID = split[2] if zoneID then @@ -398,7 +405,7 @@ function Database.New(Logger) } end - local zone = Spearhead.DcsUtil.getZoneByName(cap_route_zone) + local zone = DcsUtil.getZoneByName(cap_route_zone) if zone then table.insert(tables.capZonesByCapZoneID[zoneID].zones, zone) end @@ -406,7 +413,7 @@ function Database.New(Logger) end for _, interceptZone in pairs(self._tables.AllInterceptZones) do - local split = Spearhead.Util.split_string(interceptZone, "_") + local split = Util.split_string(interceptZone, "_") local zoneID = split[2] if zoneID then @@ -414,7 +421,7 @@ function Database.New(Logger) tables.interceptZonesByZoneID[zoneID] = {} end - local zone = Spearhead.DcsUtil.getZoneByName(interceptZone) + local zone = DcsUtil.getZoneByName(interceptZone) if zone then table.insert(tables.interceptZonesByZoneID[zoneID], zone) end @@ -443,12 +450,12 @@ function Database.New(Logger) if missions == 0 then missions = 1 end self._logger:info("initiated the database with amount of zones: ") - self._logger:info("Stages: " .. Spearhead.Util.tableLength(self._tables.StageZones)) - self._logger:info("Total Missions: " .. Spearhead.Util.tableLength(self._tables.MissionZoneData)) + self._logger:info("Stages: " .. Util.tableLength(self._tables.StageZones)) + self._logger:info("Total Missions: " .. Util.tableLength(self._tables.MissionZoneData)) self._logger:info("Average units per mission: " .. totalUnits / missions) - self._logger:info("Random Missions: " .. Spearhead.Util.tableLength(self._tables.RandomMissionZones)) - self._logger:info("Farps: " .. Spearhead.Util.tableLength(self._tables.AllFarpZones)) - self._logger:info("Airbases: " .. Spearhead.Util.tableLength(self._tables.AirbaseDataPerAirfield)) + self._logger:info("Random Missions: " .. Util.tableLength(self._tables.RandomMissionZones)) + self._logger:info("Farps: " .. Util.tableLength(self._tables.AllFarpZones)) + self._logger:info("Airbases: " .. Util.tableLength(self._tables.AirbaseDataPerAirfield)) return self @@ -470,7 +477,7 @@ end local getAvailableCAPGroups = function() local result = {} for name, value in pairs(is_group_taken) do - if value == false and Spearhead.Util.startswith(name, "CAP") then + if value == false and Util.startswith(name, "CAP") then table.insert(result, name) end end @@ -480,7 +487,7 @@ end ---@private function Database:initAvailableUnits() do - local all_groups = Spearhead.classes.helpers.MizGroupsManager.getAllGroupNames() + local all_groups = MizGroupsManager.getAllGroupNames() for _, value in pairs(all_groups) do is_group_taken[value] = false end @@ -492,9 +499,9 @@ end ---@return AirbaseData? function Database:getAirbaseDataForDrawLayer(layer_object) for _, airbase in pairs(world.getAirbases()) do - local zone = Spearhead.DcsUtil.getAirbaseZoneByName(airbase:getName()) + local zone = DcsUtil.getAirbaseZoneByName(airbase:getName()) - if zone and Spearhead.Util.is2dPointInZone({ x = layer_object.mapX, y = layer_object.mapY }, zone) == true then + if zone and Util.is2dPointInZone({ x = layer_object.mapX, y = layer_object.mapY }, zone) == true then return self:getOrCreateAirbaseData(airbase:getName()) end end @@ -555,7 +562,7 @@ function Database:loadCapUnits() local point = airbase:getPoint() ---@type SpearheadTriggerZone? - local zone = Spearhead.DcsUtil.getAirbaseZoneByName(airbase:getName()) + local zone = DcsUtil.getAirbaseZoneByName(airbase:getName()) if zone == nil then zone = { @@ -569,15 +576,15 @@ function Database:loadCapUnits() local baseData = self:getOrCreateAirbaseData(airbase:getName()) - local groups = Spearhead.DcsUtil.areGroupsInCustomZone(all_groups, zone) + local groups = DcsUtil.areGroupsInCustomZone(all_groups, zone) for _, groupName in pairs(groups) do is_group_taken[groupName] = true - if Spearhead.Util.startswith(groupName, "CAP_A", true) or Spearhead.Util.startswith(groupName, "CAP_B", true) then + if Util.startswith(groupName, "CAP_A", true) or Util.startswith(groupName, "CAP_B", true) then table.insert(baseData.CapGroups, groupName) - elseif Spearhead.Util.startswith(groupName, "CAP_I", true) then + elseif Util.startswith(groupName, "CAP_I", true) then table.insert(baseData.InterceptGroups, groupName) - elseif Spearhead.Util.startswith(groupName, "CAP_S", true) then + elseif Util.startswith(groupName, "CAP_S", true) then table.insert(baseData.SweepGroups, groupName) end end @@ -588,19 +595,19 @@ end ---@private function Database:loadBlueSamUnits() - local all_groups = Spearhead.classes.helpers.MizGroupsManager.getAllGroupNames() + local all_groups = MizGroupsManager.getAllGroupNames() for _, blueSamZone in pairs(self._tables.BlueSams) do local samData = self:getOrCreateBlueSamDataForZone(blueSamZone) - local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, blueSamZone) + local groups = DcsUtil.getGroupsInZone(all_groups, blueSamZone) for _, groupName in pairs(groups) do is_group_taken[groupName] = true table.insert(samData.groups, groupName) end - local triggerZone = Spearhead.DcsUtil.getZoneByName(blueSamZone) + local triggerZone = DcsUtil.getZoneByName(blueSamZone) if triggerZone then for _, kvPair in pairs(triggerZone.properties) do - if kvPair.key and Spearhead.Util.startswith(kvPair.key, "buildable") then + if kvPair.key and Util.startswith(kvPair.key, "buildable") then local number = tonumber(kvPair.value) if number and number > 0 then samData.buildingKilos = number @@ -631,9 +638,9 @@ function Database:LoadZoneData(missionZoneName) dependsOn = {} } - local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, missionZoneName) + local groups = DcsUtil.getGroupsInZone(all_groups, missionZoneName) for _, groupName in pairs(groups) do - if Spearhead.classes.helpers.MizGroupsManager.IsGroupStatic(groupName) == true then + if MizGroupsManager.IsGroupStatic(groupName) == true then local object = StaticObject.getByName(groupName) if object and object:getCoalition() == coalition.side.RED then @@ -656,26 +663,26 @@ function Database:LoadZoneData(missionZoneName) for _, sceneryObject in pairs(self._tables.AllSceneryObjects) do local point = sceneryObject:GetPoint() if point then - if Spearhead.DcsUtil.isPositionInZone(point.x, point.z, missionZoneName) == true then + if DcsUtil.isPositionInZone(point.x, point.z, missionZoneName) == true then table.insert(self._tables.MissionZoneData[missionZoneName].SceneryTargets, sceneryObject) end end end -- Check for properties and adds the settings to the mission data - local triggerZone = Spearhead.DcsUtil.getZoneByName(missionZoneName) + local triggerZone = DcsUtil.getZoneByName(missionZoneName) if triggerZone and triggerZone.properties then for _, kvPair in pairs(triggerZone.properties) do local key = kvPair.key - if Spearhead.Util.startswith(key, "dependson", true) == true then + if Util.startswith(key, "dependson", true) == true then table.insert(self._tables.MissionZoneData[missionZoneName].dependsOn, kvPair.value) - elseif Spearhead.Util.startswith(key, "completeat") == true then + elseif Util.startswith(key, "completeat") == true then local value = tonumber(kvPair.value) if value then if value > 1 and value <= 100 then value = value / 100 elseif value > 100 then - Spearhead.AddMissionEditorWarning("Mission with zonename: " .. missionZoneName .. " has a complete at value of " .. value .. " which is higher than 100, this will not work as intended") + MissionEditorWarnings.Add("Mission with zonename: " .. missionZoneName .. " has a complete at value of " .. value .. " which is higher than 100, this will not work as intended") end self._tables.MissionZoneData[missionZoneName].completeAt = value end @@ -690,8 +697,8 @@ function Database:LoadZoneData(missionZoneName) for key, layer_object in pairs(layer.objects) do local vec2 = { x = layer_object.mapX, y = layer_object.mapY } - if triggerZone and Spearhead.Util.is2dPointInZone(vec2, triggerZone) then - if layer_object.name and Spearhead.Util.startswith(layer_object.name, "briefing_", true) then + if triggerZone and Util.is2dPointInZone(vec2, triggerZone) then + if layer_object.name and Util.startswith(layer_object.name, "briefing_", true) then local description = layer_object.text if description and description ~= "" then self._tables.MissionZoneData[missionZoneName].description = description @@ -725,16 +732,16 @@ function Database:loadFarpData() for _, farpZone in pairs(self._tables.AllFarpZones) do local farpzoneData = self:getOrCreateFarpDataForZone(farpZone) - local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, farpZone) + local groups = DcsUtil.getGroupsInZone(all_groups, farpZone) for _, groupName in pairs(groups) do is_group_taken[groupName] = true table.insert(farpzoneData.groups, groupName) end - local triggerZone = Spearhead.DcsUtil.getZoneByName(farpZone) + local triggerZone = DcsUtil.getZoneByName(farpZone) if triggerZone then for _, kvPair in pairs(triggerZone.properties) do - if kvPair.key and Spearhead.Util.startswith(kvPair.key, "buildable", true) == true then + if kvPair.key and Util.startswith(kvPair.key, "buildable", true) == true then local number = tonumber(kvPair.value) if number and number > 0 then farpzoneData.buildingKilos = number @@ -754,7 +761,7 @@ function Database:loadAirbaseGroups() if base then local basedata = self:getOrCreateAirbaseData(baseName) local point = base:getPoint() - local airbaseZone = Spearhead.DcsUtil.getAirbaseZoneByName(baseName) + local airbaseZone = DcsUtil.getAirbaseZoneByName(baseName) if airbaseZone == nil then airbaseZone = { @@ -768,9 +775,9 @@ function Database:loadAirbaseGroups() if airbaseZone and base:getDesc().category == Airbase.Category.AIRDROME then - local groups = Spearhead.DcsUtil.areGroupsInCustomZone(all_groups, airbaseZone) + local groups = DcsUtil.areGroupsInCustomZone(all_groups, airbaseZone) for _, groupName in pairs(groups) do - if Spearhead.classes.helpers.MizGroupsManager.IsGroupStatic(groupName) == true then + if MizGroupsManager.IsGroupStatic(groupName) == true then local object = StaticObject.getByName(groupName) if object then if object:getCoalition() == coalition.side.RED then @@ -806,9 +813,9 @@ function Database:loadMiscGroupsInStages() for _, stageZone in pairs(self._tables.StageZones) do stageZone.MiscGroups = {} - local groups = Spearhead.DcsUtil.getGroupsInZone(all_groups, stageZone.StageZoneName) + local groups = DcsUtil.getGroupsInZone(all_groups, stageZone.StageZoneName) for _, groupName in pairs(groups) do - if Spearhead.classes.helpers.MizGroupsManager.IsGroupStatic(groupName) == true then + if MizGroupsManager.IsGroupStatic(groupName) == true then local object = StaticObject.getByName(groupName) if object and object:getCoalition() ~= coalition.side.NEUTRAL then is_group_taken[groupName] = true @@ -846,14 +853,14 @@ function Database:GetCapZoneForZoneID(zoneID) if capZonesForID and capZonesForID.zones then - local count = Spearhead.Util.tableLength(capZonesForID.zones) + local count = Util.tableLength(capZonesForID.zones) if count == 0 then self._logger:warn("Tried to get cap zone for zoneID: " .. zoneID .. " but cap zones were empty for this ID") return nil end capZonesForID.current = capZonesForID.current + 1 - if Spearhead.Util.tableLength(capZonesForID.zones) < capZonesForID.current then + if Util.tableLength(capZonesForID.zones) < capZonesForID.current then capZonesForID.current = 1 end return capZonesForID.zones[capZonesForID.current] diff --git a/src/classes/spearhead_events.lua b/src/classes/spearhead_events.lua index 2b2a09d..1ac612e 100644 --- a/src/classes/spearhead_events.lua +++ b/src/classes/spearhead_events.lua @@ -1,3 +1,7 @@ +local Logger = require("classes.util.Logger") +local Persistence = require("classes.persistence.Persistence") +local DcsUtil = require("classes.util.DcsUtil") + ---@class SpearheadEvents local SpearheadEvents = {} do @@ -7,10 +11,9 @@ do ---@param logLevel LogLevel SpearheadEvents.Init = function(logLevel) - logger = Spearhead.LoggerTemplate.new("Events", logLevel) + logger = Logger.new("Events", logLevel) end - local warn = function(text) if logger then logger:warn(text) @@ -50,7 +53,7 @@ do ---@param newStageNumber number SpearheadEvents.PublishStageNumberChanged = function(newStageNumber) pcall(function () - Spearhead.classes.persistence.Persistence.SetActiveStage(newStageNumber) + Persistence.SetActiveStage(newStageNumber) end) for _, callable in pairs(OnStageNumberChangedListeners) do @@ -68,8 +71,7 @@ do logError(err) end end - Spearhead.LoggerTemplate.new("Events", "INFO"):info("Published stage number changed to: " .. tostring(newStageNumber)) - Spearhead.StageNumber = newStageNumber + Logger.new("Events", "INFO"):info("Published stage number changed to: " .. tostring(newStageNumber)) end end @@ -349,7 +351,7 @@ do end if event.id == world.event.S_EVENT_MISSION_END then - Spearhead.classes.persistence.Persistence.UpdateNow() + Persistence.UpdateNow() end local AI_GROUPS = {} @@ -370,7 +372,7 @@ do return false end - local players = Spearhead.DcsUtil.getAllPlayerUnits() + local players = DcsUtil.getAllPlayerUnits() local unitName = unit:getName() for i, unit in pairs(players) do if unit:getName() == unitName then diff --git a/src/classes/spearhead_routeutil.lua b/src/classes/spearhead_routeutil.lua index 74cc5f5..6e5f143 100644 --- a/src/classes/spearhead_routeutil.lua +++ b/src/classes/spearhead_routeutil.lua @@ -1,3 +1,6 @@ +local DcsUtil = require("classes.util.DcsUtil") +local Util = require("classes.util.Util") + ---@class SpearheadRouteUtil local ROUTE_UTIL = {} do --setup route util @@ -23,7 +26,7 @@ do --setup route util ---@return table task local RtbTask = function(airdromeId, basePoint, speed) if basePoint == nil then - basePoint = Spearhead.Util.getAirbaseById(airdromeId):getPoint() + basePoint = DcsUtil.getAirbaseById(airdromeId):getPoint() end return { @@ -215,7 +218,7 @@ do --setup route util ---@param deviationDistance number ---@return table? route ROUTE_UTIL.createCapMission = function(groupName, airdromeId, capPoint, racetrackSecondPoint, altitude, speed, durationOnStation, attackHelos, deviationDistance) - local baseName = Spearhead.DcsUtil.getAirbaseName(airdromeId) + local baseName = DcsUtil.getAirbaseName(airdromeId) if baseName == nil then return nil end @@ -295,7 +298,7 @@ do --setup route util TODO: Test the creation and pubishing of event and the timing of said event ]] -- - local base = Spearhead.DcsUtil.getAirbaseById(airdromeId) + local base = DcsUtil.getAirbaseById(airdromeId) if base == nil then return nil, "No airbase found for ID " .. tostring(airdromeId) end @@ -308,7 +311,7 @@ do --setup route util end local units = group:getUnits() - while pos == nil and i <= Spearhead.Util.tableLength(units) do + while pos == nil and i <= Util.tableLength(units) do local unit = units[i] if unit and unit:isExist() == true and unit:inAir() == true then pos = unit:getPoint() diff --git a/src/classes/stageClasses/GlobalStageManager.lua b/src/classes/stageClasses/GlobalStageManager.lua index d1f43f7..140ca47 100644 --- a/src/classes/stageClasses/GlobalStageManager.lua +++ b/src/classes/stageClasses/GlobalStageManager.lua @@ -1,4 +1,11 @@ +local Events = require("classes.spearhead_events") +local Util = require("classes.util.Util") +local Logger = require("classes.util.Logger") +local MissionEditorWarnings = require("classes.util.MissionEditorWarnings") +local ExtraStage = require("classes.stageClasses.Stages.ExtraStage") +local PrimaryStage = require("classes.stageClasses.Stages.PrimaryStage") +local WaitingStage = require("classes.stageClasses.Stages.WaitingStage") local StagesByName = {} @@ -29,7 +36,7 @@ GlobalStageManager.getCurrentStage = function() return currentStage end ---@param spawnManager SpawnManager ---@return nil function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnManager) - local logger = Spearhead.LoggerTemplate.new("StageManager", logLevel) + local logger = Logger.new("StageManager", logLevel) logger:info("Using Stage Log Level: " .. logLevel) local self = setmetatable({}, GlobalStageManager) self.database = database @@ -48,28 +55,28 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa } - Spearhead.Events.AddStageNumberChangedListener(OnStageNumberChangedListener) + Events.AddStageNumberChangedListener(OnStageNumberChangedListener) for _, stageName in pairs(database:getStagezoneNames()) do logger:debug("Found stage zone with name: " .. stageName) - if Spearhead.Util.startswith(stageName, "missionstage", true) then + if Util.startswith(stageName, "missionstage", true) then local valid = true - local split = Spearhead.Util.split_string(stageName, "_") - if Spearhead.Util.tableLength(split) < 2 then - Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a order number or valid format") + local split = Util.split_string(stageName, "_") + if Util.tableLength(split) < 2 then + MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a order number or valid format") valid = false end - if Spearhead.Util.tableLength(split) < 3 then - Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a stage name") + if Util.tableLength(split) < 3 then + MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a stage name") end local orderNumber = nil local isSideStage = false if valid == true then local orderNumberString = string.lower(split[2]) - if Spearhead.Util.startswith(orderNumberString, "x") == true then + if Util.startswith(orderNumberString, "x") == true then isSideStage = true orderNumberString = string.gsub(orderNumberString, "x", "") @@ -79,13 +86,13 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa end if orderNumber == nil then - Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a valid order number : " .. split[2]) + MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a valid order number : " .. split[2]) valid = false end end local stageDisplayName = split[3] - local stagelogger = Spearhead.LoggerTemplate.new(stageName, logLevel) + local stagelogger = Logger.new(stageName, logLevel) if valid == true and orderNumber then ---@type StageInitData @@ -96,13 +103,13 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa } if isSideStage == true then - local stage = Spearhead.classes.stageClasses.Stages.ExtraStage.New(database, stageConfig, stagelogger, initData, spawnManager) + local stage = ExtraStage.New(database, stageConfig, stagelogger, initData, spawnManager) stage:AddStageCompleteListener(self) if SideStageByIndex[tostring(orderNumber)] == nil then SideStageByIndex[tostring(orderNumber)] = {} end table.insert(SideStageByIndex[tostring(orderNumber)], stage) else - local stage = Spearhead.classes.stageClasses.Stages.PrimaryStage.New(database, stageConfig, stagelogger, initData, spawnManager) + local stage = PrimaryStage.New(database, stageConfig, stagelogger, initData, spawnManager) stage:AddStageCompleteListener(self) if StagesByIndex[tostring(orderNumber)] == nil then StagesByIndex[tostring(orderNumber)] = {} end @@ -111,13 +118,13 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa end end - if Spearhead.Util.startswith(stageName, "waitingstage", true) then + if Util.startswith(stageName, "waitingstage", true) then local valid = true - local split = Spearhead.Util.split_string(stageName, "_") + local split = Util.split_string(stageName, "_") - if Spearhead.Util.tableLength(split) < 3 then - Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a order number or valid format") + if Util.tableLength(split) < 3 then + MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a order number or valid format") valid = false end @@ -126,19 +133,19 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa local stageIndex = tonumber(stageIndexString) if not stageIndex then - Spearhead.AddMissionEditorWarning("Stage zone with name " .. stageName .. " does not have a valid order number") + MissionEditorWarnings.Add("Stage zone with name " .. stageName .. " does not have a valid order number") valid = false end local waitingSecondsString = split[3] local waitingSeconds = tonumber(waitingSecondsString) if not waitingSeconds then - Spearhead.AddMissionEditorWarning("Waiting Stage zone with name " .. stageName .. " does not have a valid amount of seconds parameter") + MissionEditorWarnings.Add("Waiting Stage zone with name " .. stageName .. " does not have a valid amount of seconds parameter") valid = false end if valid == true then - local stagelogger = Spearhead.LoggerTemplate.new(stageName, logLevel) + local stagelogger = Logger.new(stageName, logLevel) ---@type WaitingStageInitData local initData = { @@ -147,7 +154,7 @@ function GlobalStageManager.NewAndStart(database, stageConfig, logLevel, spawnMa stageZoneName = stageName, waitingSeconds = waitingSeconds --[[@as integer]] } - local waitingStage = Spearhead.classes.stageClasses.Stages.WaitingStage.New(database, stageConfig, stagelogger, initData, spawnManager) + local waitingStage = WaitingStage.New(database, stageConfig, stagelogger, initData, spawnManager) if WaitingStagesByIndex[tostring(stageIndex)] == nil then WaitingStagesByIndex[tostring(stageIndex)] = {} @@ -203,7 +210,7 @@ function GlobalStageManager:OnStageComplete(stage) local newStageNumber = currentStage + 1 self:UpdateDrawings(newStageNumber) self.logger:debug("Setting next stage to: " .. tostring(newStageNumber)) - Spearhead.Events.PublishStageNumberChanged(newStageNumber) + Events.PublishStageNumberChanged(newStageNumber) end end end @@ -224,7 +231,7 @@ end GlobalStageManager.printFullOverview = function () - local logger = Spearhead.LoggerTemplate.new("StageOverview", "INFO") + local logger = Logger.new("StageOverview", "INFO") logger:info("Stage overview:") local max = 0 diff --git a/src/classes/stageClasses/SpecialZones/BlueSam.lua b/src/classes/stageClasses/SpecialZones/BlueSam.lua index 61243d4..bc8baf8 100644 --- a/src/classes/stageClasses/SpecialZones/BlueSam.lua +++ b/src/classes/stageClasses/SpecialZones/BlueSam.lua @@ -1,3 +1,7 @@ +local BuildableZone = require("classes.stageClasses.SpecialZones.abstract.BuildableZone") +local SpearheadGroup = require("classes.stageClasses.Groups.SpearheadGroup") +local Util = require("classes.util.Util") +local DcsUtil = require("classes.util.DcsUtil") ---@class BlueSam : BuildableZone ---@field Activate fun(self: BlueSam) @@ -20,7 +24,7 @@ BlueSam.__index = BlueSam ---@return BlueSam? function BlueSam.New(database, logger, zoneName, spawnManager) - setmetatable(BlueSam, Spearhead.classes.stageClasses.SpecialZones.abstract.BuildableZone) + setmetatable(BlueSam, BuildableZone) local self = setmetatable({}, BlueSam) self._database = database @@ -53,39 +57,39 @@ function BlueSam.New(database, logger, zoneName, spawnManager) end for _, groupName in pairs(blueSamData.groups) do - local SpearheadGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName, spawnManager, true) - if SpearheadGroup then + local spearheadGroup = SpearheadGroup.New(groupName, spawnManager, true) + if spearheadGroup then - if SpearheadGroup:GetCoalition() == 2 or SpearheadGroup:GetCoalition() == 0 then - table.insert(self._blueGroups, SpearheadGroup) + if spearheadGroup:GetCoalition() == 2 or spearheadGroup:GetCoalition() == 0 then + table.insert(self._blueGroups, spearheadGroup) end - for _, unit in pairs(SpearheadGroup:GetObjects()) do - if SpearheadGroup:GetCoalition() == 1 then + for _, unit in pairs(spearheadGroup:GetObjects()) do + if spearheadGroup:GetCoalition() == 1 then table.insert(blueUnitsPos, unit:getPoint()) - elseif SpearheadGroup:GetCoalition() == 2 then + elseif spearheadGroup:GetCoalition() == 2 then table.insert(redUnitsPos, unit:getPoint()) end end end - SpearheadGroup:Destroy() + spearheadGroup:Destroy() end --Cleanup units local cleanup_distance = 5 for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do for redUnitName, redUnitPos in pairs(redUnitsPos) do - local distance = Spearhead.Util.VectorDistance3d(blueUnitPos, redUnitPos) + local distance = Util.VectorDistance3d(blueUnitPos, redUnitPos) if distance <= cleanup_distance then self._cleanupUnits[redUnitName] = true end end end - local zone = Spearhead.DcsUtil.getZoneByName(zoneName) + local zone = DcsUtil.getZoneByName(zoneName) if zone then - Spearhead.classes.stageClasses.SpecialZones.abstract.BuildableZone.New(self, zone, self._buildableCrateKilos or 0, "SAM_CRATE", self._blueGroups, logger, database) + BuildableZone.New(self, zone, self._buildableCrateKilos or 0, "SAM_CRATE", self._blueGroups, logger, database) end return self @@ -104,9 +108,9 @@ function BlueSam:GetNoLandingZone() end end - local vecs = Spearhead.Util.getConvexHull(points) + local vecs = Util.getConvexHull(points) - local zone = Spearhead.DcsUtil.getZoneByName(self._zoneName) + local zone = DcsUtil.getZoneByName(self._zoneName) if zone == nil then self._logger:error("Zone not found: " .. self._zoneName) return nil @@ -136,7 +140,7 @@ end function BlueSam:SpawnGroups() for unitName, needsCleanup in pairs(self._cleanupUnits) do - Spearhead.DcsUtil.DestroyUnit(unitName) + DcsUtil.DestroyUnit(unitName) end for _, group in pairs(self._blueGroups) do diff --git a/src/classes/stageClasses/SpecialZones/FarpZone.lua b/src/classes/stageClasses/SpecialZones/FarpZone.lua index a871cbd..ec82cdb 100644 --- a/src/classes/stageClasses/SpecialZones/FarpZone.lua +++ b/src/classes/stageClasses/SpecialZones/FarpZone.lua @@ -1,4 +1,8 @@ - +local BuildableZone = require("classes.stageClasses.SpecialZones.abstract.BuildableZone") +local Util = require("classes.util.Util") +local DcsUtil = require("classes.util.DcsUtil") +local SupplyHub = require("classes.stageClasses.SpecialZones.SupplyHub") +local SpearheadGroup = require("classes.stageClasses.Groups.SpearheadGroup") ---@class FarpZone: BuildableZone ---@field private _startingFarp boolean @@ -18,14 +22,14 @@ FarpZone.__index = FarpZone ---@param spawnManager SpawnManager ---@return FarpZone function FarpZone.New(database, logger, zoneName, spawnManager) - setmetatable(FarpZone, Spearhead.classes.stageClasses.SpecialZones.abstract.BuildableZone) + setmetatable(FarpZone, BuildableZone) local self = setmetatable({}, FarpZone) self._database = database self._logger = logger self._zoneName = zoneName - local split = Spearhead.Util.split_string(zoneName, "_") + local split = Util.split_string(zoneName, "_") if string.lower(split[2]) == "a" then self._startingFarp = true else @@ -44,7 +48,7 @@ function FarpZone.New(database, logger, zoneName, spawnManager) self._padNames = farpData.padNames for _, supplyHubName in pairs(farpData.supplyHubNames) do - local supplyHub = Spearhead.classes.stageClasses.SpecialZones.SupplyHub.new(database, logger, supplyHubName) + local supplyHub = SupplyHub.new(database, logger, supplyHubName) if supplyHub then table.insert(self._supplyHubs, supplyHub) end @@ -52,15 +56,15 @@ function FarpZone.New(database, logger, zoneName, spawnManager) for _, groupName in pairs(farpData.groups) do - local group = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName, spawnManager, true) + local group = SpearheadGroup.New(groupName, spawnManager, true) table.insert(self._groups, group) group:Destroy() end - local zone = Spearhead.DcsUtil.getZoneByName(zoneName) + local zone = DcsUtil.getZoneByName(zoneName) if zone then self._logger:debug("Creating Buildable zone: " .. zoneName .. " with " .. (farpData.buildingKilos or "nil") .. " kilos") - Spearhead.classes.stageClasses.SpecialZones.abstract.BuildableZone.New(self, zone, farpData.buildingKilos or 0, "FARP_CRATE", self._groups, logger, database) + BuildableZone.New(self, zone, farpData.buildingKilos or 0, "FARP_CRATE", self._groups, logger, database) end end self:Deactivate() diff --git a/src/classes/stageClasses/SpecialZones/StageBase.lua b/src/classes/stageClasses/SpecialZones/StageBase.lua index c808d2d..552b98f 100644 --- a/src/classes/stageClasses/SpecialZones/StageBase.lua +++ b/src/classes/stageClasses/SpecialZones/StageBase.lua @@ -1,3 +1,9 @@ +local BuildableZone = require("classes.stageClasses.SpecialZones.abstract.BuildableZone") +local DcsUtil = require("classes.util.DcsUtil") +local SpearheadGroup = require("classes.stageClasses.Groups.SpearheadGroup") +local SupplyHub = require("classes.stageClasses.SpecialZones.SupplyHub") +local Util = require("classes.util.Util") + ---@class StageBase : BuildableZone ---@field private _database Database ---@field private _logger Logger @@ -21,7 +27,7 @@ StageBase.__index = StageBase ---@param spawnManager SpawnManager ---@return StageBase? function StageBase.New(databaseManager, logger, airbaseName, spawnManager) - setmetatable(StageBase, Spearhead.classes.stageClasses.SpecialZones.abstract.BuildableZone) + setmetatable(StageBase, BuildableZone) local self = setmetatable({}, StageBase) self._database = databaseManager @@ -33,7 +39,7 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager) self._supplyHubs = {} self._airbase = Airbase.getByName(airbaseName) - self._initialSide = Spearhead.DcsUtil.getStartingCoalition(self._airbase) + self._initialSide = DcsUtil.getStartingCoalition(self._airbase) do --init local airbaseData = databaseManager:getAirbaseDataForZone(airbaseName) @@ -49,7 +55,7 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager) local blueUnitsPos = {} for _, groupName in pairs(airbaseData.RedGroups) do - local shGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName, spawnManager, true) + local shGroup = SpearheadGroup.New(groupName, spawnManager, true) table.insert(self._red_groups, shGroup) for _, unit in pairs(shGroup:GetObjects()) do @@ -60,7 +66,7 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager) end for _, groupName in pairs(airbaseData.BlueGroups) do - local shGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup.New(groupName, spawnManager, true) + local shGroup = SpearheadGroup.New(groupName, spawnManager, true) table.insert(self._blue_groups, shGroup) for _, unit in pairs(shGroup:GetObjects()) do @@ -71,7 +77,7 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager) end for _, supplyHubName in pairs(airbaseData.supplyHubNames) do - local supplyHub = Spearhead.classes.stageClasses.SpecialZones.SupplyHub.new(databaseManager, logger, + local supplyHub = SupplyHub.new(databaseManager, logger, supplyHubName) if supplyHub then table.insert(self._supplyHubs, supplyHub) @@ -86,7 +92,7 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager) for blueUnitName, blueUnitPos in pairs(blueUnitsPos) do for redUnitName, redUnitPos in pairs(redUnitsPos) do - local distance = Spearhead.Util.VectorDistance3d(blueUnitPos, redUnitPos) + local distance = Util.VectorDistance3d(blueUnitPos, redUnitPos) if distance <= cleanup_distance then self._cleanup_units[redUnitName] = true end @@ -94,9 +100,9 @@ function StageBase.New(databaseManager, logger, airbaseName, spawnManager) end end - local zone = Spearhead.DcsUtil.getAirbaseZoneByName(airbaseName) + local zone = DcsUtil.getAirbaseZoneByName(airbaseName) if zone then - Spearhead.classes.stageClasses.SpecialZones.abstract.BuildableZone.New(self, zone, airbaseData.buildingKilos or 0, "AIRBASE_CRATE", self._blue_groups, logger, databaseManager) + BuildableZone.New(self, zone, airbaseData.buildingKilos or 0, "AIRBASE_CRATE", self._blue_groups, logger, databaseManager) end end @@ -126,8 +132,8 @@ function StageBase:CleanRedUnits() for unitName, shouldClean in pairs(self._cleanup_units) do if shouldClean == true then - Spearhead.DcsUtil.DestroyUnit(unitName) - Spearhead.DcsUtil.CleanCorpse(unitName) + DcsUtil.DestroyUnit(unitName) + DcsUtil.CleanCorpse(unitName) end end end diff --git a/src/classes/stageClasses/SpecialZones/SupplyHub.lua b/src/classes/stageClasses/SpecialZones/SupplyHub.lua index 3c5fb11..6b9b674 100644 --- a/src/classes/stageClasses/SpecialZones/SupplyHub.lua +++ b/src/classes/stageClasses/SpecialZones/SupplyHub.lua @@ -1,3 +1,8 @@ +local Util = require("classes.util.Util") +local DcsUtil = require("classes.util.DcsUtil") +local SupplyUnitsTracker = require("classes.stageClasses.helpers.SupplyUnitsTracker") +local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper") + ---@class SupplyHub ---@field private _database Database ---@field private _logger Logger @@ -26,18 +31,18 @@ function SupplyHub.new(database, logger, zoneName) self._logger = logger self._zoneName = zoneName - local split = Spearhead.Util.split_string(zoneName, "_") + local split = Util.split_string(zoneName, "_") if string.lower(split[2]) == "a" then self._activeAtStart = true else self._activeAtStart = false end - self._zone = Spearhead.DcsUtil.getZoneByName(zoneName) + self._zone = DcsUtil.getZoneByName(zoneName) - self._supplyUnitsTracker = Spearhead.classes.stageClasses.helpers.SupplyUnitsTracker.getOrCreate(logger.LogLevel) + self._supplyUnitsTracker = SupplyUnitsTracker.getOrCreate(logger.LogLevel) self._inZone = {} - self._missionCommandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(logger.LogLevel) + self._missionCommandsHelper = MissionCommandsHelper.getOrCreate(logger.LogLevel) self._logger:debug("Creating Supply Hub zone: " .. self._zoneName) @@ -66,14 +71,14 @@ function SupplyHub:Activate() self._logger:debug("Activating Supply Hub zone: " .. self._zoneName) - local zone = Spearhead.DcsUtil.getZoneByName(self._zoneName) + local zone = DcsUtil.getZoneByName(self._zoneName) if zone and self._drawID == nil then ---@type DrawColor local fillColor = { r=0, g=1, b=0, a=0.2 } ---@type DrawColor local lineColor = { r=0, g=1, b=0, a=1} local lineStyle = 1 - self._drawID = Spearhead.DcsUtil.DrawZone(zone, lineColor, fillColor, lineStyle) + self._drawID = DcsUtil.DrawZone(zone, lineColor, fillColor, lineStyle) end self._supplyUnitsTracker:RegisterHub(self) diff --git a/src/classes/stageClasses/SpecialZones/abstract/BuildableZone.lua b/src/classes/stageClasses/SpecialZones/abstract/BuildableZone.lua index 9294973..5851d10 100644 --- a/src/classes/stageClasses/SpecialZones/abstract/BuildableZone.lua +++ b/src/classes/stageClasses/SpecialZones/abstract/BuildableZone.lua @@ -1,3 +1,6 @@ +local Util = require("classes.util.Util") +local Persistence = require("classes.persistence.Persistence") +local BuildableMission = require("classes.stageClasses.missions.BuildableMission") ---@class BuildableZone : OnCrateDroppedListener ---@field protected _targetZone SpearheadTriggerZone @@ -22,11 +25,11 @@ function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroup self._requiredKilos = kilosRequired or 0 self._buildableGroups = buildableGroups or {} self._buildableLogger = logger - local totalGroups = Spearhead.Util.tableLength(self._buildableGroups) + local totalGroups = Util.tableLength(self._buildableGroups) self._groupsPerKilo = totalGroups / self._requiredKilos self._receivedBuildingKilos = 0 - local persistedKilos = Spearhead.classes.persistence.Persistence.GetZoneDeliveredKilos(targetZone.name) + local persistedKilos = Persistence.GetZoneDeliveredKilos(targetZone.name) if persistedKilos and persistedKilos > 0 then self._buildableLogger:debug("Zone " .. targetZone.name .. " already has " .. persistedKilos .. " kilos delivered") self._receivedBuildingKilos = persistedKilos @@ -66,7 +69,7 @@ function BuildableZone:New(targetZone, kilosRequired, crateType, buildableGroup local noLandingZone = self:GetNoLandingZone() if kilosRequired and kilosRequired > 0 then - self._buildableMission = Spearhead.classes.stageClasses.missions.BuildableMission.new(database, logger, targetZone, noLandingZone, kilosRequired, crateType) + self._buildableMission = BuildableMission.new(database, logger, targetZone, noLandingZone, kilosRequired, crateType) self._buildableMission:AddOnCrateDroppedOfListener(self) else self._buildableMission = nil @@ -138,7 +141,7 @@ end ---@param kilos number function BuildableZone:FinaliseCrate(kilos) self._receivedBuildingKilos = self._receivedBuildingKilos + kilos - Spearhead.classes.persistence.Persistence.SetZoneDeliveredKilos(self._targetZone.name, self._receivedBuildingKilos) + Persistence.SetZoneDeliveredKilos(self._targetZone.name, self._receivedBuildingKilos) if self._receivedBuildingKilos >= self._requiredKilos then self:OnBuildingComplete() end @@ -157,7 +160,7 @@ function BuildableZone:GetNoLandingZone() end end - local vecs = Spearhead.Util.getConvexHull(points) + local vecs = Util.getConvexHull(points) ---@type SpearheadTriggerZone local spearheadZone = { diff --git a/src/classes/stageClasses/Stages/BaseStage/Stage.lua b/src/classes/stageClasses/Stages/BaseStage/Stage.lua index 912727c..8444329 100644 --- a/src/classes/stageClasses/Stages/BaseStage/Stage.lua +++ b/src/classes/stageClasses/Stages/BaseStage/Stage.lua @@ -1,3 +1,16 @@ +local SpearheadGroup = require("classes.stageClasses.Groups.SpearheadGroup") +local MissionCommandsHelper = require("classes.stageClasses.helpers.MissionCommandsHelper") +local DcsUtil = require("classes.util.DcsUtil") +local FarpZone = require("classes.stageClasses.SpecialZones.FarpZone") +local SupplyHub = require("classes.stageClasses.SpecialZones.SupplyHub") +local Util = require("classes.util.Util") +local ZoneMission = require("classes.stageClasses.missions.ZoneMission") +local MissionEditorWarnings = require("classes.util.MissionEditorWarnings") +local Persistence = require("classes.persistence.Persistence") +local StageBase = require("classes.stageClasses.SpecialZones.StageBase") +local BlueSam = require("classes.stageClasses.SpecialZones.BlueSam") +local Events = require("classes.spearhead_events") +local GlobalCapManager = require("classes.capClasses.GlobalCapManager") ---@alias StageColor ---| "RED" @@ -71,8 +84,6 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority logger:debug("[BaseStage] Initiating stage with name: " .. initData.stageZoneName) - local SpearheadGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup - self.zoneName = initData.stageZoneName self.stageNumber = initData.stageNumber self._isActive = false @@ -101,11 +112,11 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority self._activeStage = -99 self._preActivated = false self._stageConfig = stageConfig or {} - self._missionCommandsHelper = Spearhead.classes.stageClasses.helpers.MissionCommandsHelper.getOrCreate(logger.LogLevel) + self._missionCommandsHelper = MissionCommandsHelper.getOrCreate(logger.LogLevel) - local zone = Spearhead.DcsUtil.getZoneByName(self.zoneName) + local zone = DcsUtil.getZoneByName(self.zoneName) if zone then - self._stageDrawingId = Spearhead.DcsUtil.DrawZone(zone, Stage.StageColors.INVISIBLE, Stage.StageColors.INVISIBLE, 4) + self._stageDrawingId = DcsUtil.DrawZone(zone, Stage.StageColors.INVISIBLE, Stage.StageColors.INVISIBLE, 4) end self._spawnedGroups = {} @@ -114,13 +125,13 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority local farpNames = database:getFarpNamesInStage(self.zoneName) for _, farpName in pairs(farpNames) do - local farp = Spearhead.classes.stageClasses.SpecialZones.FarpZone.New(database, logger, farpName, spawnManager) + local farp = FarpZone.New(database, logger, farpName, spawnManager) table.insert(self._db.farps, farp) end local supplyHubNames = database:getSupplyHubsInStage(self.zoneName) for _, supplyHubName in pairs(supplyHubNames) do - local supplyHub = Spearhead.classes.stageClasses.SpecialZones.SupplyHub.new(database, logger, supplyHubName) + local supplyHub = SupplyHub.new(database, logger, supplyHubName) table.insert(self._db.supplyHubs, supplyHub) end @@ -144,17 +155,17 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority do -- load tables local missionZones = database:getMissionsForStage(self.zoneName) - self._logger:debug("Found " .. Spearhead.Util.tableLength(missionZones) .. " mission zones for stage: " .. self.zoneName) + self._logger:debug("Found " .. Util.tableLength(missionZones) .. " mission zones for stage: " .. self.zoneName) for _, missionZone in pairs(missionZones) do - local mission = Spearhead.classes.stageClasses.missions.ZoneMission.new(missionZone, self._missionPriority, database, logger, self, spawnManager) + local mission = ZoneMission.new(missionZone, self._missionPriority, database, logger, self, spawnManager) if mission then self._db.missionsByCode[mission.code] = mission if mission.name and self._db.missionsByName[mission.name] == nil then self._db.missionsByName[mission.name] = mission else - Spearhead.AddMissionEditorWarning("DUPLICATE MISSION NAME ALERT: " .. mission.name .. " in zone: " .. self.zoneName) + MissionEditorWarnings.Add("DUPLICATE MISSION NAME ALERT: " .. mission.name .. " in zone: " .. self.zoneName) end if mission.missionType == "SAM" then @@ -170,7 +181,7 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority ---@type table> local randomMissionByName = {} for _, missionZoneName in pairs(randomMissionNames) do - local mission = Spearhead.classes.stageClasses.missions.ZoneMission.new(missionZoneName, self._missionPriority, database, logger, self, spawnManager) + local mission = ZoneMission.new(missionZoneName, self._missionPriority, database, logger, self, spawnManager) if mission then if randomMissionByName[mission.name] == nil then randomMissionByName[mission.name] = {} @@ -181,18 +192,18 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority for missionName, missions in pairs(randomMissionByName) do - local missionZonePicked = Spearhead.classes.persistence.Persistence.GetPickedRandomMission(missionName) + local missionZonePicked = Persistence.GetPickedRandomMission(missionName) if missionZonePicked == nil then - local mission = Spearhead.Util.randomFromList(missions) --[[@as Mission]] + local mission = Util.randomFromList(missions) --[[@as Mission]] if mission then - Spearhead.classes.persistence.Persistence.RegisterPickedRandomMission(mission.name, mission.zoneName) + Persistence.RegisterPickedRandomMission(mission.name, mission.zoneName) self._db.missionsByCode[mission.code] = mission if mission.name and self._db.missionsByName[mission.name] == nil then self._db.missionsByName[mission.name] = mission else - Spearhead.AddMissionEditorWarning("DUPLICATE MISSION NAME ALERT: " .. mission.name .. " in zone: " .. self.zoneName) + MissionEditorWarnings.Add("DUPLICATE MISSION NAME ALERT: " .. mission.name .. " in zone: " .. self.zoneName) end if mission.missionType == "SAM" then @@ -223,13 +234,13 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority local airbaseNames = database:getAirbaseNamesInStage(self.zoneName) if airbaseNames ~= nil and type(airbaseNames) == "table" then for _, airbaseName in pairs(airbaseNames) do - local airbase = Spearhead.classes.stageClasses.SpecialZones.StageBase.New(database, logger, airbaseName, spawnManager) + local airbase = StageBase.New(database, logger, airbaseName, spawnManager) table.insert(self._db.airbases, airbase) end end for _, samZoneName in pairs(database:getBlueSamsInStage(self.zoneName)) do - local blueSam = Spearhead.classes.stageClasses.SpecialZones.BlueSam.New(database, logger, samZoneName, spawnManager) + local blueSam = BlueSam.New(database, logger, samZoneName, spawnManager) table.insert(self._db.blueSams, blueSam) end @@ -242,7 +253,7 @@ function Stage:superNew(database, stageConfig, logger, initData, missionPriority end end - Spearhead.Events.AddStageNumberChangedListener(self) + Events.AddStageNumberChangedListener(self) return self end @@ -304,14 +315,14 @@ function Stage:CheckAndUpdateSelf() end local max = dbTables.maxMissions - local availableMissionsCount = Spearhead.Util.tableLength(getAvailableMissions()) + local availableMissionsCount = Util.tableLength(getAvailableMissions()) local activeCount = getActiveMissionsCount() if activeCount < max and availableMissionsCount > 0 then for i = activeCount+1, max do if availableMissionsCount == 0 then i = max+1 --exits this loop else - local mission = Spearhead.Util.randomFromList(getAvailableMissions()) --[[@as Mission]] + local mission = Util.randomFromList(getAvailableMissions()) --[[@as Mission]] if mission then mission:SpawnActive() activeCount = activeCount + 1; @@ -391,8 +402,8 @@ function Stage:MarkStage(stageColor) end if self._stageDrawingId and self._stageConfig.isDrawStagesEnabled == true then - Spearhead.DcsUtil.SetLineColor(self._stageDrawingId, lineColor) - Spearhead.DcsUtil.SetFillColor(self._stageDrawingId, fillColor) + DcsUtil.SetLineColor(self._stageDrawingId, lineColor) + DcsUtil.SetFillColor(self._stageDrawingId, fillColor) end end @@ -405,7 +416,7 @@ function Stage:ActivateStage() self:PreActivate(false) - self._logger:debug("Activating Misc groups for zone. Count: " .. Spearhead.Util.tableLength(self._db.miscGroups)) + self._logger:debug("Activating Misc groups for zone. Count: " .. Util.tableLength(self._db.miscGroups)) for _, miscGroup in pairs(self._db.miscGroups) do miscGroup:Spawn() end @@ -452,7 +463,7 @@ function Stage:OnStageNumberChanged(number) if self.stageNumber - self._activeStage == self._stageConfig.AmountPreactivateStage then self._logger:debug("Pre-activating stage: " .. self.zoneName .. " with number: " .. number) self:PreActivate(true) - elseif Spearhead.capInfo.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then + elseif GlobalCapManager.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then self:PreActivate(false) end @@ -547,7 +558,7 @@ end function Stage:ActivateBlueStage() - self._logger:debug("Setting stage '" .. Spearhead.Util.toString(self.zoneName) .. "' to blue") + self._logger:debug("Setting stage '" .. Util.toString(self.zoneName) .. "' to blue") for _, mission in pairs(self._db.missions) do mission:SpawnPersistedState() diff --git a/src/classes/stageClasses/Stages/ExtraStage.lua b/src/classes/stageClasses/Stages/ExtraStage.lua index 240be3d..2b62446 100644 --- a/src/classes/stageClasses/Stages/ExtraStage.lua +++ b/src/classes/stageClasses/Stages/ExtraStage.lua @@ -1,4 +1,7 @@ +local Stage = require("classes.stageClasses.Stages.BaseStage.Stage") +local GlobalCapManager = require("classes.capClasses.GlobalCapManager") + ---@class ExtraStage : Stage local ExtraStage = {} ExtraStage.__index = ExtraStage @@ -13,7 +16,6 @@ ExtraStage.__index = ExtraStage ---@return ExtraStage function ExtraStage.New(database, stageConfig, logger, initData, spawnManager) - local Stage = Spearhead.classes.stageClasses.Stages.BaseStage.Stage setmetatable(ExtraStage, Stage) local self = setmetatable({}, { __index = ExtraStage }) --[[@as ExtraStage]] @@ -46,7 +48,7 @@ function ExtraStage:OnStageNumberChanged(number) if self.stageNumber - self._activeStage == self._stageConfig.AmountPreactivateStage then self._logger:debug("Pre-activating stage: " .. self.zoneName .. " with number: " .. number) self:PreActivate(true) - elseif Spearhead.capInfo.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then + elseif GlobalCapManager.IsCapActiveWhenZoneIsActive(self.zoneName, number) == true then self:PreActivate(false) end diff --git a/src/classes/stageClasses/Stages/PrimaryStage.lua b/src/classes/stageClasses/Stages/PrimaryStage.lua index d305377..cde2148 100644 --- a/src/classes/stageClasses/Stages/PrimaryStage.lua +++ b/src/classes/stageClasses/Stages/PrimaryStage.lua @@ -1,4 +1,6 @@ +local Stage = require("classes.stageClasses.Stages.BaseStage.Stage") + ---@class PrimaryStage : Stage local PrimaryStage = {} @@ -13,7 +15,6 @@ PrimaryStage.__index = PrimaryStage ---@return PrimaryStage function PrimaryStage.New(database, stageConfig, logger, initData, spawnManager) - local Stage = Spearhead.classes.stageClasses.Stages.BaseStage.Stage setmetatable(PrimaryStage, Stage) local self = setmetatable({}, { __index = PrimaryStage }) --[[@as PrimaryStage]] diff --git a/src/classes/stageClasses/Stages/WaitingStage.lua b/src/classes/stageClasses/Stages/WaitingStage.lua index 8c3e073..13a79a0 100644 --- a/src/classes/stageClasses/Stages/WaitingStage.lua +++ b/src/classes/stageClasses/Stages/WaitingStage.lua @@ -1,3 +1,4 @@ +local Stage = require("classes.stageClasses.Stages.BaseStage.Stage") ---@class WaitingStage : Stage ---@field private _waitTimeSeconds integer @@ -18,8 +19,6 @@ local WaitingStageInitData = {} ---@param spawnManager SpawnManager ---@return WaitingStage function WaitingStage.New(database, stageConfig, logger, initData, spawnManager) - - local Stage = Spearhead.classes.stageClasses.Stages.BaseStage.Stage setmetatable(WaitingStage, Stage) local self = setmetatable({}, { __index = WaitingStage }) --[[@as WaitingStage]] diff --git a/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua b/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua index f9bbdb4..7362529 100644 --- a/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua +++ b/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua @@ -5,8 +5,6 @@ local SpearheadEvents = require("classes.spearhead_events") local SupplyConfigHelper = require("classes.stageClasses.helpers.SupplyConfigHelper") local MaxLoadConfig = require("classes.stageClasses.helpers.MaxLoadConfig") -env.info("Spearhead SupplyUnitsTracker loaded") - ---@class SupplyUnitEventListener ---@field supplyUnitSpawned fun(self:SupplyUnitEventListener, unit:Unit) | nil ---@field enteredSupplyHub fun(self:SupplyUnitEventListener, unit:Unit, hub:SupplyHub) | nil diff --git a/src/classes/stageClasses/missions/RunwayStrikeMission.lua b/src/classes/stageClasses/missions/RunwayStrikeMission.lua index a130e33..27826bf 100644 --- a/src/classes/stageClasses/missions/RunwayStrikeMission.lua +++ b/src/classes/stageClasses/missions/RunwayStrikeMission.lua @@ -1,3 +1,7 @@ +local Mission = require("classes.stageClasses.missions.baseMissions.Mission") +local Util = require("classes.util.Util") +local DcsUtil = require("classes.util.DcsUtil") + ---@class RunwayStrikeMission : Mission ---@field runwayBombingTracker RunwayBombingTracker ---@field private _runway Runway @@ -22,8 +26,7 @@ local RunwayStrikeMission = {} ---@param runwayBombingTracker RunwayBombingTracker ---@return RunwayStrikeMission? function RunwayStrikeMission.new(runway, airbaseName, database, logger, runwayBombingTracker) - - local Mission = Spearhead.classes.stageClasses.missions.baseMissions.Mission + RunwayStrikeMission.__index = RunwayStrikeMission setmetatable(RunwayStrikeMission, Mission) local self = setmetatable({}, RunwayStrikeMission) @@ -82,7 +85,7 @@ function RunwayStrikeMission:RunwayHit(impactPoint, explosiveMass) for _, section in pairs(self._runwaySections) do local zone = self:SectionToSpearheadZone(section) - if Spearhead.Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then + if Util.is3dPointInZone({ x = impactPoint.x, z = impactPoint.y, y = 0 }, zone) then if section.kilosHit == nil then section.kilosHit = 0 end @@ -153,10 +156,10 @@ function RunwayStrikeMission:Draw() local color = { r=0, g=1, b=0, a=0.5 } - runwaySection.drawID = Spearhead.DcsUtil.DrawZone(zone, color, color, 5) + runwaySection.drawID = DcsUtil.DrawZone(zone, color, color, 5) else - Spearhead.DcsUtil.SetFillColor(runwaySection.drawID, fillColor) - Spearhead.DcsUtil.SetLineColor(runwaySection.drawID, lineColor) + DcsUtil.SetFillColor(runwaySection.drawID, fillColor) + DcsUtil.SetLineColor(runwaySection.drawID, lineColor) end end @@ -342,7 +345,7 @@ local repairStaticConfigs = { ---@private ---@param section RunwaySection function RunwayStrikeMission:AddOrUpdateRepairStatics(section) - if Spearhead.Util.tableLength(section.repairGroups) > 0 then + if Util.tableLength(section.repairGroups) > 0 then return end @@ -351,7 +354,7 @@ function RunwayStrikeMission:AddOrUpdateRepairStatics(section) y = section.center.y, } - local repairGroup = Spearhead.Util.randomFromList(repairStaticConfigs) + local repairGroup = Util.randomFromList(repairStaticConfigs) for _, repairStatic in pairs(repairGroup) do repairStatic.x = location.x + repairStatic.x @@ -369,7 +372,7 @@ end ---@private ---@param section RunwaySection function RunwayStrikeMission:RemoveRepairStatics(section) - if Spearhead.Util.tableLength(section.repairGroups) == 0 then + if Util.tableLength(section.repairGroups) == 0 then return end diff --git a/src/classes/stageClasses/missions/ZoneMission.lua b/src/classes/stageClasses/missions/ZoneMission.lua index 49609f7..34c9c12 100644 --- a/src/classes/stageClasses/missions/ZoneMission.lua +++ b/src/classes/stageClasses/missions/ZoneMission.lua @@ -1,3 +1,11 @@ +local Util = require("classes.util.Util") +local MissionEditorWarnings = require("classes.util.MissionEditorWarnings") +local Mission = require("classes.stageClasses.missions.baseMissions.Mission") +local SpearheadGroup = require("classes.stageClasses.Groups.SpearheadGroup") +local Events = require("classes.spearhead_events") +local BattleManager = require("classes.stageClasses.helpers.BattleManager") +local DcsUtil = require("classes.util.DcsUtil") + --- ZoneMission is missions that are defined by zones in the ME ---@class ZoneMission : Mission, OnUnitLostListener ---@field private _state MissionState @@ -26,13 +34,13 @@ local ZoneMission = {} ---@param input string ---@return ParsedMissionName? local function ParseZoneName(input) - local split_name = Spearhead.Util.split_string(input, "_") - local split_length = Spearhead.Util.tableLength(split_name) - if Spearhead.Util.startswith(input, "RANDOMMISSION") == true and split_length < 4 then - Spearhead.AddMissionEditorWarning("Random Mission with zonename " .. input .. " not in right format") + local split_name = Util.split_string(input, "_") + local split_length = Util.tableLength(split_name) + if Util.startswith(input, "RANDOMMISSION") == true and split_length < 4 then + MissionEditorWarnings.Add("Random Mission with zonename " .. input .. " not in right format") return nil elseif split_length < 3 then - Spearhead.AddMissionEditorWarning("Mission with zonename" .. input .. " not in right format") + MissionEditorWarnings.Add("Mission with zonename" .. input .. " not in right format") return nil end @@ -47,7 +55,7 @@ local function ParseZoneName(input) if inputType == "sam" then parsedType = "SAM" end if parsedType == "nil" then - Spearhead.AddMissionEditorWarning("Mission with zonename '" .. + MissionEditorWarnings.Add("Mission with zonename '" .. input .. "' has an unsupported type '" .. (type or "nil")) return nil end @@ -69,7 +77,6 @@ MINIMAL_UNITS_ALIVE_RATIO = 0.21 ---@param spawnManager SpawnManager ---@return ZoneMission? function ZoneMission.new(zoneName, priority, database, logger, parentStage, spawnManager) - local Mission = Spearhead.classes.stageClasses.missions.baseMissions.Mission ZoneMission.__index = ZoneMission setmetatable(ZoneMission, Mission) @@ -112,8 +119,6 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw self._parentStage = parentStage self._dependencies = {} - local SpearheadGroup = Spearhead.classes.stageClasses.Groups.SpearheadGroup - if missionData.dependsOn then for _, dependency in pairs(missionData.dependsOn) do self._dependencies[dependency] = false @@ -129,7 +134,7 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw end self._missionGroups.sceneryTargets = missionData.SceneryTargets or {} - if Spearhead.Util.tableLength(self._missionGroups.sceneryTargets) > 0 then + if Util.tableLength(self._missionGroups.sceneryTargets) > 0 then self._missionGroups.hasTargets = true end @@ -145,10 +150,10 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw local spearheadGroup = SpearheadGroup.New(groupName, spawnManager, true) table.insert(self._missionGroups.redGroups, spearheadGroup) - local isGroupTarget = Spearhead.Util.startswith(string.lower(groupName), "tgt_") + local isGroupTarget = Util.startswith(string.lower(groupName), "tgt_") for _, unit in pairs(spearheadGroup:GetObjects()) do local unitName = unit:getName() - local isUnitTarget = Spearhead.Util.startswith(string.lower(unitName), "tgt_") + local isUnitTarget = Util.startswith(string.lower(unitName), "tgt_") if self._missionGroups.unitsAlive[groupName] == nil then self._missionGroups.unitsAlive[groupName] = {} @@ -167,18 +172,18 @@ function ZoneMission.new(zoneName, priority, database, logger, parentStage, spaw self._missionGroups.targetsAlive[groupName][unitName] = true end - Spearhead.Events.addOnUnitLostEventListener(unitName, self) + Events.addOnUnitLostEventListener(unitName, self) end spearheadGroup:Destroy() end if self.missionType == "CAS" then - self._battleManager = Spearhead.classes.stageClasses.helpers.BattleManager.New(self._missionGroups.redGroups, self._missionGroups.blueGroups, self.zoneName, self._logger.LogLevel) + self._battleManager = BattleManager.New(self._missionGroups.redGroups, self._missionGroups.blueGroups, self.zoneName, self._logger.LogLevel) end - self._logger:debug("Mission " .. self.name .. " group count: " .. Spearhead.Util.tableLength(missionData.RedGroups)) + self._logger:debug("Mission " .. self.name .. " group count: " .. Util.tableLength(missionData.RedGroups)) return self end @@ -327,7 +332,7 @@ function ZoneMission:UpdateState(checkHealth, messageIfDone) end if self._state == "COMPLETED" and self._lastContactMarkerID then - Spearhead.DcsUtil.RemoveMark(self._lastContactMarkerID) + DcsUtil.RemoveMark(self._lastContactMarkerID) end if self._state == "COMPLETED" and self._battleManager then @@ -511,10 +516,10 @@ function ZoneMission:MarkLastContact(unit) if self._lastContactMarkerID then - Spearhead.DcsUtil.RemoveMark(self._lastContactMarkerID) + DcsUtil.RemoveMark(self._lastContactMarkerID) end - self._lastContactMarkerID = Spearhead.DcsUtil.AddMarkToAll("Last Contact: " .. self.name .. " [" .. self.code .. "]", point) + self._lastContactMarkerID = DcsUtil.AddMarkToAll("Last Contact: " .. self.name .. " [" .. self.code .. "]", point) end return ZoneMission diff --git a/src/classes/util/DcsUtil.lua b/src/classes/util/DcsUtil.lua index 0366be0..ce05cdd 100644 --- a/src/classes/util/DcsUtil.lua +++ b/src/classes/util/DcsUtil.lua @@ -1,4 +1,5 @@ local Util = require("classes.util.Util") +local SpearheadSceneryObject = require("classes.stageClasses.Groups.SpearheadSceneryObject") ---DCS UTIL Takes inspiration from MIST but only takes the things it needs, changes for DCS updates and different vision for advanced mission scripting stuff. ---It also adds functions that make the other TDCS scripts easier without taking too much "control" away like MOOSE can sometimes. @@ -552,7 +553,7 @@ do -- INIT DCS_UTIL if object and object:isExist() and object:hasAttribute("Buildings") then - local obj = Spearhead.classes.stageClasses.Groups.SpearheadSceneryObject.New(object["id_"]) + local obj = SpearheadSceneryObject.New(object["id_"]) table.insert(sceneryObjects, obj) end end From 8da10e4d03cb809e039fabde20b2cf7e3e4db467 Mon Sep 17 00:00:00 2001 From: dutchie031 Date: Tue, 7 Apr 2026 22:48:15 +0200 Subject: [PATCH 6/9] Trying out the github action --- .github/workflows/compile-artifact.yml | 9 +++++---- _docs/pages/first-start.html | 4 ++-- _docs/pages/reference.html | 13 ++++++++++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/.github/workflows/compile-artifact.yml b/.github/workflows/compile-artifact.yml index ed2cade..f4285a8 100644 --- a/.github/workflows/compile-artifact.yml +++ b/.github/workflows/compile-artifact.yml @@ -12,10 +12,11 @@ jobs: with: fetch-depth: 0 - - name: Run SpearheadCompile - run: | - mkdir -p ./output - python3 SpearheadCompile.py . ./output/spearhead.lua + - name: Compile Spearhead + uses: dutchie031/DcsMissionScriptingTools/github-action@feature/github-action + with: + source-root: ./src + output-file: ./output/spearhead.lua - name: Upload spearhead.lua artifact uses: actions/upload-artifact@v4 diff --git a/_docs/pages/first-start.html b/_docs/pages/first-start.html index 4c68496..917e450 100644 --- a/_docs/pages/first-start.html +++ b/_docs/pages/first-start.html @@ -90,13 +90,13 @@ Missions are also defined by trigger zones.
They'll be picked up by Spearhead when named according to the naming convention:
MISSION_[Type]_[FreeForm]
- Where [Type] is the type of mission.
+ Where [Type] is the type of mission. See all

CAS

- Personally CAS is one of our favorite mission types.
+ Personally CAS is one of my favorite mission types.
Mostly because it's easy to set up and creates a truly immersive experience.

diff --git a/_docs/pages/reference.html b/_docs/pages/reference.html index 01e2bf8..34a1228 100644 --- a/_docs/pages/reference.html +++ b/_docs/pages/reference.html @@ -80,11 +80,22 @@ Example: MISSION_DEAD_BYRON

- Missions are completable objectives with specific types, such as DEAD, BAI, STRIKE, or SAM.
+ Missions are completable objectives with specific types.
Randomized missions can be defined using the format: RANDOMMISSION_[Type]_[Name]_[Index].

+

+ Valid Types: +

    +
  • SAM
  • +
  • DEAD
  • +
  • BAI
  • +
  • CAS
  • +
  • STRIKE
  • +
+

+

CAP Routes

Format: CAPROUTE_[routeID]_[Name]
From dc167663132cd68c31a2fbfad2c8bab572bcc82b Mon Sep 17 00:00:00 2001 From: dutchie031 Date: Sat, 25 Apr 2026 21:54:28 +0200 Subject: [PATCH 7/9] updated the github action ref --- .github/workflows/compile-artifact.yml | 2 +- .gitignore | 4 +++- .vscode/settings.json | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compile-artifact.yml b/.github/workflows/compile-artifact.yml index f4285a8..f277883 100644 --- a/.github/workflows/compile-artifact.yml +++ b/.github/workflows/compile-artifact.yml @@ -13,7 +13,7 @@ jobs: fetch-depth: 0 - name: Compile Spearhead - uses: dutchie031/DcsMissionScriptingTools/github-action@feature/github-action + uses: dutchie031/DcsMissionScriptingTools/github-action@actions/v1 with: source-root: ./src output-file: ./output/spearhead.lua diff --git a/.gitignore b/.gitignore index 5dcda86..cd31b20 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ **/.DS_Store .DS_Store -/dist \ No newline at end of file +/dist + +.vscode/settings.json \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 3f5d3b0..0123d56 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,6 +18,7 @@ }, "livePreview.serverRoot": "/_docs", "Lua.workspace.library": [ - "/Users/ex61wi/Developer/personal/DcsMissionScriptingTools/dutchies-dcs-scripting-tools/lua-addons" + "/Users/ex61wi/Developer/personal/DcsMissionScriptingTools/dutchies-dcs-scripting-tools/lua-addons", + "c:\\Users\\Tim\\.vscode\\extensions\\dutchie031.dutchies-dcs-scripting-tools-0.0.3\\lua-addons" ], } \ No newline at end of file From c2f97dbe932c22489a78851dc66ba8cf3e73189c Mon Sep 17 00:00:00 2001 From: dutchie031 Date: Sat, 6 Jun 2026 19:10:03 +0200 Subject: [PATCH 8/9] initial working version --- .github/workflows/release.yml | 259 +++++++++++++++++----------------- _docs/img/first-start/1.png | Bin 0 -> 338288 bytes _docs/index.html | 33 +++-- _docs/pages/about_us.html | 6 +- 4 files changed, 149 insertions(+), 149 deletions(-) create mode 100644 _docs/img/first-start/1.png diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ddb2e44..26df86d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,131 +1,128 @@ -name: Publish Release - -on: - workflow_dispatch: - inputs: - release_candidate: - description: 'Is this a release candidate?' - required: true - default: true - type: boolean - change_size: - type: choice - description: Version Bump - options: - - patch - - minor - - major - default: patch - branches: - - main - - automation - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Fetch all history for all branches and tags - - - name: Run SpearheadCompile - run: | - mkdir -p ./output - python3 SpearheadCompile.py . ./output/spearhead.lua - - - name: Calculate Tag - id: calculate_tag - run: | - # Get latest tag with 'v' prefix - latest_tag=$(git tag --sort=-v:refname | grep -E '^v' | head -n 1) - if [ -z "$latest_tag" ]; then - latest_tag="v0.0.0" - fi - echo "Latest tag: $latest_tag" - # Remove 'v' prefix for version parsing - version=${latest_tag#v} - IFS='.' read -r major minor patch <<< "$version" - bump="${{ inputs.change_size }}" - if [ "$bump" = "major" ]; then - major=$((major+1)) - minor=0 - patch=0 - elif [ "$bump" = "minor" ]; then - minor=$((minor+1)) - patch=0 - else - patch=$((patch+1)) - fi - new_tag="v${major}.${minor}.${patch}" - if [ "${{ inputs.release_candidate }}" = "true" ]; then - new_tag="$new_tag-rc" - fi - echo "New tag: $new_tag" - echo "new_tag=$new_tag" >> "$GITHUB_OUTPUT" - - - name: Check for existing tag - id: check_tag - run: | - if git tag | grep -q "^${{ steps.calculate_tag.outputs.new_tag }}$"; then - echo "Tag already exists. Exiting." - exit 1 - fi - - - name: Generate Release Notes - id: generate_release_notes - run: | - if [ "${{ inputs.release_candidate }}" != "true" ]; then - git log $(git describe --tags --abbrev=0 $(git tag --sort=-v:refname | grep -v '-rc' | head -n 1))..HEAD --pretty=format:'- %s%n %b' > release_notes.txt - else - git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:'- %s%n %b' > release_notes.txt - fi - awk -i inplace '!seen[$0]++' release_notes.txt - cat release_notes.txt - shell: bash - - - name: Apply Tag to Current Commit - run: | - git config user.name "github-actions" - git config user.email "github-actions@github.com" - git tag ${{ steps.calculate_tag.outputs.new_tag }} - git push origin ${{ steps.calculate_tag.outputs.new_tag }} - - - name: Copy Release to Versioned File - run: | - cp ./output/spearhead.lua ./output/spearhead.${{ steps.calculate_tag.outputs.new_tag }}.lua - echo "Versioned file created: spearhead.${{ steps.calculate_tag.outputs.new_tag }}.lua" - - - name: Create GitHub Release - uses: actions/create-release@v1 - id: create_release - with: - tag_name: ${{ steps.calculate_tag.outputs.new_tag }} - release_name: Release ${{ steps.calculate_tag.outputs.new_tag }} - draft: true - body_path: release_notes.txt - prerelease: ${{ inputs.release_candidate }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Upload Spearhead.lua Asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./output/spearhead.lua - asset_name: spearhead.lua - asset_content_type: application/octet-stream - - - name: Upload Spearhead.lua Asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./output/spearhead.${{steps.calculate_tag.outputs.new_tag}}.lua - asset_name: spearhead.${{steps.calculate_tag.outputs.new_tag}}.lua - asset_content_type: application/octet-stream - +name: Publish Release + +on: + workflow_dispatch: + inputs: + release_candidate: + description: 'Is this a release candidate?' + required: true + default: true + type: boolean + change_size: + type: choice + description: Version Bump + options: + - patch + - minor + - major + default: patch +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch all history for all branches and tags + + - name: Compile Spearhead + uses: dutchie031/DcsMissionScriptingTools/github-action@actions/v1 + with: + source-root: ./src + output-file: ./output/spearhead.lua + + - name: Calculate Tag + id: calculate_tag + run: | + # Get latest tag with 'v' prefix + latest_tag=$(git tag --sort=-v:refname | grep -E '^v' | head -n 1) + if [ -z "$latest_tag" ]; then + latest_tag="v0.0.0" + fi + echo "Latest tag: $latest_tag" + # Remove 'v' prefix for version parsing + version=${latest_tag#v} + IFS='.' read -r major minor patch <<< "$version" + bump="${{ inputs.change_size }}" + if [ "$bump" = "major" ]; then + major=$((major+1)) + minor=0 + patch=0 + elif [ "$bump" = "minor" ]; then + minor=$((minor+1)) + patch=0 + else + patch=$((patch+1)) + fi + new_tag="v${major}.${minor}.${patch}" + if [ "${{ inputs.release_candidate }}" = "true" ]; then + new_tag="$new_tag-rc" + fi + echo "New tag: $new_tag" + echo "new_tag=$new_tag" >> "$GITHUB_OUTPUT" + + - name: Check for existing tag + id: check_tag + run: | + if git tag | grep -q "^${{ steps.calculate_tag.outputs.new_tag }}$"; then + echo "Tag already exists. Exiting." + exit 1 + fi + + - name: Generate Release Notes + id: generate_release_notes + run: | + if [ "${{ inputs.release_candidate }}" != "true" ]; then + git log $(git describe --tags --abbrev=0 $(git tag --sort=-v:refname | grep -v '-rc' | head -n 1))..HEAD --pretty=format:'- %s%n %b' > release_notes.txt + else + git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:'- %s%n %b' > release_notes.txt + fi + awk -i inplace '!seen[$0]++' release_notes.txt + cat release_notes.txt + shell: bash + + - name: Apply Tag to Current Commit + run: | + git config user.name "github-actions" + git config user.email "github-actions@github.com" + git tag ${{ steps.calculate_tag.outputs.new_tag }} + git push origin ${{ steps.calculate_tag.outputs.new_tag }} + + - name: Copy Release to Versioned File + run: | + cp ./output/spearhead.lua ./output/spearhead.${{ steps.calculate_tag.outputs.new_tag }}.lua + echo "Versioned file created: spearhead.${{ steps.calculate_tag.outputs.new_tag }}.lua" + + - name: Create GitHub Release + uses: actions/create-release@v1 + id: create_release + with: + tag_name: ${{ steps.calculate_tag.outputs.new_tag }} + release_name: Release ${{ steps.calculate_tag.outputs.new_tag }} + draft: true + body_path: release_notes.txt + prerelease: ${{ inputs.release_candidate }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Spearhead.lua Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./output/spearhead.lua + asset_name: spearhead.lua + asset_content_type: application/octet-stream + + - name: Upload Spearhead.lua Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./output/spearhead.${{steps.calculate_tag.outputs.new_tag}}.lua + asset_name: spearhead.${{steps.calculate_tag.outputs.new_tag}}.lua + asset_content_type: application/octet-stream + diff --git a/_docs/img/first-start/1.png b/_docs/img/first-start/1.png new file mode 100644 index 0000000000000000000000000000000000000000..cb3ff69a1b53a474ec1dd2b7a6c31eb96be5fe93 GIT binary patch literal 338288 zcmZ6zWk6iZ(lv|)cL?t8?hxGF-QC??g1b8e53a$3!{7vm;2zxF-XZ7Q?>_hYHN))Q zwX0XJs;=(ci%3Oz2?SUiSP&2p1Sv^TWe^Yu3J?(RF=#O0FYrx-?I0k?AX1`2s-B=H zuTVL92Ywqc`Tjon>ptfp>q4kig=C{i>wm6nIQ7dDMRn`pXnWo%OClndq~gUMhJax&tDk8@-^|ix~{JSz#K>}A~U7J zpvXXmFW@>udcf7)0NdX&YQBn~w3gE9`36Pm= zHi;mbx4%!HO3bHX{Z4bA_?ah<0Js-rey!u+$tJq-oZJwz=$mLDq&D;G9H`Uh5@Ej zc?SxS)&I0(1dSfNdP;EZE|KCEHv9p>A?e&HFp)8_Kkpv3O9}xEDr`80LWEE8_ZuAQ zH!E%Auk=e!Gkf&nh9P87-yQM7NtuCeY9fF(%>KlYCPVt$Cl4aQ!jF5eQI@zy>K}*X zy+xsI^}$Tbe6F9uZfsr9dg6G_e%qh%C$0bO(}gE;t5u4q^D0CL1Wb^e2#5^@(41AL z5KluY^h=cqOoa*UZN^RNUpN4PAc=VKLCGV?oXnsz>>p2e0o(QIw24bmmHa@j2OO0s zX$KTEY5U*4#)E;fK>iEnEh!Mpr+RXBwEqEf6&Ez=Igt!_jr35Rh3cQip#S+WY(yqX zGHLYO)_)SZLj9i`k->Drfd1$VsTCIiA6NGXoj~y4aq6K&1Gg8B-?6Q8gI;Yb1HzFB7fJpB{a+sZ`w_%W47@GNkSrA$>|Qu<_y0E7 zbRq#>+u5~aQUABQ*)R!A|5)z{`{;uRQ?mH~8sv`yi42&7K_0pM?voJ{{{iv;d7$U_ z4`P0QI4^NZO8@^|TZ#f+TZ+;r^#k%FC|oMAN$x*<2D&B+B&JWF7|VZg?lC78d=dgS z(Fy%PY*3{*|G%jG4TT}B!&i{QFa44vOovNo^6Xp?vR!k|Rf&d5Fs7eZ~sHhy5QX(S- z{`CT+ZTTA~ouuWLkG6TPQfvQugexqFKA1lC1$PGB>MOEmF*M(t+^s|u8TR-Zu%(j+ zKM?-ENEyN*Gr@ybn83NFxF#Ym`9chrKJ8zRkS>u@wC2@j2d%BjQ=yY&Z*ulOE-^{{ zFSx)I!UekT@|bqpXrX+=MF?SUzc*%fX36H=2u-{dONs0u$Au@p8CQ*F2F)@69TX3p z5b)o@+4P5DVoYyaoZ`IV@dN3hv z8`A$P=wkBRK|x|OPHv+LKGDU>3PxfH{}v`vR|G5&z9Sk(*Gxy04eW13d(J^8Tz&)- z23e2q+iCf2qd@!9Gy?!LPQF%(wi_EgV4_u$WO(vUh}#Bx@mt0G3*3}^31 zJ)pq^uEa8i`Wx@xPJ&29z9Ik3Kp*5MKn(J+N_DC_INs0W^W?>d-aE)0>&A|-7k(D= z*o2Z}-RS23>HTc=yqz^%2UQM%=)L!QfX1c=B=Oyb@JS;%U^c2B*ZWrEz6O=~B^D(L z8P^gn>|Y;HVmX^gTuEudecMs4Eg{ZCMAj=~UR*gCrBMHT`G%ugV%h1#At`-B#(8Q|pJ@!DGgS57QX*>lDmBb@W*$nU%8pAnzWQI&}n9^>Gfl^P))Ra*>+zhb`= z;#@I!adcaD77*+NJHp8Y=!HFjdhAVDJD;0+jB8=lL2qua*RI=ztxbvb>4tZXb>iOz~;04&$5oTLg zjKA!8wHkg-NK6O@@x=Vm+$)t3_Xo*z*RQE#@2i=v{!n~Ngsh~*AHi=ATpi>DkA!C$ zIu474mt}-bonHKJuj5}ify11$hIbO^cZ}o zQ+A+Id-!zs@V&QVxQ>?sbh7`ql=v(s2h;gd3q7V38I-Sd43Hde7@#CL=idjAVPu`C zBS6;qT}3#zXHbHCPRG^J)117p1=dMeE;(u-H*l1`kqJz5*aDnym6_gdNaHFQ8`QZM zb$o~PLU!V{4|bxQe#W;@Da$52*8!bD)>yI=iH2SkW8fbs&Q7)N%*QQ3!?U$pVTRFC@$Guum><@u* zRV~(f4zW*8w;}KkJ?l7Xi=u$SidMhEZ)>U>J9=ndcpb06KBn0j<%6$C}@w*#8C_x?!5wDmaHY0HD zR+GNAa3*bo4iE{z2YJHBc}9D0|5(F|Co?{l25H*a1to-(Afi|&0pmzm6_ikPjvin&^N&>;bnV;p=b1VJ}kty!MfJ ztbK$PRJ~6|u3YPC--dJy<8eC!Gdd`V52kq17Iehwg4n0)IaL`dUPUu8 zG5PW{>?%!(lE5iv@ZkwHQtVr8xXA^Q0cw@iH9ZyuXiH}G=tLZSCSPshSi0U zQIO=gq!1lS_RN|H97|%|N@p`bbso}vSy>}f@Oh*PtVMW`>mK( zfHgprx73ab^3uBKi>P2rU}RisYB;3yB;f(|i-$swj+z|JUkKG4JBn!C#)Fi?-m1$q zd)c_Eb30Ci+Eaj)dBUjK2m_E4$HGkdUW*AT7`9ALi#eXU98N8c%f=V%iIwE&Hi#Y~ zvPCqmMOMHm89c}^NvmNfYr*OeN0~Wp9*e3=!P}X6BvXn&L!gkj=#APV8#Td_8w@2x zz%V8$JBS{wK;9b2VcB8$*-a;yk#r*cE1AeFiUX&Xla<&$ZkVdBc_M^JdDB;P)ri?1 znv5;iCGWV9Ieb!(VMn~0uRVww#&=uZaG3q1Ok~{E?xI7l+dn##ATKYn=53|qBpD_d zs3zz~r-tv^fBqu=MWe_lDJ>%%Nm-yBJ`p$E8n*l*0gqhNgVNmlQ!E-Jl=Or@OT1+Z ztYu9rB^RnJWgne5uqC3Cnws!Zl74txNLg2Z`;Sv=lnY#M_-N8*MjWiEW3XXM71@_M1&iV zEDruoagH-mqFLvJfSq+7q+mB%KWO5;#3&07SYY?Lue>kBT%Q$m+f zfpwmf9iY>Lr_;Y+G=NoajoK1bjW~q4C#R<@gf@50{82|l52YVgJxe9|gDB7p!A`Ml zL~E~k=@VKFM8NacYL4m{6D~O=mn7EFYB3eceJ18DOh8ICNjY+fAyr0xlG?y-EJ60+ z>qN%8bSqyuh)ZUvRmZ%C8JV7yx#K7E=cTJAk~DKg2zpc<0WhMNXZzd3&y&=E9vw7C z?0xOfA}U^PmDJCxx#cpn9&&AO7Xor`yU)qel}cMw^6r$Q4Z3Gku$HA=zGz@(eG2k9|02WS&SbDB%#>@#>BVZnrs|pJmm^`7l>9Rlvoam=2!sKr z)?#|oTj@u~_v!38r&wHH)N_(_^3=K9%+C5bagXlO@E=_5wCscoRwC4zl_B?a z8m^S@1PpY#%T8V*(#l3$B-B0DE*(kfcJ)-?Qr^)yHpcn^@Y#5{?F}f--_FlXjG!K z*H3NQ=YC#~jJ`6?6`NE1FgSH_cIO%#zo^#mDMTUvLQ78mW%{uGFvec7{%7LVT;C*G z$Z4#fPkwpLx|Z|R51YIQ%HAy@mt$sFWlG?RzpgWdu3nD+ssGvvTgq{-%c7DJV6O1` zz{8LcX`j#d=gU)(%N~1ZM#e4FXej{m1zacs7JXc95>_{!;CXNF$!`xz|GS*@tut!) z1DS`gCH3L^X3KFYVW*fkE}Mn)k)5n;Ne)WXBL);iq;ITI&8o4+5WC z=e4s2zo|t!M##y|->O@*Vcv9-Q_K@t@}2H(&p}aZ3Tk>^R``9*Ss2N2E#y0uoOO}F zdTcm#W`#%2{m+B3QW1SCFwka=nF(Ll2Zc2dr>-&@ft*=S-{gPDT-ugMY&weu&oFKs zS0_)ydtK+29qG@ny!TuK=(okJxN9=5rKC8?pq5@+i}zMh%$lS^C6`fsaVY2gHvBtz z29 zbmiCty)*#LpeDJnLa%v?=FlkTH1<^?z)8ZS_y_T?hVzo6k~YVNExnTbh51i-fNGmp zqp3qEjPq}Kv z9NyDHy22?(1PVeW4$%guoV968Qyhk$?E48;6kNqp%Xr*c$TMWOLOHYna z&gpB388*58Jfj_`Tq`XYw|vGfhGxC2j`a!;H9z7hCBs>&SQy8P#mT8u18v*DA_+aT zQaj&8VF40QQ6`s;yvieX1bAM6dD1>(Jf|2Liqt}#Sk(6p0TnN+`g*-%o|jC!2}~ZB z9BfO(l89<3P+g>-?kx&eP3-Xe@6}(DBWE4=QuoXc^jCUo=Pkp+=8_#a_?|<*;LA=8 zW)2NGX6qM(%@qELk*gz9&a2r^EPJ199FNR<7UA@498V9px(%b`|3QD>)>QdM;px)D)A^pAilUOT6PhO@ zqV*_M{_Gom74D^jv}(6vewc~Rk|~>BmJMlVg0_BW1kj`+|8uA9A)s`s(P!T1RnKeb zkp&lTs;Aw`zn5FHqBiGV!?_|svH!%=NkwXaz1T1GINZ`~bl01TP_R&SpgbDC`zYcM zWrur~>wibfTN7O^X`S>Ni_X6o^k&4u{fGWB_ti4KCQ5F0m-dTl&8!KG+|Yhix|<&F z;^R1`V#TTTH?Mn{)Swc}LV13MAUpin(nDp9-bhYH|D*dm-n*q5I`)1uIBXe-H^UCe zNQU_15Ca3=hZDqV%?_s7z`=N329wK1JQyy#xM+u_&naf&4W#J@S-ULQoQHj{&cOhS z#$|qn5vG`V_%JQGQUFbSjSa)^=~<5Uo@Nv8t6K=D6{7 zh74oi?1|7X!b653MdSy13a7uzr5$x@86`a32%}w zc}(I{PY?8sFvE%-!PAXe$|;4W?^{!nGPO-1v(Ay#g)u)ib3_mwT=vMacE@06bT^vm zO?4bz-R_&;N(&28k26ve+qccgW7g;!Yc-c=oFqxumHN}pmtv5`gO?}iPPzHkI*9H= z2Q~O93@Nm5{*y2nst{TsajM#Z{+IbPM;=ATpODv{i*R)bP+KS?wp%|$ODt?bFx5QK zUUbAeYT}!sE6{Hw>f@Qaxg376>4FgfpZA9~DH4&Dd6SU2RZsi8tMz8tAsp{d{gc=wl8fR=B*4q?SgEtm6 z^?t9gcZ9?c_yeMg2LGw+7TxmieVSG*@SH)C= zeVG>+mjeqqAU($YivpO<%b`^GE4hXl8JK@Igf-|-%}8o5RiS+oL1Tg_GLlM1=$Madp^~(KZ2uQ~Y^kN|XMz~~E$ygnuh}bO2s;#w;1O@E9`Br*nzY`+tEvbJWA(mx zOK4D`2tAX}a`bd#_MJ<5iOK{Et1w-va1OzQM#C7KYriIft*qEixAiZ9&W?E2_mZxYUXXP&IHfr*7X0DN&r2 za|K-M*7nD$dO}GFk>}`T{9azt$kWnI)G$_~H`cr}0}wDskkevh_YKF?(bzQ3Mr%^{ z-g?T|PQU~%}nSZc-48yBSoM?QuM)}qOTwR-(1EvCNl zJ~>#L58qn0ss=OI>Hw?Jce~ux2K6MH&aG>zv#p0~C|FN)=|Sz&yEduK zi!mTQzqz9eE%V6IsQ_o6R+ZW9ZL*!Y(*akLNRz&Y`EK|=v`xG}yNJlM3yk4@d78`3 zv!E&SWoeiPIJI5V=94ISZ4u^0^Y$v~5Fxn{POV;PEcb!ruZ3 zBkTP2>Lpc!cWgDZ3FWyjwSvZ<;^Ae{UEn(O3mu>-=cHVcRyY@wCPgR5`|h-q#=@$) zqwrFcQ*2FW<{ON$jU=1`hK^je?u)yZ3ZimwR0m&;3c{@wxxKt~c9fUJc;R z0U>LuYKg}bk|86DKIl*9%Fn`kkyDE;95)o7eBDOOy1dR`PEf@Lv-%#vi-v9Rp_ycD z8BwxhJ#HxZ5qXDviZZtj0s_vH8P6^l#p2!`WT)yAk8YI5yB5mxmUlxPjXcL$m9E-O zLZ;>n9>38qW)>K?|2EU#tRXzFxK>NcjA9^gDEPjV?sdizoXWpSo=dhC3qk=Mqn*h( zm#YsyK^G1#CzA`+rT6-hjzmV$A>ZZq_)~-7^JNY z;Rag?Wf^dWew#cv4tN<0b5^;WylGbq_w2|MGBUQ20mdyCdVU6%a?4MwEc@I)lUzwJ z>XO&x}Y+BCqy{6Q~e|P-bR{; zLnR~LIwn*A_H9BSqLY7G>E3w@xM-*|_n9u4Jw5lm*WGpogAgY|UV1(}noJ|UPpV{S z<8zwkvm(F74xDl$=B0?x4L!9J*@Lzn2)X2DGlZjq_J%I+ zD9XP)YHeJ1cgGtYo<)mjjbPW?P|5v>ztzpLZpE?uearjVSd{&7BACck{-$H}WbenL zM1b=puo|Uhn5tY>Dm}KAge_8vDztZq9^fMa4Md=B) zx|dUuReNyiXS{N`kt5?lf%(udnQuA1mY29ES36FfO46!TM49!4dY#M{Pv79gb8FA9 zERJIv(I#N(;@V(6+Pg4oxPfjG`Dc zeDC@$uj|eZg;1Pug;6(7HBFjywMc75v$l5OFW(m+E3!&QQ({0_U}LF6dM3i)9m!q& zpFO932*T+ooW8ru$s0X&2z2Xa0(A;TyK{Yez*JV!6@o^0Z+ zchSA`{g@WGc15{gjQ4fN)WudJ=X%b0SK)&#h55#uOk)Hol>~1Y+TGI-G!V7HUxJ!y?CYBscWFE*A(3r( z@N6+n?2)HZQ=a*+uWJ~z0k5VS^3vvUlshw*&Hec`=iA=3pLC+~M>upsM~1k=C#G4s zhfSwj*#oJ4w=p%YqZ;q=DrVU}dCYDd>u3`@EPe&)31d$z#L_Faz0>KIMl*QnDR`aj z2d-1hXU%Q8@_aTA@d-QKh)1@W*?v0JoT_xUoWES|dvy5}RN8H}F>c-Qpv{v-71U0( zY%oV|EzRHLNXB)G!SE-jE+f01Xx5R~Y?}x)gWPX6e6Oe|H9RaNj$wHy7Jb>h+4Xew zK2JRQCa}G0S5ZL%GmCmJ#4ZM*$Bd~JQXCiEcvJ2I@DdZXeKFTcExJ>@@{z}#SA9c&RM9HQqky8)I|4e z_+gcap?f6Y-pCaiXi<~s!#;I|KndYWcrtM?yWty|hRmX{^0RYKG3;SbD#T9=zHigB zkP-kgm?04+j1-4sNEU~5en|iLv&Xif;_iY9fTJ@h6i!5*gk|&VD1f#nStU>~Ru=)e zVG{uW3Py|(r$#w+P8zUXqKqtQ8TDdcL$JbPVLWa3kS1eN2*=r7CkDA)rSBI)IX||0PA4! zIODw{QR$va7YUjc`-mbzrk82uj@N`9K;G}v)3>78F-xZc%kZ1iI(ktm&PEtS^|Rcx zXRaAGjc9i0Qu5OU82tE;YzzU-Eh1-Yb89xf9|wolwhCV3^702{N(Hv!Y&e4u`f-rb z6<3)1uU6}1re_&{@EhVWe7V_=OGGJD@-s3F~I1?o{EIZ0PF50 zi>HqLA;2fp1Z{z%GiMZSPBFz$MTsPv=r#)}#i%vH2E%x0*3!3>25^AV;;ryU*ZiSq z{FDU#D8K-dx-k>Z+Z9V+cb0LnB+f#ITUaP%Rda07F**as9i^3N>s`Uv#ziX7yY=I0 zziPl0&Ow+DZxj8b^;4XWRYk!xq#dW0R#qtQr?AEu^QweSn6r#{H(0e16eoWtp>+0S zy00Oq2S&zHY_Cf*`inucv;nqTky8uzZip6{DxHX&x{xlKcA?a5pUSy2(tsQ5_8n;Y zilQR}+pNE2m73&jUZeYgn+nf~0!!E}ehT(QRBxFAOJD59%iD(<`HEDKV`boE2$FX~ zT5$4SZki3bVwPnR7lA8qTBXW;MXj{RNvaa9P{$w|TCP$b6SP^Q2CvgI;Cq>ePYmXc zho*8{hI7^}!xb^hu>AvbYWUBp-VrlwYn$-8F4$4#2-=A$p`l{3c4kWPuLYmwDQX}t z+*mLcHK~37JhsoC>oLj=H|kV5lGkJsWLGkgQ*v^nT{yXB**TUJ95RGQr&&c6s)^i6 z2)p@6^XfpYsZ%C(y2m$Exq!7)L0Dy#x~i9X`aXyS6QB6#@@}-fM#5GFS>g^E_1ZktjmeDmQINmJ`Dh?S4uSrsI+{ATVO2AKw)kwH*d z`KYU*?_@0*Kfp?aRDVl^%tFE)N)4RyrB&cIMO#r3u)PcpLg(Pedsa0(G%7nhc{EsXS0%qp#6=)`i%&DGSD zM!aK!=V0o#93Oh3f0&3gmg|BwKe5a8n_)!f2dCiC`Fp1IvoDx<0AZ12=`=4LM>oTY zLHq=Kxuc6=u+{nrP+k-puKztgqF#XT-bt#}vKErJF-taHjezQ5vb<~iXWQGLF zYebPe;|tue)gC{XH`D0J-diZ#*LcopTBFO*Ho&Jdy=pd7pW9crRj7X2EJMgK%u713 z0FPb_)2@N%=&eCyUs*LzGXV@{_LeKnX{BoGrYpA5S6cgs&tLec&;jUpUkZK0%P7w) z7mI)4LmQS)^G*M_mfQo7`{3t?q`acxpFU7TP#UCAFhy(E-t*P z_%gWWQr~^+6)vCf9r&Irdi`cbcL~kCo!%l~Rr`=Ch?Y4UwrzFYW2i1QXx{puRziB> zlUqgx_h&ENeIJ4DoSd^P=&4DDso6+xL+&gb>537CDU2EU8tAvfK`mBVE?Y7BS!^G; zqspIVv9%sPzRgbNu*Zu({@gw-5Qm5Gs>F-~Hx``GOUU`Mt9ewr@nCR|S59!|;s)hb zgs0xhY~o*E8hl1#y}sJm`YG>STuQ}nv(yVWX~}BdoJ~~bqb290&7_(9q_Hp4*$wSs z$!gK6@>>i~p2nCd?#sMV1@6^v}UCq2@LhPc< zjM_yjqGD~i*|6`IF*ySs25m7iHYeIydP?@R*3a|bqTTE07GIw%yAlxu7WUQWDAIDh zeng^;H~vmCw@fV``~j1?(n!-{p(^jsWuquQjHV6GP&65pQpihTbW{_@oru?>+y@}W zAem6Xf9}?us2c~?RgPDwmFHc~_1!jArW*PH%lPqOJV57lNHnEE+ufT-jjw0ZR{50u zeMfr#G?U!)DZ(qTZ0~!n)#Z75g{!DKea)>1E9aPtoUi7h?SB3leg4z=^3_0p`PDQv zgN^|*edN=PI#ku6PeQ(qz9etb#co+k_jRKTWtYp|F0DrV+n?>AgQFpmJVLTH`lRM+yX@bmD|bajUr z30}`akqoT3?nX-%!n!OB*4d2){LMmV4p}8?Nms2VAzFfVK=HfI0=^EXYyMK^O7MFm&y=-M8 zbmUc>Wa6s2{-~n3Ujx1wxm>Fx>~cQ+6VG6~p`(7Idlwu4^C=u`C;w^aY{#LB>~(Yd zEaWq5Gq;+WR$Qv+ox#fi91xQ$;8c;(cEKsy7cF%rX|Cqnr5f5M?|^( za)mUkgx0%U=~vLxgnQ-ujd}-ShPPvE?v^*!ytYCx#*30$hy34-)%C3tkmy5S$lV^R zHJ&&8_GUQW#QO8!m&LSH^B@#!;sXO#bTtV<089~D4n^2yzV*O%3X8u;pTi>3CEn2#tX>^+#9v-&_ zzbL3HjJP~7t-Xq}pDk`$M-EPPY;+iUWFZ97d-FLg}t!Xfb_gw|m zsv%9p5fiwl7k9Ysi=(=bih^-MZ}w3hTlafro26SlbIKt3cla;1jOvA-YLseQ=Goz# z?;b550oQ@g?(9{&(U}v7^-+eroQW|XD@td>;xW0 z;#|zXADq+MsNlMJzP;U`LUDJtOa56=Q?MC3X?UcM)WVCHnI7yv(4QSUdz#vfS&2Xy z_$;LZP|Y__nCiprS)c`xcp&YqY^0A=XxnTE*{Iogn?S9Jv%_<+@IsuOt=~|7pV$W9 zs9Afh9?eNls-c)tukrQV4PRNl}>%L zADSObD=*QKMB(!O876d8Le+}eSmonv*vG4Nee3&R_TEz2XdrD5-yFX8=g;bZ?Wpgv z2Fuom4fKbZRj_g4DPD_>>QvT{*|V`_*vSR`s-vRV=mGP7T;ywdY3+T z(5WIHW#Wh?%AHB;hH{as=2vN?r@}|skB-t|-R+2|S}7To%T&3C7560hmvvvp7TBSJ z8=bJQL#$l+Q+`Spy{LWkUYFh01(*^J{#s1aQ8Bo%e>Xo=cr#g>bVz&7bJokX4n+?F z9*TgUjGb{CN*&xU55*oBkoXGY=~qa4XoY0mADMWYSzM_?|K@mdelWzEu)~7_;`Jx| zxV6yUq$Qvc0u60Z{2b*VHPd|Xmo}wO22bgcP1^-r)ydd0a6(W(DoPQ;joraN3z@{< zpa~hH(VrY@w={LfTU<(Pi)1hX`GNo9zSdle>8|(%e75L-ri?@!+;ia z{b9Q-I&?0l@)}iw!*iw*IispYg>N&M=iX4LrS8IAyAyWhc<|l1Q>; zOOI#qOAID?9!|AAxwj%3?J6XuRl|%nC*oza$z4de&){LNK{22|L6%8q1KPbQp{|IoJizp2vG~`q!cQ#~CN@6%bou-H_ztpF%*w5%OsfNEx4$mrF210o@;v((fM3L#S9*eDFm@? z#`?)LD;!N;Gp_z@fiHtFFVwtjiqotpfFCbH;+R*tNu1@%M-n_}0caq1mV)z*(| zgJ0AfkRU;k{o;uo2I3no`2Z)e)HegRAmF$R+(C*Z4{TU6p> zNd-?K8t@?upODuc~R02ZkHyU)&%N5py3^#I+eIlf{ z3`OV-OE`Za`0Yeks4Y7_7|>2JzOxOY!+nxr;DI$!8o>INf(QwC{VgfTVviX`FT|VU zKx`N?p$GPMBB+A{8Sq%!r79Pq(ZH%PA!dL~bNCyEFv%iK=IyX$guxsgGVUeAvMz&; z@KvPH4;%RnJ*5ZTW^0vKXEHp6wHyg9lCzW*J@-Zn2gD6CBF@Dy>&rWaOsSbHw7_hp zNc(QXJ`_hNU3d`?2M2o|{UZm;!5NcFyzqyW182I-m+m}>O;S(T3nS>w)7S-L|1Gb9 zjcv?bA^CwFJi$ENN0e6J8BT%eh}{QKQL>N2^#6zzh9F$EP*V)mg4OlZXSm9Fey-yp zY$7tBQM99>@}`AG@9^9SA{N_dl}5B45}vj)L!kaW1;2>_22?nJnVM`QE&O|^oj?Fg z5xoIxEq^fO^L_wVu~JT8MmF;%{rKUvoXq|XxVM&)P&upe=YS@WMTbeLzQ1SnZDD|R zY+*PP<$=PC9v(EJc{DIwNx?{QSUi?WQE}myB=hEuHDs)org$uoMSKESL_C`)mt)m1B&P3i)w2dODu0C@1fRg9*_3w^5SZs0 zhacEy@w=7Xp1GU`!?^v{sYiT`hp8B8zF$eFPS&Ao1{1Y?5r`!I>qR5Vhpxrz>x~61 z3X$JmDT(1N1e;XTVS#ary1Mf3p94HU^`RjxBj@J=ZNu!j`0^5r{EH-}7`}TJ@OuO( z4-mz>S3wevhyc5blns=1AgzEG`~53Efug8;qzyDz92o9${n<(NeiKnM3vX;#dCtuu zQn>2bRAe!|q@(uW+ia}{?wrE+yWTs2j1tHzJ_z8D@g-T`nu=CYP)CaJ@1ass zNPvTXO9@ofJh68D_;i)`kEu;&5)iL%Qi-pJ-fC8tnMSFcnJC~s$PiSBA+9huX`J3S zIl)($EqkXjF<8A~!`V>fnJNFE`vn>(G$Bxx$_0|QjvRVbfznKCNcr4fI*BEo7L*&6 zp-GH3`YNk*j>)W{P1nX5wgA;O^&5wc@aS`h_C{fScl$*f-e4glI*@TY-VKRPXkgiC zK-{wz*o#d*Y+}Q7rr1P@7iHI56f0yWf!yKgihu8^mSA%yqB1l9;{mlclH>U}*xiPT zsskh%740x@N)1J$5H+6gQC3p}acaVcLTD4^qtm2khAey^+;ahoDy;I#E$)*@0>OA~ zN9WEEQ%l9j$5(Gtc-rcI4hd8@c3wt4R45l8|$NTaq%+or_jf%~~h4k$Qbo zsh@x26&M!Wpxk&2_roQRZ3t-AUs;w{ZLU)*R?3B)d+I__+2|bZESK8`Gt>TTxr$K& z2NW@S;6sSgPXu(R|C{{p)fouMgTJw=`3Y1l#ydl?R1Jn|)i9%_V{OSLkV#W)`gtTc zOR0NeuYs(XfUsaS^E0nZL*9oiBpGF(4hmu+uSZ-m&cGFFp=+mD{68$q>7gLthX^DN zy)VI4D;YeHKyP7W#xTJ;{%ytvIv-TDw$s65rknd~4E74q!ZK1e5J=gYU8%i~envuY z2s;C7O@$Mvk^jp2SRm>KYA>iXFM%hPM1OyE5`x)m1q}-`l&|&BWeZ>`PXD~`pfEF< zlNf$$8D3F=%Va$>AVMLSMG3(l`Qf3$5x5lPe*{?s_(1l`nf4#5n@Md+kOr@vhCwb3 zZxwK7^zTFp{7y_ZNU}q@FOx*tn?_VZH(Ga``Z| z{K4O4KH;a2h(DGIoRf#Hq6MYKtB_KZxw>YEw}I^_Feq;~)73l{aoe=KC)dp^tdX-c z`{boyY6+7+pwOeGhdD2juYsJQYhBhd{^cT8Q%ZoKxA za2d15_-OQ$;Y|&FFZk5nZLBo*!3L+TKU>H?hLS+-BUx1Bk?#OQ^&^2VDS|C2f7|tHOe5X5*_mBrRt;B;3t4=aN&LNg!6yAGsr_1Er_$i(CX*MvB)m8_L!x z!IZfU?Kc5n+SmTF{!oMuj0y!aEB>e#YrqREL zQ$P>|_5h_F-*^bVfhnhaL~Jey##s((g~CGRm~>4HwR|lmv*xc_pMIoO5T3{y?Ob^u z;h9jUA|qk?u_+%KnQNTg={Tq`8JZ8wSAdqCa8Xf(plxx$@BnsMUAC8T31?_v$ATc0 z?$qc@$@1wKySX^Z2xmrbKzC!1Uc--=T=Au?RGa);uPX67l$%I&Aa;b0{N|*h{^I5+ z@ln2@z(tb%Cp`Is0%^ous*|+hW5OQ9>}-5Xj_!zsw0rOf;y3EL)++Z7Fb`G+@z*0z zHS<5{3-u3u!9VB=DNiBtuOUqvuy(pj>F+c`l?=32IjhpZXv&2kgKFkIv2Ph#S=R%O zLu6LxLE~rnD-rdzf4N&EXlMce=EtEcit2ymr?!4znnkoptpXtowBzrNl0{r-#)di` zrhoWU0kIOiIFxfyeXFEeS%3?fEo9}EvtN=^z@%!(`XLi#^$fZ(N=7AG`x{f6aS$n% zY*P#_+NQv@kCpie5+7K!GdySoDKG{iEWfZ93o9l{@Sb^sB}f7TuvOy&8d~P0{13td zO%}n}R@|{ku~qgu$15u-JNqfS+2?jE|GwAB1QtwOpv-mI4YrR&VIil8wrdehhq?Zw+_hIg zY%Cg0fwW5VIMs#^oD%{{pn+xBSv)TFzuM}$Hs}k!Zcu#X!bFmAS*a~hfrCX^&2*EX zF3?kZM>H&S)o|d<@rXJVXE`zJt){d-yPU;)SBY;_Z0{H#`8NyBe0^MwP|vC+NTT%tmJBLUu74-=FE)DUzNc91Xa}*F6NSYw*lUIOKRgpekj?(| z_F-Aff>;Cd4Msi=5ezg`bafIfUPZeayc{7Ol7!Gf^dH(=`dzviwZCZ=m_oz_<3wR? zr;iFd7p&;N3p9Liit!&#dCVcl-Kj`%?LIslywin5cus-fWTS=Eai`W{k@ZDfG^c4a>|t3Q-#ny+GuL6$nSt?q3+? zbfY#|Gs5w+8WnFGVN1M!rxCrVFTaI8W1?g57T)I7;c3p1F{g#RD16L5J-Ht_f|72=NFw1!hKG&?N8`eA;;TjP*0lnXAgYnh77C5We3| zyfmmEQ)0k}Aivv4Nci7Ae(jlnFdnm^cBYDs@++~{+#?tyeCzP)tb_;D{dpQK!j~!w zn@_Wg!|(hO+T@f~8YFf`X*Yz@LbCdi(|f!@lmC^z_3k$%RQ0yadFQ$py$@Hc}s| z=>jQhVu*#PK&mLlbf1L^4&@KU{wn9cb(9w!wjih=~EK)p9d_5_=p!(d)AzXLJQMO$Kv#2S7Kh>J&r`AbcBH$O-zYjTK%uOQ}y z|6e5~j_x6BOgF@|P<$m(j%pZ$8bS~~v)}Su9`iC}vO0hXfn&|!qa~hK z@-J&iAL{ZXd(zy$|9t>hgK7ZhDb+2dQP?YBL80{fYja)mP~y@lgdk)^|Gu-9pA=>I zj|HnAU2N~mwi5+Udy-g>R#Ctf<`g|ntLA3}hbyFlKnNsyB2A(H9UU7f!OumJwp?o! z51;+H=(twxt11>7f+T7fE`pAFruVLIL+LC`x_>9OFzStQgktNFM(rfJaAE(#Ag#l0 z2i6Ma7hr#<{0|1169>Anl84@x0)T~!xnds<4s+eaOt;)Bl49^6kioX+NJ`|NK9w-k z_qF3`YDY=#BAM74PbJo%fE_CU9lN!V)m{RFki`63+df1Dq9dw3rX;;|j_qWPW}-x! zvBeDlj}TPVtr>ZAkcJ(e`qMOiyJGc`ZXp+?(u=h7s49g`3J21cKN zC93D}tNP!{ikTKzSz(~mxennx5#L)?vLhKQ;y0&C@XEu}q(i;1n+=9Gn-S8ECNg<} z1XMmI-=u%6JI8;kiU4WzH^Bai*6C8LxSM$C?@}vVM5ZOKXw|TaDk`E1C6ma;Sk`O8 zXSXZBB|L<;Bev~xN^{nCwbKPYcJhS=Y9)$_Cx1`~0+sszqE}71}<%4Q9<6n`|c{f%ZjbkDXKKbh7h9X0{#Wdf+O4#P`6u2 z!$|8EsWzE_t~q07HIYsFk(7JPKmNG_xMw)}zlqCJ6&m=8Aq!cGiTb+1isb)s^_Ou~ zK3}{5sz`TtD&5izf^>IxH`0yNM!G?|YtxN{v~+`XcX!u)@cjPwo_k*La(|d-X3bjP znyy8DV)|r~#LnJ7XtM;1aWXVVeC)F><#{d~txb#kHzUHVDlkgMDSsdQn7@uN7EP@U zpCYrQKw*u|?h?s_Di{I##(EjQt zGN`x9oouccs%hl~2%K}bNW@{LyF9e&eb1I$ktz`tj@BKwOF0A~ydoQ~xKKbQendb-qHq00M1fY?@Q;HBj zp;H|v2+I|+C!ci6==RyfVC6_tU4tqealO{EmLF3P5uZv`{H-j?c@@D+Ry}3UBUtwC z+TWRB2ont$ece8aZNU1AzcuL5neytqAK!99Q13a8t-i&21Ba(-0B()(C9>d{-$gZ4 zj73%vhA1QK!U?>M_W1U@sfEX*Rfnt@pK3Riq^hSSP7s0nJKAx?e1X245m@%OYm0^9b~=N7=NE&Ak>4#_oFRruJ1- zkkW_|WkWv2Zsa~yWd6SKUrA;uAmqSA4iU*g-GV{%m`=p`UZ$G-9=o3VAIMQFK1NgK z{ae7^&nO6LXQTjP9->6wnB+Yux;YX$dJ}VD{UB)*~zx=vKMMCj>@C>7YqT+ zgsldI6bpXab;@r*j`FWS=eLmv*1s>dqZ^;~q-?(a`uMYC_sE5%mNqfKC=5!MR0UE^-Xcf}9bxtn|C>>u z(Z)?f)rD2zv|KbLyYnA_mI&V3QzBT`vf)toSYyfWU>`@gGrQ0V49=&*nN~~oSjKzr zXK-3U6%%QwnLM=Zz=fR*5kq2^Z0$iazBI|r&)-+Q#PP^{ksVj>f=Ct?R8@_CLzC1f@^hZH>dZEc*CFc)JYe)J50&i=S1Dbj2J~f z6jn2g05OITkKwdCs$Z`~@720_`|SLp%Y(cIvg-i`DF8B~q4xX#LS{?B_>vzX8z|qu zg@ka$2M0qz{;kc3EPF(=L?BMl@gS>8B_q>biiRzt6dZ6`g%si?O+CUsa%Txuh= z0`(Gk-lmgZV?(S#&vpPEZrqi^N-n`S!H;JOJXId}Q2t(yvgB*>yMdl2`wMH6={+Tw z+qaJ-Cl1U(P5kbh4q;}joxLZQ0=CUuMM8=ux~$DDk7%c^3;cGsDdFw=d%yo&>X75> zBHtw^CkN)BDq(2?=cLoX;@daLI10^J&7_vUT+hX{efvBA-raF7u1 z>yTgI?LF*;+IX7M(+n1OHS56sRepF-jk$*X;dD%p93HO|-4a|kup^YGyWI!GcRljD zkQ_}Y7U!82mIO;}j}}pIB#XZ*piN$PR|m9bDJ8aV#n(7jSK+jMH1E(>bbNf{E`P&y zFsR89;+S^p(6d?8u;^h3jEIYTs-?TI9(wU{! z_^LW_=y$Va?dMp$A95X8sbU8mNH+^6sC>C^AbHvgw|~A^d_8U#xQJ`0ZJu?%RxbE(wkP<5#Mr;_b#H5;-S1*gkPDCZ#iu7r2QJHV zm*nX>%kKr|^)mc+x$*VkHA(O`Bsfds=>etjd1oy1?)3HcW%l(bbmDXe+D`@h#bue2 zZ%S>d!0#!MZO+P3;EJ#PQa+s6lZ3+whIoVhhPcA=u=S@t2F&Nm)^&Zs$7nzP9|+V@ zug694hqLf+@m5d!@|0$2c@87`FP?_%y|EY-LBJk*A|La?_|XEra{u~U(EH!sNyl}g zNcrgo?6KvVf~vUL?VIb-0l?S^oG!{ zakrJB>FL}xwx_q8N}cGT$_3F4^tAF8@D_H_IE)-n zjS|smc1twH$qaLi&9v;0JV)Q?|K>f*yX|{9)sK2EYTSL<8aem7e~$9I+{_HI^E+Sp zNJ;s&x_*!GE9&~zuiI|Em)CwT>aUx9$uCdhyRQek$*;>3?snv&pNo|I%p7iUbd4Io`WZT!r*P{qt zPiex>pZ%O=WTd4RpTe{5dOE*Je_y_n>q>n6wtd14d3SA^d! z8Kl7w@rY2GEr|=gfBZVD0kzUAHAADLD|;NYb52RYoiXZ;g!263rSDk4Gr+$az2ts) z7%_a_re1T3brLbGMsvj1Ph!g0@BQHBw?$*W_(6T01bt=Q^UjdIV^?h@;ecMF&nY9@ z+Z3G!b!BCc?~_c+)k>GmL^GHXR(>+@F$D#nSqFS5`Jv-O^_kk_4;rY03ZHL7Ql^fo zp#CemIs0xb5!To)wVfJ^(-BqDGC3aeBte5@^VPTIIoju^rk6@LzB|4$Yx-1>Va0mW zO83&whXMVv0J>0qImmtuU^rO&Mqz*p`Y3i&bZ8oUZ!YGXih=rJ@qo`Fb%)DNyXIFx zyE#Xbf|e2{B8W}ZV?TIxWCT3dR&-N4=BGA$72I23!6r2&V`L<3ZqDf(RAzMt8Ll~6P1_gFJ3J6-g5J6Eq#_@J5%Kg;{Nj_VwH_h$<<*n3 ztzz?IA_hb>!yALAJsa=;FInsi7XYY>bx{)Coh6MuKM?+{(fDPg_y|inmt?Y%VkRjm zTS)Ec>uP9Yvt+_DU&ax=n30@l{8`&&1{T!igfO%&5wz*iGDF*=*J`)t9xfYE7QvRv z((LJprFOb^fQO6QuxLBS=l^F77ofejsu#~7+a_LS4hM(+n#x z-{+x{`bNTRvY8AUy4M}Xoy(v>rYf=+rjn{EnrZ?5q|WfP z`Zq8>0=JnmR9gD>mH2>r7ieKA$gbPIEXS+0vh5gC9W_1FrL zgl>|2Yu9MSV`TodYfGdC9BI{*E$2y)9D@HhK^9}@X#$X@Ez|6JNt%20i^qVdj?fre z#xCtsZ<>mxMp8zS`>*|j6wpu@SbkLIZ;}eshxLPvu$!Av3ARB;!fs|%B2)(CqHeQB z{pLxDi8}Hy30XFy%=iILJ++04zh@CAn$+)s&e9?(yMxEX5_o9k1{#v`qhCM1F zhPcS~T{ASB2SIvjF}GhqNkaF)+=S9ktg^lhQx=3t1*shx~wV&;uM414}%m&hH5}K@v1M+8m320jQA6B;c^&KaOq-fo4z1k=P zEFBxDsRGcT^M;9wgZd=_liz#Y<_<8REOR4oQ8=9IgqeRr4&@L%W-e6Q-`+gS`h36$ zzZ}V6fM7Bp3Dlb(VNQ{mgUg|z6%HAah93%-Qk?1N_(30LS^zap5J0+SQT>y*<$+Pp z#>9e_iBnn4gm5BPk{JmlvY?1uF(Jo_hlP`)0Vcr#v9iK`_};(4Kq0~(05lvgrEBOX zo@rP->Wq!=;?-dNVcy~$KR_&X^?uugPd;VfdpGIw?|cY^ekX)W{f#c^d%!2}gE4DfTVCL9rEZ}G zEk8dLzOlPw&+cVFd@eO&pPY=`7}FgNcG9HMq(;=X`|t8{ZBE1^m*J67CPv2n=4vN% zh6^ex3Y*eODT%Y2TcqnNWOxV?ED8ynL1uUVKC#ur(vhrzA5wAo_SpwQ+UMfDQ7DR; z!xwGa^8o$`3G6lpyz0KBjwD`F!dVwg`Bs46h(Fk3qiI{nS{o^a zb!d^+<>NmASO~YWVX<6YtHtST^V6bvlm#(0Fm9bI$uK2`cqOg= z#E<#bOv8cX`xxEM*<=0abtzZX`kCC+b|#+sGXxH{Zm#ZfvuO@AZ=*w&Cp> ztp4;}wS+7f#bn}*t^FUy*gyzdR^?{SHSABO?9f!wIAvCUK9^y@9nxK4{jfJ2w0NBP zGcB8$B{lY|rt(bGXr_NoY-%d}k01+`Vc?=HqwYQlqNnSp5X&PRGq8sg&8Fy)s4Hj- z+~rCw)JQtn+e=GN|Hi93jvE~8K^&de(jrLrNj1(yG2yFZRM#C4+=f-37R5 zzD%)4_vxzqbZaYdG4ifyZN;6;tuMz=OHfr%SKPh4OL`h|y1a}bNzI*6Np2xT?hk!> zO3&%6*4QSjBmfPN%gD%`maW{+?6&jqaHuN$9I8Te4)3*LLCi3-Xsy?@jf}MARW=sf zsO~g~Oq>Pq>%Wa&kxNe-gw{tB$P7)zk{i%&AU1KJOk_!XzwaUZzl1{dz-DCgkIdJ` zYVV2`gKSxhggW?(-xayy=>9okz~(QTEa#QP5titWU2gx)>ThVUka(%10|Tk4DJl9I zgM$)9mhiFigew_o@iQ}6kMU~IrsJ3+EHa8(5947*jw{! zL?HKYhl)Sia)_+I%l_jjTese%16+Or0zyi#{zw!+83AojM*hdMm2ti=`$|L8%CuZe z%OJpt>vHe?!@u0Esc0k=?SpCQU~UC`ELN5G0GyyR*MHX|YEyU}_GQ8)a9g9cH@EJkSzsVY zZNuf@;mELD7OqT6n*?&DZUw^n^R}(Df9kzr4uWA(Cb$q5X+}me+N7Jae$ofc}svgWVAvrejo@sZ{jDchv#cNlfb!5A(OGI znVpFVrqm`?cs!#mew6>EDV zte`TP7>7&2^!5i;vdvHQh*|CBCEG&(6npMs6+aD>oT%{C7EC!`>x7Q#2R=+Oghmx|{U4?~-!3>=-a0{q^>2 zf8Ft%p0+Qn^dNXMnPoa|>d6ITynbkst%wjqb{V^?;>is_WXoR+2v zJKNtAS9c9_Q7*z9+=J&!61^s8SM`|&36<2J>0&M53K8RM=t1Uybyz)%g=`=w=FcBW zI$CK8Nk!GIg={LG;?>`4cE!l0Q_U+f|CY}8l+ON|phwl9;YZlTyP*YYxz$KQK zJGEV0LhH4$mdVt52bDEuTdOrV2+zu@9H)#?Tg%F*)5^lnD#!vPtI1G%ZER`k&eEhN zQOsvsc4y19zJrtuh0CRu3`0i@Iafo4v@5aCz3&zxgdHy82rOUpXga@Fu&HI3SZIiW1J57py@!!# z*CVNkFdH-)OrZmjJq*h?wI6ku1hOMGUkzk`$J>`b|CmizPcuE*oO;=<2*{m8chz+C zQz5#Kl(Obcl-LrDxuc99V8k9CVKhNGc(BkP6bXm+z%1;Y)457&1Ar0(bkkjL`0PHEqqCV#7KxJv<&Fb zE^pY}6>OdSdpC8f*Y3RSX{Pii#Tw^j1IO=q)$eA|&+od$9G~&;j2aR$(iCD#T^(lB zS2DhFI=1SgTPMCED|}JbU%%#a^Uu;dm{Dva-_Le$M7t6aWM5o{UmkBD+kb_N(LZ)? zt$;VFOAF}`X(6;BhRmn>?^_>A8GN8McV?KuWb9|+o50{`SW^dEq(&*%fW(hD2w>1U zdVJZs&FWJiJM9{cyCL4&P*R7br=(~qyR4P!KXixA2j7dfkC+kQ6Yp|YR581GdNSV7 zHyOePIJ`{OKP()i9@I#1eGEm%{|?EVCJR9Y?E2CXeB|J%DJ5s!K**ePBiuk19e3^w z{&T*estE~9Q^jAsCdg1CY(S6=j{wiQEB9zL zqcLiRLU8i`umEbimlFx84n1lD?`Db`IwM6dFS0 zFx)V}r+*8NZs2%NfiJn+?JAcN>Y~`-ZfB4Nx>^hGUG;l{WOjX1`89D;@rq5B|Urmnh(oWz(m3#J4^374w4vW&k)h& zn;oBTE(OaetG0}U@pL8`;vv7X)(vg<_e)62oLQwpEB1Vgk$gC5b8-Fir>wO&5r^l# zlj}g=qt+kBS#{P0IPbR&p+%TYRG19FTp)l^h1MvkYsX)Gs{>swnpJL@l& zTtgvR62VIVTe3-8gFp+w%_N2TLGpu7Co`K?H{h2g4&2f28Nv=%xf2`H2o>~$1dq*nI1m`LwV3h!s&b5Z|`RMent<~ z7^XxD=wPlo{>~5IE?CuiB+j9SN}bA)Cd-(tJj=%H4;L_C*XZVU`-OOoVW=iGCp=Cg zQ(s3}E9$2ZDd_-uU-cfS*QyW?y2jrWFa6#ue%?&P&BpcvfB*R8tfsosd&597W<=x9 zDH>6t^_KfIybbEw!(F}Aq#KH3JD(9z`RiJi6-C^ORwV{${Awsq=jl@tA-KXh;fN{2UM0N<+TMGQz-JN6$Z}W_Rrcj<83+41&2sh1A_47Y+t1kaC0v?|P&fGS#yQ zt2ht>A-L3%<9vwS$X6&XZnxH{on@`#CxtBCKaQ~gLr5Ca`9Il}I@b1^?g~(>pmBV+ zS9VDT_+a-s08cx&xQ~zcr+kilr$(+obg0H(a+e7wVdU%wt*iF)bv>qVqB@w}+j*>> zFCXG!5E}*{vCgJFWL4oW!13rt0UX>DoSf}JiToo9mXFkpiW8GzXLCB)$N!)I01f`E<*w#YL|ws#XQMB>R~oTw}r#7fc#7j8wx z!3}SsF<*?E6z5RXrqB*-Wb}dR;0*C*xqo1mv9wSWU>1Qd68&cuF@rR;&OwD-r>cD3 zkKqco_Wh4S~S~X4;PK<@i@H+K z(pA=^G7$D%-=Z{JyP`x#e3?#2#7<1aEJ>y8Bj(@51+k~e#$a0@%L@5r+hdBRLB6kn z_P_DB`%G(8`NyhStXlR{*il&3`J)I!e6=pJ6-GPrM?f~M%VN_99g!Q*1>xnKMkV80 zZ;Zhk>1;a$SX_*UT4E5_?9oW=-x{RPq}h_Wns7;831M+@hw-$3qqMEZ*<_7-vusf~ z8-Ub#^OX>LdAVCdOpKcFFno%F;7^}0Vi4x6I%+aE=bvG2+*?m(qlnTnGim7Q zEiCOu24#kZ2D+?PP0-nNFWr~!zM5-7!+#CwXQnQOI&#luP#JQl7IZPK)nwAMBuvY2 zn6u^o6zJMQ1Rt{Nv4@0ZB{Efg+Nl8QFis0l!s;D&0hz|aEQp}GtJvW-7h2LW;0Fu@ zDfWli;EDlMwT6a-oTRXv3b8%rv7MGkmWwW+V*6+-upnQstxi-j;spW}3YJ7-JPp@y zYY_F8RZz&mDS2IgC zRE@3Z9Zw)fkX;M(pWm!BY*YS%!!Lkdu^$Z>cPVd)_gDVDy?*oe5^w+WKXFDhE6^Fy zI7%d75}!E@rNVK7t6i70gsP}tr~J-FI9EK5r8ehFN>cKBg2E<^UX@Vu_D+bje(L{r zs7AvVQmwz33>&^ew9zRBrzZU5VrL&=MpO|O5mA0!`!R1+nEl?ln;f<|$N17vRJICH zBoB`8{IAjHJlK&QffEW*G#LoUP{1HfN*WEeT6bv5ZLO=T`?2d$w49lo99~sMJx;L~ zT|qET0J5VF3w!cBDj>_IhEEmamjDD7;RxZ&{LclZP^jd}V4PU$mU|@gsEWfkk?l*& z{Z$OQU)`tK3k&{*>U;h#o_DyYKXe0$0}bQI{_BVM;NM(M2RZ1`Z!Ra6Jnl9<-Dn29 z#=*HN>|L(W0-5LQF}-JA{riyi%dW=3aH|lInAt8)a`H2-sFhMzfB#qE08VahNyWtZ zUvOExdCfMwN!Q%JG^eI&6^M%Si>aBYct2R1QX_}Y7_p>%_uth3<)D2a^It}3nF-T{ z)l72H6(8xBA%HegYhYpP0TwVTOIcSn3cjV;3noMy8akx!&!0*i+XM>D5KoQIs>-qN zg`vcQ=q{v9By%j_myPlMm^FTk+x?~60_I90Le|W2P`JltiSg$)j&_|}CCLp#fDb2{ z@RZj;rnib~3WgSLmV&i019X8sts*b~SxV-fAgdTvdiqyX+RB(;hkwX9K^PgNweRj3 z+O$=G6jf9Q#U$gyH`9w!X8FqGeLEUJd@e61U)-sF8 zbw$r5XGE0$q{qg@#;zPDbNz%*iH)BVA4aku=vR1)FqA>Uj41>U(yDsrH zXms6!*#20J8q-N7!9-FRQ54ViIKyHYh{KaLv+*G<8Zc>Cw3*xM9~t@UtgFgi44Y&c zdr-AVz^gq`ym)-!zLZ*_DMKl$Xrj4)Lad>ok*NYi_aSNzIbeVJY1l_^^iQRG`F~Wp zB4*@oBGaMx{KmB84jw6F)ks|m&eL(pf4aR(G*bSLkSD@`bMT@pb|DT>N{>{+X^}8dm@c8fgj6M<0RJ#KT<{C1nC_#*B7vM@PrOW5-^vw1fnMJx{|gdUAHI zu4;cm%vTBUPI+9`pLZM00Wu&tDKSXca1a>ciHY%wEGcOws6+;S#F74lj2|$n22!9T z)nX7Pg9|M(^Cp_X)TxC)uI~U#9}99C5^Zj!JuE(eK#N54Lqy@WERW{B6LF$noLM&i zoQ9+;KD_kQ)wMBsHB8w3O@UVeDOecrdC1F!<~px+L@l+JeVzzjut+UzSVP!FSLu5b z9g`rJa`ibB@~4$^vkL+Y3Bp6Xv&R&ghWG$k*aj<2^~jPer^O`Mf8bxa7$A4NnNtTl zD;j$xY3`~$N+r?{z24P&`8^%#8*7f$$f$XWc`9kyc_tMen1xfIhYDkLPz??F9&y*z zxv+6I!U*RaELf$@Xz8kk=HK5#C7CkmweC2!h=~`vN4aqmhLs6n{TvY{$IqeBiZ$Ir z3!2M9+=0?#h67hJS^l%Sr~v|az{zk(&CP6iTF*%?2vp4P?*w+fk&6Ls1CXom zK{P)1aq1d8xlx-(OZ5}=LrbjTB@8|G(5=1?7MK)2YE)*7l9F6p)pE2Q7Z!w#lUzmQ zFAEm|AzsUU(^fbfk&t?DQMmJWC#fMEr}CrZY~&bX=LxB=GLk^LGXW@{a_=P!vI43UKae$qdeFrrSHMQ$kYh7U02supZYrYmYJ_DA87)i(k zGpOSaM#tn9;C$Fzq+mb?yodmmhO_W7QPe|LSxaIy&aYt5gawbS`C{haWGB-_B;Y4$XUP${i`A;yvta&84w4@XE z?Ki(C+CTv%B_)fjGi&8zNU_`JoX{Z#qoVmb{kiO^WeWMU4k#j?7m?2bbZ0PaKoaN` z(xAdamx4r9QGhNNV)~_f!|!z<*46NNV4w`~z3#Jn9z#H{wc8<`e4U?nLrA(2E~-wo z{_&6h3EIsMFEb^QacO=mH-s#4Px6sfvBUK+I%wDK=FbLoAVEZHy>ml@BD-n(A#Q76 z!WY!xgVynJ%rXdm$@8l*Oh9Y*W@o?*a}|k${tSPFutpgZ6O*~QX=>Fe=^2kHS30t! zQa0qx>@hQSUK91|eodbBJf3K7G4b4^Xi`PqRq6+?7iRRq*@U{CLf5^ZqD+D5zY7@| z=Vg>QC@9fiNDUJ0>wl7dP|;O4nYXIm!=#|A7W@|etdz-#vn{yqLIlkyxArj>Hud}0 zSd(DWIq-$~A-KJYKK~F-bdL-Yu_i~@M$MZUlPyfJ?Q(4>!e#7)-|xz6tTq2J%vE(Z zr&oe)a(sSaa>|n9oYzflciWsB*IG=Nhf#-l+7GAOPNtfPDSqJl#V_fGkr+r!t`R^K zFq7=e0wA@`ZEwo`nF_$Nz!B(2oGf54c8O6mqh|Ppyie^MbN|g6j0%yK)v<8P+;9>9 zF{Gv#C`lmIs6{Eif&*w|&7{{A1l~s&XwlZ(iPohf2`+Dg#g+8*;nHK-WUWd!ms?~1 z@Fzo$^%5!+I>!Yj-<7{hTwdFgUpMP}Ym0=Txb?GX%;Z(;1ySLGBT7n0`-@YtL&Gi+ zq?;r=St90bl-LsuEkxDOb~MD+WWgIeZf4(ixx#WMf^w-*$-^Q(VCmd&a7z9y!`Dnw zB)DG0A%~GsOvps_+CDH4=^np*+z#LDq?6jY>DFKJxRYK)_Wt6!W0ovPae03l-Q>q= z^_vARK<(ZZisX@L%t_^^fM4r`RlToGp=|s+N>WhXQ1UoVO*&xEH1=yw5&?Sosk^<{ z)BgYCl8ZD%2WGsx*oc6CY6J23|CiCmN&2q+t+(oE(~&!s>)XDOIpQ4bJ6eH114Op zF-aV0sr~8c{ON>z1+K4(Ai_xKz%QR^rE)PBfBXmnXzyIKy;O&Etf6n-GA;^~@!w^} zjuyV{m5hb34|N$q9zY;itE%RFWm zl`y>Oj3pPR+ZU?w)?x(!?90w{r{aq!N((%o=4pVDc>E6>B@qGcHM>tgO_c-Sd2u(6 zPv%(PX}tQ#+{ofbZnI+}t$KpZtjsF%Y8S1lGkIKo=e4C7YsM;RJvL{^_4W7T_Pm4s zK2c4WM-A?_b`Y$=y*gG<9bOduJ!fx>P`4_uj~I zzZR=tm6quqUsgGndUn<~r}^j>;j&#%^&T%QmnIBIv4|5&FjY)onN(gq`pL_R51JEF z8g>SgQn-03$&$tE4{zJcR}5SgEXTl9BVzU{$Y_e4uM88ZHq=O2?~h(Blx;=0Bh5+A zCu!C)6g4xpfCm-zc1tS8ij6F>**hRddvHspHdo-SEk%v0(JJkviPkZxsaqC_az^ZL z_B4Z4#{jzuDqbisF%=L>@;oZQ@%@S0%yFcO{O^6mrHprfJwTY;F`|HRD~n`G=Xa1s zMD=x28JjGD6Tjbe9!ZO?T)gHU3GR3JY)wi|s>kjroJpm)YKOjJW}bKFp1P@pKw3Gw z9T%ac^`VXqqSz^$8q7;#l7G)pV* zhkKnmN2AWS3nbi%=H>-Hj08Re<~Z2NQ63aIc@dT1r?9=GgfX49R$!iC0H3Sdd0zjPw= z=Z;Cf-ZnWk7ko0fpkq2cBxrH2SkFzYmKml0+GS;=lsj*vr||<3ksdpltJd)YJ zdIo+-m0|c0R)!@LZH=p&e2NSu$k2fh7q`&jpr9=z{yGK>uHK;zE5s`rc?epI)t+T zQgc)AmJxy*#;;Y|`$wc%8rFvX%~zAeY57k<1)zCYkSd>3JH#f-!}Qlfx&yic3p|e0 z9%;kua_Kd4%zi!fS7(rL85GWI-Zm9?|NNz`tT=lb zuiO#Hi$Y&&Qxz9|kcBN1eDeooeYW&}y9)qxM-SFOQCxD}XI~6ZX{CG_*uEr#c4czm z+iINyUb52)2pxXfnqIoPB77LTTtd-*CNZA+WQsMDXN&;=93HMTuTt;W+!cj;G^T&G?0nSELc#yMfNm;+v1=F0Z ztN%7sCxdW#v2yXC4ToiFdh%|s($7K*QRVJfYFBb(n^muzlJ^fV9tNNsap&`1rX$|h zmFsls%>0g62^#Hs*jAib>1x^j5 z0B?^Fpry-+9ku@m5v`*RoY^fMDd?~Nph0XZ(qIu)fdX>D+l!_`N3#g%a6ZDuurDY= zSV3YHMR3xa7*nyu7A$FyU#fmG;P&78T@1?WKl@VS%AVa%D#a6|X_dlDGqVMTF`O?m z_LV5(sHn21{Tj_=J!!Ct7XItsE&{9<>U6`4=UEi(^xorK>VpxmdBnJL{dcP2nJ{@(~hSbLc zR+1i{A;2TmLg!mKJazk`h&j8vJ#EI#YOA2I$HId#1`-28NwqQa@wBF#gDfBNOrfoW zILH;J#rbm^E)YI+{vbsvhohl_o~hvWaT8s4ng`1QxhgiM?Qy>=sr;iWpbqCDOVUs^ za3J!PfC15+9BxhE8<5%ncniY-Z{d+0XObHMw-P-?;_B}ziRi|tyh3m)D7X!TbFow) zt+DqAQ^~IzE6Brg;yj0?nUa|)S`vH@a4J!okexk_Hpa%`%c)>xPe-vYT$qJwe>WzI zmGrcVEt~BWh?7}wP(*`2q4lG^I{qlci&`JYI})Ol3sVc2JWWli>yUFZrO;#T76u8ALnV`tyQGgxxL{pUx*b0!>p`FS%&c>8I0 zO;6f%#2U`JeQ%DaYI+^~w^D{&#<$S9NAk?DR?hx$Qd`(S_suU};r=fw1c={wfc)za z)Lb#8f|2g2GlKy=;;vp-&)@Ybg?)Ptla3ZjFzH!=3V&Z$lMndwx@PF?D5r)A&gh=%Zod zlz;EDxxjS`^$&P+v)x+OA&<;tQ+8hK#fIjhQrax(3*w)th$<=+hE)EW8YY2ZF<2Dn zQKa7i%HO0`gA_S;!_mdX%&ka8t98omTjCa9=q?Z zWwe2#vZD*SRSZ=8)_>c9lwCr%!v)g6@TS==YK9nma0TCPNf}dZlQLmm)~#*g^|;AF}FEa?@fJ<8D%4v@heMLoSwF{ zV;$UGDd(ovTUR>R*>TNBBUsBBwY_ClsbBEeWDQw6jjHJx z$5d1Z0fx3$?2$l}wYx|$#CTj_qO)OsW9Qeq;V4b@IArOt%hs!Y zF0Kh=bEg*4WYlmE=jOxKtkv*T3H)ggaTcrLwUx-X#Q&^O(qlVO`#&ro&kIminTe1` zmWrdd+>z+f#{SXnG7L5>!|WX$VSvSUG71>mcNA{+zEReH8b)B+2l>zZA|K$x@tQzv z^E+jALSXfKvVVmvr=p@{2JlP5r-fN-I@;F7h63Bw%w~JPfKCnya)T`Ya~N!Csrf7V zZ@z{!_(*^4-L!03V~e?X8*!s-J#n7{Ndzu+JPj2cg>%~qN85~}_xo!Bf=pqG{RdKb zFTGeEv?xqqxcu}2vTuU`VI;livkS~n?ZuQvCS3{R+`X{qt}|0NbEia;yPKY?Ep%N^ z*JLhDn&6!dT;}~CaWCa3ZBjxopUTl-{U-uYHi2gkrq4;KSLav7g2L(s zzlV@TRu+E7%34Q##pPNG3(N~dZW6ril<(DjB*$FDUUeeU*X~k59w>AJ6aAl9Wmr&! z*S^3q3m)p|QF(1Ih_B8*>1|?mWz7v%K6Et|u1*iX+hrfANAPqqQXf&z2nf*+YJLTcYV)z6g z?aC<5JSvo-+QaDzLtNYhL)?m>9eJI;3b4CI72W;HPR0?)vU5BsuGzt}80uSKSkL9F8w`c3or zeY#+LzxCC3PCM_Q@IKTbxdC#LZ!>qBV$jYO=BORmSu;~Qh(%z2_UKN~( zrwj2ecLNb!8z#CqY>7E|lyq%kvn9v8leZ)?oX!VJB^H_!Uu5&peFOmTo~5kYj;>z< zIcNu;M~eZwv;%=H*b(uDgj^Gl9xl6>(gr-^9Zd>h32{`>WBh=o!YRyi{E7;hWD3Z2 z&uu62V|lRjnL&5rW2D>$ z5u`sTXS0)jT>cuQOXL+c`VM4E65bD82xhj zyv`6&{T<*1SXb~B)c_H#$XX68#4Q;z*(b#(G>ni&6m!`vBc0{-CMkAzoE#upb3D?4 zq5*i{UA;TZ_ENf!0H1|eLxdtU@(XH zzJ-@S#=T?G)x38nhPIuTK3Kw|y5qUTd-mYi0R01UyLO^CSFlLQSR&;{lwjVJ3tnQc z&2RJ3M0*o^`SKCxBH>7(_@&STr{SiR>@Px<-ehf5pv>*u%7(2VC0>|EY)J83t^ z=K{Y3)6grl%DXPmtv`FMaGx{g&pdgbO=Z!@O6xzgF@;f3-0bWq4p!mU)$c#7jq2`y z2o%@m^=;b{oS*k`_O*=5*^e5{%Zs;naei%Xd>u)+A`4F}Dp84FgN1%_Lb!B7Kv;L! z((+O^6Rt7bTX(Rq#5)gna=IM<)_!}%n#HBsUktUAu=X7K6j|%FFQ-iZM;AQ`tHnI)m7YvY&cwJz(NK zip2mQej$#s-u5#@we=PD;l-zA-G0`kyoGmISVcc}0E4AdKPYM)R$KO^k2%^ja_i$) zqMKNB42)H+41AqPqgD!()bAvli_r>!wi6)4z{jeQoaR z+1`Bs$4%SX#j=QJE@s$M|M~wg_0IiqzG1g_o1`(?Wa5c!Ta9howw*L?+}O5l+qP}n z$uqxwKllFZH!y$9TytK>xsJ8I3mpmf6+O$hN1w&HiO&0?rRpQC%IgKM%WhlO_a^A; zdSUCsxAV2Y_wBamtBTI|<%#dD%iH2>t76n!Bcd2>@a5}mH|gs`wd-ED^P%DE+EZ5~ zlkf9Y_47E0%>Ero*YkzX_w0$c&GzXEm-pI9TiVt4N%HN&j0N+_i2QcN0B?W2 zW0eMUu!dLZeoj>!&p!NQy1_(O#35bt1V3HqIe6VI_udpP!-9TGTFM)ED?|B!4>62( zBDC{m;l3^SeHcvJvD71=w?!WAp<){CKzk^U3&g}GsP==%-pU@u)w0YPQz>KVRPef9 zy;K8F{gczjzR3U{+RndiLIP8Qrn^gI7ar!RW;=uO_xJNJzqL0zU+h?Wm@o^zE_q$e zkKHswU;CD)3E`=xsF1Ibl9eu3uia00ppJEBtNi1X=Ka-YD@zsOQXP+BKiYvr`^wvG z)pL@KQL|*WH$STKYf=ECvFe(K37^#*4x&By=XP7Kul_^#^I$vcqx$5zZ=yV7kw_d8xSF0ibg{NrI_%#;vv;s{)~1`p9Y5Gq z{;^LN-W`bgP;vCVYc5=5&!tMibncKWmgrK>VVw{HH`->j7Pyt_%N^WSA_}ALA}F4l z$C`#9x{M%wYX^P|(;7l_zVo=>smbPORVgiA>o^xnGThoOE-q7a$7=!pUCqFNu#cXc z7TSMlCo>Cy_|Pp4a_yBYgmcg{b*N3`T@f^YAKhNiuB(_+JWRaI`j@=bxuPBb1WpC@ z5A#i~rnU~4In;u0!ZOR2k}6!@=zQHPvk9O26BHvqylZT=)OmEDJHEE2bSx#L?=Dvq zZ=Sw-HY3JQBH&F8a$WVz4jrz9AQY2xHA4z3;n&tgD5El-I=sDdnu#r!c%#rNecsmI znCjMZg^;FDJAFob)!S2hCPWmmf<;TRiHhA=GO#jr*Y%jRpjb;vl zLxXKREqabxNvWbU)o7_leQ!^Iiyqzt@BHHC!4}6!ZLM^V_rBA!F0~C8R-#KIhmCvE z6-8@pGRzH~jxVQNNmbg%Cmy!UuXl$xZ*@zYOHv4%!spf-`Ee+xT;Ms)9jn|ybvN_* z!QE{EJ8*S_*nNYpTqVCqOI$r}wWhXpwSk}JL$gzD?|fJHTb+C@`V2RoV7N;@`K9p0Ef2UelD1!BF+cpC#=PpB_-%3jyOzc2~Dd65e^WnhuSf+LcMKwHs* z(H*4b&Kw_}qMRTQ*unWVxRs3|R|S@$%%9fA28%D8Je; z%TtWg{*(gChZfGU!TFvvK`KAxy4yJ#Q zX0mQ2h$u`clSjkvr^+;E7>*Ra@VdnJH5U{lrlWH>KOeu+f3fLI{`05mD=rA4@z1EC z{`tC$ug#`0d6d!t9bHhzcJ{fy8*b{{^2$O-BayW5@B%S>VrR>IB}^1!JxA^|k2hF+il0q zljEo8hw8^V5Sy2}J{_K)_w}!n7!O}sDuoxqM4x(Mw_QtDz8!4OtH1XjKSmJ3doIcQ zA>aMGwXr^gpLo8Uvz|>(E`}o4d9};DSMiu9zRloiFD#YzE-VGO*iL!vwRO76ho&)6 zxS03-`wPmAfhakDH2Kp?`SO_pyFGsX>p$LfDg}9P`UoO-4aN-0Bk-yTtfX=vsaN_e z?nlsFI^SMjVvtD3ns+bES$>qXq!#9c26e~oD?L-|OQrqmn0+K0!=`iDCp7`JUC2oh z6BD3M%;u_7Z)PmL1wAf7|a@u#kuyVXOtV zZ#$(hFUaYeE$BIA7+|0Yib;)@QM=C^Nj|(N)YjgD`2-wElMJL#E|@hi8XCrtkO-ep zKqd7Bp~U$l?y)Gxp(cbm$ADZJv_%1o)URqu5K5!IX8Q}kF0XSDp^sk{dd(drLoVYg*_yRsi4vvS%ljnAZolOgpkrVz_4wtI4 z$*bin0_dvI(!`4*{vltSJmdG6cDl4s{M4eRqhf(~`Zp;v#pC<3pV?8;M-*q<;e09k zxj%a_mQhloLw#Q7s3x2!RA9TcIr_BK_Fkr&vDC0XP%z^CHfLgj=5rk|W-YxEQ|&jt z9FdZK2^U04F1X7M`C#A+B}(*ohR6v<-N(W<(eZlvf+X9B>$@~RKcAJBCR}sJF%tTQ zWbEP^-FY(@r2AE>Z!Rt_=?DV`&hvjZ+Xk3eg~NvHP^D|9`Qciod3b zXUZYQcqEL~PaZ~Co+TD&@(eLVit00O{QQo^1}Edeb<4dkU7sdoXdZNNr5{L0TD=-Z z5;BIMT4|q<>Z2p>BhOm-&G8m`9LjqsxiVdpnhUd5bWEBI(fvQ~bI><}jq|VGle#R> zse1w;bTwc-_lF4lVc-dJuVN!m@q=iW;3=NWDq>3Pc7PpzcHT7*rDri97_Ky`)R}$h#r6s!K`-71b z+|(S9Z!niQv^Lj5LkU0;R!G756J7;1ez+Eb8sN^u4fYR>zG#VyHkA)!b}StB6lDR{ zR0;Og*~*ruJt_v2NJRJ#ef|GqiMvH1hWP@&LF+9Pi0$?O8$qYW{67hMkfbT5A(&^L zdXRE(?QrBK+h?~H&)|C9azPZpq!Ugykl=Lkaro+u`r=uuU>SRNR$EQ*2L~}R(mOAd zpAX_9P^{v%kVtUV^{bo_t=lL{x>uuZ2(o$1b_vv6zwUXtd zdj~fU)7WEHzmc;qrn=8)e3CYL93H}-30~5i6YX8W(a}=!twzkr+@1k{#HVe-UnUVX zDIziwd-->$b&gzE?%$Yf@?a(Dlr&6S9Gr{u^H#491bK}{wPvztqbsiC^~R0#WX7z2 zjE1fc(Xx$EQLYhD&Laj&3-{h_O(&^Q7m>*~?Uk0mA%L;T6*;X{vf}hQ7BZ>w2GnSMeIsJzf1_cKG;}ruqjU-@^ zo?8wYJ|&cazl6YqXW$1cQf1`h^^bGR{xUJN7TUv`<_7A?DV^UgbQT`^A!aQ05ipFI z=AnBezcTGgq$`|-7Z zm5BB8af3oJs;3XZIk&q+@i`_WEBJ&Mq`!D(lok%8%&f5|M+Y@Sa|wg>lLP1V=!Y4l zw&b>+-MTFr6qqOIIj5LfCmq2;5Pn9p*&BGJpa{Md4G)Wz9ZOc_-c zfWDCSGCMNz+o5#y_q5sPxz*J*zP9R)mr!cOh(hcQW; zTcewv-lk2Hu8sN6f|lIf{V{Y?OG@2sjyI&wvo9&h4SC%=uCP)_upWr4wIgQ`Z!mhz z3K^grIqE=<5ewiT& zvhsq$jY9)raixIXNBW=ThA^djljg+#QGucT8c6>nB!VzMg{L6QPm!pThj*s}>J^J1 z!^eiZ8V%+%yblmgn)G0KF!CjrL4U3WXRQL_LQ$i;sQbtQ6r?B=TN@z6&m8!{V!v zrg*YE_r*39WywHww5`7$Nj&;?4*YWFWcjY#PfyadJ!4%iU5t+0zq>OhrV8 zLLfzHOqlwAg0%K!MtvpefwF=MCK?uN&sxkZqNSU_#AME{5J9gckbK36q>@G-;)J+yZ2N2tVLu_Q=> ztG$@A`7JuFP3^N%RPdOjUl1SIyt*_$^U+A!a9?tLzccIYV?UPhwgBG4*ye!tgwfe{}`37YZy_O9xAFq~(n|93UayZ0{jfs_#oR&5@2J$3- zSlHcfBt|s_i>{fa%3QlUZ~PTOtJ3oH=5T81xQoopgi9^NYR%zykn>QwZE^;lU3%Bq zZmV5&uUux{Y4ezZ_+(@Dt+WX6+7@SS9c52!))`$X$*scURD}d4w%P`hJw!{DMNC1t;HD9c2wKkgIg*j!OBM3GIn`?2sc**T>)}s@8#`FL!~bxBLg_)K>hYI<@FN%b+0Q;N9V@9x#9efRLSh)b_hWSjTo}A zV@FtBS6kTS`_IIze7Q!1IW2I$KK}MLHc)7*w>MV&=eYm%{UlCMP>Vl)M6?Vd5~$zu zLH9Tw08gtn0Y?0NjL}I71_MZ^MpGCXs^4P-%1L$mA?y9;2$ohvD_hA%RYtK^NgLX$ zRV%0Nv!l*410|uuC{>pB3kq6}4i5_p4{3ERG&EqPWTfYqQJ^7&8B#M7+xLr4s!#3UIa-prRV~Bous*_lx$e zH;lW_a5B_}-ZuJks+LrWr&CY^x87%&QUAXM71uCkYGVrXzV8b!OR^v#m~9I!(SuMh zNj*`!FLNZuE{8@zJ${L~AlPZ{6-0g)$*MVAG;QN$e^|`=sPbJ{w7(cep?eLug(})= zS+sj?`GE4`wWSBGjwbsZvpjDknT5N2?$Lo$h6&L@Z`t1Qh`I)b^U49Fu>qVB{_uVG zND)e^NjG4&jA;a6*_uw;W#n`c68EGeB-4N(5^7<2lkkJxfx*SbOe)nXS63uvHrE@l zCa%6;vcixCX(8WKQs~b=BSRD81U1z4KPLed{rsQ7wSz5Gidh=3#OD1ORc765G|ZEA&yClzwy& zh|gYiUhm?4WcVs4Yg#jEHZ7g2YiT{t`yDyXjzN*3Azpa~@9g{DyMC>FJ-U8PfwuI@ zRtZ;jjLZ|&R^&fnf8>zBpF_$4Dkw{H&cLQz5g+Q{QKG_5PD=hEG(*AgGcNN1U_`AJ zm$zCZq-AQ?tX^K+fqnVJFOTOXR~Hvg?i=JmslwuD5Oce|wT0LoQhS(k9>uHx9UyK8 zJ*$0`e=5HxGeQrDvx~!uEj))-hq;n%SM9COnBo>(vtos?q@gUWsdj&KXp(EvJVlHj z1R8*;Mkt_P1AtB=qf?>u@D~m(=PuY-qoWd%68C|9F*Pj=sUk!GcWYL#WB|P=2)1)J z!%Iom#lD2Kkws|PBP@-~rfDng3+?CmAAmQ*UVn0rFlq`L_-m0FLEkHw;J?s4`!-ADR;8h`QM)7 z13mktZqcT|8N$?HqFr(5p%4FeF#(f)d{ohXe577zd#PQ-l*pHzghtMwbfO>(3YK~;n{+!&vjRDFl1~j<0kj(Yu7v37ou=A zG+T;vF&><^8BA)_2vR#JWeJdm;TC0z;Gf~1db)>KGl+JiuPv|E8>;;Y*IHQvKfySD zKm}@R@4LY1NUExzhA%6viAD1l8-Pv5#R-=QW`_ydK(34V3&sQ*awF~P|LxHfBK)%#NhqcfS#cn2 z%5q^H%KaOr&>V)Mt?smOrPM(nWkF!&^1k(b-s(Dmn1YGHTGm9nqJ@wyR@t@;v|KYK z7_oFdpRC+CD53i4SlUwCak2(G>maAx-V3lrrYvVjK$qa~j6J1d4BHde^dDKgAl~jP zWs-FDJaZsYU+Ib1=xiO0KwizJ*Qf8XU>tL4wcI&yXmzu_ZCo8^lo(oP-UXq@*==M6 zJ~H<``2T*0FdFD7W0H^ut=fTsda(~N(3A(l|4%=ri&RQ)J#f6-qLZ2|zGXrulH&)I z{$mfQWco3GL6wo@#elVNC-eN($P|p!%Ew``Gvall^HqD3b6H0ABI=xSF?$d7kjyks z>4&zK2uN3l}Nx9?31UgDVD-1-u^_QOJi5GHW7}jV`NFzq6A!sRZvLN2~dnn z8QG};*JjH`19Pb<@-{V<6LkW`{wz>ba^{t<%4g!tp`? ze!p&n{7)B|B+3|7SokBX{BP|3gBv5$TwEM7FmX9|-D+1}VoAPc(vf=TZjK4g`eI{j z?E5G^;<>NCF=pSidSadWcxxpSe0WkNHU))v@#4{)1JJZ4nH+1y5Gm}qwXj5k90&s` zz#HzWaYMkTF~q*uf3F6vVej#sJK1)>5HfCOZgt$DZJ;t+uv8;aew{mEW>W~g?#G(f zf^$lOOC3U8Ic=SVsDgDz)oU;T3mpes0kd^M?|v$fr7VzYIa5AkS5l;1-^ggO~-8mR6{$VP`azt=H((3rP_JvKb8y z#y5&Bbl%vNS=Vq9B<$!SCMrEeE)z3b zdeEGns!F6fecwPC@nAXQ)?OTfBH|?Z6tx5liH=WRN1OZ4v_m5Ay`^Z^nLedpHWD%l zB5lc*cb7N4C1_r5kcFOe>WPd2=1=CKc*bSsmDzFMqPy?Xa6w+hq%f2c6mKMQ#-^KfyVZpCG!FvsxOS1P50c!(=i)J zg`pgvVdl}%bl|`SWZ>bQb1OBhv>vgF^1jY%B9Age%=o;VqRaAZTd{V{W!*0yC%5tR z{Pti63_#g_r_(r7qzp{(Y#Nre;(`9dO=_a8BAl0uUc1>@OOS{D?cro8ZgF>qLgQpDB|3Gc^pQb8%QAwoY^2!ZN?-@ms>V*#mC z)u*^a+j6VoId(uxZ45T%R|slCKqm#A6&3ysgf+*vX%*TN-oxCS>@!}K;o1iDcsGkj z_h_k(lN~F}%UrGP&Fc&6Q=;nf0Qc0iFyaQht22x9!ua9s?%8{{!CC(YzV{KnudJ=F zv8#{gskgUOnXHdLg_t_q6sf%4j{s@;kVjlJ;m|^}33@l$#%RBW=JMg@#_(7mh%E_C z?O-09nWYI08NgIm*N!hl6jM-8Fdi}A@)EQfdwWw83c^?I$M@II8>uHEXb+uOu%?lS zi$J$wsnn(Dn<>u`%@kLjm6(q(OPiM1EFdf|VF%3+zKlmG@Pkjm^))_tC5E3+`mci~pQ%EhqQW6O&(9 zPmcho?$#&Tfh;gVKVU0E^+C+V1!E<|EbK0iT5k(4$c!tq^A4Z#w&4uR!wF?|WUOkR znQ`ynLyppWWC33NbqQO6F;Cf?w<1Ca&-#qgm--1@n|vUh<9!}Gx&~-CR&^|JEO*rQ za?G)~=-0BA0a1OlX7~0+qaWryNVnnr1fTL38oPLw?ONx$fO1AH2L`TZX&&;r@=GmSBpji!|jl)jYH&g@fHivwmv}m`G~1*YD{yKi(JOx1hD?D<~xFD1KX( zyL+MSn}*^HnffYiAkFDFY0RCs5D_FEs)(mYVDu`f{JXch5~VFld52``QJR72snE!y znV5Wlg26kHx^CBMxVCwF{&5oJpgGxjzbn{6z#0+$ynkEY)}P`_&g8Bs@R<{VBdf%! zeJ*3-eXSVGRq&x;Yf#GNsoUByuHxEkrO&As=~Z9f(SWl5f%S_XK993w|9PqsE#5?# zPxrXQrN`J<_RyM7EBHs)go!2B<`A6KON^IgD2KbJ!2r__Ol68$+b+iNT@v$`qD?bu z$_d!lJOIua8qivUm2M)0Dr>~_PAwf0jmr1Kt!cv|oFwjRDdZceytoLz+wb%=t<4v! zAJv|v87*(KuH1?;_VJ-?Er6llN6(Fd2PbU`?JSq?f+;Vab%%M9$A|%GoVAS~dhXQ7 zHzABg^O)iZ_IGTdPYpUJWgCjAwKN1Zo2pGs^%oXdX@p2iU~59;^ACr%&BCnM>`JP~bJdfbi@{Ofhk=0LWO`Ej zn2c4tdUQx0c|eXB`HeN6Tzy=?MJ2fV4ip;(PNj{T>>H`cs3;Z9{R$1c2nhItctb=b zK@T`&$E6fYOfXZTrW~(Jre0pjPF`+mfgjaVhfKi??qPQVPs61?gx*Y*>aFGbnE(n4 zM!YI3i_1!H*0td#RZ+0y*vkYP@UzBiqx31{F?SSSC?Q#_eL4vrJ}Rk}phT^`7@G>QYo^b*HTD_0^s0+_d%@!da&Y5z>3f6f4Rt|zN>c$PINFMrJRH5W z#DBYr8%Z*a7apax+jfaXtD$4&A9V#O!sgHYQqH8hTnqB*y2*8Xjn5;SNjeVXY~w>p zPOV%mcMe?})&j8|;~K8b#MR`ff+p#>cu9dH56{o`d!n$n$|odbzVQHo2^wionPJSKhiAq^=FIM~_&KBgzHOVxu7&wu`2~&V%*q2mv zSW_}^+?j(~u5Cuk;YNDwN}s~zc)D$f>J{n8p|*8I^vT(U2nLr44e8DyGNj5?^Qinm z$Bco56~%R{J~_)Zv#E!i$FsQ$o*qAnFY2-y1Ernz`-INI#hA;%n8z}~3GF%X<|w*g zaA_i($Uf_dJ{;B8v>KZox5U_MKVg`7eBp%k*@(fM#=+0U;-3x6LE8opv|e7zn{#4I z#C0oHi-Tr@MbyIY2|J04F_n!onN)ofd*}1hyBNen{li)towbRfmy0=*i!-#u4U>;% z!H8Ykui;3-HKLoHUDQN;Fbp@e1PlB2T!(ft zu;xFca+RBnGq2#r8#(;rM9sDO%@xiyZ9{xN<@0uH$J5kiD7DXQgYy^=utnDf(TjG= zs1l9UusvI@Jk}u#MI)En^D%-7F~b}gmR>cnUNte6B+4adBj!`vK~0~tM4(nRowRA8 z)d)@oQTjRsFL7`LL&PGIT|`|xn=|0q1;&{TmUWiF;b2k`M=&;a=2+RV6_&*r@WLGL ztd)0lhIb~MBY9s^4&AA2qFqnJ;@s98*D7EM(p2>GRFtnq%u%``!1w@jgQo}!OD2=^ zX>X&g3<}b8oYYcUSlPp)Hq?>$#cSC#8#8stLTB4NxP+#xpLFmh&UP1U(w@4}b#yFj zN}DsZ32bZ@P>I_bCQL6~P*jw-f2fL@7}&Q17m0wU)m066AX{BW$D)g*i%1Ch9TXysk=sG)=CbQ6$jZ4yUP6Bqpu9glUM_BAPR2^$NDAZ=bh zv$=fss6MCNL-J*I&!&rW zeVt(;gek1(#Y~8zf+m8-?{zI(&`M@**Xf);FIrNdgyo#OcXFqP!@Zc;pe@StXiD!m;ZiRW$&b>U(zFCQxx?#fQ+3r4tvcTgQKqHu8 zUFW^_6tC@8HK1Pu0tBxz?MT*2k2-?>iq<#e9b02z)grxsa>QTf1O zu3ipkqbW)=z=N+P5HzHogsfFy1SNVx8AoDIrRsAAhEN_LBe0UprDErBxC-_?CgbOU!;H%KlLjY&0xKuv4lGqPmUm`M&o^}Bh|1r|A#TmuC#RW7=1Rmz zLOX>-OxUAxnwQfHhR>QHF0XWzBS@=boXU1ppLeQ3O%pq!7%F`xgYyflwR?6C+vDW# z7;K5AhwpcvV&9ss-J=jDgy9(PAo%}4?GUar7MZX1Uq_;IS-2*;vmbTdXS~(AplM2f znduVZVJPJvKeoR2^qiEJ8-x{7Qk~N3bY9*lbbw1=WqI}KJF+yf9u}gWrlNr&$vOXs z#R1Z;_fH-md3J2Xp;=3jfg-og4Emz9wXzsim_KJaHp02VGPjA`BA>~vMKx#Pg$9)}+m%l9+iOhS=` zJW`H5J$`>|SZri$qH&e__!}WbSZ`1`BRt9!b}NBIZJ)mI)8P?1C8b1I`0bCne+enH zRImYuC&`PyzsSXi1fzBH>l3* z9yym?)^n9e^bzUgZXAJjxn?9ls4~x{k__mL#t@3t@A4M^ky2wR=GA@ zPYeI`r9j;Hyd$mUyJl9%Heu6XFageF2Cf)ws3xe&4i_*Iu24L>)9{M^K}z~q#8N9c zn}kk?xM|U}8jFJ5mI|f7sP$TU1<_w659*|{kL;dzUd4%*vggiosJQ@T-M}?{s0b`xIIZDWZaZ{*oH@ zYnhqt_yNZxsf3<^GHk`SpjuaEl&LNBYyQ96?sndidGCeDK%yS3u)wumJL)rZWnhp% z&XMls#tI|*^`%=(d>2_uCu1E%i5&^`NZRzh%u^4#Wg98f?vFiDr8X<=P0wbF*L8yw z`SK=tlRCLqqfLkI=-M0Z{pLmp5HUH#)5L4i0zEevVv?TOk7ylMl~Ae$AzYgM<&>o{ zeCwogBSAZDSG*sKmUlocQ#)DRre)sy^Wt#xDWs`D)d3K&3oc3I95S{_`_M|Z9?I0Y z7|!DyGAhy+SInwH_VVQe(_bwKN+#EIDL8xJ@7{at2&qwL=8nVDg~1vY5SPFCu0d4R z+L2Ty=F;-rwR8+c>)`V7Xw$}9$(B(_O$e(?@g(XOk)pk|4QfO^Ik7x7Wm=~lXJc^% z)bpDw8L8G>I^2ZUB@DdyPA^)ywguUG-*sc#e$86cMM+1f#wV3|G!r*{!}C<5zX49o zMl68Lpd=QDYK!zv{drs_;h5n*VT^WI+W-Ug$?3?bsLa(?%D?jKS{PZ$_3GA=l74c; zt6c0#|FSf-v>*rwM#45OtCOYg9PetW5775hAoNMu>#CbE;2}bSMcopWm6Q`v#S^2> zIe@Uj9v-h3h??|3e#g?_QwsHk+}!r+W{8uZ;r;%=_wwD8mY2#&onVt{8#6PDniV@^ zrt21P12e>Fr;TnIG5nu^yqVJX-S+nKeWR_YjD>=N7sW8wOc@c}^lOX_tklXg+%_h_ zPq;A$B8Pb66gL$1+8T7Su$fuymznDOp)Kur3~-nFUF zKvvg@PXkpcH0iezlgq2NTA%;hk>uUZw7|T6T^`oN zv{^Lk_BohyrG3y>X36@YvCL&jVx7Vcs7tIiqlO|3#^=6 zdyT|#JY-Q&@WeJnaNz*AQFJ!}Ov!a;_&Y?KYq14^Y@k_JXRQW~@-C-z& zq*eV-@O`(~;HmJSRQ-pD4TIu07^}P0b*y^;ed)VgK!ecoxAa45lqmlbzV?3Qtr&NSRoFT5RfWJCOtxCxVje?(= zl%X5n9z1V%dfXK+e-j-dJ32~At!xSe$R8ni;aav9T7bS?a^r^Nxw$zzmhSVQtF$&w zCuz{?hN6VRC2irrMIbI)wHbwvogM_P&P>Pvt=Y1_Cw_chX8MSsmp_1zdT?+ccf4pv zZF#%fev2s*_rls*R&xCG>g-N6mytSe?NjJ)dZm9!d1tFkZ<*{%cHb8SxSd??S1~1- zDc!kcva=Bcmsk=FRdGdBTU*~=J==Vau9<>{-pX%%=v6SvZt9>#(hYP6HTOHI?q01o&Qk1>?uwu!q+q72jL zj00bL8v{HgfAqG7mW4eBIgJI6fsTi%-8ymstA ziT~a;#J{$sN3+!g0RwAkasXo$HMgBKPezB>Rjjw@k8_8$w2!seQtEbdj&0xDxwXmm zr9gL$Bvth@+AgO?)F)oG)Et66Gr0*hccu>=I5I~msW<=C@2(dj{3}i_O zG7~be`_*xA3aLsf3Kp4n0T(rJdvure-eKLq6+YGOxlzHsfq1mMP8>^%#Hx*JW}$Pu z>{3kKObo)@ssD?R3*pQX{c7^~0GLohOY-}#h6%@em`A1J4$ z=v5Q4Yv~{YM-lWk1m__F4X!?Sad9!#5hH}+tG(;DG}7D1rDqyn<951kV$0Wmf5S|kseu8J*g+JC zC$XuK@S;072QsoEb;Jq%b`h`q3!DvQWmGb{tO7IGQ7CV_HmwWr4wZ0@#~Eo5iG5dT zYHCbe=H|3jWTt+^$m4Dts>B>LdfRi8UHgw62;?tVIdEK<#Nl6aD@m)m@+se@s1v0~ z(+W1oVI`3fEfoq!?M>IXomqMY)&T?!)kPGYqjqen(v4R}T1v_Sma?`gy?4fpr@WnI z4PxK=&yrBwkm?v?W@PtgKH?{A64f{W2zfGUu{+q8X>PlYYl>&cV^RA!+zMOI`l5+Q zC@5j)M6pe5Pwf4FFb<CQORRHE<*+HdDB;4h`m##Ttgz5RP^&hy<5QQs?X!ys>rMy!<8e9z=oW&(a+yx` z$Y1wjpA$4ao7jX5a6xa*w){P#ZW2;ZRreZ|k$R)AL$h_snvS`;`uq8`^Irc{)d8`Q zggnDOOoEbex@TU?LnNrv(FK=XK~lcn!i;;W?ZZY~3CX8rP=Q*>w>7(&uPlG{RObF3o1M599*N#aq;>OfKwI%K6VcTDNU3y$>1 z$Yk>V@54VBr@*te)g@p5iQ~(e>*^AQb z|HfPoNE$ti)Ev%vDsO4}8QMWY!T2?_w}4@2 zlr~i`J1CkU*P;PXSG)`Tsf?JKn$Gxg9Q@dNTs(f2qZj!R{*CTqT zPqxR~%u{NU_0;hx&#&tzX(>=RRFcoWy`{c*aIdFyGjx<93@w}6jb8;&{G(s*A*?Rk z#B6k$JRPnyIL$vVZ%^)*@W6{VxFEB1$8)dgK=EI~<-f~K)7$`1H(?U?MdR|GZ%qfUc6csZZG_GY zGY%Ky{qF27!O;1D{V^UVFA>CdgTSG0B@ga7hX;1uJN5U@@ajL{w0!e$0&TSnQ3V^O zKVO12;RF!|JRMmk^hg$s>jv(OhznVCFDWY~kw*1XA%~OuE!x)iZdbvrn$4>(UiGk( zx~V6jCW2E~jL}=JNACF2Us)Vj|B2Dm>!N9lZGDen_AF=o}BCk*ClFHV_1&Pwnr*0#WNjKw%@b4 z=cc!vKkvtj9<$eR%j?`wWf2E5{oFP_2DceY%3S-%G1O2s=zse&Eq5n-l&Yq>C$N6_ zPM5d&Y@~Fx!??jyLUGO;AiQ)=@rz>Q?}h;z2HHZO!PhkNB2BQwz-i8~B@VRSsGj=} zm%8xcGJV>2<$Ks%9kA6uK;5nm@8p@U6K&}**lYqOWjXc9bM?s#3`SN|N1nZ7E zwTPbLesXqAvRdCjh0!3Vp^*b^OADF>&Ee)d2DEdD3Hr6!&MrIOv<6K-?_A!B;=u;M zh`Hk8R=89rd5+rd4l`^g+n+R`IMd!%u{||+f)W1!|Mk9*l)P`gbb}>pQ_u-PD%b{mRtGh#oq^y*UTXyXgDOKkvs= zR!8R+A~!CCgI>A?H9R$mteT6-vk2`{3&~^UjDE=c0)%FU{nH*97DIsSGcOqlE0xOB zQjwQ8s7ZAKh%3sE=9f~+{GJT>YW;uazJOT& zfIQp*Ev>RipPY%n+KNIS>3j6eR{$Qgx)BG~`|s^tL-6=aIlvE51~(+7o!wA!JvAk{ z2;yxom7R^pfK)v_CxweIj^E$<=zXX8Tvq}=Y_p;kxbzmbNjOO1`T4KGEj?JD37`f3 zLAdFQAYeus5Q}rJC^R&^3}-G9NpDsvzMb7lD7dKyy2=WxFu38ALL)lr8PbUuJDud^ zeI0$(eE+IjV?X_t{1_ZUc?g(Kw+avMSIS2rCs0LkfjlSRgelE+rc&nHsNtT8TgeeJCnG6Och;;7PU$J}Zla+P!O-zjSPgYlT#2 zWiL!Ro!q;8zQ5c*JMYirsUfC;9+S&9+(j-+_w2s&${N^wH#Uo8!xtxM8ef3ctsb2{-idm{bub5YsP~2XqHiww9_D2HEmcC z|D>?1T=_wj^3gKA(0@+DR1+w-RD1z(BD>MENz7Dd}W-g2SoNTaBPd*$Qfu4^js& zm*YeDR8|T%i{<2d{c7qCjx7St`qrV{@`H!TN9&k+)Y{GD;`uo|fFJL500+CN6W)|4 z&_F+mYJAkv@(jWOY*U||HZ&IO7e-VGv;aRU|35(JPJepnoV#B@Jxp^}Y`W9?{z((k zyyYd}L~=ZrxEgg=3Qe)9~{T%Q`)(B7V*JOs9jXB3b_Mjm3UHpfn=Xla0q%#PY} z`s}+hhOj&!eemCb9I-m!2a|E2;J)K}_trO5hi0iE*ZIT&w0#oOBC`D6G(FCk#PaZv zG_HfkTycAn?*le<1Bs@I+qjItU3$oy*LFQp#B6uvIOQpQfk4CWYQ=%e#ns$x4K+i# zSdI#{u!^^V{Gr4QV&URp5IBQ%-L2pf1X)vm9PZ|BS`QbsH{4I;R<{>nlLyv8 zQ|i-re0-qZ!D5$kUhuAU1!O(nIuUaW?vu+>P|u;t$WR5j&icovGi&$OY|L1iclnBS z6N`vGE=I-|{eLvQWmMGd_dQGuF!azv4~>X~ghO{JDc#-O-92=O(kUR_-Q6LjbccX+ zNIrA_zR!PM@8-p<&${xQefHU#WUtt)P1*J)dcUHt0k8W2In&R<39!Ggs|P_KVSb}Q z|E9p(65fT0{yX~UJf4rQazfZlVP?<>oI|D<;@Q7CFp>Ie0CsKCr78wu4XJk`4KB5i zUK~h{eL_!{^7h&{suuD8*q7%CMk=ct)ka&dnrJHd&Orb5JJBMwRf(0pmWfRC$m=cg zOn1K<9Wz+m>(2Iy`k^AJsB}|ifr$A zbLw08(`1QBbHM$^JyW(n`qX`aTt}z*wy($Z(Z+)9Ep@L@UVScQMqO!3ep#W1gaFp+ zorSqxvO*mu+yc(8o+aypCh_u6e_!8VgL94jktUOCmeb}QSCC-E{_N5pS~1~@I&kov z8y1z<4OYcYkHFu#>JJN=jg?PhCsj!Xa!ISk8$pgO8v^s3GjS>4hI0;Sv?b~b-|?JdhbH1vMfaI_L? ztj2K&@Cgl&l*F|!=fAS}G`%uQa?({<62tP7kM0=z(K=YL?nkkQgzr^ajy`*qB&fkl z(Ic}xlU@#YKbyP#yytc-?VV7i;|-Npxyxl%0RkREw$mkK`PLZ}bVxwbuS$@Z{IrTp zSuwa3)INVkjyMjR-#^uHzNskQk8kEZ-DrNE&p91Vp!k6msfGMw7_Z`c)p2%nf>Q^7IZ|r-KdVYg@W&>x=U2+$dtb9#{s&Aqcjy9Q_ zuGH=ArT&#y9UnUJ3uGQ}%^Gp_qwf=X?3+t`o5|_y^V(x1Cr2~M!P`D*$4f-G>*0Q@ z#37y@*o(q|Rw7Rrva$RJ^niKVJu5H8+JdAMkEC>e{ zGl63AW@815cDl6$27rJW>va{dPOOW@58JnF-L~`nkW;a;yjWX*w>#9PsASk7u73t~3=qFarQ8D%Y;=pp$=}(P0I~-MNXhF>|xa#c9Z$AjS-!^xaWh z*mnx_hlul}f5(hD{A!*y-1pY2tKM$9*swf0MBFM9OPmjszLhxZax2Yur@qgzz49az zlVOQJ##aQ)ot<@64dL6g<^5S;{g~a-TyzU_^jcTfoxRCFE9t*;nU&UFh5!heHJP2R zlRHY$>0S5d-?f$0i+jIp8@skZl{4N%_cDd=PvO4FQIo9{vqxYPNF)ge< zxZ~N=|3dv^uKmIAeDH8jxJ|9I^7(#qpz$A<<`=~*U8uYEY#5PufCR|Eo$@6I#?#8B z@m0}43&3h}%V2)wN^l&h>y&(mjg!Vcnb}nU_+z}8>K@*IE$bSH)`5J3b$3ULJjce8 z(^UXQooZ`g^++ykYDV9Gc!E1I5orw{h@2gW(Dl5PHQ95L=>2)kNAgu6Llaoe=c;c# zv|5C-NtB%0&g{O=@ZCwQ;+rbMC*CVqvPNx63ZUh_Z$$(s`8_YJzBZ;R^AH!1M^$yd zfUIC%UT<0Xw)~quhrQI%#P}z&P|B1>`a|0&cz$FBXCR%_*tz1Ga`Zl8;SA8$3}GX+ZWIy-YMZ?s3zqlc27 zLUvoP7&YW!xdQA}Zb%6P*%A`H17S#BL~muksn0Z|{`2M|A@JhQQi0h8cg8ol-Q8E< z80tU2DbKz@;XjyUN2*5=;|60hrl2e4_m?Cnj<83NrR}k5aLGDu1hL9F<#1rd8AI7& z(D>l)WY()0B|<6mpEw1hc6Hwr9u=uz(Q$}2+>U=L2J81FGzQKvTZG_kub7(OWa$e= zFtiE~WW<7^nU$krT#v5#+t-LOq&!YmoBa=X52JTlV0oyaIR$UDlid0Bp%h|f+Js?) zTow(eZjrtcP{p8&+{~cgz~aSgp7xI?TuW(6x@!E+9<$e9_E4_s`Tc}2%C^z(8y3DV z>3?oAQrWsTH?L=Vg9}EY>=$AtYn&y&RMJXL7%cS9p*d&M35UQ9vv3=SQTkCloNZ#KKPMSds3BMveO-Gvub7ktzmG~W}2(=qtVz%il ze-%Ef|Bj`2afFfXw;0hmH0{g3K5;G`h_21eg#9Es~{$0IvlqE*83N;rE?Zgoc*d9`iz28&m zMU5L4^wA3A-ExKJ)Y<#@z{9UR<+lf`5Cl#{_RA7}TiiR%zRw{Pe}&!K>eC3ITEwLy z^WK-S)m71sLcGe}MbFoX8`u#Yk2lC~wHDS8%17gpF`%jH|I92WN4LmkG>)qHemeV{ zpr9l3{5t`8RWpiG_0AM`HU0NBqX1a%-@y1RvCHc*JR!i1kB$tMgy8LGM6OyEL zfliO7d|;FveY(XYc!`=Zf+2gB%)jW5gmfNr-&@*B z>BXB5xq|w0AKq&nIF?ZaHSCf`rf+H22D}cv-B)P0+MDct+9Q9w&4s%Ex!w%YB;0HH z){xSu4yDDq#m2_IQCsSCD!yGP%b<*Vhi0ao50uf+uyB6)UfoS0+z^;o8^FA&*~^Pn zcBfNhrYSLoQyBA*4qJ*o`8#k|rhQ?d)^S9F)-}a35x5uA^e!y(G z9~fwfl@9H}1alDQ3_E!V#Os99<`D(!%lK+)YAWf8SG*mPo8vwVLrsni@IuByHYlJq zB1=w5ZZ%qNAm}%d89~V4#JYy$9z+ante^mWx3RYBCubF)Z!pZtm<#1@9lzONn9-EH+4o16l%bjzQz*2F; z+gnFkY_(CLrxs{!&`34)P8^79Rz z+`Go3?sxYtiwJ%}0t1{K^bfxw)lst&?;pIdD`-=l@o{5VffRk2h!ubi4Y?2sa(sN+ z5L&Shpi(sI*GgqPV?wM*6;p+ovQa)M1P!#A2q9Jmpx)Y2m->LLB+)0d!^!8R9y*d0wP?VXM|{yvXTa|m$_Ug(i>Y3 zVK7Jcm*eUfe=#1-69E$DNl5M*0V_zZ1=|f?Z5YtONJwjWTxtPL7KZ1`giBXyj(5={S6HglYU9?=x<4M#`66 zH=WHyh&wavtA;8R0 zJ3GfSw}ciUI){>RHo4?An{ZXk2s5*yUU2S*2Zacbn7C+h8OHA0JoI09ArNL*&%ZZv zE*ylHKK@%qT1n;^1&Y}fNcitLpc5A+%oIbxyJ2)Q+#qn6*jfdtbiUQR%WnO~OQf_Y zaz{Uql~oeSPFad(aiEuqVmP0QVgQt6jvPV{B1D8p1kOYi9BM_@^C9tM&BI-p`5Epx+WEJ-UJp3J0^?h%?Rmv zc=!dX;F<4#=rak1qbZ_?xQ-dhaMi9kEOz-r^iR~A;vp1qE*oq7iw6?7WPA4@Cr}X8 zIm)3i?w+S-xI`%+xG!+2`wVo5nr#dO5^gG|OcNmXP%NSVFwSyFaEzs1+F#cE*P8`- zF>dp=`~|GO8~_m!D2yT*LWNSNr;yA+|NX^pALbq!wU;}P6%wg!IF53Vgn}?JQd!w$ zeow=|A)UJKGQ*d=q{GuYE5Iv_4+qwV&FgbzWzHxbO6=3nHRcE(!CA+rFmwKe<8T3} zVvM=??7U;(jxS1UI-BK5Det_EI}Q$NpI?8zsv|Xj<2dED@2YXhTtM9=f?E*0`NvJ> zldq?vVRu0RXFL*6muO&)=a8BV7QP*#zR3=78!R=)E-7;(oo#d6P$EGjywb&y18UMg$}j{`iX7h}(j@?b*)MVkYqB zT580GT7f)P**jhjbrRw6k!??lph0{~s9pklK2&t8@Iz-wiN}36=>FGf4F!B3DSO=+ zLBj?^e11v%6H++S6ihb<1h zv->g|x| zoaD$^(Zu3qowvR{`V6UTJOtRA=Xu&z_ccdE|66mwb{J0-$K-*zGofLVnL7PMSL+eB z=fE_LU`O}EQ~v&blWf>KwyCf5(R*H?@<|No!NE2d>}u`rVKWCx$cwkCc>QqQboX48fy&xtizX=EL9F`2(uC!5t$sgDZbF9+onzG?i%H`*LNAuf;6HcQmt=qo6|MQ28fRfWEJ09ZH zypocU_H%w-M_XG#h2D&*_^T$AGL2C4qpqzCJ;l-Azvz*D0I`-egOpjL_vt4%nami< z+8PEGOUGGkk<73pS#T-P3;VC>uS0fC5Fo^)U&5hVw zr(JgueOf%_YdJa}-FV(pGHk!Pb?^2&8OwQzo3pIHQK-HwKp-e!#wW2In;_L{_bM$Bs|#}p)5 z1?OkzDFgGEXRuj!Zx|25;5dZ*q zlg;tpgr8z08KiRt!0~)$mLvWDhXsf|g+cz*I2LtP^Yw+x^fS8m2&t{DDGU5mTUh$F zxHLPzvdlMwjrwvC@##V5Rv|}AxYo=*C*BK zSG&^J>&w9yofSn4@oQH+4v?hO{Nk;uJGSmI`Np3UD8o6c8UD3T=*pV!<@_Su7xsn4 zZhdX<((3NE@^-j0y*vxsjjy>BjXkgN-y-xBmNdsTpZDE84QJ=^MX5zCUYZ|2ezwz- z*SDQ}2_5FZ@=Wle@kji}hBVA)fS|!4$ZhvNGox+UbX(6Ng;VI=;2E~$n-Ex3LE@J$ zy_iVD7e?R+V{Qda^#vN10%n$R7tzQdE@k@YK5D^-cr0f|FGMFqnhP(%f}T0TQCEs# zmpXdH@>(r2&jsFZJ*a=Roz3W$u^ReZ zXab^zytaEHCfUEmN(01h_$fmmdD_Y5VCIj4Vh|_@iQ=m{%8U4iWZL9pljM}0;xq+W z1vv|CULm3HmEE4=7g-++aL&BOFK5_o1Fm+H(23-3CNpIs*1nY3BE2YUYI@q3h-+wM z%;ue)oYd(|a7zVW^zak@Qd=;4Zw&r>^DmQ3CzjEa8LUP~BvA_-@3Xh5`Iu~`lA4Dg zy-nXcBX6%DK~G`r81rI70D~mcM;8DoMIo7p*(hRRTc0eZV9~V#SsLXFc8z2t(;1_g zpM`2&Z*d0g4mrKfp*>jr4R_8#ry=;zb|dXzN3e=R0KdV~YFjz^S7w59>=8Nx| z0_Hpb?L9Z$-Q}^;%1IOT(@Exs*AsGGkB)z1nbXultuiG`)0vHJg(cQzr6OGKIxFkN zO6DZ|P-9VoM<`WHO`mZhl~U=WemNn@D5nDOCGZmmMU_YRq9)@isS?i2?A`74O%)Z3 zY2TinQhvd1XKUkjnCciniBYZY;`M)!=j7rVGMZsxU^o}Vt)T03Hknsb)KLG1#v`1k}$NGe|)~ zZd$8@2S7n)kVB1!=g1*J>A+x9gikJ?pGc`cClFdN6z5k7(so&C#hu225oy8VDzgbr zkYx`VT7(0wkpok;02O`=za3F_;$8&+0mA}GL3@VM4hmwNOYGM#i%;_)C_88-R*)S8 z`YqceCwlKxo_qdWEmO9>L>ILrkq_^nOiv^B)b}n^y_C4m$sdN~Fv|Try$z!~iK!Mr zHZSZodQa~ElDKAkd`#(cu@HERi}U)NZup_Od3#nS(@Pmi6)2cMb6IoeW_oNOYXt(} zIFqvj1&gqx34owQc+BzMC!*&fc7nToZE!0P)r#uojVF>GlOj$%!LbNkH})}^B!JXq(t|i0}5QVY0zEHa!l#^4FVxg ze=?_Uv2pPLZ{&VHX=C=xe;H>Z0-%d28vtwdqC#qN3M9qUh|~#b#v(z~;Zqvr44j-) zgEhrNXZ>Eh*+cYr0;upeo=lyhKd znxRB9nuGEP1cyHrz?s7my%2KrIYM*=XvD))*ZllI>QlIV?_uOruxGEEX^3&|MmsOK zaP%M+AxnzG)XL1tnfUk2&(Ft&5$bV{2=p0xK%f;#HKs8U5A}a99{kN?KoUh}j&;(e zbdLW9&YUYsc&Xu3>ymkQ4oZrdi290I$93m1#(cp^^#f+3svJx}n>2ELeJ}SWJ}u26 zYI$dez*MjHZ_?IJNyd5avg2LFsLOskfULN&MbmF9c(Vg)C>z(?VJjCX#Rai$#lkk= zlQJQm+0nk^>&c`7JHz)cGV_P*MqsztGUj|IaAaN%giyPnKnc19jVLR_KS%5DO)M+b z$ZTwEt?ahsz>jxxzaAKX{svSGp1XnQ`o!3e0pa^4Tvd#nSn+&~kU|ykakI;Me zP#fzqTkK?eW9e~6z4PN26^agy2dmVbJ}sF^Ol(B>2v&*@Dl6kwOw-1Ejuby}&Jh|l zhaC}JNcq4>V?w9Ph=0pQ2!GD`s=@>LXTE%&zX2!x5fU0<;4`^ybGdAD9qbm#D;K(A9wQF)qG*dE1roHLHpR14i3xG;@3Gg8zaW&GAI-b$YAV)zBDBqT)gnV&iRmy2Fr z#E7_AYzlvo)C`-m(I+H7MJv*dA`5D>k(RFL9%$R;KrLzfKlqrmiAa&q6# z>h35*}h4a_VALd&~%RpcEsFA@XFz zq5(Dm2p8>Z1p0Mxgg$g;mAU!Y(xnu*e{F^1(f+$Ps5JhC+^M)K!>^7_RhqoAviMnV z-_XueI%{n?&f2L$-|~%z@nC#aVKLAQKO_Dy*%##;g)O3r-pEWlUMbR;1@#f5SoCWO zE)on)PnWKJsnuQ*~qxGO-8FcLK~pGz*uI^DV*r=!1C{FAM(w2zLrc5T1K7fBis z5i}LNZ))lVibqb%rx!R_SPG@gKPoCX)8*}-^^dII2nls2wg4mheL@u-=d;VI(NRN! zk`;%U%V@_j2<>>^&WmcR>~eisr~=DqdM>Sc*IP{H#6zNHOG%_=cdjldzNTjz^JcOr zX@1~o&X}B?_*`CE!i*6S$}ppyauI}3!6VyqJlYIA@~~Sv+W*Kt306?%qgq|^qhJDB zrJ823!ri4Kg5aYkl7@pgKC>c1(bX}bQ^a0fZ?GYGa+-pJd{UGuwt-4hCEe1Jk%i%3 z>*I=9q!8*KA{2+UPu!ye0z3Hn7wA=95qUI=)VA;cU=b>kf+a%U3cul<&EqjmX!2r#v$L)Z6|_)zQJ{pRyA0?}Ov-CYrJbZMhhqDF+t4;DR56(a#AeKR0ZuWr+0&g%Q`E6$TRCzxlBy-PARiX6xF{M z7aM_bNF#=(ntJv)+#(FJUh8l;Wy5&@cj#^gWv8RKXHjda#TJhrIqQ^+N);o)C zk=UMM2)MYD2N|>@6{ML5CcE~TbJJ5{4QMI;*YbY}sE7e%=81wDXtmUMQ9G6K_5Y9h zUM85zldTrhxBkkC9=6 zv^u-4uR1-xykfDpL50186h}Jbhk@hNY&=wFdLq4%UP!2#)Yu56gtcik_mTwak4G#~})k|n!2<6^$ z@8E|0u_gb>^;yAUZX_y_1W+=kNN2X>)QxCqqeB+kqL>VsX?xg-5Z8KVbRf&3aZDwh!Nbs^Db49^WMyW?p{rTG5l3%d*l!_v}fbC`XPt*4p>$_z;zF>$1}#=CM333)0UykCX97SK~`-9G15~ zVVbW^Z#U9{3&0Yyaq9M&hRcB_hLg$TCGbMBF$Y>Qer0<4)c3bPU1LD?>0Z9mX=8tS zKC!D;YpXi1xeul=rLB4|6+g*o0{VKFksbfI%X@f8o4YfVgxPDPtXW8?0fwXBq#Uey z9;9#|1cVXdMFb0)~3Lm#1x9cJ|K*IP4jff;RJcN-% z5UFOysV~e=`0bFQg5y!C&>c{)vpnC>mW?f&t(@)d|HA@o|F=>I zNTBCUQ7*FuN%A`UxyzIE6$4@X6eDtUue@l{@7B?NQgX97I7vn6oRrFrTDI zGcA+-3Ani@H|!4BstUzDU4CT>_hMq}9lJUHL!JT0iixDGZ5)LBF3(Ms)|(G{1Xy5o z6QVu|}{O;)Pf=qH&OIwLK9r2xbfqw3M{ zIE0G#%9xwv(y5ub@k>0-U2}1xspzk-OH1LWUHfZLNl`eQ<21|jl?Tve2DIAulLILy zIdOPBP~R}UsN5|Y;%M1Dgb*Di$x80h9x)nTXrX4dtF;&1o1Zw#4UFVgAiGAzl0wIto8|pB{ujex z_2FZ{XlmHEFm2cOxYTxFs#_4faqYJqf^9ou%*4o?s`b7;L0Jtmb8{d6mp^3>aB7xM z=lWIywXl1i32!74%_crTkr@`702h2_j^dB7KD3Ifp%6<1M<659Sbuah+TPjeO)l=& zjoC*3I^OEOQboYTA|)5C6s6tZ^f#Sr;ma3w_r}KL0f`R^L%2wyd_j&g7|qO-C|%BZ zB&N;;y@&8$GfH-_|I3A&iI$!gKr`@hFA4yDZhj)KPxCh&$~u0V6~lUiT!}xdGjCK7 zj5%)R8^-%vy8HV_6)}_nd2VUvtn2OE^?)O#1@eII!2Ywl&f5fiMuI{NsGBn3pw%KA z8u7~;{3MDUMK6bGQJuwaonJPx&_)2dDS<&=D##^TCSNZ|$$aa!Ub~EtU2ymh|~mU3=--D|l8CDl@SK;AnDAxHNR4 zLA9!kTs-q=NXTXj=^y_tL@AX!%GKMcms3K9$0t^?O$v6k>>qGc`HOjLRg?3;X=O!) zw95R!6?q(LsMuM}3NvgMnp2QoRYg@l7;7?1{Sj@E=1m#oBTK<$2tp(n1_w040Alm^ zzrg2|{WV6+Sj#OrV@*wpgko@a02`Y>DgbLME0(xNdL&aY!NIPd_}IqoHD8{Qq?KJ2{+C4eiHfQaWe3{< zo2czXv=`PkG4IvX2hEIivY^&37e4|vN!%bHV7Npb_}Qp=La=5N=IE6eLzyLA7*nIm+NR9h;&&nSVzisRCV>0`5(5cWQxlp%GqO@2#>fM;8;j2C#+f>wK2J8y z-FW{<&!e^oej0^+F9(I8WpkTK(UfZ9W#k7T0Q`id;zT#)oG@lyM@IY28#G= z+-oNKAx-c3c;)M1p&l6pHZOoOp`rpk_XEMsjlv`k)#u0P3(SC7k?2s8)o<`M*tD2N ziTt*o_`g^fB?6f!k3k}Q7k8wD(xS>by%y_PTuW& z+<2c!8tkc4!J8IG&t!SBrm3ouL}~oHa>dZKjiSC}isR4?HI&K)FArcgwEGWBV(sd% z^5tAbMa@Ld)mE4M>avbT46#x)$*-3xQ(-!Sf;dKK7LNd@9w;tO+~>fPK8Yn^_E)HZ zfL2&8vAi7Z@^X9C;cm56m5eMCqOj$%>pA}hQ|iR8d$AlAYF&2MH|B$yuC->#h*fI;~p0 z50^;NN4sTVDG)uuZIj+(pMLS%yBybeXSlMc(`a)OhF!fx&uHOxcN<+W!ZD$HW_NgW zq}5ow*{G@Xg-B1|ples&|FsC+5apxm?tKo=@yDEihn@-RrO7Oh(IcCN#=-1w_&QKi z4|VmbyhszpnK? zEQGL(qxU=D7P(nv@Pgg!0ABIpD!cpHXR-zf6D+B*a>;;!!lq8C1m!KDKxc)CY4u`+Yup;4pxM$cRB6mJ4X<>RW1$FYrd@15#X@?621t#B5wVlsW0Mnimv1A!^& zCb{NRW<#*%V?+f17)dAW1a)PolrowLBl8TceSaNTc+Ge80)7Ilqf zr(MImL(=W_IMuFX=cfSm%0v4W1Ow8m3V9k_gx+;uZTgq2`xQP!kRK2dXP_Y?4>EWI zbq<0cB;n8< zs8w(sBMC23%@_nRq2w1EBbj7(UlzEJv=E)8TWdJDDzFySsy=}Lu=yaUD7dgX|G)RT z0jO8%37|Q=kO{fb;u4Ar0-*E^L`pE=aI$kf%O+6RCMPVO%i7}6cJYU)^$0_p{tsI)|LLl_p(aWafmX=K1 zjq{Gnw>GX##FztD62 z*-L2yv)}Nk_uZPnX;LJk%0n}U8p?a8%ia7sm+9Rj|G?+tFP>|v2HKA$l>)p}!hXxA zI0#74_@kSLzklH(^h{q;1xzV^x28{Nl`K*BII+@k5s483rO^q-F2MEiHiXYMcSZARB`z|Ls?GX6 z6)1=ROYvLYQfBeNW!A6!vSNv6s5!U9v*mGDu>!n2784}R*VXW;khGX4kLKK55+P)Y zy-+)MiZRMp6cS4eK&+#{A0FD=f=XY*<`eBq+RNi9E6XZ``cVB13~iYS$HvEXJ`;Ik%G5QfKdmHXUl44 z6cD&mTj(V*^u>@x8FyiwKIJ>tP&gF`MW%Fg@U~uq-5^cNC5M%QI_W5;cc3}BU;CXa z5U?o{3ctZ1-Y9#SO;kB5DMEP*4FYlR}t*NY;r~*b1i5sY5V|3Z6=Cw&0KN-~G4x z_~0qLk-E3{XzKRmZfY3VY($2UEKXV!SK8X8mkxmmE5ewjnq7rSUY@=za~TKwNHztV zh23j|2aN|XMa!|yJghz381jEFI{f~6gdouH^hK61l)^DAU!6X$8vSH*DRoA|DSMmp z+cp`s!+aA#_JIeSc0n{4#uX;U6%2OSZp+K`oqjBt;vCJLKYNwNm=R5;SD1ao>Aor{ z2|&?~&lb2#rk~<)Q&k<0X!ZnOKV)RINwPfH8yVI7mwq#2SQ5jl!**I`Hnch$Kbz{w zWk?H$KCse<1zzC>etF^Hl&XO3oqefD&FC>U)-|p%80S@#UJj|C&Tcm2XNVk; z)SSnef6UIky<_*q2>aP;n?GHpDTEuXv4h3k-4mG@9C6Cmv0=nvxB65!Y851rx%S5m zI$>dd&7<-EuzB&N8tbk+0$r@6em3w=+~|K{BZ31NeyV70@opsMvI+d;lq z9y+Oa9&d|d1MEP9CvY8Tpws_gbWsVTCt65dl9u3ZkyNwjouq6b+|TpCFlrCY)GnH_ z-hOSl{4Jwmr^E9dP}<7c!PrXYGhr|Z4Y@IIx~NqIY<*6u5cfU0Ae;&Be%pZZ%(QPm9_=H$H%VxToWwSx5r{JwZH$FhXij0i5^*!6HH#HF_Ui7rP_rZ*S`Ax^jL# zazgq^91q6|YaR``A9wBFIMFzpx9_w^`GWZD^&%y?2D?5HR=CmcDuWKHmI=e07hPkq zsl8)1U-SO)#7T~Cu?vkic#c4ehTp6OzzPYK545AR4S83 zZMc#U%f8Axzr||BwC`nr0@-Mu6 z@cE94@f;i;#<|7LFFMLxTCcU}^~vbt7idFA`bh@;1rtAvGwPxCf>$|DeoB=uRkp?U zl%_2fznQb_7YYD<>pQr@ZWlrhl@P@T!y+amtW`Ku@c;}?6x&|OkdZ?Y1DZswAd(Q) zw~4YrLW#j}I+5LS)4vzP)u|amGcI7%Gdj{B9Dv`@X|#J60#UN}Hy#afXJyZ-;=5U# zn#4SKXV`rCzM8#<<2vjT9kY0vGaXARy)|+B94(Y80heMIC5K+ttM$k&F)gnbwOeFH zgaaI3v#@aU4@Dagkp##uV=>e0LCIDm!3by2CiV&jn^TGkdKvW8a&CB^CqeSN@yW@# zxS{jQi~mNh*cCp#r~Hfl?6H&Y>61Nh7-78p$X@Xag$)MDqiwglW*SPkHbtB$dLFn( z>DtX*?8Nl$@EC1R)YQB>eUjSzr^61=J*a-p*gK#@@`G~oDNat0{aRrw+wFE3c)2Pu zOaS)x5XhqzvP5%>VRS5=o|01>w%e?In6yA1uPZ>v9Jvsr$I#+UPRSIqe<&ib#A$HL z@;;gjv?w;G5Ud$C;gl$vyn1R|AgR`G4VWCe0S%x@h{=+vEiLr@#u?}{nu#f-PRs)$ ziOncOk*<(Pf7SY?l(8f#ih}f#iHr#0{kS&V;JzeT8=bnhL*M6Sb(wja#!`X+HCEwMDDR$ytBdE_f;$@M>N`o(Fh^!w{$n%E|!2D;30> zEC$t@sghDEZ{9!X_is{yMTTOsMmFh9(PoH3X&xrP-}u)7X<+g|R!q%3hIC1N)lZF) zWGXyeoa<9QlN+z1@csRY80m3tE@LG48|~t^my71r4W_iH8Qmzi?NG}L2Pf-Mtj1fw zoY7oY3e$G>QLFa%mG$3t@3psgwn;k&_oA|SZEM30FYB4~AHO&vY#H(Kva_!^G!qk% zh;kchYwtDu;*=Ls_l_lim?Q+Gvzl>0c(yL?pV#x_bPWs|oPPfyI{wP0gEq;Z5uq0h z`Pc!e6QqXKByy>!@BNJFLXt!6jCbbG7Gy7t3NtRvnN3$1swI*2)h$8d7FU6!qS=ax z>~fo!>MNGe@o^d-g_w&xZ~1rnhejskU`6zC_eUk$ag)9y#w;>3f;n-}VwmXJot)Q@ zw@8|syK8DXT596P?+6J!Q=YE1@*P3)S{o`jz&PQ3TN4v%$@Cl)krgpzv_`0I@w}3> zZ!M>zV&anMFJ4V;u?|7wbw;kDSY|^dV|7?%LnR$L0L-H(Q zo|AbDTW|hma->27ECN62>N69%;Wkto1gNMh?Dcy-y{8ijrd3AobCMJEFN2x?bM)Ww zi2nk~ti7nzM3EySyYTWCCKTJB{wCtQeUj^UP$J-A_q*Zdi-Kb(k{}`!H3Y(+QI}AR z>4e1(uT9?%YQFsVKn4&?CQ7H36Q&sAWPL+3Wi<0C84Fo1BuWx`7(~l#WkjcE9Dp+J zT)+&TjCkkh=zV?RYsc$q^AW#*HZdEB7UIrxlT9gvjNGrEEfgD%_z^^XJiy_$I3YJG zcVYb|IvU-Xt%MTzSzJ$ETpTMt1u;-GZ4aPp*D=ZS{si@s?$dh(6@(;aSiVsd1Sm%M zC}CukiV(~&12B-J#e=9}mMAsPrLlQ*yaJK)03&+mNI%}4C&isBTGQ|TJv~V07f~SP z?m{fJdS8V%!#vz#5HRWee*p?2k;*@1BOTi0+%L5y`k&EJgT1^{9TA|+A%CpB;c{wC zCw|T*ql8)XH?%Hhue>pc%zsojgwEQf_a4(YnP?rNFN9v5;0`2yEzwGkJCV2P`ne7yKc^wB%eLu7B{w7t*C zDQ>ax`PPK}PFQo~*S-lSJmBjsr*CmJ{FDTH4P7l9Uq%5tc?Q9f#J65&@AS>5+qX zxFP$tWY~!hny19g~iZWfj6yK!7Yv zTxBE+ASEV=7q;gek)LFRG&~PC7$i*pKdUf;~IhhUEbCU)| zAhtU}DUw-{6OzqQz5$5MNj^gH@fgsDor4J;9?>`nQA~R-jhF{8`-=KL1H=?@Fiq_M zV;Op-z`Pn`tC?eko@Y+UqK)Gp=(@~1UegZxrQ?Wjt>H(z6mDm5{R=T_&@{VBCct-E;IUbv z%k=RtrBCRj9e1_9`$c}C0L*0!vL}`pW-u(;h8!A2HO#tycxH!CjUWWtgG+o~Bah|f z(#y#u-S|)r+-a15@AgFalYcPrrhemh{rAA4<6l3XOIUE>R_eg$0#P;@A}@zY*eCQ1 zGd?)#ij_+L|Iu_7Y*BaL*Cz%Tx`wU+iJ@fZ5Tv^sq&q|!>F$>9MiE51J4CvqyQI4t zp7D1-|F_5cvA(_wLPs>YEe z#XufVy?gRgO*8>PnbYMHX)$^OT13Vq5{e{%7Zili{5-RvG!ZO=d~w&FkIERRU$g^r zjSJ|Bm=GKjyYRx>IwW#bj?|X@+{5jNplD2N8fn~}FuN5YY&75LINId=e|T6tkv25y*?u%?4V-nEM@GK;|ASk-OLAy=aydINWX2SF|lq+tX| z=4a6`(~1U^ioki~5_y;mKG8w{P7L-|jSr4F3mhBdw8JWvGD@AK~iV*GJuE*`kh^v1}S+NYTTHV7X85)Z@{(Sk?npdQ3cHS?q-#J` zD=*jXA1exu7Xd%$&CM@QO-@c}=*?;9d_fm2=n9t*MMFZYDZ6`uU&j_9i}}6hqK?lT z<@vV8_@(Ywv4>2_JjLfu;dzxcz?G$kV{sMq)nhaRmKwTZkj49sr1Q1NMpBhJ7!MfM z?Xgb9$QU^_Os~XN3Ab7I-89Tt=@kEG|ATHi>Bys6q~(ZJC|r~j6)B*@L~OxxU&2@Z zP_CUg5c{Efenj+n&kt#jAod-GOAbmOVFsydI@C_Qe5yYb|( zJDBO`c`UkP^>~K-mPGYuTyh5VpUtJPWgyfK7cMifNXOH;J0?9c)Y?8Soqd$u#MbM4 znX3)_&c(eo1ZVQpn*wux4TUbAEq@@rtM)vy`lNumsWdPB3y1GPOk2>hy4xwa&}ZZO zC!|O0Q^6G#JxzD#rLovYtdsWp-+ASRpK+t|TW{=yyu9+tA*iV5u(RGneTCfZG++Wm zIK6y$dVvF+Z#V+fXmGN#M!)Ta53sZF{Qf!+HEJ8_NFD)^5>}5Z1WG1gh;Q*BKZ~+L zCijavKj`Q~NrGNr$IaqvDjXFU=yiM%-Qn{5i8zYsOSsBkm|{AC?V_nY_i6r#v{=7T zqu-8)6hl#Q6c)6Pj2r7H5WS69>UC*bI)=4lezS?%MM{@XrcGCh_1h<;ku}xVR{#^| z%S6!vtMK6;3d_(6|F~Yg*%$cphx-rWAWqaD%5DEV&I7SBn9J}EnO-W@%A{VARr;YC z&Qh&vFDZ~HF)Nde6d{ii{%GpQ1eY~1+YG>U{m(s>M-H!>Dx00X$MU|mbo*ECV)rWb zZB@yRPLB6lq^Y`d5!t!#*&9u>S~Y<0)c{H7c(_?|6uXDwXnT}GW4HJ~;%^Oox5xdS zh>^45#7(dV0RucD4}I2LVmafx46;2`Km=Jh&iojJV9|UAWYQ~)U^8OZV7-wpJr&SG z`zb5%7WU1M7276|fBUe@=|3Vf{K;Mbv)40Dug3ZlgxucNzE8!xZ!^Wkip|%z7rhHb z(63V%XhX>Vj*}uYV0_ON1EM55m~5e|I^#%N2;mr!Nu)h0K(!3@HCi(k(8!M$|9%t3)Jxc>2bGU znDyxvu8_j^e|{Q$KA=c)Op%VP&)5nQ4LM{dZP!FW;49Z#5h;;Gy1-ov*%ps$0t-5I zzsnMD_9j_!EJUikH(nj=aTS%tG{ z*2V8g-qbDmqat7|_|1C>Tu+{}d7(}|kH1}6dTTlzu9(Ggw4us!as%VcW23{X7dNTx zj}5^eN_r`0N+C#|%{7H8fbNMp+CddvZ6nOT$OFBZUzuGoFmDhY5(qN3Jma>FX=Hrh zv?mX*0hGRE1QPI7uq2mpNFD0<`M!iqsNX+xeU}X?-bKr9Nz%`sv5I~*NmHs`#|*D; zm+9sNA6sIWaXpapeU_>oW3a zz9`*2xH%P7Ph#8Pr)|u)?)!6%{d6>k{2X$z89lDyg;v{H?~P3aOixqJdb|}D7F+-I z3soNdvSL)`#nyI{JBh)^Wp4GkpZOM992cpZknPS;a^V5hf+*wqN8)BB6Z400R2n5b z*w_&hfFj=Tg-wF!B1}Up2nhD~Pm(l4w>-dPwpX1G58sGPFk2q=?3=ex98UdrrEiyO zj*mwZ3zG>n!QWf9f5sXZv@S=YCoBMkhn5HBG_uYkIB;<1|Y z?(b=|UtPXa^+NYhwEl%P;dW^w){b)t<3Be{U!AvvfiLebV$rcRU|~&i*uGVN9lgA{ z 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%0D@`SZ<04@}Zg!#h~X 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

KKFR!Y` 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 H8+`sh605)) literal 0 HcmV?d00001 diff --git a/_docs/index.html b/_docs/index.html index 3ba81de..511d204 100644 --- a/_docs/index.html +++ b/_docs/index.html @@ -32,23 +32,28 @@

-

Welcome to Spearhead!

+

Welcome to Spearhead!

+ +

Welcome to the story teller friendly mission framework in DCS (at least, that's what we hope to create).

+

Together with mission editors we've tried to build a framework that makes creating multiplayer missions as easy as possible. + + With the help of naming conventions, trigger zones, pre-configured logic and custom configuration items, it gives a huge amount of versatility. + we're trying to strike a balance between no-code and low-code with the possibility to hook into it for the hard core scripters (like ourselves). + + + For the scripters: If you yourself are a scripter, and you're missing interfaces, please reach out to us! We want to make sure that the framework is as easy to use for scripters as it is for non-scripters, and we can't do that without your help! + +

+

+ Whether this is your first mission or your hundredth, we hope Spearhead will make your life easier. +

+ + + -

Welcome to the story teller friendly mission framework in DCS (at least, that's what we hope to create).

-

Together with mission editors we've tried to build a framework that - makes creating multiplayer missions as easy as possible. - With the help of naming conventions, trigger zones, pre-configured - logic and custom configuration items, it gives a huge amount of versatility. - we're trying to strike a balance between no-code and low-code with the possibility to hook into it for - the hard core scripters. -

-

- Whether this is your first mission or your hundredth, we hope Spearhead will make your life easier. -

- -
+