diff --git a/scripts/OlympusCommand.lua b/scripts/OlympusCommand.lua index 717b6918..8f853dbd 100644 --- a/scripts/OlympusCommand.lua +++ b/scripts/OlympusCommand.lua @@ -1,30 +1,267 @@ -dofile("C:\\Users\\dpass\\Documents\\Olympus\\scripts\\mist_4_4_90.lua") - Olympus = {} +Olympus.unitCounter = 1 function Olympus.notify(message, displayFor) trigger.action.outText(message, displayFor) end -function Olympus.move(name, lat, lng, v) +function Olympus.move(name, lat, lng, v, category) Olympus.notify("Olympus.move " .. name .. " (" .. lat .. ", " .. lng ..")", 10) local unit = Unit.getByName(name) if unit ~= nil then - local unitDestination = mist.utils.makeVec2(coord.LLtoLO(lat, lng, 0)) - vars = - { - group = unit:getGroup(), - point = mist.utils.makeVec3(unitDestination), - form = "Off Road", - heading = 0, - speed = v, - disableRoads = true - } - mist.groupToRandomPoint(vars) - Olympus.notify("Olympus.move executed succesfully", 10) + if category == 1 then + local startPoint = mist.getLeadPos(unit:getGroup()) + local endPoint = coord.LLtoLO(lat, lng, 0) + + local endPoint = airbasePos + local path = {} + path[#path + 1] = mist.fixedWing.buildWP(startPoint, flyOverPoint, v, startPoint.y, 'BARO') + path[#path + 1] = mist.fixedWing.buildWP(endPoint, TurningPoint, v, startPoint.y, 'BARO') + + mist.goRoute(unit:getGroup(), path) + Olympus.notify("Olympus.move executed succesfully on a air unit", 10) + elseif category == 2 then + vars = + { + group = unit:getGroup(), + point = coord.LLtoLO(lat, lng, 0), + form = "Off Road", + heading = 0, + speed = v, + disableRoads = true + } + mist.groupToRandomPoint(vars) + Olympus.notify("Olympus.move executed succesfully on a ground unit", 10) + else + Olympus.notify("Olympus.move not implemented yet for navy units", 10) + end else Olympus.notify("Error in Olympus.move " .. name, 10) end end +function Olympus.smoke(color, lat, lng) + Olympus.notify("Olympus.smoke " .. color .. " (" .. lat .. ", " .. lng ..")", 10) + local colorEnum = nil + if color == "green" then + colorEnum = trigger.smokeColor.Green + elseif color == "red" then + colorEnum = trigger.smokeColor.Red + elseif color == "white" then + colorEnum = trigger.smokeColor.White + elseif color == "orange" then + colorEnum = trigger.smokeColor.Orange + elseif color == "blue" then + colorEnum = trigger.smokeColor.Blue + end + trigger.action.smoke(mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)), colorEnum) +end + +function Olympus.spawnGround(coalition, type, lat, lng) + Olympus.notify("Olympus.spawnGround " .. coalition .. " " .. type .. " (" .. lat .. ", " .. lng ..")", 10) + local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)) + local unitTable = + { + [1] = + { + ["type"] = type, + ["x"] = spawnLocation.x, + ["y"] = spawnLocation.z, + ["playerCanDrive"] = true, + ["heading"] = 0, + }, + } + + local countryID = nil + if coalition == 'red' then + countryID = country.id.RUSSIA + else + countryID = country.id.USA + end + + local vars = + { + units = unitTable, + country = countryID, + category = 'vehicle', + name = "Olympus-" .. Olympus.unitCounter, + } + mist.dynAdd(vars) + Olympus.unitCounter = Olympus.unitCounter + 1 + Olympus.notify("Olympus.spawnGround completed succesfully", 10) +end + + +function Olympus.spawnAir(coalition, type, lat, lng, alt) + --https://wiki.hoggitworld.com/view/MIST_dynAdd + + local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)) + plane = { + ["modulation"] = 0, + ["tasks"] = + { + }, -- end of ["tasks"] + ["radioSet"] = false, + ["task"] = "CAP", + ["uncontrolled"] = false, + ["route"] = + { + ["points"] = + { + [1] = + { + ["alt"] = 2000, + ["action"] = "Turning Point", + ["alt_type"] = "BARO", + ["speed"] = 179.86111111111, + ["task"] = + { + ["id"] = "ComboTask", + ["params"] = + { + ["tasks"] = + { + }, -- end of ["tasks"] + }, -- end of ["params"] + }, -- end of ["task"] + ["type"] = "Turning Point", + ["ETA"] = 0, + ["ETA_locked"] = true, + ["x"] = spawnLocation.x, + ["y"] = spawnLocation.z, + ["formation_template"] = "", + ["speed_locked"] = true, + }, -- end of [1] + [2] = + { + ["alt"] = 2000, + ["action"] = "Turning Point", + ["alt_type"] = "BARO", + ["speed"] = 179.86111111111, + ["task"] = + { + ["id"] = "ComboTask", + ["params"] = + { + ["tasks"] = + { + }, -- end of ["tasks"] + }, -- end of ["params"] + }, -- end of ["task"] + ["type"] = "Turning Point", + ["ETA"] = 500.42644231043, + ["ETA_locked"] = false, + ["x"] = spawnLocation.x + 10000, + ["y"] = spawnLocation.z, + ["formation_template"] = "", + ["speed_locked"] = true, + }, -- end of [2] + }, -- end of ["points"] + }, -- end of ["route"] + ["groupId"] = 10, + ["hidden"] = false, + ["units"] = + { + [1] = + { + ["alt"] = 2000, + ["alt_type"] = "BARO", + ["skill"] = "Excellent", + ["speed"] = 180, + ["AddPropAircraft"] = + { + }, -- end of ["AddPropAircraft"] + ["type"] = type, + ["unitId"] = 10, + ["psi"] = 0.015782716092426, + ["dataCartridge"] = + { + ["GroupsPoints"] = + { + ["PB"] = + { + }, -- end of ["PB"] + ["Sequence 2 Red"] = + { + }, -- end of ["Sequence 2 Red"] + ["Start Location"] = + { + }, -- end of ["Start Location"] + ["Sequence 1 Blue"] = + { + }, -- end of ["Sequence 1 Blue"] + ["Sequence 3 Yellow"] = + { + }, -- end of ["Sequence 3 Yellow"] + ["A/A Waypoint"] = + { + }, -- end of ["A/A Waypoint"] + ["PP"] = + { + }, -- end of ["PP"] + ["Initial Point"] = + { + }, -- end of ["Initial Point"] + }, -- end of ["GroupsPoints"] + ["Points"] = + { + }, -- end of ["Points"] + }, -- end of ["dataCartridge"] + ["x"] = spawnLocation.x, + ["y"] = spawnLocation.z, + ["name"] = "Olympus-" .. Olympus.unitCounter, + ["payload"] = + { + ["pylons"] = + { + [7] = + { + ["CLSID"] = "{7575BA0B-7294-4844-857B-031A144B2595}", + }, -- end of [7] + [3] = + { + ["CLSID"] = "{7575BA0B-7294-4844-857B-031A144B2595}", + }, -- end of [3] + }, -- end of ["pylons"] + ["fuel"] = 4900, + ["flare"] = 60, + ["ammo_type"] = 1, + ["chaff"] = 60, + ["gun"] = 100, + }, -- end of ["payload"] + ["heading"] = 0, + ["callsign"] = + { + [1] = 1, + [2] = 1, + [3] = 1, + ["name"] = "Enfield11", + }, -- end of ["callsign"] + ["onboard_num"] = "010", + }, + }, -- end of ["units"] + ["x"] = spawnLocation.x, + ["y"] = spawnLocation.z, + ["name"] = "Olympus-" .. Olympus.unitCounter, + ["communication"] = true, + ["start_time"] = 0, + ["frequency"] = 305, + } + + --loads of other stuff you can do with this but this is probably what you need to startPoint + --you can also set a route here, and included first waypoint as spawn on airbases etc + --https://github.com/pydcs/dcs/blob/master/dcs/weapons_data.py all weapon pylon types + + if coalition == 'red' then + plane.country = 'Russia' + else + plane.country = 'USA' + end + plane.category = 'airplane' + + mist.dynAdd(plane) + Olympus.unitCounter = Olympus.unitCounter + 1 + Olympus.notify("Olympus.spawnAir completed succesfully", 10) +end + Olympus.notify("OlympusCommand script loaded correctly", 10) \ No newline at end of file diff --git a/scripts/OlympusExport.lua b/scripts/OlympusExport.lua index 0cf59818..9537319f 100644 --- a/scripts/OlympusExport.lua +++ b/scripts/OlympusExport.lua @@ -2,7 +2,7 @@ Olympus = {} Olympus.OlympusDLL = nil Olympus.cppRESTDLL = nil Olympus.DLLsloaded = false -Olympus.OlympusModPath = os.getenv('OLYMPUS')..'\\bin\\' +Olympus.OlympusModPath = os.getenv('DCSOLYMPUS_PATH')..'\\bin\\' log.write('Olympus.EXPORT.LUA', log.INFO, 'Executing OlympusExport.lua') @@ -13,9 +13,12 @@ function Olympus.loadDLLs() log.write('Olympus.EXPORT.LUA', log.INFO, 'Loading cpprest_2_10.dll from ['..Olympus.OlympusModPath..']') pcall(require, 'cpprest_2_10') - log.write('Olympus.EXPORT.LUA', log.INFO, 'Loading Olympus.dll from ['..Olympus.OlympusModPath..']') + log.write('Olympus.EXPORT.LUA', log.INFO, 'Loading hook.dll from ['..Olympus.OlympusModPath..']') local status - status, Olympus.OlympusDLL = pcall(require, 'Olympus') + pcall(require, 'logger') + pcall(require, 'luatools') + pcall(require, 'dcstools') + status, Olympus.OlympusDLL = pcall(require, 'hook') if not status then return false end @@ -29,6 +32,6 @@ do Olympus.loadDLLs() log.write('Olympus.EXPORT.LUA', log.INFO, OlympusName..' successfully loaded.') else - log.write('Olympus.EXPORT.LUA', log.INFO, 'Olympus.dll already initialized') + log.write('Olympus.EXPORT.LUA', log.INFO, 'hook.dll already initialized') end end \ No newline at end of file diff --git a/scripts/OlympusHook.lua b/scripts/OlympusHook.lua index da4ef406..07ef3ba9 100644 --- a/scripts/OlympusHook.lua +++ b/scripts/OlympusHook.lua @@ -2,7 +2,7 @@ Olympus = {} Olympus.OlympusDLL = nil Olympus.cppRESTDLL = nil Olympus.DLLsloaded = false -Olympus.OlympusModPath = os.getenv('OLYMPUS')..'\\bin\\' +Olympus.OlympusModPath = os.getenv('DCSOLYMPUS_PATH')..'\\bin\\' log.write('Olympus.HOOKS.LUA', log.INFO,'Executing OlympusHook.lua') @@ -13,9 +13,13 @@ function loadDLLs() log.write('Olympus.HOOKS.LUA', log.INFO, 'Loading cpprest_2_10.dll from ['..Olympus.OlympusModPath..']') pcall(require, 'cpprest_2_10') - log.write('Olympus.HOOKS.LUA', log.INFO, 'Loading Olympus.dll from ['..Olympus.OlympusModPath..']') + log.write('Olympus.HOOKS.LUA', log.INFO, 'Loading hook.dll from ['..Olympus.OlympusModPath..']') local status - status, Olympus.OlympusDLL = pcall(require, 'Olympus') + + pcall(require, 'logger') + pcall(require, 'luatools') + pcall(require, 'dcstools') + status, Olympus.OlympusDLL = pcall(require, 'hook') if not status then return false end diff --git a/src/Olympus.sln b/src/Olympus.sln index ce6e35c4..b706c6ea 100644 --- a/src/Olympus.sln +++ b/src/Olympus.sln @@ -3,12 +3,18 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.3.32929.385 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Olympus", "Olympus\Olympus.vcxproj", "{5F3FC91E-1FBC-4223-8011-9708DE913474}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "core\core.vcxproj", "{8A48D855-0E01-42BA-BD8C-07B0877C68DF}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "logger", "logger\logger.vcxproj", "{873ECABE-FCFE-4217-AC15-91959C3CF1C6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dcstools", "dcstools\dcstools.vcxproj", "{2B255368-39A0-431A-A6DE-CC739AC70DC1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "luatools", "luatools\luatools.vcxproj", "{DE139EC1-4F88-47D5-BE73-F41915FE14A3}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "utils", "utils\utils.vcxproj", "{B85009CE-4A5C-4A5A-B85D-001B3A2651B2}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "olympus", "olympus\olympus.vcxproj", "{5F3FC91E-1FBC-4223-8011-9708DE913474}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -17,14 +23,6 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5F3FC91E-1FBC-4223-8011-9708DE913474}.Debug|x64.ActiveCfg = Debug|x64 - {5F3FC91E-1FBC-4223-8011-9708DE913474}.Debug|x64.Build.0 = Debug|x64 - {5F3FC91E-1FBC-4223-8011-9708DE913474}.Debug|x86.ActiveCfg = Debug|x64 - {5F3FC91E-1FBC-4223-8011-9708DE913474}.Debug|x86.Build.0 = Debug|x64 - {5F3FC91E-1FBC-4223-8011-9708DE913474}.Release|x64.ActiveCfg = Release|x64 - {5F3FC91E-1FBC-4223-8011-9708DE913474}.Release|x64.Build.0 = Release|x64 - {5F3FC91E-1FBC-4223-8011-9708DE913474}.Release|x86.ActiveCfg = Release|x64 - {5F3FC91E-1FBC-4223-8011-9708DE913474}.Release|x86.Build.0 = Release|x64 {8A48D855-0E01-42BA-BD8C-07B0877C68DF}.Debug|x64.ActiveCfg = Debug|x64 {8A48D855-0E01-42BA-BD8C-07B0877C68DF}.Debug|x64.Build.0 = Debug|x64 {8A48D855-0E01-42BA-BD8C-07B0877C68DF}.Debug|x86.ActiveCfg = Debug|Win32 @@ -33,6 +31,30 @@ Global {8A48D855-0E01-42BA-BD8C-07B0877C68DF}.Release|x64.Build.0 = Release|x64 {8A48D855-0E01-42BA-BD8C-07B0877C68DF}.Release|x86.ActiveCfg = Release|Win32 {8A48D855-0E01-42BA-BD8C-07B0877C68DF}.Release|x86.Build.0 = Release|Win32 + {873ECABE-FCFE-4217-AC15-91959C3CF1C6}.Debug|x64.ActiveCfg = Debug|x64 + {873ECABE-FCFE-4217-AC15-91959C3CF1C6}.Debug|x64.Build.0 = Debug|x64 + {873ECABE-FCFE-4217-AC15-91959C3CF1C6}.Debug|x86.ActiveCfg = Debug|Win32 + {873ECABE-FCFE-4217-AC15-91959C3CF1C6}.Debug|x86.Build.0 = Debug|Win32 + {873ECABE-FCFE-4217-AC15-91959C3CF1C6}.Release|x64.ActiveCfg = Release|x64 + {873ECABE-FCFE-4217-AC15-91959C3CF1C6}.Release|x64.Build.0 = Release|x64 + {873ECABE-FCFE-4217-AC15-91959C3CF1C6}.Release|x86.ActiveCfg = Release|Win32 + {873ECABE-FCFE-4217-AC15-91959C3CF1C6}.Release|x86.Build.0 = Release|Win32 + {2B255368-39A0-431A-A6DE-CC739AC70DC1}.Debug|x64.ActiveCfg = Debug|x64 + {2B255368-39A0-431A-A6DE-CC739AC70DC1}.Debug|x64.Build.0 = Debug|x64 + {2B255368-39A0-431A-A6DE-CC739AC70DC1}.Debug|x86.ActiveCfg = Debug|Win32 + {2B255368-39A0-431A-A6DE-CC739AC70DC1}.Debug|x86.Build.0 = Debug|Win32 + {2B255368-39A0-431A-A6DE-CC739AC70DC1}.Release|x64.ActiveCfg = Release|x64 + {2B255368-39A0-431A-A6DE-CC739AC70DC1}.Release|x64.Build.0 = Release|x64 + {2B255368-39A0-431A-A6DE-CC739AC70DC1}.Release|x86.ActiveCfg = Release|Win32 + {2B255368-39A0-431A-A6DE-CC739AC70DC1}.Release|x86.Build.0 = Release|Win32 + {DE139EC1-4F88-47D5-BE73-F41915FE14A3}.Debug|x64.ActiveCfg = Debug|x64 + {DE139EC1-4F88-47D5-BE73-F41915FE14A3}.Debug|x64.Build.0 = Debug|x64 + {DE139EC1-4F88-47D5-BE73-F41915FE14A3}.Debug|x86.ActiveCfg = Debug|Win32 + {DE139EC1-4F88-47D5-BE73-F41915FE14A3}.Debug|x86.Build.0 = Debug|Win32 + {DE139EC1-4F88-47D5-BE73-F41915FE14A3}.Release|x64.ActiveCfg = Release|x64 + {DE139EC1-4F88-47D5-BE73-F41915FE14A3}.Release|x64.Build.0 = Release|x64 + {DE139EC1-4F88-47D5-BE73-F41915FE14A3}.Release|x86.ActiveCfg = Release|Win32 + {DE139EC1-4F88-47D5-BE73-F41915FE14A3}.Release|x86.Build.0 = Release|Win32 {B85009CE-4A5C-4A5A-B85D-001B3A2651B2}.Debug|x64.ActiveCfg = Debug|x64 {B85009CE-4A5C-4A5A-B85D-001B3A2651B2}.Debug|x64.Build.0 = Debug|x64 {B85009CE-4A5C-4A5A-B85D-001B3A2651B2}.Debug|x86.ActiveCfg = Debug|Win32 @@ -41,6 +63,14 @@ Global {B85009CE-4A5C-4A5A-B85D-001B3A2651B2}.Release|x64.Build.0 = Release|x64 {B85009CE-4A5C-4A5A-B85D-001B3A2651B2}.Release|x86.ActiveCfg = Release|Win32 {B85009CE-4A5C-4A5A-B85D-001B3A2651B2}.Release|x86.Build.0 = Release|Win32 + {5F3FC91E-1FBC-4223-8011-9708DE913474}.Debug|x64.ActiveCfg = Debug|x64 + {5F3FC91E-1FBC-4223-8011-9708DE913474}.Debug|x64.Build.0 = Debug|x64 + {5F3FC91E-1FBC-4223-8011-9708DE913474}.Debug|x86.ActiveCfg = Debug|x64 + {5F3FC91E-1FBC-4223-8011-9708DE913474}.Debug|x86.Build.0 = Debug|x64 + {5F3FC91E-1FBC-4223-8011-9708DE913474}.Release|x64.ActiveCfg = Release|x64 + {5F3FC91E-1FBC-4223-8011-9708DE913474}.Release|x64.Build.0 = Release|x64 + {5F3FC91E-1FBC-4223-8011-9708DE913474}.Release|x86.ActiveCfg = Release|x64 + {5F3FC91E-1FBC-4223-8011-9708DE913474}.Release|x86.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Olympus/Olympus.vcxproj b/src/Olympus/Olympus.vcxproj index a7bc736c..50e68d5c 100644 --- a/src/Olympus/Olympus.vcxproj +++ b/src/Olympus/Olympus.vcxproj @@ -11,12 +11,17 @@ - + + + {2b255368-39a0-431a-a6de-cc739ac70dc1} + + + {873ecabe-fcfe-4217-ac15-91959c3cf1c6} + {b85009ce-4a5c-4a5a-b85d-001b3a2651b2} - false @@ -25,7 +30,7 @@ {5f3fc91e-1fbc-4223-8011-9708de913474} Olympus 10.0 - Olympus + olympus @@ -93,9 +98,10 @@ _CRT_SECURE_NO_WARNINGS; NDEBUG;OLYMPUS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true NotUsing - pch.h - include; ..\..\third-party\lua\include; ..\utils\include; ..\shared\include - stdcpp17 + + + include;..\..\third-party\lua\include;..\utils\include;..\shared\include;..\dcstools\include;..\logger\include;..\luatools\include + stdcpp20 NoListing diff --git a/src/Olympus/Olympus.vcxproj.filters b/src/Olympus/Olympus.vcxproj.filters index e94b4033..fa32be80 100644 --- a/src/Olympus/Olympus.vcxproj.filters +++ b/src/Olympus/Olympus.vcxproj.filters @@ -9,7 +9,7 @@ - + Source Files diff --git a/src/Olympus/src/main.cpp b/src/Olympus/src/main.cpp deleted file mode 100644 index 491e923d..00000000 --- a/src/Olympus/src/main.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include "framework.h" - -#include "DCSUtils.h" -#include "Logger.h" -#include "Utils.h" - -#define DllExport __declspec( dllexport ) - -BOOL APIENTRY DllMain( HMODULE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved - ) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} - -/* Run-time linking to core dll allows for "hot swap"*/ -HINSTANCE hGetProcIDDLL = NULL; -typedef int(__stdcall* f_coreInit)(lua_State* L); -typedef int(__stdcall* f_coreDeinit)(lua_State* L); -typedef int(__stdcall* f_coreFrame)(lua_State* L); -typedef int(__stdcall* f_coreMissionData)(lua_State* L); -f_coreInit coreInit = nullptr; -f_coreDeinit coreDeinit = nullptr; -f_coreFrame coreFrame = nullptr; -f_coreMissionData coreMissionData = nullptr; - -static int onSimulationStart(lua_State* L) -{ - LOGGER->Log("onSimulationStart callback called successfully"); - - string modLocation; - string dllLocation; - char* buf = nullptr; - size_t sz = 0; - if (_dupenv_s(&buf, &sz, "OLYMPUS") == 0 && buf != nullptr) - { - modLocation = buf; - free(buf); - } - else - { - LOGGER->Log("OLYMPUS environment variable is missing"); - goto error; - } - dllLocation = modLocation + "\\bin\\core.dll"; - - LOGGER->Log("Loading core.dll"); - hGetProcIDDLL = LoadLibrary(Utils::to_wstring(dllLocation).c_str()); - - if (!hGetProcIDDLL) { - DCSUtils::LogError(L, "Error loading core DLL"); - goto error; - } - - LOGGER->Log("Core DLL loaded successfully"); - - coreInit = (f_coreInit)GetProcAddress(hGetProcIDDLL, "coreInit"); - if (!coreInit) - { - DCSUtils::LogError(L, "Error getting coreInit ProcAddress from DLL"); - goto error; - } - - coreDeinit = (f_coreDeinit)GetProcAddress(hGetProcIDDLL, "coreDeinit"); - if (!coreInit) - { - DCSUtils::LogError(L, "Error getting coreDeinit ProcAddress from DLL"); - goto error; - } - - coreFrame = (f_coreFrame)GetProcAddress(hGetProcIDDLL, "coreFrame"); - if (!coreFrame) - { - DCSUtils::LogError(L, "Error getting coreFrame ProcAddress from DLL"); - goto error; - } - - coreMissionData = (f_coreFrame)GetProcAddress(hGetProcIDDLL, "coreMissionData"); - if (!coreFrame) - { - DCSUtils::LogError(L, "Error getting coreMissionData ProcAddress from DLL"); - goto error; - } - - coreInit(L); - - DCSUtils::LogInfo(L, "Module loaded and started successfully."); - - return 0; - -error: - DCSUtils::LogError(L, "Error while loading module, see Olympus.log in temporary folder for additional details."); - return 0; -} - -static int onSimulationFrame(lua_State* L) -{ - if (coreFrame) - { - coreFrame(L); - } - return 0; -} - -static int onSimulationStop(lua_State* L) -{ - LOGGER->Log("onSimulationStop callback called successfully"); - if (hGetProcIDDLL) - { - LOGGER->Log("Trying to unload core DLL"); - if (coreDeinit) - { - coreDeinit(L); - } - - if (FreeLibrary(hGetProcIDDLL)) - { - LOGGER->Log("Core DLL unloaded successfully"); - } - else - { - DCSUtils::LogError(L, "Error unloading DLL"); - goto error; - } - - coreInit = nullptr; - coreDeinit = nullptr; - coreFrame = nullptr; - coreMissionData = nullptr; - } - - hGetProcIDDLL = NULL; - - return 0; - -error: - DCSUtils::LogError(L, "Error while unloading module, see Olympus.log in temporary folder for additional details."); - return 0; -} - - -static int setMissionData(lua_State* L) -{ - if (coreMissionData) - { - coreMissionData(L); - } - return 0; -} - -static const luaL_Reg Map[] = { - {"onSimulationStart", onSimulationStart}, - {"onSimulationFrame", onSimulationFrame}, - {"onSimulationStop", onSimulationStop}, - {"setMissionData", setMissionData }, - {NULL, NULL} -}; - -extern "C" DllExport int luaopen_Olympus(lua_State * L) -{ - luaL_register(L, "Olympus", Map); - return 1; -} diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index c7c3983c..896165cf 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -19,27 +19,34 @@ - - - - - - + + + + + + - - - - - - - + + + + + + + + + {2b255368-39a0-431a-a6de-cc739ac70dc1} + + + {873ecabe-fcfe-4217-ac15-91959c3cf1c6} + + + {de139ec1-4f88-47d5-be73-f41915fe14a3} + {b85009ce-4a5c-4a5a-b85d-001b3a2651b2} - true - false @@ -162,9 +169,10 @@ NDEBUG;CORE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true NotUsing - pch.h - include; ..\..\third-party\lua\include; ..\utils\include; ..\shared\include - stdcpp17 + + + include;..\..\third-party\lua\include;..\utils\include;..\shared\include;..\dcstools\include;..\logger\include;..\luatools\include + stdcpp20 Windows diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 8276d6d5..fe011f46 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -9,45 +9,45 @@ - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files diff --git a/src/core/include/Commands.h b/src/core/include/Commands.h index abfa1dbe..a7edde99 100644 --- a/src/core/include/Commands.h +++ b/src/core/include/Commands.h @@ -1,13 +1,14 @@ #pragma once -#include "LUAUtils.h" -#include "Utils.h" +#include "framework.h" +#include "luatools.h" +#include "utils.h" namespace CommandPriority { enum CommandPriorities { LOW, MEDIUM, HIGH }; }; namespace CommandType { - enum CommandTypes { NO_TYPE, MOVE }; + enum CommandTypes { NO_TYPE, MOVE, SMOKE, LASE, EXPLODE, SPAWN_AIR, SPAWN_GROUND }; }; /* Base command class */ @@ -27,11 +28,78 @@ protected: class MoveCommand : public Command { public: - MoveCommand(int ID, wstring unitName, Coords destination) : ID(ID), unitName(unitName), destination(destination) { priority = CommandPriority::LOW; type = CommandType::MOVE; }; + MoveCommand(int ID, wstring unitName, Coords destination, int unitCategory) : + ID(ID), + unitName(unitName), + destination(destination), + unitCategory(unitCategory) + { + priority = CommandPriority::LOW; + type = CommandType::MOVE; + }; virtual void execute(lua_State* L); private: const int ID; const wstring unitName; const Coords destination; + const int unitCategory; +}; + +/* Smoke command */ +class SmokeCommand : public Command +{ +public: + SmokeCommand(wstring color, Coords location) : + color(color), + location(location) + { + priority = CommandPriority::LOW; + type = CommandType::SMOKE; + }; + virtual void execute(lua_State* L); + +private: + const wstring color; + const Coords location; +}; + +/* Spawn ground unit command */ +class SpawnGroundCommand : public Command +{ +public: + SpawnGroundCommand(wstring coalition, wstring unitType, Coords location) : + coalition(coalition), + unitType(unitType), + location(location) + { + priority = CommandPriority::LOW; + type = CommandType::SPAWN_GROUND; + }; + virtual void execute(lua_State* L); + +private: + const wstring coalition; + const wstring unitType; + const Coords location; +}; + +/* Spawn air unit command */ +class SpawnAirCommand : public Command +{ +public: + SpawnAirCommand(wstring coalition, wstring unitType, Coords location) : + coalition(coalition), + unitType(unitType), + location(location) + { + priority = CommandPriority::LOW; + type = CommandType::SPAWN_AIR; + }; + virtual void execute(lua_State* L); + +private: + const wstring coalition; + const wstring unitType; + const Coords location; }; \ No newline at end of file diff --git a/src/core/include/LUAFunctions.h b/src/core/include/LUAFunctions.h deleted file mode 100644 index 6b86a8b7..00000000 --- a/src/core/include/LUAFunctions.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include "framework.h" - -namespace LUAFunctions -{ - void registerLuaFunctions(lua_State* L); -} \ No newline at end of file diff --git a/src/core/include/RESTServer.h b/src/core/include/RESTServer.h deleted file mode 100644 index 2b6e994a..00000000 --- a/src/core/include/RESTServer.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include "framework.h" -#include "LUAUtils.h" - -using namespace web::http; -using namespace web::http::experimental::listener; - -class UnitsFactory; -class Scheduler; - -class RESTServer -{ -public: - RESTServer(lua_State* L); - ~RESTServer(); - -private: - std::thread* serverThread; - - void handle_options(http_request request); - void handle_get(http_request request); - void handle_request(http_request request, function action); - void handle_put(http_request request); - - void task(); - - atomic runListener; -}; - diff --git a/src/core/include/Scheduler.h b/src/core/include/Scheduler.h index 649a2ac4..5d1b6580 100644 --- a/src/core/include/Scheduler.h +++ b/src/core/include/Scheduler.h @@ -1,7 +1,7 @@ #pragma once -#include "LUAUtils.h" #include "framework.h" -#include "Commands.h" +#include "luatools.h" +#include "commands.h" class Scheduler { diff --git a/src/core/include/Unit.h b/src/core/include/Unit.h index e34c3525..0e9dba63 100644 --- a/src/core/include/Unit.h +++ b/src/core/include/Unit.h @@ -1,8 +1,12 @@ #pragma once -#include "Utils.h" -#include "DCSUtils.h" -#include "LUAUtils.h" #include "framework.h" +#include "utils.h" +#include "dcstools.h" +#include "luatools.h" + +namespace UnitCategory { + enum UnitCategories { NO_CATEGORY, AIR, GROUND, NAVY }; // Do not edit, this codes are tied to values in DCS +}; class Unit { @@ -13,28 +17,31 @@ public: void update(json::value json); void setPath(list path); + void setAlive(bool newAlive) { alive = newAlive; } int getID() { return ID; } wstring getName() { return name; } wstring getUnitName() { return unitName; } wstring getGroupName() { return groupName; } - int getType() { return type; } - wstring getCountry() { return country; } + json::value getType() { return type; } // This functions returns the complete type of the object (Level1, Level2, Level3, Level4) + int getCountry() { return country; } int getCoalitionID() { return coalitionID; } double getLatitude() { return latitude; } double getLongitude() { return longitude; } double getAltitude() { return altitude; } double getHeading() { return heading; } + int getCategory(); json::value json(); protected: int ID; + bool alive = true; wstring name = L"undefined"; wstring unitName = L"undefined"; wstring groupName = L"undefined"; - int type = 0; - wstring country = L"undefined"; + json::value type = json::value::null(); + int country = 0; int coalitionID = 0; double latitude = 0; double longitude = 0; diff --git a/src/core/include/UnitsHandler.h b/src/core/include/UnitsHandler.h deleted file mode 100644 index 1ec06734..00000000 --- a/src/core/include/UnitsHandler.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "framework.h" -#include "DCSUtils.h" - -class Unit; - -class UnitsFactory -{ -public: - UnitsFactory(lua_State* L); - ~UnitsFactory(); - - Unit* getUnit(int ID); - void getMissionDB(lua_State* L); - void update(lua_State* L); - void updateAnswer(json::value& answer); - -private: - map units; - json::value missionDB; - -}; - diff --git a/src/core/src/Commands.cpp b/src/core/src/Commands.cpp index 18ade4cf..03eadba3 100644 --- a/src/core/src/Commands.cpp +++ b/src/core/src/Commands.cpp @@ -1,12 +1,12 @@ -#include "Commands.h" -#include "framework.h" -#include "Logger.h" +#include "commands.h" +#include "logger.h" +/* Move command */ void MoveCommand::execute(lua_State* L) { std::ostringstream command; - - command << "Olympus.move(\"" << Utils::to_string(unitName) << "\"," << destination.lat << "," << destination.lng << "," << 10 << ")"; + command.precision(10); + command << "Olympus.move(\"" << to_string(unitName) << "\", " << destination.lat << ", " << destination.lng << ", " << 10 << ", " << unitCategory << ")"; lua_getglobal(L, "net"); lua_getfield(L, -1, "dostring_in"); @@ -14,10 +14,73 @@ void MoveCommand::execute(lua_State* L) lua_pushstring(L, command.str().c_str()); if (lua_pcall(L, 2, 0, 0) != 0) { - LOGGER->Log("Error executing MoveCommand"); + log("Error executing MoveCommand"); } else { - LOGGER->Log("MoveCommand executed successfully"); + log("MoveCommand executed successfully"); + } +} + +/* Smoke command */ +void SmokeCommand::execute(lua_State* L) +{ + std::ostringstream command; + command.precision(10); + command << "Olympus.smoke(\"" << to_string(color) << "\", " << location.lat << ", " << location.lng << ")"; + + lua_getglobal(L, "net"); + lua_getfield(L, -1, "dostring_in"); + lua_pushstring(L, "server"); + lua_pushstring(L, command.str().c_str()); + if (lua_pcall(L, 2, 0, 0) != 0) + { + log("Error executing SmokeCommand"); + } + else + { + log("SmokeCommand executed successfully"); + } +} + +/* Spawn ground command */ +void SpawnGroundCommand::execute(lua_State* L) +{ + std::ostringstream command; + command.precision(10); + command << "Olympus.spawnGround(\"" << to_string(coalition) << "\", \"" << to_string(unitType) << "\", " << location.lat << ", " << location.lng << ")"; + + lua_getglobal(L, "net"); + lua_getfield(L, -1, "dostring_in"); + lua_pushstring(L, "server"); + lua_pushstring(L, command.str().c_str()); + if (lua_pcall(L, 2, 0, 0) != 0) + { + log("Error executing SpawnGroundCommand"); + } + else + { + log("SpawnGroundCommand executed successfully"); + } +} + +/* Spawn air command */ +void SpawnAirCommand::execute(lua_State* L) +{ + std::ostringstream command; + command.precision(10); + command << "Olympus.spawnAir(\"" << to_string(coalition) << "\", \"" << to_string(unitType) << "\", " << location.lat << ", " << location.lng << ")"; + + lua_getglobal(L, "net"); + lua_getfield(L, -1, "dostring_in"); + lua_pushstring(L, "server"); + lua_pushstring(L, command.str().c_str()); + if (lua_pcall(L, 2, 0, 0) != 0) + { + log("Error executing SpawnGroundCommand"); + } + else + { + log("SpawnAirCommand executed successfully"); } } \ No newline at end of file diff --git a/src/core/src/LUAFunctions.cpp b/src/core/src/LUAFunctions.cpp deleted file mode 100644 index a9d9e833..00000000 --- a/src/core/src/LUAFunctions.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "LUAFunctions.h" -#include "Logger.h" - -/* Executes the "OlympusCommand.lua" file to load in the "Server" Lua space all the Lua functions necessary to perform DCS commands (like moving units) */ -void LUAFunctions::registerLuaFunctions(lua_State* L) -{ - string modLocation; - - char* buf = nullptr; - size_t sz = 0; - if (_dupenv_s(&buf, &sz, "OLYMPUS") == 0 && buf != nullptr) - { - modLocation = buf; - free(buf); - } - else - { - LOGGER->Log("OLYMPUS environment variable is missing"); - return; - } - - { - ifstream f(modLocation + "\\Scripts\\mist_4_4_90.lua"); - string str; - LOGGER->Log("Reading MIST from " + modLocation + "\\Scripts\\mist_4_4_90.lua"); - if (f) { - ostringstream ss; - ss << f.rdbuf(); - str = ss.str(); - LOGGER->Log("MIST read succesfully"); - } - else - { - LOGGER->Log("Error reading MIST"); - return; - } - - lua_getglobal(L, "net"); - lua_getfield(L, -1, "dostring_in"); - lua_pushstring(L, "server"); - lua_pushstring(L, str.c_str()); - - if (lua_pcall(L, 2, 0, 0) != 0) - { - LOGGER->Log("Error registering MIST"); - } - else - { - LOGGER->Log("MIST registered successfully"); - } - } - - { - ifstream f(modLocation + "\\Scripts\\OlympusCommand.lua"); - string str; - LOGGER->Log("Reading OlympusCommand.lua from " + modLocation + "\\Scripts\\OlympusCommand.lua"); - if (f) { - ostringstream ss; - ss << f.rdbuf(); - str = ss.str(); - LOGGER->Log("OlympusCommand.lua read succesfully"); - } - else - { - LOGGER->Log("Error reading OlympusCommand.lua"); - return; - } - - lua_getglobal(L, "net"); - lua_getfield(L, -1, "dostring_in"); - lua_pushstring(L, "server"); - lua_pushstring(L, str.c_str()); - - if (lua_pcall(L, 2, 0, 0) != 0) - { - LOGGER->Log("Error registering OlympusCommand.lua"); - } - else - { - LOGGER->Log("OlympusCommand.lua registered successfully"); - } - } - -} \ No newline at end of file diff --git a/src/core/src/RESTServer.cpp b/src/core/src/RESTServer.cpp deleted file mode 100644 index 58824f36..00000000 --- a/src/core/src/RESTServer.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "RESTServer.h" -#include "Logger.h" -#include "defines.h" -#include "UnitsHandler.h" -#include "Scheduler.h" -#include "LUAUtils.h" - -extern UnitsFactory* unitsHandler; -extern Scheduler* scheduler; -extern json::value missionData; - -RESTServer::RESTServer(lua_State* L): - runListener(true) -{ - DCSUtils::LogInfo(L, "Starting RESTServer"); - serverThread = new thread(&RESTServer::task, this); -} - -RESTServer::~RESTServer() -{ - runListener = false; -} - -void RESTServer::handle_options(http_request request) -{ - http_response response(status_codes::OK); - response.headers().add(U("Allow"), U("GET, POST, PUT, OPTIONS")); - response.headers().add(U("Access-Control-Allow-Origin"), U("*")); - response.headers().add(U("Access-Control-Allow-Methods"), U("GET, POST, PUT, OPTIONS")); - response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type")); - - request.reply(response); -} - -void RESTServer::handle_get(http_request request) -{ - http_response response(status_codes::OK); - response.headers().add(U("Allow"), U("GET, POST, PUT, OPTIONS")); - response.headers().add(U("Access-Control-Allow-Origin"), U("*")); - response.headers().add(U("Access-Control-Allow-Methods"), U("GET, POST, PUT, OPTIONS")); - response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type")); - - auto answer = json::value::object(); - try - { - unitsHandler->updateAnswer(answer); - answer[L"missionData"] = missionData; - } - catch (http_exception const& e) - { - LOGGER->Log(e.what()); - } - response.set_body(answer); - - request.reply(response); -} - -void RESTServer::handle_request(http_request request, function action) -{ - auto answer = json::value::object(); - - request.extract_json().then([&answer, &action](pplx::task task) - { - try - { - auto const& jvalue = task.get(); - - if (!jvalue.is_null()) - { - action(jvalue, answer); - } - } - catch (http_exception const& e) - { - LOGGER->Log(e.what()); - } - }).wait(); - - http_response response(status_codes::OK); - response.headers().add(U("Allow"), U("GET, POST, PUT, OPTIONS")); - response.headers().add(U("Access-Control-Allow-Origin"), U("*")); - response.headers().add(U("Access-Control-Allow-Methods"), U("GET, POST, PUT, OPTIONS")); - response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type")); - response.set_body(answer); - request.reply(response); -} - -void RESTServer::handle_put(http_request request) -{ - handle_request( - request, - [](json::value const& jvalue, json::value& answer) - { - for (auto const& e : jvalue.as_object()) - { - auto key = e.first; - auto value = e.second; - scheduler->handleRequest(key, value); - } - }); -} - -void RESTServer::task() -{ - http_listener listener(REST_ADDRESS); - - std::function handle_options = std::bind(&RESTServer::handle_options, this, std::placeholders::_1); - std::function handle_get = std::bind(&RESTServer::handle_get, this, std::placeholders::_1); - std::function handle_put = std::bind(&RESTServer::handle_put, this, std::placeholders::_1); - - listener.support(methods::OPTIONS, handle_options); - listener.support(methods::GET, handle_get); - listener.support(methods::PUT, handle_put); - - try - { - listener.open() - .then([&listener]() {LOGGER->Log("RESTServer starting to listen"); }) - .wait(); - - while (runListener); - - listener.close(); - LOGGER->Log("RESTServer stopped listening"); - } - catch (exception const& e) - { - LOGGER->Log(e.what()); - } -} diff --git a/src/core/src/Scheduler.cpp b/src/core/src/Scheduler.cpp index 893cb5b8..0f848ffb 100644 --- a/src/core/src/Scheduler.cpp +++ b/src/core/src/Scheduler.cpp @@ -1,15 +1,15 @@ -#include "Scheduler.h" -#include "Logger.h" -#include "DCSUtils.h" -#include "UnitsHandler.h" -#include "Utils.h" -#include "Unit.h" +#include "scheduler.h" +#include "logger.h" +#include "dcstools.h" +#include "unitsFactory.h" +#include "utils.h" +#include "unit.h" -extern UnitsFactory* unitsHandler; +extern UnitsFactory* unitsFactory; Scheduler::Scheduler(lua_State* L) { - DCSUtils::LogInfo(L, "Units Factory constructor called successfully"); + LogInfo(L, "Units Factory constructor called successfully"); } Scheduler::~Scheduler() @@ -32,7 +32,7 @@ void Scheduler::execute(lua_State* L) { if (command->getPriority() == priority) { - LOGGER->Log("Executing command"); + log("Executing command"); switch (command->getType()) { case CommandType::MOVE: @@ -42,7 +42,30 @@ void Scheduler::execute(lua_State* L) commands.remove(command); break; } + case CommandType::SMOKE: + { + SmokeCommand* smokeCommand = dynamic_cast(command); + smokeCommand->execute(L); + commands.remove(command); + break; + } + case CommandType::SPAWN_GROUND: + { + SpawnGroundCommand* spawnCommand = dynamic_cast(command); + spawnCommand->execute(L); + commands.remove(command); + break; + } + case CommandType::SPAWN_AIR: + { + SpawnAirCommand* spawnCommand = dynamic_cast(command); + spawnCommand->execute(L); + commands.remove(command); + break; + } default: + log("Unknown command of type " + to_string(command->getType())); + commands.remove(command); break; } goto exit; @@ -62,12 +85,11 @@ void Scheduler::handleRequest(wstring key, json::value value) lock.lock(); Command* command = nullptr; - LOGGER->Log(L"Received request with ID: " + key); + log(L"Received request with ID: " + key); if (key.compare(L"setPath") == 0) { int ID = value[L"ID"].as_integer(); wstring unitName = value[L"unitName"].as_string(); - LOGGER->Log(unitName); json::value path = value[L"path"]; list newPath; for (auto const& e : path.as_object()) @@ -75,21 +97,54 @@ void Scheduler::handleRequest(wstring key, json::value value) wstring WP = e.first; double lat = path[WP][L"lat"].as_double(); double lng = path[WP][L"lng"].as_double(); - LOGGER->Log(unitName + L" set path destination " + WP + L" (" + to_wstring(lat) + L", " + to_wstring(lng) + L")"); + log(unitName + L" set path destination " + WP + L" (" + to_wstring(lat) + L", " + to_wstring(lng) + L")"); Coords dest; dest.lat = lat; dest.lng = lng; newPath.push_back(dest); - Unit* unit = unitsHandler->getUnit(ID); + Unit* unit = unitsFactory->getUnit(ID); if (unit != nullptr) { unit->setPath(newPath); - LOGGER->Log(unitName + L" new path set successfully"); + log(unitName + L" new path set successfully"); } else { - LOGGER->Log(unitName + L" not found, request will be discarded"); + log(unitName + L" not found, request will be discarded"); } } } + else if (key.compare(L"smoke") == 0) + { + wstring color = value[L"color"].as_string(); + double lat = value[L"location"][L"lat"].as_double(); + double lng = value[L"location"][L"lng"].as_double(); + log(L"Adding " + color + L" smoke at (" + to_wstring(lat) + L", " + to_wstring(lng) + L")"); + Coords loc; loc.lat = lat; loc.lng = lng; + command = dynamic_cast(new SmokeCommand(color, loc)); + } + else if (key.compare(L"spawnGround") == 0) + { + wstring coalition = value[L"coalition"].as_string(); + wstring type = value[L"type"].as_string(); + double lat = value[L"location"][L"lat"].as_double(); + double lng = value[L"location"][L"lng"].as_double(); + log(L"Spawning " + coalition + L" ground unit of type " + type + L" at (" + to_wstring(lat) + L", " + to_wstring(lng) + L")"); + Coords loc; loc.lat = lat; loc.lng = lng; + command = dynamic_cast(new SpawnGroundCommand(coalition, type, loc)); + } + else if (key.compare(L"spawnAir") == 0) + { + wstring coalition = value[L"coalition"].as_string(); + wstring type = value[L"type"].as_string(); + double lat = value[L"location"][L"lat"].as_double(); + double lng = value[L"location"][L"lng"].as_double(); + log(L"Spawning " + coalition + L" air unit of type " + type + L" at (" + to_wstring(lat) + L", " + to_wstring(lng) + L")"); + Coords loc; loc.lat = lat; loc.lng = lng; + command = dynamic_cast(new SpawnAirCommand(coalition, type, loc)); + } + else + { + log(L"Unknown command: " + key); + } if (command != nullptr) { diff --git a/src/core/src/Unit.cpp b/src/core/src/Unit.cpp index 749ae662..be385bf4 100644 --- a/src/core/src/Unit.cpp +++ b/src/core/src/Unit.cpp @@ -1,15 +1,15 @@ -#include "Unit.h" -#include "Utils.h" -#include "Logger.h" -#include "Commands.h" -#include "Scheduler.h" +#include "unit.h" +#include "utils.h" +#include "logger.h" +#include "commands.h" +#include "scheduler.h" extern Scheduler* scheduler; Unit::Unit(json::value json, int ID) : ID(ID) { - LOGGER->Log("Creating unit with ID: " + to_string(ID)); + log("Creating unit with ID: " + to_string(ID)); update(json); } @@ -18,20 +18,36 @@ Unit::~Unit() } +int Unit::getCategory() +{ + if (type.has_number_field(L"level1")) + { + return type[L"level1"].as_number().is_int32(); + } + else + { + return UnitCategory::NO_CATEGORY; + } +} + void Unit::update(json::value json) { name = json[L"Name"].as_string(); unitName = json[L"UnitName"].as_string(); groupName = json[L"GroupName"].as_string(); - //type = json[L"Type"].as_number().to_int32(); - //country = json[L"Country"].as_string(); - //coalitionID = json[L"CoalitionID"].as_number().to_int32(); + type = json[L"Type"]; + country = json[L"Country"].as_number().to_int32(); + coalitionID = json[L"CoalitionID"].as_number().to_int32(); latitude = json[L"LatLongAlt"][L"Lat"].as_number().to_double(); longitude = json[L"LatLongAlt"][L"Long"].as_number().to_double(); altitude = json[L"LatLongAlt"][L"Alt"].as_number().to_double(); heading = json[L"Heading"].as_number().to_double(); - AIloop(); + /* If the unit is alive, run the AI Loop that performs the requested commands and instructions (moving, attacking, etc) */ + if (alive) + { + AIloop(); + } } void Unit::setPath(list path) @@ -46,7 +62,7 @@ void Unit::AIloop() if (activeDestination != activePath.front()) { activeDestination = activePath.front(); - Command* command = dynamic_cast(new MoveCommand(ID, unitName, activeDestination)); + Command* command = dynamic_cast(new MoveCommand(ID, unitName, activeDestination, getCategory())); scheduler->appendCommand(command); } } @@ -56,12 +72,13 @@ json::value Unit::json() { auto json = json::value::object(); + json[L"alive"] = alive; json[L"name"] = json::value::string(name); json[L"unitName"] = json::value::string(unitName); json[L"groupName"] = json::value::string(groupName); - //json[L"type"] = type; - //json[L"country"] = json::value::string(country); - json[L"coalitionID"] = type; + json[L"type"] = type; + json[L"country"] = country; + json[L"coalitionID"] = coalitionID; json[L"latitude"] = latitude; json[L"longitude"] = longitude; json[L"altitude"] = altitude; diff --git a/src/core/src/UnitsHandler.cpp b/src/core/src/UnitsHandler.cpp deleted file mode 100644 index 5c421696..00000000 --- a/src/core/src/UnitsHandler.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "UnitsHandler.h" -#include "Logger.h" -#include "Unit.h" -#include "framework.h" -#include "Utils.h" - -UnitsFactory::UnitsFactory(lua_State* L) -{ - DCSUtils::LogInfo(L, "Units Factory constructor called successfully"); -} - -UnitsFactory::~UnitsFactory() -{ - -} - -Unit* UnitsFactory::getUnit(int ID) -{ - if (units.find(ID) == units.end()) { - return nullptr; - } - else { - return units[ID]; - } -} - -void UnitsFactory::update(lua_State* L) -{ - //lua_getglobal(L, "net"); - //lua_getfield(L, -1, "dostring_in"); - //lua_pushstring(L, "server"); - //lua_pushstring(L, "dostring_in(\"export\", \"Olympus.OlympusDLL.test()\")"); - //lua_pcall(L, 2, 0, 0); - - map unitJSONs = DCSUtils::getAllUnits(L); - - for (auto const& p : unitJSONs) - { - int ID = p.first; - if (units.count(ID) == 0) - { - units[ID] = new Unit(p.second, ID); - } - units[ID]->update(p.second); - } -} - -void UnitsFactory::updateAnswer(json::value& answer) -{ - // TODO THREAT SAFEY! - auto unitsJson = json::value::object(); - - for (auto const& p : units) - { - unitsJson[to_wstring(p.first)] = p.second->json(); - } - - answer[L"units"] = unitsJson; -} diff --git a/src/core/src/main.cpp b/src/core/src/main.cpp deleted file mode 100644 index 2059ffd1..00000000 --- a/src/core/src/main.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "framework.h" -#include "DCSUtils.h" -#include "Logger.h" -#include "defines.h" -#include "UnitsHandler.h" -#include "RESTServer.h" -#include "Scheduler.h" -#include "LUAFunctions.h" - -auto before = std::chrono::system_clock::now(); -UnitsFactory* unitsHandler = nullptr; -RESTServer* restserver = nullptr; -Scheduler* scheduler = nullptr; -json::value missionData; - -/* Standard DllMain entry point */ -BOOL APIENTRY DllMain( HMODULE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved - ) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} - -#define DllExport __declspec( dllexport ) - -/* Called when DCS simulation stops. All singleton instances are deleted. */ -extern "C" DllExport int coreDeinit(lua_State* L) -{ - LOGGER->Log("Olympus coreDeinit called successfully"); - - delete unitsHandler; - delete restserver; - delete scheduler; - - LOGGER->Log("All singletons objects destroyed successfully"); - - return(0); -} - -/* Called when DCS simulation starts. All singletons are instantiated, and the custom Lua functions are registered in the Lua state. */ -extern "C" DllExport int coreInit(lua_State* L) -{ - unitsHandler = new UnitsFactory(L); - restserver = new RESTServer(L); - scheduler = new Scheduler(L); - - LUAFunctions::registerLuaFunctions(L); - - return(0); -} - -extern "C" DllExport int coreFrame(lua_State* L) -{ - const std::chrono::duration duration = std::chrono::system_clock::now() - before; - - // TODO make intervals editable - if (duration.count() > UPDATE_TIME_INTERVAL) - { - if (unitsHandler != nullptr) - { - unitsHandler->update(L); - } - - // TODO allow for different intervals - if (scheduler != nullptr) - { - scheduler->execute(L); - } - before = std::chrono::system_clock::now(); - } - return(0); -} - -extern "C" DllExport int coreMissionData(lua_State * L) -{ - lua_getglobal(L, "Olympus"); - lua_getfield(L, -1, "missionData"); - missionData = LUAUtils::tableToJSON(L, -1); - - return(0); -} \ No newline at end of file diff --git a/src/utils/include/DCSUtils.h b/src/utils/include/DCSUtils.h deleted file mode 100644 index d28f59b3..00000000 --- a/src/utils/include/DCSUtils.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "framework.h" -#include "LUAUtils.h" - -namespace DCSUtils -{ - void LogInfo(lua_State* L, string message); - void LogWarning(lua_State* L, string message); - void LogError(lua_State* L, string message); - void Log(lua_State* L, string message, int level); - - map getAllUnits(lua_State* L); -} diff --git a/src/utils/include/LUAUtils.h b/src/utils/include/LUAUtils.h deleted file mode 100644 index b3041552..00000000 --- a/src/utils/include/LUAUtils.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "framework.h" - -namespace LUAUtils -{ - void stackUpdate(lua_State* L, int& stackDepth, int initialStack = 0); - void stackPop(lua_State* L, int popDepth = 1); - void stackClean(lua_State* L, int stackDepth); - json::value tableToJSON(lua_State* L, int index); -} - -#define STACK_UPDATE LUAUtils::stackUpdate(L, stackDepth, initialStack); -#define STACK_INIT int stackDepth = 0; int initialStack = 0; LUAUtils::stackUpdate(L, initialStack); -#define STACK_POP(X) LUAUtils::stackPop(L, X); STACK_UPDATE; -#define STACK_CLEAN STACK_UPDATE; LUAUtils::stackClean(L, stackDepth); diff --git a/src/utils/include/Logger.h b/src/utils/include/Logger.h deleted file mode 100644 index b0993aef..00000000 --- a/src/utils/include/Logger.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "framework.h" - -#define LOGGER Logger::GetLogger() -class Logger -{ -public: - void Log(const std::string& sMessage); - void Log(const std::wstring& sMessage); - void Log(const char* format, ...); - - Logger& operator<<(const string& sMessage); - - static Logger* GetLogger(); -private: - Logger(); - Logger(const Logger&) {}; // copy constructor is private - Logger& operator=(const Logger&) { return *this; }; // assignment operator is private - - static const std::string m_sFileName; - static Logger* m_pThis; - static ofstream m_Logfile; - - void Open(); - void Close(); -}; diff --git a/src/utils/include/Utils.h b/src/utils/include/Utils.h index 14351d00..ea7c34a1 100644 --- a/src/utils/include/Utils.h +++ b/src/utils/include/Utils.h @@ -7,15 +7,12 @@ struct Coords { double alt = 0; }; -bool operator== (const Coords& a, const Coords& b); -bool operator!= (const Coords& a, const Coords& b); -bool operator== (const Coords& a, const int& b); -bool operator!= (const Coords& a, const int& b); +// Get current date/time, format is YYYY-MM-DD.HH:mm:ss +const DllExport std::string CurrentDateTime(); +std::wstring DllExport to_wstring(const std::string& str); +std::string DllExport to_string(const std::wstring& wstr); -namespace Utils -{ - // Get current date/time, format is YYYY-MM-DD.HH:mm:ss - const std::string CurrentDateTime(); - std::wstring to_wstring(const std::string& str); - std::string to_string(const std::wstring& wstr); -} +bool DllExport operator== (const Coords& a, const Coords& b); +bool DllExport operator!= (const Coords& a, const Coords& b); +bool DllExport operator== (const Coords& a, const int& b); +bool DllExport operator!= (const Coords& a, const int& b); diff --git a/src/utils/src/DCSUtils.cpp b/src/utils/src/DCSUtils.cpp deleted file mode 100644 index cc01727b..00000000 --- a/src/utils/src/DCSUtils.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "DCSUtils.h" -#include "Logger.h" - -void DCSUtils::LogInfo(lua_State* L, string message) -{ - STACK_INIT; - - lua_getglobal(L, "log"); - lua_getfield(L, -1, "INFO"); - int infoLevel = (int) lua_tointeger(L, -1); - STACK_POP(1); - - STACK_CLEAN; - - DCSUtils::Log(L, message, infoLevel); -} - -void DCSUtils::LogWarning(lua_State* L, string message) -{ - STACK_INIT; - - lua_getglobal(L, "log"); - lua_getfield(L, -1, "WARNING"); - int warningLevel = (int)lua_tointeger(L, -1); - STACK_POP(1); - - STACK_CLEAN; - - DCSUtils::Log(L, message, warningLevel); -} - -void DCSUtils::LogError(lua_State* L, string message) -{ - STACK_INIT; - - lua_getglobal(L, "log"); - lua_getfield(L, -1, "ERROR"); - int errorLevel = (int)lua_tointeger(L, -1); - STACK_POP(1); - - STACK_CLEAN; - - DCSUtils::Log(L, message, errorLevel); -} - -void DCSUtils::Log(lua_State* L, string message, int level) -{ - STACK_INIT; - - lua_getglobal(L, "log"); - lua_getfield(L, -1, "write"); - lua_pushstring(L, "Olympus.dll"); - lua_pushnumber(L, level); - lua_pushstring(L, message.c_str()); - lua_pcall(L, 3, 0, 0); - - STACK_CLEAN; -} - -map DCSUtils::getAllUnits(lua_State* L) -{ - int res = 0; - map units; - - STACK_INIT; - - lua_getglobal(L, "Export"); - lua_getfield(L, -1, "LoGetWorldObjects"); - res = lua_pcall(L, 0, 1, 0); - - if (res != 0) - { - DCSUtils::LogError(L, "Error retrieving World Objects"); - goto exit; - } - - if (!lua_istable(L, 2)) - { - DCSUtils::LogError(L, "Error retrieving World Objects"); - goto exit; - } - else - { - lua_pushnil(L); - while (lua_next(L, 2) != 0) - { - int ID = lua_tonumber(L, -2); - units[ID] = LUAUtils::tableToJSON(L, -1); - STACK_POP(1) - } - } - -exit: - STACK_CLEAN; - return units; -} \ No newline at end of file diff --git a/src/utils/src/LUAUtils.cpp b/src/utils/src/LUAUtils.cpp deleted file mode 100644 index 0b47e077..00000000 --- a/src/utils/src/LUAUtils.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "LUAUtils.h" -#include "Logger.h" -#include "Utils.h" - -void LUAUtils::stackUpdate(lua_State* L, int& stackDepth, int initialStack) -{ - stackDepth = lua_gettop(L) - initialStack; -} - -void LUAUtils::stackPop(lua_State* L, int popDepth) -{ - lua_pop(L, popDepth); -} - -void LUAUtils::stackClean(lua_State* L, int stackDepth) -{ - lua_pop(L, stackDepth); -} - -json::value LUAUtils::tableToJSON(lua_State* L, int index) -{ - auto json = json::value::object(); - - if (lua_istable(L, index)) - { - STACK_INIT; - - lua_pushvalue(L, index); - lua_pushnil(L); - while (lua_next(L, -2)) - { - lua_pushvalue(L, -2); - const char* key = lua_tostring(L, -1); - if (lua_istable(L, -2)) - { - json[Utils::to_wstring(key)] = tableToJSON(L, -2); - } - else if (lua_isnumber(L, -2)) - { - json[Utils::to_wstring(key)] = json::value::number(lua_tonumber(L, -2)); - } - else if (lua_isboolean(L, -2)) - { - json[Utils::to_wstring(key)] = json::value::boolean(lua_toboolean(L, -2)); - } - else if (lua_isstring(L, -2)) // Keep last, only checks if it can be stringified - { - json[Utils::to_wstring(key)] = json::value::string(Utils::to_wstring(lua_tostring(L, -2))); - } - lua_pop(L, 2); - } - lua_pop(L, 1); - - STACK_CLEAN; - } - return json; -} diff --git a/src/utils/src/Logger.cpp b/src/utils/src/Logger.cpp deleted file mode 100644 index 4ec67b26..00000000 --- a/src/utils/src/Logger.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "Logger.h" -#include "Utils.h" -#include "defines.h" - -const string Logger::m_sFileName = LOG_NAME; -Logger* Logger::m_pThis = NULL; -ofstream Logger::m_Logfile; - -Logger::Logger() -{ - -} -Logger* Logger::GetLogger() -{ - if (m_pThis == NULL) { - m_pThis = new Logger(); - std::filesystem::path dirPath = std::filesystem::temp_directory_path(); - m_Logfile.open((dirPath.string() + m_sFileName).c_str(), ios::out | ios::app); - m_pThis->Log("**************************************************"); - } - return m_pThis; -} - -void Logger::Open() -{ - std::filesystem::path dirPath = std::filesystem::temp_directory_path(); - m_Logfile.open((dirPath.string() + m_sFileName).c_str(), ios::out | ios::app); -} - -void Logger::Close() -{ - m_Logfile.close(); -} - -void Logger::Log(const char* format, ...) -{ - Open(); - char* sMessage = NULL; - int nLength = 0; - va_list args; - va_start(args, format); - // Return the number of characters in the string referenced the list of arguments. - // _vscprintf doesn't count terminating '\0' (that's why +1) - nLength = _vscprintf(format, args) + 1; - sMessage = new char[nLength]; - vsprintf_s(sMessage, nLength, format, args); - //vsprintf(sMessage, format, args); - m_Logfile << Utils::CurrentDateTime() << ":\t"; - m_Logfile << sMessage << "\n"; - va_end(args); - Close(); - - delete[] sMessage; -} - -void Logger::Log(const string& sMessage) -{ - Open(); - m_Logfile << Utils::CurrentDateTime() << ":\t"; - m_Logfile << sMessage << "\n"; - Close(); -} - -void Logger::Log(const wstring& sMessage) -{ - Open(); - m_Logfile << Utils::CurrentDateTime() << ":\t"; - m_Logfile << Utils::to_string(sMessage) << "\n"; - Close(); -} - -Logger& Logger::operator<<(const string& sMessage) -{ - Open(); - m_Logfile << "\n" << Utils::CurrentDateTime() << ":\t"; - m_Logfile << sMessage << "\n"; - return *this; - Close(); -} \ No newline at end of file diff --git a/src/utils/src/Utils.cpp b/src/utils/src/Utils.cpp index 5092b9a9..53d1994f 100644 --- a/src/utils/src/Utils.cpp +++ b/src/utils/src/Utils.cpp @@ -1,8 +1,8 @@ #include "framework.h" -#include "Utils.h" +#include "utils.h" // Get current date/time, format is YYYY-MM-DD.HH:mm:ss -const std::string Utils::CurrentDateTime() +const std::string CurrentDateTime() { time_t now = time(NULL); struct tm tstruct; @@ -12,7 +12,7 @@ const std::string Utils::CurrentDateTime() return buf; } -std::wstring Utils::to_wstring(const std::string& str) +std::wstring to_wstring(const std::string& str) { int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); std::wstring wstrTo(size_needed, 0); @@ -20,7 +20,7 @@ std::wstring Utils::to_wstring(const std::string& str) return wstrTo; } -std::string Utils::to_string(const std::wstring& wstr) +std::string to_string(const std::wstring& wstr) { if (wstr.empty()) { diff --git a/src/utils/src/main.cpp b/src/utils/src/main.cpp deleted file mode 100644 index 0e28dd00..00000000 --- a/src/utils/src/main.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "framework.h" - -BOOL APIENTRY DllMain( HMODULE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved - ) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} - diff --git a/src/utils/utils.vcxproj b/src/utils/utils.vcxproj index 4f82a67c..55c1cdee 100644 --- a/src/utils/utils.vcxproj +++ b/src/utils/utils.vcxproj @@ -19,17 +19,10 @@ - - - - + - - - - - + 16.0 @@ -59,7 +52,7 @@ Unicode - StaticLibrary + DynamicLibrary false v143 true @@ -151,9 +144,10 @@ NDEBUG;UTILS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true NotUsing - pch.h + + include;..\..\third-party\lua\include;..\shared\include - stdcpp17 + stdcpp20 Windows diff --git a/src/utils/utils.vcxproj.filters b/src/utils/utils.vcxproj.filters index ede7f53f..ae6b4d5a 100644 --- a/src/utils/utils.vcxproj.filters +++ b/src/utils/utils.vcxproj.filters @@ -11,33 +11,12 @@ - - Header Files - - - Header Files - - - Header Files - - + Header Files - - Source Files - - - Source Files - - - Source Files - - - Source Files - - + Source Files diff --git a/www/css/SelectionWheel.css b/www/css/SelectionWheel.css index 07aaa31b..e2c5fc34 100644 --- a/www/css/SelectionWheel.css +++ b/www/css/SelectionWheel.css @@ -1,17 +1,23 @@ :root { - --blue: #2d3e50AA; - --white: #FFFFFFAA; + --blue: #2196F3aa; + --red: #f32121aa; + --normal: #2196F3aa; + --highlight: #FFFFFFAA; --start_angle: 0deg; --gradient_start: 0deg; --gradient_stop: 0deg; --animation_duration: 0.1s; + --color_transition: 0.4s; } #selection-wheel { + width: 100%; border-radius: 50%; - background: conic-gradient(from var(--start_angle), var(--white) 0deg, var(--white) calc(var(--gradient_start) - 1deg), var(--blue) var(--gradient_start), var(--blue) var(--gradient_stop), var(--white) calc(var(--gradient_stop) + 1deg), var(--white) 360deg); + /*background: conic-gradient(from var(--start_angle), var(--highlight) 0deg, var(--highlight) calc(var(--gradient_start) - 1deg), var(--normal) var(--gradient_start), var(--normal) var(--gradient_stop), var(--highlight) calc(var(--gradient_stop) + 1deg), var(--highlight) 360deg);*/ + background: var(--normal); -webkit-mask: radial-gradient(transparent 30%,#000 31%); mask: radial-gradient(transparent 30%,#000 31%); + transition: background-color var(--color_transition); } #selection-wheel:before { content: ""; @@ -26,4 +32,108 @@ width: 0px; height: 0px; transition: width var(--animation_duration), height var(--animation_duration), left var(--animation_duration), top var(--animation_duration); + overflow: visible; + display: flex; + align-items: center; + justify-content: center; } + +.selection-wheel-button { + position: fixed; + z-index: 1000; + width: 50px; + height: 50px; + opacity: 0; + transition: opacity var(--animation_duration), left var(--animation_duration), top var(--animation_duration); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; +} + +.selection-wheel-image { + width: 45px; + height: 45px; + /*filter: invert(100%);*/ + transition: width var(--animation_duration), height var(--animation_duration); + filter: drop-shadow(1px 1px 0px var(--highlight)) drop-shadow(1px -1px 0px var(--highlight)) drop-shadow(-1px 1px 0px var(--highlight)) drop-shadow(-1px -1px 0px var(--highlight)); + +} + +.selection-wheel-button:hover { + filter: drop-shadow(1px 1px 2px var(--highlight)) drop-shadow(1px -1px 2px var(--highlight)) drop-shadow(-1px 1px 2px var(--highlight)) drop-shadow(-1px -1px 2px var(--highlight)); +} + +.selection-wheel-button:hover .selection-wheel-image { + width: 50px; + height: 50px; + /*filter: invert(21%) sepia(23%) saturate(775%) hue-rotate(170deg) brightness(92%) contrast(90%);*/ +} + +/* The switch - the box around the slider */ +.switch { + opacity: 0; + position: absolute; + top: 50%; + display: inline-block; + width: 60px; + height: 34px; + margin-top: -17px; + transition: opacity var(--animation_duration); +} + +/* Hide default HTML checkbox */ +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +/* The slider */ +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--blue); + -webkit-transition: var(--color_transition); + transition: var(--color_transition); +} + +.slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: var(--color_transition); + transition: var(--color_transition); +} + +input:checked + .slider { + background-color: var(--red); +} + +input:focus + .slider { + box-shadow: 0 0 1px var(--red); +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +/* Rounded sliders */ +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} \ No newline at end of file diff --git a/www/css/UnitMarker.css b/www/css/UnitMarker.css index a312cd36..b437094e 100644 --- a/www/css/UnitMarker.css +++ b/www/css/UnitMarker.css @@ -26,6 +26,7 @@ position: absolute; animation: spin 4s linear infinite; opacity: 0; + filter: drop-shadow(0px 0px 1px #00FF00AA); } @keyframes spin { @@ -35,15 +36,23 @@ } .unitmarker-icon-img-blue { - filter: invert(37%) sepia(21%) saturate(7402%) hue-rotate(193deg) brightness(103%) contrast(104%) drop-shadow(1px 1px 0 black) drop-shadow(-1px -1px 0 black); + filter: invert(37%) sepia(21%) saturate(7402%) hue-rotate(193deg) brightness(103%) contrast(104%) drop-shadow(1px 1px 0 #000000AA) drop-shadow(1px -1px 0 #000000AA) drop-shadow(-1px 1px 0 #000000AA) drop-shadow(-1px -1px 0 #000000AA); +} + +.unitmarker-icon-img-red { + filter: invert(21%) sepia(96%) saturate(4897%) hue-rotate(353deg) brightness(108%) contrast(90%) drop-shadow(1px 1px 0 #000000AA) drop-shadow(1px -1px 0 #000000AA) drop-shadow(-1px 1px 0 #000000AA) drop-shadow(-1px -1px 0 #000000AA); } .unitmarker-icon-img-hovered { - filter: invert(100%) sepia(3%) saturate(0%) hue-rotate(125deg) brightness(103%) contrast(103%) drop-shadow(1px 1px 0 black) drop-shadow(-1px -1px 0 black); + filter: invert(100%) sepia(3%) saturate(0%) hue-rotate(125deg) brightness(103%) contrast(103%) drop-shadow(1px 1px 3px black) drop-shadow(-1px -1px 3px black); } .unitmarker-icon-img-selected { - filter: invert(100%) sepia(3%) saturate(0%) hue-rotate(125deg) brightness(103%) contrast(103%) drop-shadow(1px 1px 0 black) drop-shadow(-1px -1px 0 black); + filter: invert(100%) sepia(3%) saturate(0%) hue-rotate(125deg) brightness(103%) contrast(103%) drop-shadow(1px 1px 3px black) drop-shadow(-1px -1px 3px black); +} + +.unitmarker-container-table-dead { + filter: brightness(50%); } .unitmarker-name-div { @@ -56,4 +65,8 @@ white-space: nowrap; -webkit-text-fill-color: white; -webkit-text-stroke: 1px; +} + +.unitmarker-container-table-dead .unitmarker-name-div{ + opacity: 0; } \ No newline at end of file diff --git a/www/index.html b/www/index.html index d6685411..bc6d94e7 100644 --- a/www/index.html +++ b/www/index.html @@ -8,6 +8,7 @@ + @@ -16,6 +17,7 @@ integrity="sha256-o9N1jGDZrf5tS+Ft4gbIK7mYMipq9lqpVJ91xHSyKhg=" crossorigin=""> + @@ -23,6 +25,7 @@ + diff --git a/www/js/Map.js b/www/js/Map.js index 86bc0fc0..9d3cc288 100644 --- a/www/js/Map.js +++ b/www/js/Map.js @@ -14,13 +14,36 @@ class Map setInterval(() => this.update(), 250); // Register event handles - this._map.on('contextmenu', (e) => this.onContextMenu(e)); - this._map.on('click', (e) => this.onClick(e)); - this._map.on('dblclick', (e) => this.onDoubleClick(e)); - + this._map.on('contextmenu', (e) => this._onContextMenu(e)); + this._map.on('click', (e) => this._onClick(e)); + this._map.on('dblclick', (e) => this._onDoubleClick(e)); + this._map.on('movestart', () => {this.removeSelectionWheel(); this.removeSelectionScroll();}); + this._map.on('zoomstart', () => {this.removeSelectionWheel(); this.removeSelectionScroll();}); + this._map.on('selectionend', (e) => unitsFactory.selectFromBounds(e.selectionBounds)); + this.setState("IDLE"); this._selectionWheel = undefined; + this._selectionScroll = undefined; + + /* Edit the default zoom box effect to use it as a multiple units selection */ + L.Map.BoxZoom.prototype._onMouseUp = function (e) { + if ((e.which !== 1) && (e.button !== 1)) { return; } + + this._finish(); + + if (!this._moved) { return; } + // Postpone to next JS tick so internal click event handling + // still see it as "moved". + setTimeout(L.bind(this._resetState, this), 0); + var bounds = new L.LatLngBounds( + this._map.containerPointToLatLng(this._startPoint), + this._map.containerPointToLatLng(this._point)); + + this._map.fire('selectionend', {selectionBounds: bounds}); + } + + this._activeCoalition = "blue"; } getMap() @@ -28,7 +51,7 @@ class Map return this._map; } - // GET new data from the server + /* GET new data from the server */ update() { // Request the updated unit data from the server @@ -49,7 +72,7 @@ class Map xmlHttp.send( null ); } - // State machine + /* State machine */ setState(newState) { this._state = newState; @@ -63,16 +86,30 @@ class Map } } - // Event handlers - onContextMenu(e) + /* Set the active coalition (for persistency) */ + setActiveCoalition(coalition) { - unitsFactory.deselectAllUnits(); - this._removeSelectionWheel(); + this._activeCoalition = coalition; } - onClick(e) + getActiveCoalition() { - this._removeSelectionWheel(); + return this._activeCoalition; + } + + /* Event handlers */ + // Right click + _onContextMenu(e) + { + unitsFactory.deselectAllUnits(); + this.removeSelectionWheel(); + this.removeSelectionScroll(); + } + + _onClick(e) + { + this.removeSelectionWheel(); + this.removeSelectionScroll(); if (this._state === "IDLE") { @@ -87,12 +124,19 @@ class Map } } - onDoubleClick(e) + _onDoubleClick(e) { - this._selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, ['1', '2', '3']); + var options = [ + {'tooltip': 'Air unit', 'src': 'spawnAir.png', 'callback': () => this._unitSelectAir(e)}, + {'tooltip': 'Ground unit', 'src': 'spawnGround.png', 'callback': () => this._groundSpawnMenu(e)}, + {'tooltip': 'Smoke', 'src': 'spawnSmoke.png', 'callback': () => this._smokeSpawnMenu(e)}, + {'tooltip': 'Explosion', 'src': 'spawnExplosion.png', 'callback': () => this._explosionSpawnMenu(e)} + ] + this._selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, options); } - _removeSelectionWheel() + /* Selection wheel and selection scroll functions */ + removeSelectionWheel() { if (this._selectionWheel !== undefined) { @@ -100,4 +144,95 @@ class Map this._selectionWheel = undefined; } } + + removeSelectionScroll() + { + if (this._selectionScroll !== undefined) + { + this._selectionScroll.remove(); + this._selectionScroll = undefined; + } + } + + /* Spawn a new air unit selection wheel (TODO, divide units by type, like bomber, fighter, tanker etc)*/ + _airSpawnMenu(e) + { + this.removeSelectionWheel(); + this.removeSelectionScroll(); + var options = [ + + ] + this._selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, options); + } + + /* Spawn a new ground unit selection wheel */ + _groundSpawnMenu(e) + { + this.removeSelectionWheel(); + this.removeSelectionScroll(); + var options = [ + {'coalition': true, 'tooltip': 'Howitzer', 'src': 'spawnHowitzer.png', 'callback': () => this._unitSelectGround(e, "Howitzers")}, + {'coalition': true, 'tooltip': 'SAM', 'src': 'spawnSAM.png', 'callback': () => this._unitSelectGround(e, "SAM")}, + {'coalition': true, 'tooltip': 'IFV', 'src': 'spawnIFV.png', 'callback': () => this._unitSelectGround(e, "IFV")}, + {'coalition': true, 'tooltip': 'Tank', 'src': 'spawnTank.png', 'callback': () => this._unitSelectGround(e, "Tanks")}, + {'coalition': true, 'tooltip': 'MLRS', 'src': 'spawnMLRS.png', 'callback': () => this._unitSelectGround(e, "MLRS")}, + {'coalition': true, 'tooltip': 'Radar', 'src': 'spawnRadar.png', 'callback': () => this._unitSelectGround(e, "Radar")}, + {'coalition': true, 'tooltip': 'Unarmed', 'src': 'spawnUnarmed.png', 'callback': () => this._unitSelectGround(e, "Unarmed")} + ] + this._selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, options); + } + + /* Spawn smoke selection wheel */ + _smokeSpawnMenu(e) + { + this.removeSelectionWheel(); + this.removeSelectionScroll(); + var options = [ + {'tooltip': 'Red smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.removeSelectionWheel(); this.removeSelectionScroll(); unitsFactory.spawnSmoke('red', e.latlng)}, 'tint': 'red'}, + {'tooltip': 'White smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.removeSelectionWheel(); this.removeSelectionScroll(); unitsFactory.spawnSmoke('white', e.latlng)}, 'tint': 'white'}, + {'tooltip': 'Blue smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.removeSelectionWheel(); this.removeSelectionScroll(); unitsFactory.spawnSmoke('blue', e.latlng)}, 'tint': 'blue'}, + {'tooltip': 'Green smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.removeSelectionWheel(); this.removeSelectionScroll(); unitsFactory.spawnSmoke('green', e.latlng)}, 'tint': 'green'}, + {'tooltip': 'Orange smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.removeSelectionWheel(); this.removeSelectionScroll(); unitsFactory.spawnSmoke('orange', e.latlng)}, 'tint': 'orange'}, + ] + this._selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, options); + } + + /* Spawn an explosion selection wheel (TODO) */ + _explosionSpawnMenu(e) + { + this.removeSelectionWheel(); + this.removeSelectionScroll(); + var options = [ + + ] + this._selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, options); + } + + /* Show unit selection for ground units */ + _unitSelectAir(e) + { + this.removeSelectionWheel(); + this.removeSelectionScroll(); + var options = unitTypes.air; + options.sort(); + this._selectionScroll = new SelectionScroll(e.originalEvent.x, e.originalEvent.y, options, (type) => { + this.removeSelectionWheel(); + this.removeSelectionScroll(); + unitsFactory.spawnAirUnit(type, e.latlng, this._activeCoalition) + }); + } + + /* Show unit selection for ground units */ + _unitSelectGround(e, group) + { + this.removeSelectionWheel(); + this.removeSelectionScroll(); + var options = unitTypes.vehicles[group]; + options.sort(); + this._selectionScroll = new SelectionScroll(e.originalEvent.x, e.originalEvent.y, options, (type) => { + this.removeSelectionWheel(); + this.removeSelectionScroll(); + unitsFactory.spawnGroundUnit(type, e.latlng, this._activeCoalition) + }); + } } \ No newline at end of file diff --git a/www/js/SelectionWheel.js b/www/js/SelectionWheel.js index cec004d2..0fba7d52 100644 --- a/www/js/SelectionWheel.js +++ b/www/js/SelectionWheel.js @@ -1,6 +1,7 @@ class SelectionWheel { - constructor(x, y, options) { + constructor(x, y, options) + { if (options.length > 1) { this._x = x; @@ -8,20 +9,53 @@ class SelectionWheel this._options = options; this._angularSize = 360 / this._options.length; + /* Create the container of the wheel */ this._container = document.createElement("div"); this._container.id = 'selection-wheel-container'; this._container.style.left = this._x + "px"; this._container.style.top = this._y + "px"; document.getElementById("map-container").appendChild(this._container); + /* Create the wheel itself */ this._wheel = document.createElement("div"); this._wheel.id = 'selection-wheel'; this._container.appendChild(this._wheel); - this._wheel.addEventListener('mousemove', (e) => this._onMouseMove(e)); - this._wheel.style.setProperty('--gradient_start', this._angularSize / 2 + 'deg'); - this._wheel.style.setProperty('--gradient_stop', (360 - this._angularSize / 2) + 'deg'); + /* Create the buttons */ + this._buttons = []; + for (let id in this._options) + { + var button = document.createElement("div"); + button.classList.add("selection-wheel-button"); + button.style.left = this._x - 25 + "px"; + button.style.top = this._y - 25 + "px"; + button.addEventListener('click', (e) => this._options[id].callback(e)); + this._container.appendChild(button); + this._buttons.push(button); + var image = document.createElement("img"); + image.classList.add("selection-wheel-image"); + image.src = `img/buttons/${this._options[id].src}` + if ('tint' in this._options[id]) + { + button.style.setProperty('background-color', this._options[id].tint); + image.style.opacity = 0; + } + button.appendChild(image); + } + + /* Show the coalition switch if requested */ + this._switchLabel = document.createElement("label"); + this._switchLabel.classList.add("switch"); + this._switchLabel.innerHTML = ` ` + this._container.appendChild(this._switchLabel); + document.getElementById("coalition-switch").addEventListener('change', (e) => this._onSwitch(e)) + + if (map.getActiveCoalition() == "red") + { + document.getElementById("coalition-switch").checked = true; + } + window.setTimeout(() => this._show(), 100); } } @@ -34,17 +68,32 @@ class SelectionWheel _show() { - this._container.style.width = "200px"; - this._container.style.height = "200px"; - this._container.style.left = this._x - 100 + "px"; - this._container.style.top = this._y - 100 + "px"; + this._container.style.width = 220 + "px"; + this._container.style.height = 220 + "px"; + this._container.style.left = this._x - 110 + "px"; + this._container.style.top = this._y - 110 + "px"; + + var r = 80; + for (let id in this._buttons) + { + var angle = parseInt(id) * this._angularSize; + this._buttons[id].style.opacity = 1; + this._buttons[id].style.left = this._x + r * Math.sin(deg2rad(angle)) - 25 + "px"; + this._buttons[id].style.top = this._y - r * Math.cos(deg2rad(angle)) - 25 + "px"; + } + + this._switchLabel.style.opacity = 1; } - _onMouseMove(e) + _onSwitch(e) { - var angle = -rad2deg(Math.atan2(e.x - this._x, e.y - this._y)) + 180 + this._angularSize / 2; - var index = Math.floor(angle / this._angularSize) - this._wheel.style.transform = 'rotate('+ (index * this._angularSize) + 'deg)'; + if (e.currentTarget.checked) { + document.documentElement.style.setProperty('--normal', getComputedStyle(this._container).getPropertyValue("--red")); + map.setActiveCoalition("red"); + } else { + document.documentElement.style.setProperty('--normal', getComputedStyle(this._container).getPropertyValue("--blue")); + map.setActiveCoalition("blue"); + } } } diff --git a/www/js/Unit.js b/www/js/Unit.js index 60fa0428..cadde854 100644 --- a/www/js/Unit.js +++ b/www/js/Unit.js @@ -17,15 +17,12 @@ class Unit this.coalitionID = undefined; this.country = undefined; this.activePath = undefined; + this.alive = undefined; + this.type = undefined; this._pathMarkers = []; - this._pathPolyline = new L.Polyline([], { - color: '#2d3e50', - weight: 3, - opacity: 0.5, - smoothFactor: 1 - }); + this._pathPolyline = new L.Polyline([], {color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1}); this._pathPolyline.addTo(map.getMap()); } @@ -38,7 +35,9 @@ class Unit this.longitude = response["longitude"]; this.altitude = response["altitude"]; this.heading = response["heading"]; - this.coalitionID = response["coalitionID"] + this.coalitionID = response["coalitionID"]; + this.alive = response["alive"]; + this.type = response["type"]; /* Only present if an active path is available */ if ("activePath" in response) @@ -46,6 +45,11 @@ class Unit this.drawMarker(); + if (!this.alive) + { + this.setSelected(false); + } + if (this._selected && this.activePath != undefined) { this.drawPath(); @@ -58,9 +62,13 @@ class Unit setSelected(selected) { - this._selected = selected; - this.marker.setSelected(selected); - unitsFactory.onUnitSelection(); + /* Only alive units can be selected */ + if (this.alive || !selected) + { + this._selected = selected; + this.marker.setSelected(selected); + unitsFactory.onUnitSelection(); + } } getSelected() @@ -72,12 +80,10 @@ class Unit { var xhr = new XMLHttpRequest(); xhr.open("PUT", RESTaddress); - xhr.setRequestHeader("Content-Type", "application/json"); - xhr.onreadystatechange = () => { if (xhr.readyState === 4) { - console.log(this.unitName + " add destination to (" + latlng.lat + ", " + latlng.lng + ")") + console.log(this.unitName + " add destination to " + ConvertDDToDMS(latlng.lat, false) + " " + ConvertDDToDMS(latlng.lng, true)) } }; @@ -105,7 +111,6 @@ class Unit onClick(e) { - // TODO if ctrl is pressed, don't deselect the other units if (!e.originalEvent.ctrlKey) { unitsFactory.deselectAllUnits(); @@ -117,10 +122,12 @@ class Unit { var zIndex = this.marker.getZIndex(); var newLatLng = new L.LatLng(this.latitude, this.longitude); + this.marker.setCoalitionID(this.coalitionID); this.marker.setLatLng(newLatLng); this.marker.setUnitName(this.unitName); this.marker.setAngle(this.heading); this.marker.setZIndex(zIndex); + this.marker.setAlive(this.alive); } drawPath() diff --git a/www/js/UnitMarker.js b/www/js/UnitMarker.js index 2cd7507c..c4156eaa 100644 --- a/www/js/UnitMarker.js +++ b/www/js/UnitMarker.js @@ -14,9 +14,6 @@ L.Marker.UnitMarker = L.Marker.extend( { if (unitName in unitIcons) img.src = unitIcons[unitName]; else img.src = "img/units/undefined.png"; - - // Set image class, TODO: make fuction to change coalition - img.classList.add("unitmarker-icon-img-blue"); } // Set the unit name in the marker @@ -26,7 +23,39 @@ L.Marker.UnitMarker = L.Marker.extend( this.unitName = unitName; }, - // Rotates the marker to show heading + setCoalitionID: function(coalitionID) + { + var img = this._icon.querySelector("#icon-img"); + if (img!== undefined) + { + if (coalitionID == 2) + { + img.classList.add("unitmarker-icon-img-blue"); + } + else + { + img.classList.add("unitmarker-icon-img-red"); + } + } + }, + + setAlive: function(alive) + { + var table = this._icon.querySelector("#container-table"); + if (table!== undefined) + { + if (alive) + { + table.classList.remove("unitmarker-container-table-dead"); + } + else + { + table.classList.add("unitmarker-container-table-dead"); + } + } + this.alive = alive; + }, + setAngle: function(angle) { if (this._angle !== angle){ @@ -90,7 +119,10 @@ L.Marker.UnitMarker.addInitHook(function() this.setIcon(icon); this.on('mouseover',function(e) { - e.target.setHovered(true); + if (e.target.alive) + { + e.target.setHovered(true); + } }); this.on('mouseout',function(e) { @@ -103,7 +135,7 @@ var unitIcons = "A-4E-C": "img/units/a-4.png" } -var iconHtml = ` +var iconHtml = `
diff --git a/www/js/UnitsFactory.js b/www/js/UnitsFactory.js index e5965950..9d7f0e30 100644 --- a/www/js/UnitsFactory.js +++ b/www/js/UnitsFactory.js @@ -63,7 +63,7 @@ class UnitsFactory } } - clearDestinations(latlng) + clearDestinations() { for (let ID in this._units) { @@ -73,4 +73,68 @@ class UnitsFactory } } } + + selectFromBounds(bounds) + { + this.deselectAllUnits(); + for (let ID in this._units) + { + var latlng = new L.LatLng(this._units[ID].latitude, this._units[ID].longitude); + if (bounds.contains(latlng)) + { + this._units[ID].setSelected(true); + } + } + } + + spawnSmoke(color, latlng) + { + var xhr = new XMLHttpRequest(); + xhr.open("PUT", RESTaddress); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + console.log("Added " + color + " smoke at " + ConvertDDToDMS(latlng.lat, false) + " " + ConvertDDToDMS(latlng.lng, true)); + } + }; + + var command = {"color": color, "location": latlng}; + var data = {"smoke": command} + + xhr.send(JSON.stringify(data)); + } + + spawnGroundUnit(type, coalition, latlng) + { + var xhr = new XMLHttpRequest(); + xhr.open("PUT", RESTaddress); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + console.log("Added " + coalition + " " + type + " at " + ConvertDDToDMS(latlng.lat, false) + " " + ConvertDDToDMS(latlng.lng, true)); + } + }; + + var command = {"type": type, "location": latlng, "coalition": coalition}; + var data = {"spawnGround": command} + + xhr.send(JSON.stringify(data)); + } + + spawnAirUnit(type, latlng, coalition) + { + var xhr = new XMLHttpRequest(); + xhr.open("PUT", RESTaddress); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + console.log("Added " + coalition + " " + type + " at " + ConvertDDToDMS(latlng.lat, false) + " " + ConvertDDToDMS(latlng.lng, true)); + } + }; + + var command = {"type": type, "location": latlng, "coalition": coalition}; + var data = {"spawnAir": command} + + xhr.send(JSON.stringify(data)); + } } \ No newline at end of file