Started migration to TypeScript
@ -1,39 +1,81 @@
|
||||
Olympus = {}
|
||||
Olympus.unitCounter = 1
|
||||
|
||||
Olympus.payloadRegistry = {}
|
||||
|
||||
function Olympus.notify(message, displayFor)
|
||||
trigger.action.outText(message, displayFor)
|
||||
end
|
||||
|
||||
function Olympus.move(unitName, lat, lng, altitude, speed, category, targetName)
|
||||
Olympus.notify("Olympus.move " .. unitName .. " (" .. lat .. ", " .. lng ..") " .. altitude .. "m " .. speed .. "m/s " .. category .. " target " .. targetName, 10)
|
||||
local unit = Unit.getByName(unitName)
|
||||
if unit ~= nil then
|
||||
-- Gets a unit class reference from a given ObjectID (the ID used by Olympus for unit referencing)
|
||||
function Olympus.getUnitByID(ID)
|
||||
for name, table in pairs(mist.DBs.unitsByName) do
|
||||
local unit = Unit.getByName(name)
|
||||
if unit and unit:getObjectID() == ID then
|
||||
return unit
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Olympus.getCountryIDByCoalition(coalition)
|
||||
local countryID = 0
|
||||
if coalition == 'red' then
|
||||
countryID = country.id.RUSSIA
|
||||
else
|
||||
countryID = country.id.USA
|
||||
end
|
||||
return countryID
|
||||
end
|
||||
|
||||
function Olympus.getCoalitionByCoalitionID(coalitionID)
|
||||
local coalition = "neutral"
|
||||
if coalitionID == 1 then
|
||||
coalition = "red"
|
||||
elseif coalitionID == 2 then
|
||||
coalition = "blue"
|
||||
end
|
||||
return coalition
|
||||
end
|
||||
|
||||
-- Builds a valid task depending on the provided options
|
||||
function Olympus.buildTask(options)
|
||||
local task = nil
|
||||
-- Engage specific target by ID. Checks if target exists.
|
||||
if options['id'] == 'EngageUnit' and options['targetID'] ~= nil then
|
||||
local target = Olympus.getUnitByID(options['targetID'])
|
||||
if target and target:isExist() then
|
||||
task = {
|
||||
id = 'EngageUnit',
|
||||
params = {
|
||||
unitId = options['targetID'],
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
return task
|
||||
end
|
||||
|
||||
-- Move a unit. Since most tasks in DCS are Enroute tasks, this function is the main way to control the unit AI
|
||||
function Olympus.move(ID, lat, lng, altitude, speed, category, taskOptions)
|
||||
Olympus.notify("Olympus.move " .. ID .. " (" .. lat .. ", " .. lng ..") " .. altitude .. "m " .. speed .. "m/s " .. category, 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
if category == "Aircraft" then
|
||||
local startPoint = mist.getLeadPos(unit:getGroup())
|
||||
local endPoint = coord.LLtoLO(lat, lng, 0)
|
||||
|
||||
local task = nil
|
||||
if targetName ~= "" then
|
||||
targetID = Unit.getByName(targetName):getID()
|
||||
task = {
|
||||
id = 'EngageUnit',
|
||||
params = {
|
||||
unitId = targetID,
|
||||
}
|
||||
}
|
||||
end
|
||||
local path = {
|
||||
[1] = mist.fixedWing.buildWP(startPoint, flyOverPoint, speed, altitude, 'BARO'),
|
||||
[2] = mist.fixedWing.buildWP(endPoint, turningPoint, speed, altitude, 'BARO')
|
||||
}
|
||||
|
||||
local path = {}
|
||||
path[#path + 1] = mist.fixedWing.buildWP(startPoint, flyOverPoint, speed, altitude, 'BARO')
|
||||
if task ~= nil then
|
||||
path[#path].task = task
|
||||
end
|
||||
path[#path + 1] = mist.fixedWing.buildWP(endPoint, turningPoint, speed, altitude, 'BARO')
|
||||
if task ~= nil then
|
||||
path[#path].task = task
|
||||
-- If a task exists assign it to the controller
|
||||
local task = Olympus.buildTask(taskOptions)
|
||||
if task then
|
||||
path[1].task = task
|
||||
path[2].task = task
|
||||
end
|
||||
|
||||
-- Assign the mission task to the controller
|
||||
local missionTask = {
|
||||
id = 'Mission',
|
||||
params = {
|
||||
@ -47,7 +89,7 @@ function Olympus.move(unitName, lat, lng, altitude, speed, category, targetName)
|
||||
if groupCon then
|
||||
groupCon:setTask(missionTask)
|
||||
end
|
||||
Olympus.notify("Olympus.move executed succesfully on a air unit", 10)
|
||||
Olympus.notify("Olympus.move executed successfully on a Aircraft", 2)
|
||||
elseif category == "GroundUnit" then
|
||||
vars =
|
||||
{
|
||||
@ -59,17 +101,18 @@ function Olympus.move(unitName, lat, lng, altitude, speed, category, targetName)
|
||||
disableRoads = true
|
||||
}
|
||||
mist.groupToRandomPoint(vars)
|
||||
Olympus.notify("Olympus.move executed succesfully on a ground unit", 10)
|
||||
Olympus.notify("Olympus.move executed succesfully on a ground unit", 2)
|
||||
else
|
||||
Olympus.notify("Olympus.move not implemented yet for " .. category, 10)
|
||||
Olympus.notify("Olympus.move not implemented yet for " .. category, 2)
|
||||
end
|
||||
else
|
||||
Olympus.notify("Error in Olympus.move " .. unitName, 10)
|
||||
Olympus.notify("Error in Olympus.move " .. unitName, 2)
|
||||
end
|
||||
end
|
||||
|
||||
-- Creates a simple smoke on the ground
|
||||
function Olympus.smoke(color, lat, lng)
|
||||
Olympus.notify("Olympus.smoke " .. color .. " (" .. lat .. ", " .. lng ..")", 10)
|
||||
Olympus.notify("Olympus.smoke " .. color .. " (" .. lat .. ", " .. lng ..")", 2)
|
||||
local colorEnum = nil
|
||||
if color == "green" then
|
||||
colorEnum = trigger.smokeColor.Green
|
||||
@ -85,14 +128,15 @@ function Olympus.smoke(color, lat, lng)
|
||||
trigger.action.smoke(mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)), colorEnum)
|
||||
end
|
||||
|
||||
function Olympus.spawnGround(coalition, type, lat, lng, ID)
|
||||
Olympus.notify("Olympus.spawnGround " .. coalition .. " " .. type .. " (" .. lat .. ", " .. lng ..")", 10)
|
||||
-- Spawns a single ground unit
|
||||
function Olympus.spawnGroundUnit(coalition, unitType, lat, lng)
|
||||
Olympus.notify("Olympus.spawnGroundUnit " .. coalition .. " " .. unitType .. " (" .. lat .. ", " .. lng ..")", 2)
|
||||
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0))
|
||||
local unitTable =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["type"] = type,
|
||||
["type"] = unitType,
|
||||
["x"] = spawnLocation.x,
|
||||
["y"] = spawnLocation.z,
|
||||
["playerCanDrive"] = true,
|
||||
@ -100,13 +144,8 @@ function Olympus.spawnGround(coalition, type, lat, lng, ID)
|
||||
},
|
||||
}
|
||||
|
||||
local countryID = nil
|
||||
if coalition == 'red' then
|
||||
countryID = country.id.RUSSIA
|
||||
else
|
||||
countryID = country.id.USA
|
||||
end
|
||||
|
||||
local countryID = Olympus.getCountryIDByCoalition(coalition)
|
||||
|
||||
local vars =
|
||||
{
|
||||
units = unitTable,
|
||||
@ -116,17 +155,31 @@ function Olympus.spawnGround(coalition, type, lat, lng, ID)
|
||||
}
|
||||
mist.dynAdd(vars)
|
||||
Olympus.unitCounter = Olympus.unitCounter + 1
|
||||
Olympus.notify("Olympus.spawnGround completed succesfully", 10)
|
||||
Olympus.notify("Olympus.spawnGround completed succesfully", 2)
|
||||
end
|
||||
|
||||
function Olympus.spawnAir(coalition, unitType, lat, lng, payloadName)
|
||||
local alt = 5000
|
||||
Olympus.notify("Olympus.spawnAir " .. coalition .. " " .. unitType .. " (" .. lat .. ", " .. lng ..") " .. payloadName, 10)
|
||||
-- Spawns a single aircraft. Spawn options are:
|
||||
-- payloadName: a string, one of the names defined in unitPayloads.lua. Must be compatible with the unitType
|
||||
-- airbaseName: a string, if present the aircraft will spawn on the ground of the selected airbase
|
||||
-- payload: a table, if present the unit will receive this specific payload. Overrides payloadName
|
||||
function Olympus.spawnAircraft(coalition, unitType, lat, lng, spawnOptions)
|
||||
local payloadName = spawnOptions["payloadName"]
|
||||
local airbaseName = spawnOptions["airbaseName"]
|
||||
local payload = spawnOptions["payload"]
|
||||
|
||||
Olympus.notify("Olympus.spawnAircraft " .. coalition .. " " .. unitType .. " (" .. lat .. ", " .. lng ..")", 2)
|
||||
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0))
|
||||
local payload = {}
|
||||
if Olympus.unitPayloads[unitType][payloadName] ~= nil then
|
||||
payload = Olympus.unitPayloads[unitType][payloadName]
|
||||
|
||||
if payload == nil then
|
||||
if payloadName and payloadName ~= "" and Olympus.unitPayloads[unitType][payloadName] ~= nil then
|
||||
payload = Olympus.unitPayloads[unitType][payloadName]
|
||||
else
|
||||
payload = {}
|
||||
end
|
||||
end
|
||||
|
||||
local countryID = Olympus.getCountryIDByCoalition(coalition)
|
||||
|
||||
local unitTable =
|
||||
{
|
||||
[1] =
|
||||
@ -134,12 +187,10 @@ function Olympus.spawnAir(coalition, unitType, lat, lng, payloadName)
|
||||
["type"] = unitType,
|
||||
["x"] = spawnLocation.x,
|
||||
["y"] = spawnLocation.z,
|
||||
["alt"] = alt,
|
||||
["skill"] = "Excellent",
|
||||
["payload"] =
|
||||
{
|
||||
["pylons"] = payload,
|
||||
["fuel"] = 4900,
|
||||
["flare"] = 60,
|
||||
["ammo_type"] = 1,
|
||||
["chaff"] = 60,
|
||||
@ -151,31 +202,97 @@ function Olympus.spawnAir(coalition, unitType, lat, lng, payloadName)
|
||||
[1] = 1,
|
||||
[2] = 1,
|
||||
[3] = 1,
|
||||
["name"] = "Enfield11",
|
||||
["name"] = "Olympus" .. Olympus.unitCounter,
|
||||
},
|
||||
["name"] = "Olympus-" .. Olympus.unitCounter
|
||||
},
|
||||
}
|
||||
|
||||
local countryID = nil
|
||||
if coalition == 'red' then
|
||||
countryID = country.id.RUSSIA
|
||||
else
|
||||
countryID = country.id.USA
|
||||
-- If a airbase is provided the first waypoint is set as a From runway takeoff.
|
||||
local route = {}
|
||||
if airbaseName and airbaseName ~= "" then
|
||||
local airbase = Airbase.getByName(airbaseName)
|
||||
if airbase then
|
||||
local airbaseID = airbase:getID()
|
||||
route =
|
||||
{
|
||||
["points"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["action"] = "From Runway",
|
||||
["task"] =
|
||||
{
|
||||
["id"] = "ComboTask",
|
||||
["params"] = {["tasks"] = {},},
|
||||
},
|
||||
["type"] = "TakeOff",
|
||||
["ETA"] = 0,
|
||||
["ETA_locked"] = true,
|
||||
["x"] = spawnLocation.x,
|
||||
["y"] = spawnLocation.z,
|
||||
["formation_template"] = "",
|
||||
["airdromeId"] = airbaseID,
|
||||
["speed_locked"] = true,
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local vars =
|
||||
{
|
||||
units = unitTable,
|
||||
country = countryID,
|
||||
category = 'airplane',
|
||||
task = "CAP",
|
||||
tasks = {},
|
||||
name = "Olympus-" .. Olympus.unitCounter,
|
||||
route = route,
|
||||
task = 'CAP',
|
||||
}
|
||||
|
||||
mist.dynAdd(vars)
|
||||
|
||||
-- Save the payload to be reused in case the unit is cloned. TODO: save by ID not by name (it works but I like consistency)
|
||||
Olympus.payloadRegistry[vars.name] = payload
|
||||
Olympus.unitCounter = Olympus.unitCounter + 1
|
||||
Olympus.notify("Olympus.spawnAir completed succesfully", 10)
|
||||
Olympus.notify("Olympus.spawnAir completed successfully", 2)
|
||||
end
|
||||
|
||||
Olympus.notify("OlympusCommand script loaded correctly", 10)
|
||||
-- Clones a unit by ID. Will clone the unit with the same original payload as the source unit. TODO: only works on Olympus unit not ME units.
|
||||
function Olympus.clone(ID)
|
||||
Olympus.notify("Olympus.clone " .. ID, 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
local coalition = Olympus.getCoalitionByCoalitionID(unit:getCoalition())
|
||||
local lat, lng, alt = coord.LOtoLL(unit:getPoint())
|
||||
|
||||
-- TODO: only works on Aircraft
|
||||
local spawnOptions = {
|
||||
payload = Olympus.payloadRegistry[unitName]
|
||||
}
|
||||
Olympus.spawnAircraft(coalition, unit:getTypeName(), lat + 0.001, lng + 0.001, spawnOptions)
|
||||
end
|
||||
Olympus.notify("Olympus.clone completed successfully", 2)
|
||||
end
|
||||
|
||||
function Olympus.follow(leaderID, ID)
|
||||
Olympus.notify("Olympus.follow " .. ID .. " " .. leaderID, 2)
|
||||
local leader = Olympus.getUnitByID(leaderID)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
local followTask = {
|
||||
id = 'Follow',
|
||||
params = {
|
||||
groupId = leader:getGroup():getID(),
|
||||
pos = {x = 0 , y = 0, z = 20} ,
|
||||
lastWptIndexFlag = false,
|
||||
lastWptIndex = 1
|
||||
}
|
||||
}
|
||||
Olympus.notify("Olympus.follow group ID" .. unit:getGroup():getID(), 2)
|
||||
unit:getGroup():getController():pushTask(followTask)
|
||||
Olympus.notify("Olympus.follow completed successfully", 2)
|
||||
end
|
||||
|
||||
|
||||
|
||||
Olympus.notify("OlympusCommand script loaded successfully", 2)
|
||||
@ -11,8 +11,6 @@ function Olympus.setMissionData(arg, time)
|
||||
local bullseyeVec3 = coalition.getMainRefPoint(0)
|
||||
local bullseyeLatitude, bullseyeLongitude, bullseyeAltitude = coord.LOtoLL(bullseyeVec3)
|
||||
local bullseye = {}
|
||||
bullseye["x"] = bullseyeVec3.x
|
||||
bullseye["y"] = bullseyeVec3.z
|
||||
bullseye["lat"] = bullseyeLatitude
|
||||
bullseye["lng"] = bullseyeLongitude
|
||||
|
||||
@ -41,9 +39,26 @@ function Olympus.setMissionData(arg, time)
|
||||
end
|
||||
end
|
||||
|
||||
-- Airbases data
|
||||
local base = world.getAirbases()
|
||||
local basesData = {}
|
||||
for i = 1, #base do
|
||||
local info = {}
|
||||
local latitude, longitude, altitude = coord.LOtoLL(Airbase.getPoint(base[i]))
|
||||
info["callsign"] = Airbase.getCallsign(base[i])
|
||||
info["coalition"] = Airbase.getCoalition(base[i])
|
||||
info["lat"] = latitude
|
||||
info["lng"] = longitude
|
||||
if Airbase.getUnit(base[i]) then
|
||||
info["unitId"] = Airbase.getUnit(base[i]):getID()
|
||||
end
|
||||
basesData[i] = info
|
||||
end
|
||||
|
||||
-- Assemble missionData table
|
||||
missionData["bullseye"] = bullseye
|
||||
missionData["unitsData"] = unitsData
|
||||
missionData["airbases"] = basesData
|
||||
|
||||
local command = "Olympus.missionData = " .. Olympus.serializeTable(missionData) .. "\n" .. "Olympus.OlympusDLL.setMissionData()"
|
||||
net.dostring_in("export", command)
|
||||
@ -55,8 +70,6 @@ function Olympus.serializeTable(val, name, skipnewlines, depth)
|
||||
depth = depth or 0
|
||||
|
||||
local tmp = string.rep(" ", depth)
|
||||
|
||||
|
||||
if name then
|
||||
if type(name) == "number" then
|
||||
tmp = tmp .. "[" .. name .. "]" .. " = "
|
||||
@ -67,11 +80,9 @@ function Olympus.serializeTable(val, name, skipnewlines, depth)
|
||||
|
||||
if type(val) == "table" then
|
||||
tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")
|
||||
|
||||
for k, v in pairs(val) do
|
||||
tmp = tmp .. Olympus.serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
|
||||
end
|
||||
|
||||
tmp = tmp .. string.rep(" ", depth) .. "}"
|
||||
elseif type(val) == "number" then
|
||||
tmp = tmp .. tostring(val)
|
||||
|
||||
8
scripts/server.py
Normal file
@ -0,0 +1,8 @@
|
||||
try:
|
||||
# Python 2
|
||||
from SimpleHTTPServer import test, SimpleHTTPRequestHandler
|
||||
except ImportError:
|
||||
# Python 3
|
||||
from http.server import test, SimpleHTTPRequestHandler
|
||||
|
||||
test(SimpleHTTPRequestHandler)
|
||||
44
scripts/server.spec
Normal file
@ -0,0 +1,44 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['server.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='server',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
||||
@ -8,7 +8,7 @@ namespace CommandPriority {
|
||||
};
|
||||
|
||||
namespace CommandType {
|
||||
enum CommandTypes { NO_TYPE, MOVE, SMOKE, LASE, EXPLODE, SPAWN_AIR, SPAWN_GROUND };
|
||||
enum CommandTypes { NO_TYPE, MOVE, SMOKE, LASE, EXPLODE, SPAWN_AIR, SPAWN_GROUND, CLONE, LAND, REFUEL, FOLLOW };
|
||||
};
|
||||
|
||||
/* Base command class */
|
||||
@ -17,7 +17,7 @@ class Command
|
||||
public:
|
||||
int getPriority() { return priority; }
|
||||
int getType() { return type; }
|
||||
virtual void execute(lua_State* L) = 0;
|
||||
virtual wstring getString(lua_State* L) = 0;
|
||||
|
||||
protected:
|
||||
int priority = CommandPriority::LOW;
|
||||
@ -28,28 +28,26 @@ protected:
|
||||
class MoveCommand : public Command
|
||||
{
|
||||
public:
|
||||
MoveCommand(int ID, wstring unitName, Coords destination, double speed, double altitude, wstring unitCategory, wstring targetName):
|
||||
ID(ID),
|
||||
unitName(unitName),
|
||||
MoveCommand(int ID, Coords destination, double speed, double altitude, wstring unitCategory, wstring taskOptions):
|
||||
ID(ID),
|
||||
destination(destination),
|
||||
speed(speed),
|
||||
altitude(altitude),
|
||||
unitCategory(unitCategory),
|
||||
targetName(targetName)
|
||||
taskOptions(taskOptions)
|
||||
{
|
||||
priority = CommandPriority::LOW;
|
||||
priority = CommandPriority::HIGH;
|
||||
type = CommandType::MOVE;
|
||||
};
|
||||
virtual void execute(lua_State* L);
|
||||
virtual wstring getString(lua_State* L);
|
||||
|
||||
private:
|
||||
const int ID;
|
||||
const wstring unitName;
|
||||
const Coords destination;
|
||||
const wstring unitCategory;
|
||||
const double speed;
|
||||
const double altitude;
|
||||
const wstring targetName;
|
||||
const wstring taskOptions;
|
||||
};
|
||||
|
||||
/* Smoke command */
|
||||
@ -63,7 +61,7 @@ public:
|
||||
priority = CommandPriority::LOW;
|
||||
type = CommandType::SMOKE;
|
||||
};
|
||||
virtual void execute(lua_State* L);
|
||||
virtual wstring getString(lua_State* L);
|
||||
|
||||
private:
|
||||
const wstring color;
|
||||
@ -71,10 +69,10 @@ private:
|
||||
};
|
||||
|
||||
/* Spawn ground unit command */
|
||||
class SpawnGroundCommand : public Command
|
||||
class SpawnGroundUnitCommand : public Command
|
||||
{
|
||||
public:
|
||||
SpawnGroundCommand(wstring coalition, wstring unitType, Coords location) :
|
||||
SpawnGroundUnitCommand(wstring coalition, wstring unitType, Coords location) :
|
||||
coalition(coalition),
|
||||
unitType(unitType),
|
||||
location(location)
|
||||
@ -82,7 +80,7 @@ public:
|
||||
priority = CommandPriority::LOW;
|
||||
type = CommandType::SPAWN_GROUND;
|
||||
};
|
||||
virtual void execute(lua_State* L);
|
||||
virtual wstring getString(lua_State* L);
|
||||
|
||||
private:
|
||||
const wstring coalition;
|
||||
@ -91,23 +89,60 @@ private:
|
||||
};
|
||||
|
||||
/* Spawn air unit command */
|
||||
class SpawnAirCommand : public Command
|
||||
class SpawnAircraftCommand : public Command
|
||||
{
|
||||
public:
|
||||
SpawnAirCommand(wstring coalition, wstring unitType, Coords location, wstring payloadName) :
|
||||
SpawnAircraftCommand(wstring coalition, wstring unitType, Coords location, wstring payloadName, wstring airbaseName) :
|
||||
coalition(coalition),
|
||||
unitType(unitType),
|
||||
location(location),
|
||||
payloadName(payloadName)
|
||||
payloadName(payloadName),
|
||||
airbaseName(airbaseName)
|
||||
{
|
||||
priority = CommandPriority::LOW;
|
||||
type = CommandType::SPAWN_AIR;
|
||||
};
|
||||
virtual void execute(lua_State* L);
|
||||
|
||||
virtual wstring getString(lua_State* L);
|
||||
|
||||
private:
|
||||
const wstring coalition;
|
||||
const wstring unitType;
|
||||
const Coords location;
|
||||
const wstring payloadName;
|
||||
const wstring airbaseName;
|
||||
};
|
||||
|
||||
/* Clone unit command */
|
||||
class CloneCommand : public Command
|
||||
{
|
||||
public:
|
||||
CloneCommand(int ID) :
|
||||
ID(ID)
|
||||
{
|
||||
priority = CommandPriority::LOW;
|
||||
type = CommandType::CLONE;
|
||||
};
|
||||
virtual wstring getString(lua_State* L);
|
||||
|
||||
private:
|
||||
const int ID;
|
||||
};
|
||||
|
||||
/* Follow command */
|
||||
class FollowCommand : public Command
|
||||
{
|
||||
public:
|
||||
FollowCommand(int leaderID, int ID) :
|
||||
leaderID(leaderID),
|
||||
ID(ID)
|
||||
{
|
||||
priority = CommandPriority::LOW;
|
||||
type = CommandType::FOLLOW;
|
||||
};
|
||||
virtual wstring getString(lua_State* L);
|
||||
|
||||
private:
|
||||
const int leaderID;
|
||||
const int ID;
|
||||
};
|
||||
|
||||
@ -14,26 +14,27 @@ public:
|
||||
~Unit();
|
||||
|
||||
void update(json::value json);
|
||||
json::value json();
|
||||
|
||||
void setPath(list<Coords> path);
|
||||
void setActiveDestination(Coords newActiveDestination) { activeDestination = newActiveDestination; }
|
||||
void setAlive(bool newAlive) { alive = newAlive; }
|
||||
void setTarget(int targetID);
|
||||
wstring getTarget();
|
||||
wstring getCurrentTask();
|
||||
|
||||
void resetActiveDestination();
|
||||
void setLeader(bool newLeader) { leader = newLeader; }
|
||||
void setWingman(bool newWingman) { wingman = newWingman; }
|
||||
void setWingmen(vector<Unit*> newWingmen) { wingmen = newWingmen; }
|
||||
void setFormation(wstring newFormation) { formation = newFormation; }
|
||||
|
||||
virtual void changeSpeed(wstring change) {};
|
||||
virtual void changeAltitude(wstring change) {};
|
||||
|
||||
virtual double getTargetSpeed() { return targetSpeed; };
|
||||
virtual double getTargetAltitude() { return targetAltitude; };
|
||||
void resetActiveDestination();
|
||||
|
||||
int getID() { return ID; }
|
||||
wstring getName() { return name; }
|
||||
wstring getUnitName() { return unitName; }
|
||||
wstring getGroupName() { return groupName; }
|
||||
json::value getType() { return type; } // This functions returns the complete type of the object (Level1, Level2, Level3, Level4)
|
||||
json::value getType() { return type; } // This function returns the complete type of the object (Level1, Level2, Level3, Level4)
|
||||
int getCountry() { return country; }
|
||||
int getCoalitionID() { return coalitionID; }
|
||||
double getLatitude() { return latitude; }
|
||||
@ -42,34 +43,40 @@ public:
|
||||
double getHeading() { return heading; }
|
||||
json::value getFlags() { return flags; }
|
||||
Coords getActiveDestination() { return activeDestination; }
|
||||
|
||||
virtual wstring getCategory() { return L"No category"; };
|
||||
|
||||
json::value json();
|
||||
wstring getTarget();
|
||||
wstring getCurrentTask() { return currentTask; }
|
||||
virtual double getTargetSpeed() { return targetSpeed; };
|
||||
virtual double getTargetAltitude() { return targetAltitude; };
|
||||
|
||||
protected:
|
||||
int ID;
|
||||
bool AI = false;
|
||||
bool alive = true;
|
||||
wstring name = L"undefined";
|
||||
wstring unitName = L"undefined";
|
||||
wstring groupName = L"undefined";
|
||||
json::value type = json::value::null();
|
||||
int country = NULL;
|
||||
int coalitionID = NULL;
|
||||
double latitude = NULL;
|
||||
double longitude = NULL;
|
||||
double altitude = NULL;
|
||||
double heading = NULL;
|
||||
double speed = NULL;
|
||||
json::value flags = json::value::null();
|
||||
Coords oldPosition = Coords(0); // Used to approximate speed
|
||||
int targetID = NULL;
|
||||
bool holding = false;
|
||||
bool looping = false;
|
||||
|
||||
double targetSpeed = 0;
|
||||
double targetAltitude = 0;
|
||||
bool AI = false;
|
||||
bool alive = true;
|
||||
wstring name = L"undefined";
|
||||
wstring unitName = L"undefined";
|
||||
wstring groupName = L"undefined";
|
||||
json::value type = json::value::null();
|
||||
int country = NULL;
|
||||
int coalitionID = NULL;
|
||||
double latitude = NULL;
|
||||
double longitude = NULL;
|
||||
double altitude = NULL;
|
||||
double heading = NULL;
|
||||
double speed = NULL;
|
||||
json::value flags = json::value::null();
|
||||
Coords oldPosition = Coords(0); // Used to approximate speed
|
||||
int targetID = NULL;
|
||||
bool holding = false;
|
||||
bool looping = false;
|
||||
wstring taskOptions = L"{}";
|
||||
wstring currentTask = L"";
|
||||
bool leader = false;
|
||||
bool wingman = false;
|
||||
wstring formation = L"";
|
||||
vector<Unit*> wingmen;
|
||||
double targetSpeed = 0;
|
||||
double targetAltitude = 0;
|
||||
|
||||
list<Coords> activePath;
|
||||
Coords activeDestination = Coords(0);
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
#pragma once
|
||||
#include "framework.h"
|
||||
|
||||
#define PROTECTED_CALL "Olympus = {}\n \
|
||||
function Olympus.protectedCall(...)\n\n \
|
||||
local status, retval = pcall(...)\n \
|
||||
if not status then\n \
|
||||
trigger.action.outText(\"ERROR: \" ..retval, 20)\n \
|
||||
end\n \
|
||||
end\n \
|
||||
trigger.action.outText(\"Olympus.protectedCall registered successfully\", 10)\n"
|
||||
|
||||
void registerLuaFunctions(lua_State* L);
|
||||
|
||||
@ -1,86 +1,87 @@
|
||||
#include "commands.h"
|
||||
#include "logger.h"
|
||||
#include "dcstools.h"
|
||||
|
||||
/* Move command */
|
||||
void MoveCommand::execute(lua_State* L)
|
||||
wstring MoveCommand::getString(lua_State* L)
|
||||
{
|
||||
std::ostringstream command;
|
||||
command.precision(10);
|
||||
command << "Olympus.move(\"" << to_string(unitName) << "\", " << destination.lat << ", " << destination.lng << ", " << altitude << ", " << speed << ", \"" << to_string(unitCategory) << "\", \"" << to_string(targetName) << "\")";
|
||||
|
||||
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 MoveCommand");
|
||||
}
|
||||
else
|
||||
{
|
||||
log("MoveCommand executed successfully");
|
||||
}
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.move, "
|
||||
<< ID << ", "
|
||||
<< destination.lat << ", "
|
||||
<< destination.lng << ", "
|
||||
<< altitude << ", "
|
||||
<< speed << ", "
|
||||
<< "\"" << unitCategory << "\"" << ", "
|
||||
<< taskOptions;
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
/* Smoke command */
|
||||
void SmokeCommand::execute(lua_State* L)
|
||||
wstring SmokeCommand::getString(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");
|
||||
}
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.smoke, "
|
||||
<< "\"" << color << "\"" << ", "
|
||||
<< location.lat << ", "
|
||||
<< location.lng;
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
/* Spawn ground command */
|
||||
void SpawnGroundCommand::execute(lua_State* L)
|
||||
wstring SpawnGroundUnitCommand::getString(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");
|
||||
}
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.spawnGroundUnit, "
|
||||
<< "\"" << coalition << "\"" << ", "
|
||||
<< "\"" << unitType << "\"" << ", "
|
||||
<< location.lat << ", "
|
||||
<< location.lng;
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
/* Spawn air command */
|
||||
void SpawnAirCommand::execute(lua_State* L)
|
||||
wstring SpawnAircraftCommand::getString(lua_State* L)
|
||||
{
|
||||
std::ostringstream command;
|
||||
command.precision(10);
|
||||
command << "Olympus.spawnAir(\"" << to_string(coalition) << "\", \"" << to_string(unitType) << "\", " << location.lat << ", " << location.lng << "," << "\"" << to_string(payloadName) << "\")";
|
||||
std::wostringstream optionsSS;
|
||||
optionsSS.precision(10);
|
||||
optionsSS << "{"
|
||||
<< "payloadName = \"" << payloadName << "\", "
|
||||
<< "airbaseName = \"" << airbaseName << "\","
|
||||
<< "}";
|
||||
|
||||
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 SpawnAirCommand");
|
||||
}
|
||||
else
|
||||
{
|
||||
log("SpawnAirCommand executed successfully");
|
||||
}
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.spawnAircraft, "
|
||||
<< "\"" << coalition << "\"" << ", "
|
||||
<< "\"" << unitType << "\"" << ", "
|
||||
<< location.lat << ", "
|
||||
<< location.lng << ","
|
||||
<< optionsSS.str();
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
/* Clone unit command */
|
||||
wstring CloneCommand::getString(lua_State* L)
|
||||
{
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.clone, "
|
||||
<< ID;
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
/* Follow unit command */
|
||||
wstring FollowCommand::getString(lua_State* L)
|
||||
{
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.follow, "
|
||||
<< leaderID << ","
|
||||
<< ID;
|
||||
|
||||
return commandSS.str();
|
||||
}
|
||||
@ -33,42 +33,15 @@ void Scheduler::execute(lua_State* L)
|
||||
{
|
||||
if (command->getPriority() == priority)
|
||||
{
|
||||
log("Executing command");
|
||||
switch (command->getType())
|
||||
wstring commandString = L"Olympus.protectedCall(" + command->getString(L) + L")";
|
||||
if (dostring_in(L, "server", to_string(commandString)))
|
||||
{
|
||||
case CommandType::MOVE:
|
||||
{
|
||||
MoveCommand* moveCommand = dynamic_cast<MoveCommand*>(command);
|
||||
moveCommand->execute(L);
|
||||
commands.remove(command);
|
||||
break;
|
||||
}
|
||||
case CommandType::SMOKE:
|
||||
{
|
||||
SmokeCommand* smokeCommand = dynamic_cast<SmokeCommand*>(command);
|
||||
smokeCommand->execute(L);
|
||||
commands.remove(command);
|
||||
break;
|
||||
}
|
||||
case CommandType::SPAWN_GROUND:
|
||||
{
|
||||
SpawnGroundCommand* spawnCommand = dynamic_cast<SpawnGroundCommand*>(command);
|
||||
spawnCommand->execute(L);
|
||||
commands.remove(command);
|
||||
break;
|
||||
}
|
||||
case CommandType::SPAWN_AIR:
|
||||
{
|
||||
SpawnAirCommand* spawnCommand = dynamic_cast<SpawnAirCommand*>(command);
|
||||
spawnCommand->execute(L);
|
||||
commands.remove(command);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log("Unknown command of type " + to_string(command->getType()));
|
||||
commands.remove(command);
|
||||
break;
|
||||
log(L"Error executing command " + commandString);
|
||||
}
|
||||
{
|
||||
log(L"Command " + commandString + L" executed succesfully");
|
||||
}
|
||||
commands.remove(command);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -130,7 +103,7 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
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<Command*>(new SpawnGroundCommand(coalition, type, loc));
|
||||
command = dynamic_cast<Command*>(new SpawnGroundUnitCommand(coalition, type, loc));
|
||||
}
|
||||
else if (key.compare(L"spawnAir") == 0)
|
||||
{
|
||||
@ -140,15 +113,16 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
wstring payloadName = value[L"payloadName"].as_string();
|
||||
log(L"Spawning " + coalition + L" air unit of type " + type + L" with payload " + payloadName + L" at (" + to_wstring(lat) + L", " + to_wstring(lng) + L")");
|
||||
command = dynamic_cast<Command*>(new SpawnAirCommand(coalition, type, loc, payloadName));
|
||||
wstring airbaseName = value[L"airbaseName"].as_string();
|
||||
log(L"Spawning " + coalition + L" air unit of type " + type + L" with payload " + payloadName + L" at (" + to_wstring(lat) + L", " + to_wstring(lng) + L" " + airbaseName + L")");
|
||||
command = dynamic_cast<Command*>(new SpawnAircraftCommand(coalition, type, loc, payloadName, airbaseName));
|
||||
}
|
||||
else if (key.compare(L"attackUnit") == 0)
|
||||
{
|
||||
int unitID = value[L"unitID"].as_integer();
|
||||
int ID = value[L"ID"].as_integer();
|
||||
int targetID = value[L"targetID"].as_integer();
|
||||
|
||||
Unit* unit = unitsFactory->getUnit(unitID);
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
Unit* target = unitsFactory->getUnit(targetID);
|
||||
|
||||
wstring unitName;
|
||||
@ -193,6 +167,43 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
unit->changeAltitude(value[L"change"].as_string());
|
||||
}
|
||||
}
|
||||
else if (key.compare(L"cloneUnit") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
command = dynamic_cast<Command*>(new CloneCommand(ID));
|
||||
log(L"Cloning unit " + to_wstring(ID));
|
||||
}
|
||||
else if (key.compare(L"setLeader") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
json::value wingmenIDs = value[L"wingmenIDs"];
|
||||
vector<Unit*> wingmen;
|
||||
if (unit != nullptr)
|
||||
{
|
||||
for (auto itr = wingmenIDs.as_array().begin(); itr != wingmenIDs.as_array().end(); itr++)
|
||||
{
|
||||
Unit* wingman = unitsFactory->getUnit(itr->as_integer());
|
||||
if (wingman != nullptr)
|
||||
{
|
||||
wingman->setWingman(true);
|
||||
wingmen.push_back(wingman);
|
||||
log(L"Setting " + wingman->getName() + L" as wingman leader");
|
||||
}
|
||||
}
|
||||
unit->setWingmen(wingmen);
|
||||
unit->setLeader(true);
|
||||
unit->resetActiveDestination();
|
||||
log(L"Setting " + unit->getName() + L" as formation leader");
|
||||
}
|
||||
}
|
||||
else if (key.compare(L"setFormation") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
wstring formation = value[L"formation"].as_string();
|
||||
unit->setFormation(formation);
|
||||
}
|
||||
else
|
||||
{
|
||||
log(L"Unknown command: " + key);
|
||||
|
||||
@ -116,59 +116,46 @@ wstring Unit::getTarget()
|
||||
}
|
||||
}
|
||||
|
||||
wstring Unit::getCurrentTask()
|
||||
void Unit::AIloop()
|
||||
{
|
||||
if (activePath.size() == 0)
|
||||
// For wingman units, the leader decides the active destination
|
||||
if (!wingman)
|
||||
{
|
||||
return L"Idle";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getTarget().empty())
|
||||
/* Set the active destination to be always equal to the first point of the active path. This is in common with all AI units */
|
||||
if (activePath.size() > 0)
|
||||
{
|
||||
if (looping)
|
||||
if (activeDestination != activePath.front())
|
||||
{
|
||||
return L"Looping";
|
||||
}
|
||||
else if (holding)
|
||||
{
|
||||
return L"Holding";
|
||||
}
|
||||
else
|
||||
{
|
||||
return L"Reaching destination";
|
||||
activeDestination = activePath.front();
|
||||
Command* command = dynamic_cast<Command*>(new MoveCommand(ID, activeDestination, getTargetSpeed(), getTargetAltitude(), getCategory(), taskOptions));
|
||||
scheduler->appendCommand(command);
|
||||
|
||||
if (leader)
|
||||
{
|
||||
for (auto itr = wingmen.begin(); itr != wingmen.end(); itr++)
|
||||
{
|
||||
// Manually set the path and the active destination of the wingmen
|
||||
(*itr)->setPath(activePath);
|
||||
(*itr)->setActiveDestination(activeDestination);
|
||||
Command* command = dynamic_cast<Command*>(new FollowCommand(ID, (*itr)->getID()));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return L"Attacking " + getTarget();
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
log(unitName + L" no more points in active path");
|
||||
activeDestination = Coords(0); // Set the active path to NULL
|
||||
currentTask = L"Idle";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::AIloop()
|
||||
{
|
||||
/* Set the active destination to be always equal to the first point of the active path. This is in common with all AI units */
|
||||
if (activePath.size() > 0)
|
||||
{
|
||||
if (activeDestination != activePath.front())
|
||||
{
|
||||
activeDestination = activePath.front();
|
||||
Command* command = dynamic_cast<Command*>(new MoveCommand(ID, unitName, activeDestination, getTargetSpeed(), getTargetAltitude(), getCategory(), getTarget()));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
log(unitName + L" no more points in active path");
|
||||
activeDestination = Coords(0); // Set the active path to NULL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This function calls again the MoveCommand to reach the active destination. This is useful to change speed and altitude, for example */
|
||||
/* This function reset the activation so that the AI lopp will call again the MoveCommand. This is useful to change speed and altitude, for example */
|
||||
void Unit::resetActiveDestination()
|
||||
{
|
||||
log(unitName + L" resetting active destination");
|
||||
@ -197,6 +184,15 @@ json::value Unit::json()
|
||||
json[L"flags"] = flags;
|
||||
json[L"category"] = json::value::string(getCategory());
|
||||
json[L"currentTask"] = json::value::string(getCurrentTask());
|
||||
json[L"leader"] = leader;
|
||||
json[L"wingman"] = wingman;
|
||||
json[L"formation"] = json::value::string(formation);
|
||||
|
||||
int i = 0;
|
||||
for (auto itr = wingmen.begin(); itr != wingmen.end(); itr++)
|
||||
{
|
||||
json[L"wingmenIDs"][i++] = (*itr)->getID();
|
||||
}
|
||||
|
||||
/* Send the active path as a json object */
|
||||
if (activePath.size() > 0) {
|
||||
@ -224,6 +220,21 @@ AirUnit::AirUnit(json::value json, int ID) : Unit(json, ID)
|
||||
|
||||
void AirUnit::AIloop()
|
||||
{
|
||||
if (targetID != 0)
|
||||
{
|
||||
std::wostringstream taskOptionsSS;
|
||||
taskOptionsSS << "{"
|
||||
<< "id = 'EngageUnit'" << ","
|
||||
<< "targetID = " << targetID << ","
|
||||
<< "}";
|
||||
taskOptions = taskOptionsSS.str();
|
||||
currentTask = L"Attacking " + getTarget();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentTask = L"Reaching destination";
|
||||
}
|
||||
|
||||
/* Call the common AI loop */
|
||||
Unit::AIloop();
|
||||
|
||||
@ -257,6 +268,7 @@ void AirUnit::AIloop()
|
||||
activePath.push_back(point3);
|
||||
activePath.push_back(Coords(latitude, longitude));
|
||||
holding = true;
|
||||
currentTask = L"Holding";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,12 +1,39 @@
|
||||
#include "scriptLoader.h"
|
||||
#include "logger.h"
|
||||
#include "luatools.h"
|
||||
#include "dcstools.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
bool executeLuaScript(lua_State* L, string path)
|
||||
{
|
||||
replace(path.begin(), path.end(), '\\', '/');
|
||||
// Encase the loading call in a procted call to catch any syntax errors
|
||||
string str = "Olympus.protectedCall(dofile, \"" + path + "\")";
|
||||
if (dostring_in(L, "server", str.c_str()) != 0)
|
||||
{
|
||||
log("Error registering " + path);
|
||||
}
|
||||
else
|
||||
{
|
||||
log(path + " registered successfully");
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 registerLuaFunctions(lua_State* L)
|
||||
{
|
||||
string modLocation;
|
||||
|
||||
if (dostring_in(L, "server", PROTECTED_CALL))
|
||||
{
|
||||
log("Error registering protectedCall");
|
||||
}
|
||||
else
|
||||
{
|
||||
log("protectedCall registered successfully");
|
||||
}
|
||||
|
||||
char* buf = nullptr;
|
||||
size_t sz = 0;
|
||||
if (_dupenv_s(&buf, &sz, "DCSOLYMPUS_PATH") == 0 && buf != nullptr)
|
||||
@ -20,97 +47,7 @@ void registerLuaFunctions(lua_State* L)
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
ifstream f(modLocation + "\\Scripts\\mist_4_4_90.lua");
|
||||
string str;
|
||||
log("Reading MIST from " + modLocation + "\\Scripts\\mist_4_4_90.lua");
|
||||
if (f) {
|
||||
ostringstream ss;
|
||||
ss << f.rdbuf();
|
||||
str = ss.str();
|
||||
log("MIST read succesfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
log("Error registering MIST");
|
||||
}
|
||||
else
|
||||
{
|
||||
log("MIST registered successfully");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ifstream f(modLocation + "\\Scripts\\OlympusCommand.lua");
|
||||
string str;
|
||||
log("Reading OlympusCommand.lua from " + modLocation + "\\Scripts\\OlympusCommand.lua");
|
||||
if (f) {
|
||||
ostringstream ss;
|
||||
ss << f.rdbuf();
|
||||
str = ss.str();
|
||||
log("OlympusCommand.lua read succesfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
log("Error registering OlympusCommand.lua");
|
||||
}
|
||||
else
|
||||
{
|
||||
log("OlympusCommand.lua registered successfully");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ifstream f(modLocation + "\\Scripts\\unitPayloads.lua");
|
||||
string str;
|
||||
log("Reading unitPayloads.lua from " + modLocation + "\\Scripts\\unitPayloads.lua");
|
||||
if (f) {
|
||||
ostringstream ss;
|
||||
ss << f.rdbuf();
|
||||
str = ss.str();
|
||||
log("unitPayloads.lua read succesfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
log("Error reading unitPayloads.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)
|
||||
{
|
||||
log("Error registering unitPayloads.lua");
|
||||
}
|
||||
else
|
||||
{
|
||||
log("unitPayloads.lua registered successfully");
|
||||
}
|
||||
}
|
||||
|
||||
executeLuaScript(L, modLocation + "\\Scripts\\mist_4_4_90.lua");
|
||||
executeLuaScript(L, modLocation + "\\Scripts\\OlympusCommand.lua");
|
||||
executeLuaScript(L, modLocation + "\\Scripts\\unitPayloads.lua");
|
||||
}
|
||||
@ -6,6 +6,6 @@ void DllExport LogInfo(lua_State* L, string message);
|
||||
void DllExport LogWarning(lua_State* L, string message);
|
||||
void DllExport LogError(lua_State* L, string message);
|
||||
void DllExport Log(lua_State* L, string message, int level);
|
||||
|
||||
int DllExport dostring_in(lua_State* L, string target, string command);
|
||||
map<int, json::value> DllExport getAllUnits(lua_State* L);
|
||||
|
||||
|
||||
@ -92,4 +92,14 @@ map<int, json::value> getAllUnits(lua_State* L)
|
||||
exit:
|
||||
STACK_CLEAN;
|
||||
return units;
|
||||
}
|
||||
|
||||
|
||||
int dostring_in(lua_State* L, string target, string command)
|
||||
{
|
||||
lua_getglobal(L, "net");
|
||||
lua_getfield(L, -1, "dostring_in");
|
||||
lua_pushstring(L, target.c_str());
|
||||
lua_pushstring(L, command.c_str());
|
||||
return lua_pcall(L, 2, 0, 0);
|
||||
}
|
||||
37
www/css/AirbaseMarker.css
Normal file
@ -0,0 +1,37 @@
|
||||
.airbasemarker-container-table {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
left: -30px;
|
||||
top: -30px;
|
||||
border: 1px transparent solid;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.airbasemarker-icon-img {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
display: block;
|
||||
opacity: 0.8;
|
||||
position: absolute;
|
||||
filter: drop-shadow(1px 1px 0 white) drop-shadow(1px -1px 0 white) drop-shadow(-1px 1px 0 white) drop-shadow(-1px -1px 0 white);
|
||||
}
|
||||
|
||||
.airbasemarker-icon-img-blue {
|
||||
filter: invert(37%) sepia(21%) saturate(7402%) hue-rotate(193deg) brightness(103%) contrast(104%) drop-shadow(1px 1px 0 white) drop-shadow(1px -1px 0 white) drop-shadow(-1px 1px 0 white) drop-shadow(-1px -1px 0 white);
|
||||
}
|
||||
|
||||
.airbasemarker-icon-img-red {
|
||||
filter: invert(21%) sepia(96%) saturate(4897%) hue-rotate(353deg) brightness(108%) contrast(90%) drop-shadow(1px 1px 0 white) drop-shadow(1px -1px 0 white) drop-shadow(-1px 1px 0 white) drop-shadow(-1px -1px 0 white);
|
||||
}
|
||||
|
||||
.airbasemarker-name-div {
|
||||
bottom: -20px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
font: 800 14px Arial;
|
||||
white-space: nowrap;
|
||||
-webkit-text-fill-color: white;
|
||||
-webkit-text-stroke: 1px;
|
||||
}
|
||||
@ -40,3 +40,47 @@ body{
|
||||
color: rgb(255, 154, 154);
|
||||
text-shadow: 1px 1px #000, -1px -1px #000, 1px -1px #000, -1px 1px #000;
|
||||
}
|
||||
|
||||
/* The snackbar - position it at the bottom and in the middle of the screen */
|
||||
#snackbar {
|
||||
visibility: hidden; /* Hidden by default. Visible on click */
|
||||
min-width: 250px; /* Set a default minimum width */
|
||||
background-color: #2d3e50; /* Black background color */
|
||||
color: #fff; /* White text color */
|
||||
text-align: center; /* Centered text */
|
||||
border-radius: 2px; /* Rounded borders */
|
||||
padding: 16px; /* Padding */
|
||||
position: fixed; /* Sit on top of the screen */
|
||||
z-index: 1000; /* Add a z-index if needed */
|
||||
top: 120px; /* 30px from the bottom */
|
||||
}
|
||||
|
||||
/* Show the snackbar when clicking on a button (class added with JavaScript) */
|
||||
#snackbar.show {
|
||||
visibility: visible; /* Show the snackbar */
|
||||
/* Add animation: Take 0.5 seconds to fade in and out the snackbar.
|
||||
However, delay the fade out process for 2.5 seconds */
|
||||
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
||||
animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
||||
}
|
||||
|
||||
/* Animations to fade the snackbar in and out */
|
||||
@-webkit-keyframes fadein {
|
||||
from {top: 0; opacity: 0;}
|
||||
to {top: 120px; opacity: 1;}
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {top: 0; opacity: 0;}
|
||||
to {top: 120px; opacity: 1;}
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeout {
|
||||
from {top: 120px; opacity: 1;}
|
||||
to {top: 0; opacity: 0;}
|
||||
}
|
||||
|
||||
@keyframes fadeout {
|
||||
from {top: 120px; opacity: 1;}
|
||||
to {top: 0; opacity: 0;}
|
||||
}
|
||||
@ -6,3 +6,23 @@
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.leaflet-container.move-cursor-enabled {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.leaflet-container.attack-cursor-enabled {
|
||||
cursor: url("../img/buttons/attack.png") 25 15,auto;
|
||||
}
|
||||
|
||||
.leaflet-marker-icon.attack-cursor-enabled {
|
||||
cursor: url("../img/buttons/attack.png") 25 15,auto;
|
||||
}
|
||||
|
||||
.leaflet-container.formation-cursor-enabled {
|
||||
cursor: url("../img/buttons/formation.png") 25 15,auto;
|
||||
}
|
||||
|
||||
.leaflet-marker-icon.formation-cursor-enabled {
|
||||
cursor: url("../img/buttons/formation.png") 25 15,auto;
|
||||
}
|
||||
|
||||
|
||||
@ -8,14 +8,14 @@
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 10px;
|
||||
bottom: -80px;
|
||||
bottom: -102px;
|
||||
transition: bottom 0.2s;
|
||||
}
|
||||
|
||||
.unit-control-panel {
|
||||
background-color: #202831;
|
||||
height: 40px;
|
||||
width: 400px;
|
||||
width: 200px;
|
||||
border: solid white 1px;
|
||||
font-size: 12px;
|
||||
position: fixed;
|
||||
@ -28,7 +28,23 @@
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
.action-panel {
|
||||
background-color: #202831;
|
||||
height: 400px;
|
||||
width: 40px;
|
||||
border: solid white 1px;
|
||||
font-size: 12px;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 10px;
|
||||
top: 50px;
|
||||
transition: height 0.2s;
|
||||
display: flex;
|
||||
align-content: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.settings-panel {
|
||||
background-color: #202831;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
@ -38,6 +54,19 @@
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
.formation-control-panel {
|
||||
background-color: #202831;
|
||||
height: 100px;
|
||||
width: 300px;
|
||||
border: solid white 1px;
|
||||
font-size: 12px;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 830px;
|
||||
bottom: -102px;
|
||||
transition: bottom 0.2s;
|
||||
}
|
||||
|
||||
.panel-table {
|
||||
text-shadow: 1px 1px #000, -1px -1px #000, 1px -1px #000, -1px 1px #000;
|
||||
height: 100%;
|
||||
@ -70,8 +99,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
transition: font-size 0.05s;
|
||||
text-shadow: 1px 1px #000, -1px -1px #000, 1px -1px #000, -1px 1px #000;
|
||||
}
|
||||
|
||||
.panel-button:hover {
|
||||
@ -80,6 +107,7 @@
|
||||
|
||||
.panel-button:active {
|
||||
color: white;
|
||||
filter: brightness(110%);
|
||||
}
|
||||
|
||||
.panel-button-disabled {
|
||||
|
||||
BIN
www/img/airbase.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
www/img/buttons/attack.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
www/img/buttons/bomb.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
www/img/buttons/carpet.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
www/img/buttons/formation.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
www/img/buttons/land.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
www/img/buttons/spawnAWACS.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
www/img/buttons/spawnCAP.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
www/img/buttons/spawnCAS.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
www/img/buttons/spawnDrone.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
www/img/buttons/spawnStrike.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
www/img/buttons/spawnTanker.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
www/img/buttons/spawnTransport.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
@ -7,6 +7,7 @@
|
||||
<link rel="stylesheet" href="css/index.css"/>
|
||||
<link rel="stylesheet" href="css/panels.css"/>
|
||||
<link rel="stylesheet" href="css/Map.css"/>
|
||||
<link rel="stylesheet" href="css/AirbaseMarker.css"/>
|
||||
<link rel="stylesheet" href="css/UnitMarker.css"/>
|
||||
<link rel="stylesheet" href="css/SelectionWheel.css"/>
|
||||
<link rel="stylesheet" href="css/SelectionScroll.css"/>
|
||||
@ -18,36 +19,48 @@
|
||||
<script src="https://unpkg.com/leaflet@1.9.2/dist/leaflet.js"
|
||||
integrity="sha256-o9N1jGDZrf5tS+Ft4gbIK7mYMipq9lqpVJ91xHSyKhg="
|
||||
crossorigin=""></script>
|
||||
<script src="js/payloadNames.js"></script>
|
||||
<script src="js/ControlPanel.js"></script>
|
||||
<script src="js/DCSCommands.js"></script>
|
||||
<script src="js/Utils.js"></script>
|
||||
<script src="js/unitTypes.js"></script>
|
||||
<script src="js/PanelButton.js"></script>
|
||||
<script src="js/UnitInfoPanel.js"></script>
|
||||
<script src="js/UnitControlPanel.js"></script>
|
||||
<script src="js/Map.js"></script>
|
||||
<script src="js/MissionData.js"></script>
|
||||
<script src="js/Unit.js"></script>
|
||||
<script src="js/UnitMarker.js"></script>
|
||||
<script src="js/UnitsManager.js"></script>
|
||||
<script src="js/SelectionWheel.js"></script>
|
||||
<script src="js/SelectionScroll.js"></script>
|
||||
|
||||
<script src="js/DCS/payloadNames.js"></script>
|
||||
<script src="js/DCS/DCSCommands.js"></script>
|
||||
|
||||
<script src="js/Panels/PanelButton.js"></script>
|
||||
<script src="js/Panels/SettingsPanel.js"></script>
|
||||
<script src="js/Panels/UnitInfoPanel.js"></script>
|
||||
<script src="js/Panels/UnitControlPanel.js"></script>
|
||||
<script src="js/Panels/FormationControlPanel.js"></script>
|
||||
<script src="js/Panels/ActionPanel.js"></script>
|
||||
|
||||
<script src="js/Units/unitTypes.js"></script>
|
||||
<script src="js/Units/Unit.js"></script>
|
||||
<script src="js/Units/UnitMarker.js"></script>
|
||||
<script src="js/Units/UnitsManager.js"></script>
|
||||
|
||||
<script src="js/Other/Utils.js"></script>
|
||||
<script src="js/Other/AirbaseMarker.js"></script>
|
||||
<script src="js/Other/MissionData.js"></script>
|
||||
|
||||
<script src="js/Map/Map.js"></script>
|
||||
<script src="js/Map/SelectionWheel.js"></script>
|
||||
<script src="js/Map/SelectionScroll.js"></script>
|
||||
|
||||
<script src="js/index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<table id="content-table">
|
||||
<tr id="header">
|
||||
<td>
|
||||
<div class="control-panel" id="top-control-panel"></div>
|
||||
<div class="settings-panel" id="settings-panel"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="map-container">
|
||||
<div id="map"></div>
|
||||
<div id="log"></div>
|
||||
<div class="unit-info-panel" id="left-panel"></div>
|
||||
<div class="unit-control-panel" id="top-panel"></div>
|
||||
<div class="action-panel" id="action-panel"></div>
|
||||
<div class="unit-info-panel" id="unit-info-panel"></div>
|
||||
<div class="unit-control-panel" id="unit-control-panel"></div>
|
||||
<div class="formation-control-panel" id="formation-control-panel"></div>
|
||||
<div id="snackbar">ASD</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -32,7 +32,7 @@ function spawnGroundUnit(type, latlng, coalition)
|
||||
xhr.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
function spawnAirUnit(type, latlng, coalition, payloadName)
|
||||
function spawnAircraft(type, latlng, coalition, payloadName = "", airbaseName = "")
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("PUT", RESTaddress);
|
||||
@ -43,25 +43,42 @@ function spawnAirUnit(type, latlng, coalition, payloadName)
|
||||
}
|
||||
};
|
||||
|
||||
var command = {"type": type, "location": latlng, "coalition": coalition, "payloadName": payloadName};
|
||||
var command = {"type": type, "location": latlng, "coalition": coalition, "payloadName": payloadName, "airbaseName": airbaseName};
|
||||
var data = {"spawnAir": command}
|
||||
|
||||
xhr.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
function attackUnit(unitID, targetID)
|
||||
function attackUnit(ID, targetID)
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("PUT", RESTaddress);
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4) {
|
||||
console.log("Unit " + unitsManager.getUnit(unitID).unitName + " attack " + unitsManager.getUnit(targetID).unitName );
|
||||
console.log("Unit " + unitsManager.getUnitByID(ID).unitName + " attack " + unitsManager.getUnitByID(targetID).unitName );
|
||||
}
|
||||
};
|
||||
|
||||
var command = {"unitID": unitID, "targetID": targetID};
|
||||
var command = {"ID": ID, "targetID": targetID};
|
||||
var data = {"attackUnit": command}
|
||||
|
||||
xhr.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
function cloneUnit(ID)
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("PUT", RESTaddress);
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4) {
|
||||
console.log("Unit " + unitsManager.getUnitByID(ID).unitName + " cloned");
|
||||
}
|
||||
};
|
||||
|
||||
var command = {"ID": ID};
|
||||
var data = {"cloneUnit": command}
|
||||
|
||||
xhr.send(JSON.stringify(data));
|
||||
}
|
||||
@ -17,7 +17,10 @@ class Map
|
||||
this._map.on('movestart', () => {this.removeSelectionWheel(); this.removeSelectionScroll();});
|
||||
this._map.on('zoomstart', () => {this.removeSelectionWheel(); this.removeSelectionScroll();});
|
||||
this._map.on('selectionend', (e) => unitsManager.selectFromBounds(e.selectionBounds));
|
||||
|
||||
this._map.on('keyup', (e) => unitsManager.handleKeyEvent(e));
|
||||
|
||||
this._map._container.classList.add("action-cursor");
|
||||
|
||||
this.setState("IDLE");
|
||||
|
||||
this._selectionWheel = undefined;
|
||||
@ -52,14 +55,42 @@ class Map
|
||||
setState(newState)
|
||||
{
|
||||
this._state = newState;
|
||||
|
||||
var cursorElements = document.getElementsByClassName("action-cursor");
|
||||
for (let item of cursorElements)
|
||||
{
|
||||
item.classList.remove("move-cursor-enabled", "attack-cursor-enabled", "formation-cursor-enabled");
|
||||
}
|
||||
if (this._state === "IDLE")
|
||||
{
|
||||
L.DomUtil.removeClass(this._map._container, 'move-cursor-enabled');
|
||||
|
||||
}
|
||||
else if (this._state === "UNIT_SELECTED")
|
||||
else if (this._state === "MOVE_UNIT")
|
||||
{
|
||||
L.DomUtil.addClass(this._map._container, 'move-cursor-enabled');
|
||||
for (let item of cursorElements)
|
||||
{
|
||||
item.classList.add("move-cursor-enabled");
|
||||
}
|
||||
}
|
||||
else if (this._state === "ATTACK")
|
||||
{
|
||||
for (let item of cursorElements)
|
||||
{
|
||||
item.classList.add("attack-cursor-enabled");
|
||||
}
|
||||
}
|
||||
else if (this._state === "FORMATION")
|
||||
{
|
||||
for (let item of cursorElements)
|
||||
{
|
||||
item.classList.add("formation-cursor-enabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getState()
|
||||
{
|
||||
return this._state;
|
||||
}
|
||||
|
||||
/* Set the active coalition (for persistency) */
|
||||
@ -77,6 +108,7 @@ class Map
|
||||
// Right click
|
||||
_onContextMenu(e)
|
||||
{
|
||||
this.setState("IDLE");
|
||||
unitsManager.deselectAllUnits();
|
||||
this.removeSelectionWheel();
|
||||
this.removeSelectionScroll();
|
||||
@ -90,7 +122,7 @@ class Map
|
||||
{
|
||||
|
||||
}
|
||||
else if (this._state === "UNIT_SELECTED")
|
||||
else if (this._state === "MOVE_UNIT")
|
||||
{
|
||||
if (!e.originalEvent.ctrlKey)
|
||||
{
|
||||
@ -102,13 +134,16 @@ class Map
|
||||
|
||||
_onDoubleClick(e)
|
||||
{
|
||||
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);
|
||||
if (this._state == 'IDLE')
|
||||
{
|
||||
var options = [
|
||||
{'tooltip': 'Air unit', 'src': 'spawnAir.png', 'callback': () => this._aircraftSpawnMenu(e)},
|
||||
{'tooltip': 'Ground unit', 'src': 'spawnGround.png', 'callback': () => this._groundUnitSpawnMenu(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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Selection wheel and selection scroll functions */
|
||||
@ -130,30 +165,42 @@ class Map
|
||||
}
|
||||
}
|
||||
|
||||
/* Spawn a new air unit selection wheel (TODO, divide units by type, like bomber, fighter, tanker etc)*/
|
||||
_airSpawnMenu(e)
|
||||
/* Show unit selection for air units */
|
||||
spawnFromAirbase(e)
|
||||
{
|
||||
this._selectAircraft(e);
|
||||
}
|
||||
|
||||
/* Spawn a new ground unit selection wheel */
|
||||
_aircraftSpawnMenu(e)
|
||||
{
|
||||
this.removeSelectionWheel();
|
||||
this.removeSelectionScroll();
|
||||
var options = [
|
||||
|
||||
{'coalition': true, 'tooltip': 'CAP', 'src': 'spawnCAP.png', 'callback': () => this._selectAircraft(e, "CAP")},
|
||||
{'coalition': true, 'tooltip': 'CAS', 'src': 'spawnCAS.png', 'callback': () => this._selectAircraft(e, "CAS")},
|
||||
{'coalition': true, 'tooltip': 'Tanker', 'src': 'spawnTanker.png', 'callback': () => this._selectAircraft(e, "tanker")},
|
||||
{'coalition': true, 'tooltip': 'AWACS', 'src': 'spawnAWACS.png', 'callback': () => this._selectAircraft(e, "awacs")},
|
||||
{'coalition': true, 'tooltip': 'Strike', 'src': 'spawnStrike.png', 'callback': () => this._selectAircraft(e, "strike")},
|
||||
{'coalition': true, 'tooltip': 'Drone', 'src': 'spawnDrone.png', 'callback': () => this._selectAircraft(e, "drone")},
|
||||
{'coalition': true, 'tooltip': 'Transport', 'src': 'spawnTransport.png','callback': () => this._selectAircraft(e, "transport")},
|
||||
]
|
||||
this._selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, options);
|
||||
}
|
||||
|
||||
/* Spawn a new ground unit selection wheel */
|
||||
_groundSpawnMenu(e)
|
||||
_groundUnitSpawnMenu(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")}
|
||||
{'coalition': true, 'tooltip': 'Howitzer', 'src': 'spawnHowitzer.png', 'callback': () => this._selectGroundUnit(e, "Howitzers")},
|
||||
{'coalition': true, 'tooltip': 'SAM', 'src': 'spawnSAM.png', 'callback': () => this._selectGroundUnit(e, "SAM")},
|
||||
{'coalition': true, 'tooltip': 'IFV', 'src': 'spawnIFV.png', 'callback': () => this._selectGroundUnit(e, "IFV")},
|
||||
{'coalition': true, 'tooltip': 'Tank', 'src': 'spawnTank.png', 'callback': () => this._selectGroundUnit(e, "Tanks")},
|
||||
{'coalition': true, 'tooltip': 'MLRS', 'src': 'spawnMLRS.png', 'callback': () => this._selectGroundUnit(e, "MLRS")},
|
||||
{'coalition': true, 'tooltip': 'Radar', 'src': 'spawnRadar.png', 'callback': () => this._selectGroundUnit(e, "Radar")},
|
||||
{'coalition': true, 'tooltip': 'Unarmed', 'src': 'spawnUnarmed.png', 'callback': () => this._selectGroundUnit(e, "Unarmed")}
|
||||
]
|
||||
this._selectionWheel = new SelectionWheel(e.originalEvent.x, e.originalEvent.y, options);
|
||||
}
|
||||
@ -185,11 +232,11 @@ class Map
|
||||
}
|
||||
|
||||
/* Show unit selection for air units */
|
||||
_unitSelectAir(e)
|
||||
_selectAircraft(e, group)
|
||||
{
|
||||
this.removeSelectionWheel();
|
||||
this.removeSelectionScroll();
|
||||
var options = unitTypes.air;
|
||||
var options = unitTypes.air[group];
|
||||
options.sort();
|
||||
this._selectionScroll = new SelectionScroll(e.originalEvent.x, e.originalEvent.y, options, (unitType) => {
|
||||
this.removeSelectionWheel();
|
||||
@ -205,16 +252,23 @@ class Map
|
||||
this.removeSelectionScroll();
|
||||
var options = [];
|
||||
options = payloadNames[unitType]
|
||||
options.sort();
|
||||
this._selectionScroll = new SelectionScroll(e.originalEvent.x, e.originalEvent.y, options, (payloadName) => {
|
||||
this.removeSelectionWheel();
|
||||
this.removeSelectionScroll();
|
||||
spawnAirUnit(unitType, e.latlng, this._activeCoalition, payloadName);
|
||||
});
|
||||
if (options != undefined && options.length > 0)
|
||||
{
|
||||
options.sort();
|
||||
this._selectionScroll = new SelectionScroll(e.originalEvent.x, e.originalEvent.y, options, (payloadName) => {
|
||||
this.removeSelectionWheel();
|
||||
this.removeSelectionScroll();
|
||||
spawnAircraft(unitType, e.latlng, this._activeCoalition, payloadName, e.airbaseName);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnAircraft(unitType, e.latlng, this._activeCoalition);
|
||||
}
|
||||
}
|
||||
|
||||
/* Show unit selection for ground units */
|
||||
_unitSelectGround(e, group)
|
||||
_selectGroundUnit(e, group)
|
||||
{
|
||||
this.removeSelectionWheel();
|
||||
this.removeSelectionScroll();
|
||||
@ -36,6 +36,7 @@ class SelectionWheel
|
||||
var image = document.createElement("img");
|
||||
image.classList.add("selection-wheel-image");
|
||||
image.src = `img/buttons/${this._options[id].src}`
|
||||
image.title = this._options[id].tooltip;
|
||||
if ('tint' in this._options[id])
|
||||
{
|
||||
button.style.setProperty('background-color', this._options[id].tint);
|
||||
@ -1,39 +0,0 @@
|
||||
class MissionData
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this._bullseye = undefined;
|
||||
this._bullseyeMarker = undefined;
|
||||
}
|
||||
|
||||
update(data)
|
||||
{
|
||||
this._bullseye = data.missionData.bullseye;
|
||||
this._unitsData = data.missionData.unitsData;
|
||||
this._drawBullseye();
|
||||
}
|
||||
|
||||
getUnitData(ID)
|
||||
{
|
||||
if (ID in this._unitsData)
|
||||
{
|
||||
return this._unitsData[ID];
|
||||
}
|
||||
else
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
_drawBullseye()
|
||||
{
|
||||
if (this._bullseyeMarker === undefined)
|
||||
{
|
||||
this._bullseyeMarker = L.marker([this._bullseye.lat, this._bullseye.lng]).addTo(map.getMap());
|
||||
}
|
||||
else
|
||||
{
|
||||
this._bullseyeMarker .setLatLng(new L.LatLng(this._bullseye.lat, this._bullseye.lng));
|
||||
}
|
||||
}
|
||||
}
|
||||
53
www/js/Other/AirbaseMarker.js
Normal file
@ -0,0 +1,53 @@
|
||||
L.Marker.AirbaseMarker = L.Marker.extend(
|
||||
{
|
||||
options: {
|
||||
name: "No name",
|
||||
position: undefined,
|
||||
coalitionID: 2,
|
||||
iconSrc: "img/airbase.png"
|
||||
},
|
||||
|
||||
// Marker constructor
|
||||
initialize: function(latlng, options) {
|
||||
this._latlng = latlng;
|
||||
if (options != undefined)
|
||||
{
|
||||
L.setOptions(this, options);
|
||||
}
|
||||
var icon = new L.DivIcon({
|
||||
html: `<table class="unitmarker-container-table" id="container-table">
|
||||
<tr>
|
||||
<td>
|
||||
<img class="airbasemarker-icon-img" id="icon-img" src="${this.options.iconSrc}">
|
||||
<div class="airbasemarker-name-div" id="name">${this.options.name}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>`,
|
||||
className: 'airbase-marker-icon'}); // Set the marker, className must be set to avoid white square
|
||||
this.setIcon(icon);
|
||||
},
|
||||
|
||||
setCoalitionID: function(coalitionID)
|
||||
{
|
||||
this.options.coalitionID = coalitionID;
|
||||
// Set the coalitionID
|
||||
var img = this._icon.querySelector("#icon-img");
|
||||
img.classList.remove("airbasemarker-icon-img-blue");
|
||||
img.classList.remove("airbasemarker-icon-img-red");
|
||||
if (this.options.coalitionID == 2)
|
||||
{
|
||||
img.classList.add("airbasemarker-icon-img-blue");
|
||||
}
|
||||
else if (this.options.coalitionID == 1)
|
||||
{
|
||||
img.classList.add("airbasemarker-icon-img-red");
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// By default markers can be hovered and clicked
|
||||
L.Marker.AirbaseMarker.addInitHook(function()
|
||||
{
|
||||
|
||||
});
|
||||
67
www/js/Other/MissionData.js
Normal file
@ -0,0 +1,67 @@
|
||||
class MissionData
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this._bullseye = undefined;
|
||||
this._bullseyeMarker = undefined;
|
||||
this._airbasesMarkers = {};
|
||||
}
|
||||
|
||||
update(data)
|
||||
{
|
||||
this._bullseye = data.missionData.bullseye;
|
||||
this._unitsData = data.missionData.unitsData;
|
||||
this._airbases = data.missionData.airbases;
|
||||
this._drawBullseye();
|
||||
this._drawAirbases();
|
||||
}
|
||||
|
||||
getUnitData(ID)
|
||||
{
|
||||
if (ID in this._unitsData)
|
||||
{
|
||||
return this._unitsData[ID];
|
||||
}
|
||||
else
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
_drawBullseye()
|
||||
{
|
||||
if (this._bullseyeMarker === undefined)
|
||||
{
|
||||
this._bullseyeMarker = new L.Marker([this._bullseye.lat, this._bullseye.lng]).addTo(map.getMap());
|
||||
}
|
||||
else
|
||||
{
|
||||
this._bullseyeMarker.setLatLng(new L.LatLng(this._bullseye.lat, this._bullseye.lng));
|
||||
}
|
||||
}
|
||||
|
||||
_drawAirbases()
|
||||
{
|
||||
for (let idx in this._airbases)
|
||||
{
|
||||
var airbase = this._airbases[idx]
|
||||
if (this._airbasesMarkers[idx] === undefined)
|
||||
{
|
||||
this._airbasesMarkers[idx] = new L.Marker.AirbaseMarker(new L.LatLng(airbase.lat, airbase.lng), {name: airbase.callsign}).addTo(map.getMap());
|
||||
this._airbasesMarkers[idx].on('click', (e) => this._onAirbaseClick(e));
|
||||
}
|
||||
else
|
||||
{
|
||||
this._airbasesMarkers[idx].setCoalitionID(airbase.coalition);
|
||||
this._airbasesMarkers[idx].setLatLng(new L.LatLng(airbase.lat, airbase.lng));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onAirbaseClick(e)
|
||||
{
|
||||
e.airbaseName = e.sourceTarget.options.name;
|
||||
e.coalitionID = e.sourceTarget.coalitionID;
|
||||
map.spawnFromAirbase(e);
|
||||
}
|
||||
}
|
||||
30
www/js/Panels/ActionPanel.js
Normal file
@ -0,0 +1,30 @@
|
||||
class ActionPanel
|
||||
{
|
||||
constructor(id)
|
||||
{
|
||||
this._panel = document.getElementById(id);
|
||||
|
||||
this._attackButton = new PanelButton(this._panel, "img/buttons/attack.png", "Attack unit");
|
||||
this._bombButton = new PanelButton(this._panel, "img/buttons/bomb.png", "Precision bombing");
|
||||
this._carpetButton = new PanelButton(this._panel, "img/buttons/carpet.png", "Carpet bombing");
|
||||
this._landButton = new PanelButton(this._panel, "img/buttons/land.png", "Land here");
|
||||
this._formationButton = new PanelButton(this._panel, "img/buttons/formation.png", "Create formation");
|
||||
|
||||
this._attackButton.addCallback(() => map.setState("ATTACK"));
|
||||
this._bombButton.addCallback(() => map.setState("BOMB"));
|
||||
this._carpetButton.addCallback(() => map.setState("CARPET_BOMB"));
|
||||
this._landButton.addCallback(() => map.setState("LAND"));
|
||||
this._formationButton.addCallback(() => map.setState("FORMATION"));
|
||||
|
||||
this.setEnabled(false);
|
||||
}
|
||||
|
||||
setEnabled(enabled)
|
||||
{
|
||||
this._attackButton.setEnabled(enabled);
|
||||
this._bombButton.setEnabled(false);
|
||||
this._carpetButton.setEnabled(false);
|
||||
this._landButton.setEnabled(false);
|
||||
this._formationButton.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
82
www/js/Panels/FormationControlPanel.js
Normal file
@ -0,0 +1,82 @@
|
||||
class FormationControlPanel
|
||||
{
|
||||
constructor(id)
|
||||
{
|
||||
this._panel = document.getElementById(id);
|
||||
|
||||
this._formations = ["", "Echelon", "Line abreast", "Box", "Trail", "Finger tip", "Tactical line abreast", "Fluid four", "Spread four"];
|
||||
}
|
||||
|
||||
update(selectedUnits)
|
||||
{
|
||||
if (selectedUnits.length == 1)
|
||||
{
|
||||
// Don't update if user is editing
|
||||
if (selectedUnits[0].leader && !this._editing)
|
||||
{
|
||||
this._panel.style.bottom = "15px";
|
||||
this._showFormationControls(selectedUnits[0]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this._panel.style.bottom = (-this._panel.offsetHeight - 2) + "px";
|
||||
this._showFormationControls(); // Empty, cleans the panel
|
||||
}
|
||||
}
|
||||
|
||||
_showFormationControls(selectedUnit)
|
||||
{
|
||||
if (selectedUnit !== undefined)
|
||||
{
|
||||
this._panel.innerHTML = `
|
||||
<div style="display: flex">
|
||||
<table class="panel-table" id="unit-info-table">
|
||||
<tr>
|
||||
<td colspan="4" class="panel-title">
|
||||
FORMATION CONTROL
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="panel-label">
|
||||
Formation:
|
||||
</td>
|
||||
<td class="panel-content">
|
||||
${selectedUnit.formationID}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="panel-label">
|
||||
Formation type:
|
||||
</td>
|
||||
<td class="panel-content">
|
||||
<select id="formation-type-select"></select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
|
||||
var select = document.getElementById("formation-type-select");
|
||||
for(var i = 0; i < this._formations.length; i++) {
|
||||
var opt = this._formations[i];
|
||||
var el = document.createElement("option");
|
||||
el.textContent = opt;
|
||||
el.value = opt;
|
||||
select.appendChild(el);
|
||||
}
|
||||
|
||||
select.addEventListener("focus", () => this._editing = true)
|
||||
select.addEventListener("blur", () => this._editing = false)
|
||||
object.addEventListener("change", () => leader.setformation());
|
||||
|
||||
select.value = selectedUnit.formation;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._panel.innerHTML = ``;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
class PanelButton
|
||||
{
|
||||
constructor(parent, icon)
|
||||
constructor(parent, icon, tooltip)
|
||||
{
|
||||
this._div = document.createElement("div");
|
||||
this.setIcon(icon);
|
||||
this.setIcon(icon, tooltip);
|
||||
this.setSlashed(false);
|
||||
|
||||
this._div.classList.add("panel-button");
|
||||
@ -38,9 +38,16 @@ class PanelButton
|
||||
this._callbacks = [];
|
||||
}
|
||||
|
||||
setIcon(icon)
|
||||
setIcon(icon, tooltip)
|
||||
{
|
||||
this._baseIcon = `<i class="fa ${icon}"></i>`;
|
||||
if (icon.includes("png"))
|
||||
{
|
||||
this._baseIcon = `<img src="${icon}" title="${tooltip}">`;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._baseIcon = `<i class="fa ${icon}" title="${tooltip}"></i>`;
|
||||
}
|
||||
this._div.innerHTML = this._baseIcon;
|
||||
}
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
class ControlPanel
|
||||
class SettingsPanel
|
||||
{
|
||||
constructor(id)
|
||||
{
|
||||
this._panel = document.getElementById(id);
|
||||
/* Create all buttons, disabled by default */
|
||||
|
||||
/* Create all buttons, disabled by default */
|
||||
this._humanIcon = "fa-user";
|
||||
this._AIIcon = "fa-desktop";
|
||||
this._weaponsIcon = "fa-bomb";
|
||||
this._labelsIcon = "fa-font";
|
||||
this._deadIcon = "fa-skull";
|
||||
|
||||
this._humanButton = new PanelButton(this._panel, this._humanIcon);
|
||||
this._AIButton = new PanelButton(this._panel, this._AIIcon);
|
||||
this._weaponsButton = new PanelButton(this._panel, this._weaponsIcon);
|
||||
this._deadAliveButton = new PanelButton(this._panel, this._deadIcon);
|
||||
this._humanButton = new PanelButton(this._panel, this._humanIcon, "Player visibility");
|
||||
this._AIButton = new PanelButton(this._panel, this._AIIcon, "AI visibility");
|
||||
this._weaponsButton = new PanelButton(this._panel, this._weaponsIcon, "Weapons visibility");
|
||||
this._deadAliveButton = new PanelButton(this._panel, this._deadIcon, "Dead units visibility");
|
||||
|
||||
this._humanButton.addCallback(() => this._onHumanButton());
|
||||
this._AIButton.addCallback(() => this._onAIButton());
|
||||
35
www/js/Panels/UnitControlPanel.js
Normal file
@ -0,0 +1,35 @@
|
||||
class UnitControlPanel
|
||||
{
|
||||
constructor(id)
|
||||
{
|
||||
this._panel = document.getElementById(id);
|
||||
|
||||
/* Create all buttons, disabled by default */
|
||||
//this._moveButton = new PanelButton(this._panel, "fa-play");
|
||||
//this._stopButton = new PanelButton(this._panel, "fa-pause");
|
||||
this._slowButton = new PanelButton(this._panel, "fa-angle-right", "Decelerate");
|
||||
this._fastButton = new PanelButton(this._panel, "fa-angle-double-right", "Accelerate");
|
||||
this._descendButton = new PanelButton(this._panel, "fa-arrow-down", "Descend");
|
||||
this._climbButton = new PanelButton(this._panel, "fa-arrow-up", "Climb");
|
||||
//this._repeatButton = new PanelButton(this._panel, "fa-undo");
|
||||
|
||||
this.setEnabled(false);
|
||||
|
||||
//this._moveButton.addCallback(unitsManager.selectedUnitsMove);
|
||||
//this._stopButton.addCallback(() => unitsManager.selectedUnitsChangeSpeed('stop'));
|
||||
this._slowButton.addCallback(() => unitsManager.selectedUnitsChangeSpeed('slow'));
|
||||
this._fastButton.addCallback(() => unitsManager.selectedUnitsChangeSpeed('fast'));
|
||||
this._descendButton.addCallback(() => unitsManager.selectedUnitsChangeAltitude('descend'));
|
||||
this._climbButton.addCallback(() => unitsManager.selectedUnitsChangeAltitude('climb'));
|
||||
}
|
||||
|
||||
setEnabled(enabled)
|
||||
{
|
||||
//this._moveButton.setEnabled(true);
|
||||
//this._stopButton.setEnabled(true);
|
||||
this._slowButton.setEnabled(enabled);
|
||||
this._fastButton.setEnabled(enabled);
|
||||
this._descendButton.setEnabled(enabled);
|
||||
this._climbButton.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
@ -7,23 +7,15 @@ class UnitInfoPanel
|
||||
|
||||
update(selectedUnits)
|
||||
{
|
||||
if (selectedUnits.length > 0)
|
||||
if (selectedUnits.length == 1)
|
||||
{
|
||||
this._panel.style.bottom = "15px";
|
||||
if (selectedUnits.length == 1)
|
||||
{
|
||||
this._showUnitData(selectedUnits[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._showUnitData();
|
||||
this._panel.style.bottom = "-80px";
|
||||
}
|
||||
this._showUnitData(selectedUnits[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._showUnitData();
|
||||
this._panel.style.bottom = "-80px";
|
||||
this._panel.style.bottom = (-this._panel.offsetHeight - 2) + "px";
|
||||
this._showUnitData(); // Empty, cleans the panel
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,7 +32,7 @@ class UnitInfoPanel
|
||||
var displayName = ammo.desc.displayName;
|
||||
var amount = ammo.count;
|
||||
loadout += amount + "x" + displayName;
|
||||
if (parseInt(index) < Object.keys(selectedUnit.missionData.ammo).length - 1)
|
||||
if (parseInt(index) < Object.keys(selectedUnit.missionData.ammo).length)
|
||||
{
|
||||
loadout += ", ";
|
||||
}
|
||||
@ -143,15 +135,7 @@ class UnitInfoPanel
|
||||
}
|
||||
else
|
||||
{
|
||||
this._panel.innerHTML = `
|
||||
<table class="panel-table">
|
||||
<tr>
|
||||
<td class="panel-title">
|
||||
UNIT INFO
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
`;
|
||||
this._panel.innerHTML = ``;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
class UnitControlPanel
|
||||
{
|
||||
constructor(id)
|
||||
{
|
||||
this._panel = document.getElementById(id);
|
||||
|
||||
/* Create all buttons, disabled by default */
|
||||
this._moveButton = new PanelButton(this._panel, "fa-play");
|
||||
this._stopButton = new PanelButton(this._panel, "fa-pause");
|
||||
this._slowButton = new PanelButton(this._panel, "fa-angle-right");
|
||||
this._fastButton = new PanelButton(this._panel, "fa-angle-double-right");
|
||||
this._descendButton = new PanelButton(this._panel, "fa-arrow-down");
|
||||
this._climbButton = new PanelButton(this._panel, "fa-arrow-up");
|
||||
this._repeatButton = new PanelButton(this._panel, "fa-undo");
|
||||
|
||||
this._moveButton.addCallback(unitsManager.selectedUnitsMove);
|
||||
this._stopButton.addCallback(() => unitsManager.selectedUnitsChangeSpeed('stop'));
|
||||
this._slowButton.addCallback(() => unitsManager.selectedUnitsChangeSpeed('slow'));
|
||||
this._fastButton.addCallback(() => unitsManager.selectedUnitsChangeSpeed('fast'));
|
||||
this._descendButton.addCallback(() => unitsManager.selectedUnitsChangeAltitude('descend'));
|
||||
this._climbButton.addCallback(() => unitsManager.selectedUnitsChangeAltitude('climb'));
|
||||
}
|
||||
|
||||
enableButtons(enableAltitudeButtons)
|
||||
{
|
||||
this._moveButton.setEnabled(true);
|
||||
this._stopButton.setEnabled(true);
|
||||
this._slowButton.setEnabled(true);
|
||||
this._fastButton.setEnabled(true);
|
||||
if (enableAltitudeButtons)
|
||||
{
|
||||
this._descendButton.setEnabled(true);
|
||||
this._climbButton.setEnabled(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
disableButtons()
|
||||
{
|
||||
this._moveButton.setEnabled(false);
|
||||
this._stopButton.setEnabled(false);
|
||||
this._slowButton.setEnabled(false);
|
||||
this._fastButton.setEnabled(false);
|
||||
this._descendButton.setEnabled(false);
|
||||
this._climbButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,6 @@ class Unit
|
||||
// The marker is set by the inherited class
|
||||
this.marker = marker;
|
||||
this.marker.on('click', (e) => this.onClick(e));
|
||||
this.marker.on('contextmenu', (e) => this.onRightClick(e));
|
||||
|
||||
this._selected = false;
|
||||
|
||||
@ -18,6 +17,10 @@ class Unit
|
||||
this._pathPolyline.addTo(map.getMap());
|
||||
|
||||
this._targetsPolylines = [];
|
||||
|
||||
this.leader = true;
|
||||
this.wingmen = [];
|
||||
this.formation = undefined;
|
||||
}
|
||||
|
||||
update(response)
|
||||
@ -37,6 +40,19 @@ class Unit
|
||||
this.speed = response["speed"];
|
||||
this.currentTask = response["currentTask"];
|
||||
|
||||
this.leader = response["leader"];
|
||||
this.wingman = response["wingman"];
|
||||
|
||||
this.wingmen = [];
|
||||
if (response["wingmenIDs"] != undefined)
|
||||
{
|
||||
for (let ID of response["wingmenIDs"])
|
||||
{
|
||||
this.wingmen.push(unitsManager.getUnitByID(ID));
|
||||
}
|
||||
}
|
||||
this.formation = response["formation"];
|
||||
|
||||
this.missionData = missionData.getUnitData(this.ID)
|
||||
|
||||
this.setSelected(this.getSelected() & this.alive)
|
||||
@ -113,22 +129,28 @@ class Unit
|
||||
|
||||
onClick(e)
|
||||
{
|
||||
if (!e.originalEvent.ctrlKey)
|
||||
if (map.getState() === 'IDLE' || map.getState() === 'MOVE_UNIT' || e.originalEvent.ctrlKey)
|
||||
{
|
||||
unitsManager.deselectAllUnits();
|
||||
if (!e.originalEvent.ctrlKey)
|
||||
{
|
||||
unitsManager.deselectAllUnits();
|
||||
}
|
||||
this.setSelected(true);
|
||||
}
|
||||
else if (map.getState() === 'ATTACK')
|
||||
{
|
||||
unitsManager.attackUnit(this.ID);
|
||||
}
|
||||
else if (map.getState() === 'FORMATION')
|
||||
{
|
||||
unitsManager.createFormation(this.ID);
|
||||
}
|
||||
this.setSelected(true);
|
||||
}
|
||||
|
||||
onRightClick(e)
|
||||
{
|
||||
unitsManager.onUnitRightClick(this.ID);
|
||||
}
|
||||
|
||||
drawMarker(settings)
|
||||
{
|
||||
// Hide the marker if disabled
|
||||
if ((settings === 'none' || (controlPanel.getSettings().deadAlive === "alive" && !this.alive)))
|
||||
if ((settings === 'none' || (settingsPanel.getSettings().deadAlive === "alive" && !this.alive)))
|
||||
{
|
||||
// Remove the marker if present
|
||||
if (map.getMap().hasLayer(this.marker))
|
||||
@ -204,30 +226,32 @@ class Unit
|
||||
for (let index in this.missionData.targets[typeIndex])
|
||||
{
|
||||
var targetData = this.missionData.targets[typeIndex][index];
|
||||
var target = unitsManager.getUnit(targetData.object["id_"])
|
||||
var startLatLng = new L.LatLng(this.latitude, this.longitude)
|
||||
var endLatLng = new L.LatLng(target.latitude, target.longitude)
|
||||
|
||||
var color;
|
||||
if (typeIndex === "radar")
|
||||
{
|
||||
color = "#FFFF00";
|
||||
var target = unitsManager.getUnitByID(targetData.object["id_"])
|
||||
if (target != undefined){
|
||||
var startLatLng = new L.LatLng(this.latitude, this.longitude)
|
||||
var endLatLng = new L.LatLng(target.latitude, target.longitude)
|
||||
|
||||
var color;
|
||||
if (typeIndex === "radar")
|
||||
{
|
||||
color = "#FFFF00";
|
||||
}
|
||||
else if (typeIndex === "visual")
|
||||
{
|
||||
color = "#FF00FF";
|
||||
}
|
||||
else if (typeIndex === "rwr")
|
||||
{
|
||||
color = "#00FF00";
|
||||
}
|
||||
else
|
||||
{
|
||||
color = "#FFFFFF";
|
||||
}
|
||||
var targetPolyline = new L.Polyline([startLatLng, endLatLng], {color: color, weight: 3, opacity: 1, smoothFactor: 1});
|
||||
targetPolyline.addTo(map.getMap());
|
||||
this._targetsPolylines.push(targetPolyline)
|
||||
}
|
||||
else if (typeIndex === "visual")
|
||||
{
|
||||
color = "#FF00FF";
|
||||
}
|
||||
else if (typeIndex === "rwr")
|
||||
{
|
||||
color = "#00FF00";
|
||||
}
|
||||
else
|
||||
{
|
||||
color = "#FFFFFF";
|
||||
}
|
||||
var targetPolyline = new L.Polyline([startLatLng, endLatLng], {color: color, weight: 3, opacity: 1, smoothFactor: 1});
|
||||
targetPolyline.addTo(map.getMap());
|
||||
this._targetsPolylines.push(targetPolyline)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -243,7 +267,14 @@ class Unit
|
||||
attackUnit(targetID)
|
||||
{
|
||||
// Call DCS attackUnit function
|
||||
attackUnit(this.ID, targetID);
|
||||
if (this.ID != targetID)
|
||||
{
|
||||
attackUnit(this.ID, targetID);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: show a message
|
||||
}
|
||||
}
|
||||
|
||||
changeSpeed(speedChange)
|
||||
@ -281,19 +312,55 @@ class Unit
|
||||
|
||||
xhr.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
setformation(formation)
|
||||
{
|
||||
// TODO move in dedicated file
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("PUT", RESTaddress);
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4) {
|
||||
console.log(this.unitName + " formation change: " + formation);
|
||||
}
|
||||
};
|
||||
|
||||
var command = {"ID": this.ID, "formation": formation}
|
||||
var data = {"setFormation": command}
|
||||
|
||||
xhr.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
setLeader(wingmenIDs)
|
||||
{
|
||||
// TODO move in dedicated file
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("PUT", RESTaddress);
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4) {
|
||||
console.log(this.unitName + " created formation with: " + wingmenIDs);
|
||||
}
|
||||
};
|
||||
|
||||
var command = {"ID": this.ID, "wingmenIDs": wingmenIDs}
|
||||
var data = {"setLeader": command}
|
||||
|
||||
xhr.send(JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
|
||||
class AirUnit extends Unit
|
||||
{
|
||||
drawMarker()
|
||||
{
|
||||
if (this.flags.human)
|
||||
if (this.flags.Human)
|
||||
{
|
||||
super.drawMarker(controlPanel.getSettings().human);
|
||||
super.drawMarker(settingsPanel.getSettings().human);
|
||||
}
|
||||
else
|
||||
{
|
||||
super.drawMarker(controlPanel.getSettings().AI);
|
||||
super.drawMarker(settingsPanel.getSettings().AI);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -344,7 +411,7 @@ class GroundUnit extends Unit
|
||||
|
||||
drawMarker()
|
||||
{
|
||||
super.drawMarker(controlPanel.getSettings().AI);
|
||||
super.drawMarker(settingsPanel.getSettings().AI);
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,7 +431,7 @@ class NavyUnit extends Unit
|
||||
|
||||
drawMarker()
|
||||
{
|
||||
super.drawMarker(controlPanel.getSettings().AI);
|
||||
super.drawMarker(settingsPanel.getSettings().AI);
|
||||
}
|
||||
}
|
||||
|
||||
@ -372,25 +439,20 @@ class Weapon extends Unit
|
||||
{
|
||||
constructor(ID, data)
|
||||
{
|
||||
// Weapons can not be selected
|
||||
self.selectable = false;
|
||||
super(ID, data);
|
||||
// Weapons can not be selected
|
||||
this.selectable = false;
|
||||
}
|
||||
|
||||
drawMarker()
|
||||
{
|
||||
super.drawMarker(controlPanel.getSettings().weapons);
|
||||
super.drawMarker(settingsPanel.getSettings().weapons);
|
||||
}
|
||||
|
||||
onClick(e)
|
||||
{
|
||||
// Weapons can not be clicked
|
||||
}
|
||||
|
||||
onRightClick(e)
|
||||
{
|
||||
// Weapons can not be clicked
|
||||
}
|
||||
}
|
||||
|
||||
class Missile extends Weapon
|
||||
@ -25,7 +25,7 @@ L.Marker.UnitMarker = L.Marker.extend(
|
||||
</td>
|
||||
</tr>
|
||||
</table>`,
|
||||
className: 'unit-marker-icon'}); // Set the unit marker, className must be set to avoid white square
|
||||
className: 'action-cursor'}); // Set the unit marker, className must be set to avoid white square
|
||||
this.setIcon(icon);
|
||||
},
|
||||
|
||||
@ -38,18 +38,6 @@ L.Marker.UnitMarker = L.Marker.extend(
|
||||
|
||||
this._icon.style.outline = "transparent"; // Removes the rectangular outline
|
||||
|
||||
// Set the unit name in the marker
|
||||
var unitNameDiv = this._icon.querySelector("#unitName");
|
||||
if (this.options.human)
|
||||
{
|
||||
unitNameDiv.innerHTML = `<i class="fas fa-user"></i> ${this.options.unitName}`;
|
||||
}
|
||||
else
|
||||
{
|
||||
unitNameDiv.innerHTML = `${this.options.unitName}`;
|
||||
}
|
||||
unitNameDiv.style.left = (-(unitNameDiv.offsetWidth - this._icon.querySelector("#icon-img").height) / 2) + "px";
|
||||
|
||||
// Set the unit name in the marker
|
||||
var nameDiv = this._icon.querySelector("#name");
|
||||
nameDiv.innerHTML = this.options.name;
|
||||
@ -70,6 +58,7 @@ L.Marker.UnitMarker = L.Marker.extend(
|
||||
// If the unit is not alive it is drawn with darker colours
|
||||
setAlive: function(alive)
|
||||
{
|
||||
this.alive = alive
|
||||
var table = this._icon.querySelector("#container-table");
|
||||
if (alive)
|
||||
{
|
||||
@ -114,7 +103,7 @@ L.Marker.UnitMarker = L.Marker.extend(
|
||||
}
|
||||
else
|
||||
{
|
||||
if (img.classList.contains("unitmarker-icon-img-hovered")) img.classList.remove("unitmarker-icon-img-hovered");
|
||||
img.classList.remove("unitmarker-icon-img-hovered");
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -151,8 +140,11 @@ L.Marker.UnitMarker = L.Marker.extend(
|
||||
|
||||
setLabelsVisibility(visibility)
|
||||
{
|
||||
this._icon.querySelector("#unitName").style.opacity = visibility ? "1": "0";
|
||||
this._icon.querySelector("#unitName").innerHTML = visibility ? this.options.unitName : "";
|
||||
var unitNameDiv = this._icon.querySelector("#unitName");
|
||||
unitNameDiv.style.opacity = visibility ? "1": "0";
|
||||
var unitName = this.options.human ? `<i class="fas fa-user"></i> ${this.options.unitName}` : `${this.options.unitName}`;
|
||||
unitNameDiv.innerHTML = visibility ? unitName : "";
|
||||
unitNameDiv.style.left = (-(unitNameDiv.offsetWidth - this._icon.querySelector("#icon-img").height) / 2) + "px";
|
||||
//this._icon.querySelector("#name").style.opacity = visibility ? "1": "0";
|
||||
//this._icon.querySelector("#name").innerHTML = visibility ? this.options.name : "";
|
||||
this._icon.querySelector("#altitude-div").style.opacity = visibility ? "1": "0";
|
||||
@ -225,8 +217,9 @@ L.Marker.UnitMarker.WeaponMarker = L.Marker.UnitMarker.extend({})
|
||||
L.Marker.UnitMarker.WeaponMarker.addInitHook(function()
|
||||
{
|
||||
// Weapons are not selectable
|
||||
this.on('mouseover', function(e) {});
|
||||
this.on('mouseout', function(e) {});
|
||||
this.on('mouseover', function(e) {
|
||||
e.target.setHovered(false);
|
||||
});
|
||||
});
|
||||
|
||||
// Missile
|
||||
@ -3,6 +3,7 @@ class UnitsManager
|
||||
constructor()
|
||||
{
|
||||
this._units = {};
|
||||
this._copiedUnits = [];
|
||||
}
|
||||
|
||||
addUnit(ID, data)
|
||||
@ -15,7 +16,7 @@ class UnitsManager
|
||||
}
|
||||
}
|
||||
|
||||
getUnit(ID)
|
||||
getUnitByID(ID)
|
||||
{
|
||||
return this._units[ID];
|
||||
}
|
||||
@ -50,22 +51,15 @@ class UnitsManager
|
||||
{
|
||||
if (this.getSelectedUnits().length > 0)
|
||||
{
|
||||
map.setState("UNIT_SELECTED");
|
||||
unitControlPanel.enableButtons(true);
|
||||
map.setState("MOVE_UNIT");
|
||||
unitControlPanel.setEnabled(true);
|
||||
actionPanel.setEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
map.setState("IDLE");
|
||||
unitControlPanel.disableButtons();
|
||||
}
|
||||
}
|
||||
|
||||
onUnitRightClick(ID)
|
||||
{
|
||||
var selectedUnits = this.getSelectedUnits();
|
||||
for (let idx in selectedUnits)
|
||||
{
|
||||
selectedUnits[idx].attackUnit(ID);
|
||||
unitControlPanel.setEnabled(false);
|
||||
actionPanel.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,4 +129,102 @@ class UnitsManager
|
||||
selectedUnits[idx].changeAltitude(altitudeChange);
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyEvent(e)
|
||||
{
|
||||
if (e.originalEvent.code === 'KeyC' && e.originalEvent.ctrlKey)
|
||||
{
|
||||
this.copyUnits();
|
||||
}
|
||||
else if (e.originalEvent.code === 'KeyV' && e.originalEvent.ctrlKey)
|
||||
{
|
||||
this.pasteUnits();
|
||||
}
|
||||
}
|
||||
|
||||
copyUnits()
|
||||
{
|
||||
this._copiedUnits = this.getSelectedUnits();
|
||||
}
|
||||
|
||||
pasteUnits()
|
||||
{
|
||||
for (let idx in this._copiedUnits)
|
||||
{
|
||||
var unit = this._copiedUnits[idx];
|
||||
cloneUnit(unit.ID);
|
||||
}
|
||||
}
|
||||
|
||||
attackUnit(ID)
|
||||
{
|
||||
var selectedUnits = this.getSelectedUnits();
|
||||
for (let idx in selectedUnits)
|
||||
{
|
||||
selectedUnits[idx].attackUnit(ID);
|
||||
}
|
||||
}
|
||||
|
||||
createFormation(ID)
|
||||
{
|
||||
var selectedUnits = this.getSelectedUnits();
|
||||
var wingmenIDs = [];
|
||||
for (let idx in selectedUnits)
|
||||
{
|
||||
if (selectedUnits[idx].wingman)
|
||||
{
|
||||
showMessage(selectedUnits[idx].unitName + " is already in a formation.");
|
||||
return;
|
||||
}
|
||||
else if (selectedUnits[idx].leader)
|
||||
{
|
||||
showMessage(selectedUnits[idx].unitName + " is already in a formation.");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (selectedUnits[idx].category !== this.getUnitByID(ID).category)
|
||||
{
|
||||
showMessage("All units must be of the same category to create a formation.");
|
||||
}
|
||||
if (selectedUnits[idx].ID != ID)
|
||||
{
|
||||
wingmenIDs.push(selectedUnits[idx].ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wingmenIDs.length > 0)
|
||||
{
|
||||
this.getUnitByID(ID).setLeader(wingmenIDs);
|
||||
}
|
||||
else
|
||||
{
|
||||
showMessage("At least 2 units must be selected to create a formation.");
|
||||
}
|
||||
}
|
||||
|
||||
getUnitsByFormationID(formationID)
|
||||
{
|
||||
var formationUnits = [];
|
||||
for (let ID in this._units)
|
||||
{
|
||||
if (this._units[ID].formationID == formationID)
|
||||
{
|
||||
formationUnits.push(this._units[ID]);
|
||||
}
|
||||
}
|
||||
return formationUnits;
|
||||
}
|
||||
|
||||
getLeaderByFormationID(formationID)
|
||||
{
|
||||
var formationUnits = this.getUnitsByFormationID(formationID);
|
||||
for (let unit of formationUnits)
|
||||
{
|
||||
if (unit.leader)
|
||||
{
|
||||
return unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -206,68 +206,70 @@ unitTypes.vehicles.Unarmed = [
|
||||
]
|
||||
|
||||
/* AIRPLANES */
|
||||
unitTypes.air = [
|
||||
"Tornado GR4",
|
||||
"Tornado IDS",
|
||||
"F/A-18A",
|
||||
unitTypes.air = {}
|
||||
|
||||
unitTypes.air.CAP = [
|
||||
"F-4E",
|
||||
"F/A-18C",
|
||||
"MiG-29S",
|
||||
"MiG-29A",
|
||||
"F-14A",
|
||||
"Tu-22M3",
|
||||
"F-4E",
|
||||
"B-52H",
|
||||
"MiG-27K",
|
||||
"F-111F",
|
||||
"A-10A",
|
||||
"Su-27",
|
||||
"MiG-29G",
|
||||
"MiG-23MLD",
|
||||
"Su-25",
|
||||
"Su-25TM",
|
||||
"Su-25T",
|
||||
"Su-33",
|
||||
"MiG-25PD",
|
||||
"MiG-25RBT",
|
||||
"Su-30",
|
||||
"MiG-31",
|
||||
"Mirage 2000-5",
|
||||
"F-15C",
|
||||
"F-5E",
|
||||
"F-16C bl.52d",
|
||||
]
|
||||
|
||||
unitTypes.air.CAS = [
|
||||
"Tornado IDS",
|
||||
"F-4E",
|
||||
"F/A-18C",
|
||||
"MiG-27K",
|
||||
"A-10C",
|
||||
"Su-25",
|
||||
"Su-34",
|
||||
"Su-17M4",
|
||||
"MiG-31",
|
||||
"F-15E",
|
||||
]
|
||||
|
||||
unitTypes.air.strike = [
|
||||
"Tu-22M3",
|
||||
"B-52H",
|
||||
"F-111F",
|
||||
"Tu-95MS",
|
||||
"Su-24M",
|
||||
"Tu-160",
|
||||
"F-117A",
|
||||
"B-1B",
|
||||
"S-3B",
|
||||
"S-3B Tanker",
|
||||
"Mirage 2000-5",
|
||||
"F-15C",
|
||||
"F-15E",
|
||||
"F-5E",
|
||||
"MiG-29K",
|
||||
"Tu-142",
|
||||
]
|
||||
|
||||
unitTypes.air.tank = [
|
||||
"S-3B Tanker",
|
||||
"KC-135",
|
||||
"IL-78M",
|
||||
]
|
||||
|
||||
unitTypes.air.awacs = [
|
||||
"A-50",
|
||||
"E-3A",
|
||||
"E-2D",
|
||||
]
|
||||
|
||||
unitTypes.air.drone = [
|
||||
"MQ-1A Predator",
|
||||
"MQ-9 Reaper",
|
||||
]
|
||||
|
||||
unitTypes.air.transport = [
|
||||
"C-130",
|
||||
"An-26B",
|
||||
"An-30M",
|
||||
"C-17A",
|
||||
"A-50",
|
||||
"E-3A",
|
||||
"IL-78M",
|
||||
"E-2D",
|
||||
"IL-76MD",
|
||||
"F-16C bl.50",
|
||||
"F-16C bl.52d",
|
||||
"Su-24MR",
|
||||
"F-16A",
|
||||
"F-16A MLU",
|
||||
"MQ-1A Predator",
|
||||
"Yak-40",
|
||||
"A-10C",
|
||||
"KC-135",
|
||||
"L-39ZA",
|
||||
"P-51D",
|
||||
"MQ-9 Reaper",
|
||||
"FW-190D9",
|
||||
"TF-51D"
|
||||
]
|
||||
|
||||
]
|
||||
@ -1,8 +1,10 @@
|
||||
var missionData;
|
||||
var controlPanel;
|
||||
var settingsPanel;
|
||||
var unitsManager;
|
||||
var unitInfoPanel;
|
||||
var unitControlPanel;
|
||||
var unitActionPanel;
|
||||
var formationControlPanel;
|
||||
var map;
|
||||
var RESTaddress = "http://localhost:30000/restdemo";
|
||||
|
||||
@ -12,10 +14,11 @@ function setup()
|
||||
missionData = new MissionData();
|
||||
unitsManager = new UnitsManager();
|
||||
|
||||
unitInfoPanel = new UnitInfoPanel("left-panel");
|
||||
unitControlPanel = new UnitControlPanel("top-panel");
|
||||
controlPanel = new ControlPanel("top-control-panel");
|
||||
|
||||
unitInfoPanel = new UnitInfoPanel("unit-info-panel");
|
||||
unitControlPanel = new UnitControlPanel("unit-control-panel");
|
||||
formationControlPanel = new FormationControlPanel("formation-control-panel");
|
||||
settingsPanel = new SettingsPanel("settings-panel");
|
||||
actionPanel = new ActionPanel("action-panel")
|
||||
map = new Map();
|
||||
|
||||
// Main update rate. 250ms is minimum time, equal to server update time.
|
||||
@ -26,7 +29,9 @@ function resize()
|
||||
{
|
||||
var unitControlPanelHeight = document.getElementById("header").offsetHeight;
|
||||
document.getElementById("map").style.height = `${window.innerHeight - unitControlPanelHeight - 10}px`;
|
||||
document.getElementById("top-panel").style.left = `${window.innerWidth / 2 - document.getElementById("top-panel").offsetWidth / 2}px`
|
||||
document.getElementById("unit-control-panel").style.left = `${window.innerWidth / 2 - document.getElementById("unit-control-panel").offsetWidth / 2}px`
|
||||
document.getElementById("action-panel").style.top = `${window.innerHeight / 2 - document.getElementById("action-panel").offsetHeight / 2}px`
|
||||
document.getElementById("snackbar").style.left = `${window.innerWidth / 2 - document.getElementById("snackbar").offsetWidth / 2}px`
|
||||
}
|
||||
|
||||
/* GET new data from the server */
|
||||
@ -42,6 +47,7 @@ function update()
|
||||
missionData.update(data);
|
||||
unitsManager.update(data);
|
||||
unitInfoPanel.update(unitsManager.getSelectedUnits());
|
||||
formationControlPanel.update(unitsManager.getSelectedUnits());
|
||||
};
|
||||
|
||||
xmlHttp.onerror = function () {
|
||||
@ -77,4 +83,18 @@ window.console = {
|
||||
},
|
||||
|
||||
lastMessage: "none"
|
||||
}
|
||||
|
||||
function showMessage(message)
|
||||
{
|
||||
// Get the snackbar DIV
|
||||
var x = document.getElementById("snackbar");
|
||||
|
||||
// Add the "show" class to DIV
|
||||
x.className = "show";
|
||||
x.innerHTML = message;
|
||||
|
||||
// After 3 seconds, remove the show class from DIV
|
||||
setTimeout(function(){ x.className = x.className.replace("show", ""); }, 3000);
|
||||
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
python -m http.server
|
||||
11
www/tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"allowJs": true,
|
||||
"target": "es5"
|
||||
},
|
||||
"include": [
|
||||
"./js/*",
|
||||
"./js/**/*"
|
||||
]
|
||||
}
|
||||