From 3607f88e180973fb5371dee40692da3908d6988a Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Mon, 4 Sep 2023 17:27:09 +0200 Subject: [PATCH] Converted clone function to pure lua --- client/src/contextmenus/airbasecontextmenu.ts | 3 +- client/src/server/server.ts | 4 +- client/src/unit/unitsmanager.ts | 150 ++++++++---------- scripts/OlympusCommand.lua | 128 ++++++++------- src/core/include/commands.h | 8 +- src/core/include/datatypes.h | 5 + src/core/src/commands.cpp | 29 ++-- src/core/src/scheduler.cpp | 19 ++- 8 files changed, 181 insertions(+), 165 deletions(-) diff --git a/client/src/contextmenus/airbasecontextmenu.ts b/client/src/contextmenus/airbasecontextmenu.ts index 526b41da..a6304d42 100644 --- a/client/src/contextmenus/airbasecontextmenu.ts +++ b/client/src/contextmenus/airbasecontextmenu.ts @@ -2,6 +2,7 @@ import { getMap, getMissionHandler, getUnitsManager, setActiveCoalition } from " import { GAME_MASTER } from "../constants/constants"; import { Airbase } from "../mission/airbase"; import { dataPointMap } from "../other/utils"; +import { Unit } from "../unit/unit"; import { ContextMenu } from "./contextmenu"; /** This context menu is shown to the user when the airbase marker is right clicked on the map. @@ -38,7 +39,7 @@ export class AirbaseContextMenu extends ContextMenu { this.#setProperties(this.#airbase.getProperties()); this.#setParkings(this.#airbase.getParkings()); this.#setCoalition(this.#airbase.getCoalition()); - this.#showLandButton(getUnitsManager().getSelectedUnitsTypes().length == 1 && ["Aircraft", "Helicopter"].includes(getUnitsManager().getSelectedUnitsTypes()[0]) && (getUnitsManager().getSelectedUnitsCoalition() === this.#airbase.getCoalition() || this.#airbase.getCoalition() === "neutral")) + this.#showLandButton(getUnitsManager().getSelectedUnitsTypes().length == 1 && ["Aircraft", "Helicopter"].includes(getUnitsManager().getSelectedUnitsTypes()[0]) && (getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getCoalition()}) === this.#airbase.getCoalition() || this.#airbase.getCoalition() === "neutral")) this.#showSpawnButton(getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || this.#airbase.getCoalition() == getMissionHandler().getCommandedCoalition()); this.#setAirbaseData(); diff --git a/client/src/server/server.ts b/client/src/server/server.ts index c92ef643..3049fd93 100644 --- a/client/src/server/server.ts +++ b/client/src/server/server.ts @@ -198,8 +198,8 @@ export function followUnit(ID: number, targetID: number, offset: { "x": number, POST(data, () => { }); } -export function cloneUnit(ID: number, latlng: LatLng) { - var command = { "ID": ID, "location": latlng }; +export function cloneUnits(units: {ID: number, location: LatLng}[]) { + var command = { "units": units }; var data = { "cloneUnit": command } POST(data, () => { }); } diff --git a/client/src/unit/unitsmanager.ts b/client/src/unit/unitsmanager.ts index a07cbc5f..e9bc9b92 100644 --- a/client/src/unit/unitsmanager.ts +++ b/client/src/unit/unitsmanager.ts @@ -1,7 +1,7 @@ import { LatLng, LatLngBounds } from "leaflet"; import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler, getUnitsManager, getWeaponsManager } from ".."; import { Unit } from "./unit"; -import { cloneUnit, deleteUnit, refreshAll, spawnAircrafts, spawnGroundUnits, spawnHelicopters, spawnNavyUnits } from "../server/server"; +import { cloneUnits, deleteUnit, spawnAircrafts, spawnGroundUnits, spawnHelicopters, spawnNavyUnits } from "../server/server"; import { bearingAndDistanceToLatLng, deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polyContains, polygonArea, randomPointInPoly, randomUnitBlueprint } from "../other/utils"; import { CoalitionArea } from "../map/coalitionarea"; import { groundUnitDatabase } from "./groundunitdatabase"; @@ -34,10 +34,10 @@ export class UnitsManager { document.addEventListener('keyup', (event) => this.#onKeyUp(event)); document.addEventListener('exportToFile', () => this.exportToFile()); document.addEventListener('importFromFile', () => this.importFromFile()); - document.addEventListener('contactsUpdated', (e: CustomEvent) => {this.#requestDetectionUpdate = true}); - document.addEventListener('commandModeOptionsChanged', () => {Object.values(this.#units).forEach((unit: Unit) => unit.updateVisibility())}); - document.addEventListener('selectedUnitsChangeSpeed', (e: any) => {this.selectedUnitsChangeSpeed(e.detail.type)}); - document.addEventListener('selectedUnitsChangeAltitude', (e: any) => {this.selectedUnitsChangeAltitude(e.detail.type)}); + document.addEventListener('contactsUpdated', (e: CustomEvent) => { this.#requestDetectionUpdate = true }); + document.addEventListener('commandModeOptionsChanged', () => { Object.values(this.#units).forEach((unit: Unit) => unit.updateVisibility()) }); + document.addEventListener('selectedUnitsChangeSpeed', (e: any) => { this.selectedUnitsChangeSpeed(e.detail.type) }); + document.addEventListener('selectedUnitsChangeAltitude', (e: any) => { this.selectedUnitsChangeAltitude(e.detail.type) }); } getSelectableAircraft() { @@ -66,7 +66,7 @@ export class UnitsManager { } addUnit(ID: number, category: string) { - if (category){ + if (category) { /* The name of the unit category is exactly the same as the constructor name */ var constructor = Unit.getConstructor(category); if (constructor != undefined) { @@ -91,21 +91,21 @@ export class UnitsManager { return updateTime; } } - this.#units[ID]?.setData(dataExtractor); + this.#units[ID]?.setData(dataExtractor); } if (this.#requestDetectionUpdate && getMissionHandler().getCommandModeOptions().commandMode != GAME_MASTER) { /* Create a dictionary of empty detection methods arrays */ - var detectionMethods: {[key: string]: number[]} = {}; - for (let ID in this.#units) + var detectionMethods: { [key: string]: number[] } = {}; + for (let ID in this.#units) detectionMethods[ID] = []; - for (let ID in getWeaponsManager().getWeapons()) + for (let ID in getWeaponsManager().getWeapons()) detectionMethods[ID] = []; - + /* Fill the array with the detection methods */ for (let ID in this.#units) { const unit = this.#units[ID]; - if (unit.getAlive() && unit.belongsToCommandedCoalition()){ + if (unit.getAlive() && unit.belongsToCommandedCoalition()) { const contacts = unit.getContacts(); contacts.forEach((contact: Contact) => { const contactID = contact.ID; @@ -196,11 +196,11 @@ export class UnitsManager { } } - deselectUnit( ID:number ) { - if ( this.#units.hasOwnProperty( ID ) ) { + deselectUnit(ID: number) { + if (this.#units.hasOwnProperty(ID)) { this.#units[ID].setSelected(false); } else { - console.error( `deselectUnit(): no unit found with ID "${ID}".` ); + console.error(`deselectUnit(): no unit found with ID "${ID}".`); } } @@ -209,38 +209,33 @@ export class UnitsManager { this.getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setSelected(true)) } - getSelectedUnitsTypes() { - const selectedUnits = this.getSelectedUnits(); - if (selectedUnits.length == 0) + getUnitsTypes(units: Unit[]) { + if (units.length == 0) return []; - return selectedUnits.map((unit: Unit) => { + return units.map((unit: Unit) => { return unit.getCategory(); })?.filter((value: any, index: any, array: string[]) => { return array.indexOf(value) === index; }); - }; + } - /* Gets the value of a variable from the selected units. If all the units have the same value, returns the value, else returns undefined */ - getSelectedUnitsVariable(variableGetter: CallableFunction) { - const selectedUnits = this.getSelectedUnits(); - if (selectedUnits.length == 0) + /* Gets the value of a variable from the units. If all the units have the same value, returns the value, else returns undefined */ + getUnitsVariable(variableGetter: CallableFunction, units: Unit[]) { + if (units.length == 0) return undefined; - return selectedUnits.map((unit: Unit) => { + return units.map((unit: Unit) => { return variableGetter(unit); })?.reduce((a: any, b: any) => { return a === b ? a : undefined }); }; - getSelectedUnitsCoalition() { - const selectedUnits = this.getSelectedUnits(); - if (selectedUnits.length == 0) - return undefined; - return selectedUnits.map((unit: Unit) => { - return unit.getCoalition() - })?.reduce((a: any, b: any) => { - return a == b ? a : undefined - }); + getSelectedUnitsTypes() { + return this.getUnitsTypes(this.getSelectedUnits()); + }; + + getSelectedUnitsVariable(variableGetter: CallableFunction) { + return this.getUnitsVariable(variableGetter, this.getSelectedUnits()); }; getByType(type: string) { @@ -252,10 +247,9 @@ export class UnitsManager { getUnitDetectedMethods(unit: Unit) { var detectionMethods: number[] = []; for (let idx in this.#units) { - if (this.#units[idx].getAlive() && this.#units[idx].getIsLeader() && this.#units[idx].getCoalition() !== "neutral" && this.#units[idx].getCoalition() != unit.getCoalition()) - { + if (this.#units[idx].getAlive() && this.#units[idx].getIsLeader() && this.#units[idx].getCoalition() !== "neutral" && this.#units[idx].getCoalition() != unit.getCoalition()) { this.#units[idx].getContacts().forEach((contact: Contact) => { - if (contact.ID == unit.ID && !detectionMethods.includes(contact.detectionMethod)) + if (contact.ID == unit.ID && !detectionMethods.includes(contact.detectionMethod)) detectionMethods.push(contact.detectionMethod); }); } @@ -414,18 +408,18 @@ export class UnitsManager { selectedUnitsDelete(explosion: boolean = false) { var selectedUnits = this.getSelectedUnits(); /* Can be applied to humans too */ - const selectionContainsAHuman = selectedUnits.some( ( unit:Unit ) => { + const selectionContainsAHuman = selectedUnits.some((unit: Unit) => { return unit.getHuman() === true; }); - if (selectionContainsAHuman && !confirm( "Your selection includes a human player. Deleting humans causes their vehicle to crash.\n\nAre you sure you want to do this?" ) ) { + if (selectionContainsAHuman && !confirm("Your selection includes a human player. Deleting humans causes their vehicle to crash.\n\nAre you sure you want to do this?")) { return; } var immediate = false; if (selectedUnits.length > 20) immediate = confirm(`You are trying to delete ${selectedUnits.length} units, do you want to delete them immediately? This may cause lag for players.`) - + for (let idx in selectedUnits) { selectedUnits[idx].delete(explosion, immediate); } @@ -568,22 +562,22 @@ export class UnitsManager { for (let idx in selectedUnits) { var unit = selectedUnits[idx]; coalition = unit.getCoalition(); - deleteUnit(unit.ID, false, true); - units.push({unitType: unit.getName(), location: unit.getPosition()}); + units.push({ ID: unit.ID, location: unit.getPosition() }); } - const category = this.getSelectedUnitsTypes()[0]; - this.spawnUnits(category, units, coalition, true); + cloneUnits(units); + + } /***********************************************/ copyUnits() { - this.#copiedUnits = JSON.parse(JSON.stringify(this.getSelectedUnits().map((unit: Unit) => {return unit.getData()}))); /* Can be applied to humans too */ + this.#copiedUnits = JSON.parse(JSON.stringify(this.getSelectedUnits().map((unit: Unit) => { return unit.getData() }))); /* Can be applied to humans too */ getInfoPopup().setText(`${this.#copiedUnits.length} units copied`); } // TODO handle from lua pasteUnits() { - if (!this.#pasteDisabled && getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER) { + if (this.#copiedUnits.length > 0 && !this.#pasteDisabled && getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER) { /* Compute the position of the center of the copied units */ var nUnits = this.#copiedUnits.length; var avgLat = 0; @@ -595,30 +589,22 @@ export class UnitsManager { } /* Organize the copied units in groups */ - var groups: {[key: string]: any} = {}; + var groups: { [key: string]: any } = {}; this.#copiedUnits.forEach((unit: any) => { if (!(unit.groupName in groups)) groups[unit.groupName] = []; groups[unit.groupName].push(unit); }); + /* Clone the units in groups */ + var units: { ID: number, location: LatLng }[] = []; for (let groupName in groups) { - /* Paste the units as groups. Only for ground and navy units because of loadouts, TODO: find a better solution so it works for them too*/ - if (!["Aircraft", "Helicopter"].includes(groups[groupName][0].category)) { - var units = groups[groupName].map((unit: any) => { - var position = new LatLng(getMap().getMouseCoordinates().lat + unit.position.lat - avgLat, getMap().getMouseCoordinates().lng + unit.position.lng - avgLng); - getMap().addTemporaryMarker(position, unit.name, unit.coalition); - return {unitType: unit.name, location: position, liveryID: ""}; - }); - this.spawnUnits(groups[groupName][0].category, units, groups[groupName][0].coalition, true); - } - else { - groups[groupName].forEach((unit: any) => { - var position = new LatLng(getMap().getMouseCoordinates().lat + unit.position.lat - avgLat, getMap().getMouseCoordinates().lng + unit.position.lng - avgLng); - getMap().addTemporaryMarker(position, unit.name, unit.coalition); - cloneUnit(unit.ID, position); - }); - } + groups[groupName].forEach((unit: any) => { + var position = new LatLng(getMap().getMouseCoordinates().lat + unit.position.lat - avgLat, getMap().getMouseCoordinates().lng + unit.position.lng - avgLng); + getMap().addTemporaryMarker(position, unit.name, unit.coalition); + units.push({ ID: unit.ID, location: position }); + }); + cloneUnits(units); } getInfoPopup().setText(`${this.#copiedUnits.length - 1} units pasted`); } @@ -627,12 +613,12 @@ export class UnitsManager { } } - createIADS(coalitionArea: CoalitionArea, types: {[key: string]: boolean}, eras: {[key: string]: boolean}, ranges: {[key: string]: boolean}, density: number, distribution: number) { - const activeTypes = Object.keys(types).filter((key: string) => { return types[key]; }); - const activeEras = Object.keys(eras).filter((key: string) => { return eras[key]; }); - const activeRanges = Object.keys(ranges).filter((key: string) => { return ranges[key]; }); + createIADS(coalitionArea: CoalitionArea, types: { [key: string]: boolean }, eras: { [key: string]: boolean }, ranges: { [key: string]: boolean }, density: number, distribution: number) { + const activeTypes = Object.keys(types).filter((key: string) => { return types[key]; }); + const activeEras = Object.keys(eras).filter((key: string) => { return eras[key]; }); + const activeRanges = Object.keys(ranges).filter((key: string) => { return ranges[key]; }); - citiesDatabase.forEach((city: {lat: number, lng: number, pop: number}) => { + citiesDatabase.forEach((city: { lat: number, lng: number, pop: number }) => { if (polyContains(new LatLng(city.lat, city.lng), coalitionArea)) { var pointsNumber = 2 + Math.pow(city.pop, 0.2) * density / 100; for (let i = 0; i < pointsNumber; i++) { @@ -642,9 +628,9 @@ export class UnitsManager { if (polyContains(latlng, coalitionArea)) { const type = activeTypes[Math.floor(Math.random() * activeTypes.length)]; if (Math.random() < IADSDensities[type]) { - const unitBlueprint = randomUnitBlueprint(groundUnitDatabase, {type: type, eras: activeEras, ranges: activeRanges}); + const unitBlueprint = randomUnitBlueprint(groundUnitDatabase, { type: type, eras: activeEras, ranges: activeRanges }); if (unitBlueprint) { - this.spawnUnits("GroundUnit", [{unitType: unitBlueprint.name, location: latlng, liveryID: ""}], coalitionArea.getCoalition(), true); + this.spawnUnits("GroundUnit", [{ unitType: unitBlueprint.name, location: latlng, liveryID: "" }], coalitionArea.getCoalition(), true); getMap().addTemporaryMarker(latlng, unitBlueprint.name, coalitionArea.getCoalition()); } } @@ -655,19 +641,19 @@ export class UnitsManager { } exportToFile() { - var unitsToExport: {[key: string]: any} = {}; + var unitsToExport: { [key: string]: any } = {}; for (let ID in this.#units) { var unit = this.#units[ID]; if (!["Aircraft", "Helicopter"].includes(unit.getCategory())) { var data: any = unit.getData(); if (unit.getGroupName() in unitsToExport) unitsToExport[unit.getGroupName()].push(data); - else + else unitsToExport[unit.getGroupName()] = [data]; } } var a = document.createElement("a"); - var file = new Blob([JSON.stringify(unitsToExport)], {type: 'text/plain'}); + var file = new Blob([JSON.stringify(unitsToExport)], { type: 'text/plain' }); a.href = URL.createObjectURL(file); a.download = 'export.json'; a.click(); @@ -682,12 +668,12 @@ export class UnitsManager { return; } var reader = new FileReader(); - reader.onload = function(e: any) { + reader.onload = function (e: any) { var contents = e.target.result; var groups = JSON.parse(contents); for (let groupName in groups) { - if (groupName !== "" && groups[groupName].length > 0 && (groups[groupName].every((unit: any) => {return unit.category == "GroundUnit";}) || groups[groupName].every((unit: any) => {return unit.category == "NavyUnit";}))) { - var aliveUnits = groups[groupName].filter((unit: any) => {return unit.alive}); + if (groupName !== "" && groups[groupName].length > 0 && (groups[groupName].every((unit: any) => { return unit.category == "GroundUnit"; }) || groups[groupName].every((unit: any) => { return unit.category == "NavyUnit"; }))) { + var aliveUnits = groups[groupName].filter((unit: any) => { return unit.alive }); var units = aliveUnits.map((unit: any) => { return { unitType: unit.name, location: unit.position, liveryID: "" } }); @@ -707,31 +693,31 @@ export class UnitsManager { getInfoPopup().setText("Aircrafts can be air spawned during the SETUP phase only"); return false; } - spawnPoints = units.reduce((points: number, unit: any) => {return points + aircraftDatabase.getSpawnPointsByName(unit.unitType)}, 0); + spawnPoints = units.reduce((points: number, unit: any) => { return points + aircraftDatabase.getSpawnPointsByName(unit.unitType) }, 0); spawnAircrafts(units, coalition, airbase, country, immediate, spawnPoints); } else if (category === "Helicopter") { if (airbase == "" && getMissionHandler().getCommandModeOptions().restrictSpawns && getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) { getInfoPopup().setText("Helicopters can be air spawned during the SETUP phase only"); return false; } - spawnPoints = units.reduce((points: number, unit: any) => {return points + helicopterDatabase.getSpawnPointsByName(unit.unitType)}, 0); + spawnPoints = units.reduce((points: number, unit: any) => { return points + helicopterDatabase.getSpawnPointsByName(unit.unitType) }, 0); spawnHelicopters(units, coalition, airbase, country, immediate, spawnPoints); } else if (category === "GroundUnit") { if (getMissionHandler().getCommandModeOptions().restrictSpawns && getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) { getInfoPopup().setText("Ground units can be spawned during the SETUP phase only"); return false; } - spawnPoints = units.reduce((points: number, unit: any) => {return points + groundUnitDatabase.getSpawnPointsByName(unit.unitType)}, 0); + spawnPoints = units.reduce((points: number, unit: any) => { return points + groundUnitDatabase.getSpawnPointsByName(unit.unitType) }, 0); spawnGroundUnits(units, coalition, country, immediate, spawnPoints); } else if (category === "NavyUnit") { if (getMissionHandler().getCommandModeOptions().restrictSpawns && getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) { getInfoPopup().setText("Navy units can be spawned during the SETUP phase only"); return false; } - spawnPoints = units.reduce((points: number, unit: any) => {return points + navyUnitDatabase.getSpawnPointsByName(unit.unitType)}, 0); + spawnPoints = units.reduce((points: number, unit: any) => { return points + navyUnitDatabase.getSpawnPointsByName(unit.unitType) }, 0); spawnNavyUnits(units, coalition, country, immediate, spawnPoints); } - + if (spawnPoints <= getMissionHandler().getAvailableSpawnPoints()) { getMissionHandler().setSpentSpawnPoints(spawnPoints); return true; @@ -747,7 +733,7 @@ export class UnitsManager { if (event.key === "Delete") this.selectedUnitsDelete(); else if (event.key === "a" && event.ctrlKey) - Object.values(this.getUnits()).filter((unit: Unit) => {return !unit.getHidden()}).forEach((unit: Unit) => unit.setSelected(true)); + Object.values(this.getUnits()).filter((unit: Unit) => { return !unit.getHidden() }).forEach((unit: Unit) => unit.setSelected(true)); } } diff --git a/scripts/OlympusCommand.lua b/scripts/OlympusCommand.lua index 29d285bf..ddb6aedb 100644 --- a/scripts/OlympusCommand.lua +++ b/scripts/OlympusCommand.lua @@ -8,7 +8,7 @@ Olympus.OlympusModPath = os.getenv('DCSOLYMPUS_PATH')..'\\bin\\' Olympus.log = mist.Logger:new("Olympus", 'info') Olympus.unitCounter = 1 -Olympus.payloadRegistry = {} +Olympus.spawnDatabase = {} Olympus.missionData = {} Olympus.unitsData = {} @@ -382,28 +382,26 @@ end function Olympus.spawnUnits(spawnTable) Olympus.debug("Olympus.spawnUnits " .. Olympus.serializeTable(spawnTable), 2) - local unitTable = nil + local unitsTable = nil local route = nil local category = nil if spawnTable.category == 'Aircraft' then - unitTable = Olympus.generateAirUnitsTable(spawnTable.units) + unitsTable = Olympus.generateAirUnitsTable(spawnTable.units) route = Olympus.generateAirUnitsRoute(spawnTable) category = 'plane' elseif spawnTable.category == 'Helicopter' then - unitTable = Olympus.generateAirUnitsTable(spawnTable.units) + unitsTable = Olympus.generateAirUnitsTable(spawnTable.units) route = Olympus.generateAirUnitsRoute(spawnTable) category = 'helicopter' elseif spawnTable.category == 'GroundUnit' then - unitTable = Olympus.generateGroundUnitsTable(spawnTable.units) + unitsTable = Olympus.generateGroundUnitsTable(spawnTable.units) category = 'vehicle' elseif spawnTable.category == 'NavyUnit' then - unitTable = Olympus.generateNavyUnitsTable(spawnTable.units) + unitsTable = Olympus.generateNavyUnitsTable(spawnTable.units) category = 'ship' end - Olympus.debug(Olympus.serializeTable(unitTable), 5) - local countryID = 0 if spawnTable.country == nil or spawnTable.country == "" then countryID = Olympus.getCountryIDByCoalition(spawnTable.coalition) @@ -411,9 +409,14 @@ function Olympus.spawnUnits(spawnTable) countryID = country.id[spawnTable.country] end + -- Save the units in the database, for cloning + for idx, unitTable in pairs(unitTable) do + Olympus.addToDatabase(unitTable) + end + local vars = { - units = unitTable, + units = unitsTable, country = countryID, category = category, route = route, @@ -428,7 +431,7 @@ end -- Generates unit table for a air unit. function Olympus.generateAirUnitsTable(units) - local unitTable = {} + local unitsTable = {} for idx, unit in pairs(units) do local loadout = unit.loadout -- loadout: a string, one of the names defined in unitPayloads.lua. Must be compatible with the unitType local payload = unit.payload -- payload: a table, if present the unit will receive this specific payload. Overrides loadout @@ -446,7 +449,7 @@ function Olympus.generateAirUnitsTable(units) end local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0)) - unitTable[#unitTable + 1] = + unitsTable[#unitsTable + 1] = { ["type"] = unit.unitType, ["x"] = spawnLocation.x, @@ -456,15 +459,12 @@ function Olympus.generateAirUnitsTable(units) ["skill"] = "Excellent", ["payload"] = { ["pylons"] = payload, ["fuel"] = 999999, ["flare"] = 60, ["ammo_type"] = 1, ["chaff"] = 60, ["gun"] = 100, }, ["heading"] = unit.heading, - ["callsign"] = { [1] = 1, [2] = 1, [3] = 1, ["name"] = "Olympus" .. Olympus.unitCounter.. "-" .. #unitTable + 1 }, - ["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1, + ["callsign"] = { [1] = 1, [2] = 1, [3] = 1, ["name"] = "Olympus" .. Olympus.unitCounter.. "-" .. #unitsTable + 1 }, + ["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitsTable + 1, ["livery_id"] = unit.liveryID } - - -- Add the payload to the registry, used for unit cloning - Olympus.payloadRegistry[unitTable[#unitTable].name] = payload end - return unitTable + return unitsTable end function Olympus.generateAirUnitsRoute(spawnTable) @@ -525,99 +525,119 @@ end -- Generates ground units table, either single or from template function Olympus.generateGroundUnitsTable(units) - local unitTable = {} + local unitsTable = {} for idx, unit in pairs(units) do local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0)) if Olympus.hasKey(templates, unit.unitType) then for idx, value in pairs(templates[unit.unitType].units) do - unitTable[#unitTable + 1] = + unitsTable[#unitsTable + 1] = { ["type"] = value.name, ["x"] = spawnLocation.x + value.dx, ["y"] = spawnLocation.z + value.dy, ["heading"] = 0, ["skill"] = "High", - ["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1 + ["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitsTable + 1 } end else - unitTable[#unitTable + 1] = + unitsTable[#unitsTable + 1] = { ["type"] = unit.unitType, ["x"] = spawnLocation.x, ["y"] = spawnLocation.z, ["heading"] = unit.heading, ["skill"] = "High", - ["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1, + ["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitsTable + 1, ["livery_id"] = unit.liveryID } end end - return unitTable + return unitsTable end -- Generates navy units table, either single or from template function Olympus.generateNavyUnitsTable(units) - local unitTable = {} + local unitsTable = {} for idx, unit in pairs(units) do local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0)) if Olympus.hasKey(templates, unit.unitType) then for idx, value in pairs(templates[unit.unitType].units) do - unitTable[#unitTable + 1] = + unitsTable[#unitsTable + 1] = { ["type"] = value.name, ["x"] = spawnLocation.x + value.dx, ["y"] = spawnLocation.z + value.dy, ["heading"] = 0, ["skill"] = "High", - ["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1, + ["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitsTable + 1, ["transportable"] = { ["randomTransportable"] = false } } end else - unitTable[#unitTable + 1] = + unitsTable[#unitsTable + 1] = { ["type"] = unit.unitType, ["x"] = spawnLocation.x, ["y"] = spawnLocation.z, ["heading"] = unit.heading, ["skill"] = "High", - ["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1, + ["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitsTable + 1, ["transportable"] = { ["randomTransportable"] = false }, ["livery_id"] = unit.liveryID } end end - return unitTable + return unitsTable end --- 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, lat, lng, category) - Olympus.debug("Olympus.clone " .. ID .. ", " .. category, 2) - local unit = Olympus.getUnitByID(ID) - if unit then - local position = unit:getPosition() - local heading = math.atan2( position.x.z, position.x.x ) - - -- TODO: understand category in this script - local spawnTable = { +function Olympus.addToDatabase(unitTable) + -- Add the unit data to the database, used for unit cloning + Olympus.spawnDatabase[unitTable.name] = unitTable +end + +-- 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 (TO BE VERIFIED). +function Olympus.clone(cloneTable) + Olympus.debug("Olympus.clone " .. cloneTable, 2) + + local unitsTable = {} + local coalition = nil + local category = nil + + for cloneData, idx in pairs(cloneTable) do + local ID = cloneData.ID + local unit = Olympus.getUnitByID(ID) + + if unit then + local position = unit:getPosition() + local heading = math.atan2( position.x.z, position.x.x ) + + -- Update the data of the cloned unit + local unitTable = Olympus.spawnDatabase[unit:getName()] + + if unitTable then + unitTable["lat"] = lat + unitTable["lng"] = lng + unitTable["alt"] = unit:getPoint().y + unitTable["heading"] = heading + end + coalition = Olympus.getCoalitionByCoalitionID(unit:getCoalition()), - category = category, - units = { - [1] = { - lat = lat, - lng = lng, - alt = unit:getPoint().y, - heading = heading, - unitType = unit:getTypeName(), - payload = Olympus.payloadRegistry[unit:getName()] - } - } - } - Olympus.spawnUnits(spawnTable) + category = unit:getDesc().category, + + unitsTable[#unitsTable + 1] = unitTable + end end + + local spawnTable = { + coalition = coalition, + category = category, + units = unitsTable + } + Olympus.spawnUnits(spawnTable) + Olympus.debug("Olympus.clone completed successfully", 2) end @@ -915,8 +935,8 @@ end function Olympus.initializeUnits() if mist and mist.DBs and mist.DBs.MEunitsById then - for id, unitTable in pairs(mist.DBs.MEunitsById) do - local unit = Unit.getByName(unitTable["unitName"]) + for id, unitsTable in pairs(mist.DBs.MEunitsById) do + local unit = Unit.getByName(unitsTable["unitName"]) if unit then Olympus.units[unit["id_"]] = unit end diff --git a/src/core/include/commands.h b/src/core/include/commands.h index 376e3443..940dcf28 100644 --- a/src/core/include/commands.h +++ b/src/core/include/commands.h @@ -248,9 +248,8 @@ private: class Clone : public Command { public: - Clone(unsigned int ID, Coords location) : - ID(ID), - location(location) + Clone(vector cloneOptions) : + cloneOptions(cloneOptions) { priority = CommandPriority::LOW; }; @@ -258,8 +257,7 @@ public: virtual unsigned int getLoad() { return 30; } private: - const unsigned int ID; - const Coords location; + const vector cloneOptions; }; /* Delete unit command */ diff --git a/src/core/include/datatypes.h b/src/core/include/datatypes.h index 0e776308..f3859456 100644 --- a/src/core/include/datatypes.h +++ b/src/core/include/datatypes.h @@ -120,4 +120,9 @@ struct SpawnOptions { Coords location; string loadout; string liveryID; +}; + +struct CloneOptions { + unsigned int ID; + Coords location; }; \ No newline at end of file diff --git a/src/core/src/commands.cpp b/src/core/src/commands.cpp index fbfd3779..12a6d413 100644 --- a/src/core/src/commands.cpp +++ b/src/core/src/commands.cpp @@ -140,22 +140,21 @@ string SpawnHelicopters::getString() /* Clone unit command */ string Clone::getString() { - Unit* unit = unitsManager->getUnit(ID); - if (unit != nullptr) - { - std::ostringstream commandSS; - commandSS.precision(10); - commandSS << "Olympus.clone, " - << ID << ", " - << location.lat << ", " - << location.lng << ", " - << "\"" << unit->getCategory() << "\""; - return commandSS.str(); - } - else - { - return ""; + std::ostringstream unitsSS; + unitsSS.precision(10); + for (int i = 0; i < cloneOptions.size(); i++) { + unitsSS << "[" << i + 1 << "] = {" + << "ID = " << cloneOptions[i].ID << ", " + << "lat = " << cloneOptions[i].location.lat << ", " + << "lng = " << cloneOptions[i].location.lng << " }, "; } + + std::ostringstream commandSS; + commandSS.precision(10); + commandSS << "Olympus.clone, " + << "{" << unitsSS.str() << "}"; + return commandSS.str(); + } /* Delete unit command */ diff --git a/src/core/src/scheduler.cpp b/src/core/src/scheduler.cpp index a9c88b78..ca65688a 100644 --- a/src/core/src/scheduler.cpp +++ b/src/core/src/scheduler.cpp @@ -376,13 +376,20 @@ void Scheduler::handleRequest(string key, json::value value, string username) if (unit != nullptr) unit->setDesiredAltitudeType(to_string(value[L"altitudeType"])); } - else if (key.compare("cloneUnit") == 0) + else if (key.compare("cloneUnits") == 0) { - unsigned int ID = value[L"ID"].as_integer(); - double lat = value[L"location"][L"lat"].as_double(); - double lng = value[L"location"][L"lng"].as_double(); - Coords loc; loc.lat = lat; loc.lng = lng; - command = dynamic_cast(new Clone(ID, loc)); + vector cloneOptions; + + for (auto unit : value[L"units"].as_array()) { + unsigned int ID = unit[L"ID"].as_integer(); + double lat = unit[L"location"][L"lat"].as_double(); + double lng = unit[L"location"][L"lng"].as_double(); + + Coords location; location.lat = lat; location.lng = lng; + cloneOptions.push_back({ ID, location }); + } + + command = dynamic_cast(new Clone(cloneOptions)); } else if (key.compare("setROE") == 0) {