diff --git a/backend/core/include/datatypes.h b/backend/core/include/datatypes.h index cda41727..d4a9a3b0 100644 --- a/backend/core/include/datatypes.h +++ b/backend/core/include/datatypes.h @@ -16,6 +16,8 @@ namespace DataIndex { name, unitName, callsign, + unitID, + groupID, groupName, state, task, diff --git a/backend/core/include/unit.h b/backend/core/include/unit.h index 280def0e..4f8a74ac 100644 --- a/backend/core/include/unit.h +++ b/backend/core/include/unit.h @@ -75,6 +75,8 @@ public: virtual void setName(string newValue) { updateValue(name, newValue, DataIndex::name); } virtual void setUnitName(string newValue) { updateValue(unitName, newValue, DataIndex::unitName); } virtual void setCallsign(string newValue) { updateValue(callsign, newValue, DataIndex::callsign); } + virtual void setUnitID(unsigned char newValue) { updateValue(unitID, newValue, DataIndex::unitID); } + virtual void setGroupID(unsigned char newValue) { updateValue(groupID, newValue, DataIndex::groupID); } virtual void setGroupName(string newValue) { updateValue(groupName, newValue, DataIndex::groupName); } virtual void setState(unsigned char newValue) { updateValue(state, newValue, DataIndex::state); }; virtual void setTask(string newValue) { updateValue(task, newValue, DataIndex::task); } @@ -141,6 +143,8 @@ public: virtual string getCallsign() { return callsign; } virtual string getUnitName() { return unitName; } virtual string getGroupName() { return groupName; } + virtual unsigned char getUnitID() { return unitID; } + virtual unsigned char getGroupID() { return groupID; } virtual unsigned char getState() { return state; } virtual string getTask() { return task; } virtual bool getHasTask() { return hasTask; } @@ -206,6 +210,8 @@ protected: string name = ""; string unitName = ""; string callsign = ""; + unsigned char unitID = NULL; + unsigned char groupID = NULL; string groupName = ""; unsigned char state = State::NONE; unsigned char alarmState = AlarmState::AUTO; diff --git a/backend/core/src/groundunit.cpp b/backend/core/src/groundunit.cpp index 4cb8b6bf..717696ea 100644 --- a/backend/core/src/groundunit.cpp +++ b/backend/core/src/groundunit.cpp @@ -40,16 +40,6 @@ GroundUnit::GroundUnit(json::value json, unsigned int ID) : Unit(json, ID) void GroundUnit::setDefaults(bool force) { - if (!getAlive() || !getControlled() || getHuman() || !getIsLeader()) return; - - /* Set the default IDLE state */ - setState(State::IDLE); - - /* Set the default options */ - setROE(ROE::WEAPON_FREE, force); - setOnOff(onOff, force); - setFollowRoads(followRoads, force); - /* Load gun values from database */ if (database.has_object_field(to_wstring(name))) { json::value databaseEntry = database[to_wstring(name)]; @@ -74,6 +64,16 @@ void GroundUnit::setDefaults(bool force) if (databaseEntry.has_number_field(L"acquisitionRange")) setAcquisitionRange(databaseEntry[L"acquisitionRange"].as_number().to_double()); } + + if (!getAlive() || !getControlled() || getHuman() || !getIsLeader()) return; + + /* Set the default IDLE state */ + setState(State::IDLE); + + /* Set the default options */ + setROE(ROE::WEAPON_FREE, force); + setOnOff(onOff, force); + setFollowRoads(followRoads, force); } void GroundUnit::setState(unsigned char newState) diff --git a/backend/core/src/navyunit.cpp b/backend/core/src/navyunit.cpp index 916b00b4..f2112b59 100644 --- a/backend/core/src/navyunit.cpp +++ b/backend/core/src/navyunit.cpp @@ -37,6 +37,31 @@ NavyUnit::NavyUnit(json::value json, unsigned int ID) : Unit(json, ID) void NavyUnit::setDefaults(bool force) { + /* Load gun values from database */ + if (database.has_object_field(to_wstring(name))) { + json::value databaseEntry = database[to_wstring(name)]; + if (databaseEntry.has_number_field(L"barrelHeight")) + setBarrelHeight(databaseEntry[L"barrelHeight"].as_number().to_double()); + if (databaseEntry.has_number_field(L"muzzleVelocity")) + setMuzzleVelocity(databaseEntry[L"muzzleVelocity"].as_number().to_double()); + if (databaseEntry.has_number_field(L"aimTime")) + setAimTime(databaseEntry[L"aimTime"].as_number().to_double()); + if (databaseEntry.has_number_field(L"shotsToFire")) + setShotsToFire(databaseEntry[L"shotsToFire"].as_number().to_uint32()); + if (databaseEntry.has_number_field(L"engagementRange")) + setEngagementRange(databaseEntry[L"engagementRange"].as_number().to_double()); + if (databaseEntry.has_number_field(L"shotsBaseInterval")) + setShotsBaseInterval(databaseEntry[L"shotsBaseInterval"].as_number().to_double()); + if (databaseEntry.has_number_field(L"shotsBaseScatter")) + setShotsBaseScatter(databaseEntry[L"shotsBaseScatter"].as_number().to_double()); + if (databaseEntry.has_number_field(L"targetingRange")) + setTargetingRange(databaseEntry[L"targetingRange"].as_number().to_double()); + if (databaseEntry.has_number_field(L"aimMethodRange")) + setAimMethodRange(databaseEntry[L"aimMethodRange"].as_number().to_double()); + if (databaseEntry.has_number_field(L"acquisitionRange")) + setAcquisitionRange(databaseEntry[L"acquisitionRange"].as_number().to_double()); + } + if (!getAlive() || !getControlled() || getHuman() || !getIsLeader()) return; /* Set the default IDLE state */ diff --git a/backend/core/src/unit.cpp b/backend/core/src/unit.cpp index ef4e9e51..18dcc1a7 100644 --- a/backend/core/src/unit.cpp +++ b/backend/core/src/unit.cpp @@ -41,6 +41,11 @@ void Unit::update(json::value json, double dt) if (json.has_string_field(L"unitName")) setUnitName(to_string(json[L"unitName"])); + if (json.has_number_field(L"groupID")) + setGroupID(json[L"groupID"].as_number().to_int32()); + if (json.has_number_field(L"unitID")) + setUnitID(json[L"unitID"].as_number().to_int32()); + if (json.has_string_field(L"groupName")) setGroupName(to_string(json[L"groupName"])); @@ -49,8 +54,8 @@ void Unit::update(json::value json, double dt) if (json.has_number_field(L"coalitionID")) setCoalition(json[L"coalitionID"].as_number().to_int32()); - //if (json.has_number_field(L"Country")) - // setCountry(json[L"Country"].as_number().to_int32()); + if (json.has_number_field(L"country")) + setCountry(json[L"country"].as_number().to_int32()); /* All units which contain the name "Olympus" are automatically under AI control */ if (getUnitName().find("Olympus") != string::npos) @@ -267,6 +272,8 @@ void Unit::getData(stringstream& ss, unsigned long long time) case DataIndex::name: appendString(ss, datumIndex, name); break; case DataIndex::unitName: appendString(ss, datumIndex, unitName); break; case DataIndex::callsign: appendString(ss, datumIndex, callsign); break; + case DataIndex::unitID: appendNumeric(ss, datumIndex, unitID); break; + case DataIndex::groupID: appendNumeric(ss, datumIndex, groupID); break; case DataIndex::groupName: appendString(ss, datumIndex, groupName); break; case DataIndex::state: appendNumeric(ss, datumIndex, state); break; case DataIndex::task: appendString(ss, datumIndex, task); break; diff --git a/frontend/react/src/constants/constants.ts b/frontend/react/src/constants/constants.ts index 0582aafc..ad9ba46b 100644 --- a/frontend/react/src/constants/constants.ts +++ b/frontend/react/src/constants/constants.ts @@ -406,6 +406,7 @@ export const MAP_OPTIONS_DEFAULTS: MapOptions = { showUnitLabels: true, showUnitsEngagementRings: true, showUnitsAcquisitionRings: true, + showUnitsTemporaryEngagementRings: true, showRacetracks: true, fillSelectedRing: false, showMinimap: false, @@ -462,6 +463,8 @@ export enum DataIndexes { name, unitName, callsign, + unitID, + groupID, groupName, state, task, diff --git a/frontend/react/src/index.css b/frontend/react/src/index.css index 0367ad24..d76df5df 100644 --- a/frontend/react/src/index.css +++ b/frontend/react/src/index.css @@ -45,3 +45,7 @@ direction: rtl !important; } +.range-circle { + filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.8)); +} + diff --git a/frontend/react/src/interfaces.ts b/frontend/react/src/interfaces.ts index f29af8f1..281ea04d 100644 --- a/frontend/react/src/interfaces.ts +++ b/frontend/react/src/interfaces.ts @@ -232,6 +232,8 @@ export interface UnitData { name: string; unitName: string; callsign: string; + unitID: number; + groupID: number; groupName: string; state: string; task: string; diff --git a/frontend/react/src/types/types.ts b/frontend/react/src/types/types.ts index 8e9a57e5..8171da15 100644 --- a/frontend/react/src/types/types.ts +++ b/frontend/react/src/types/types.ts @@ -18,6 +18,7 @@ export type MapOptions = { showUnitLabels: boolean; showUnitsEngagementRings: boolean; showUnitsAcquisitionRings: boolean; + showUnitsTemporaryEngagementRings: boolean; showRacetracks: boolean; fillSelectedRing: boolean; showMinimap: boolean; diff --git a/frontend/react/src/ui/panels/optionsmenu.tsx b/frontend/react/src/ui/panels/optionsmenu.tsx index 98fec5b0..cfbf631a 100644 --- a/frontend/react/src/ui/panels/optionsmenu.tsx +++ b/frontend/react/src/ui/panels/optionsmenu.tsx @@ -160,6 +160,17 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre {}}> Show detection rings +
getApp().getMap().setOption("showUnitsTemporaryEngagementRings", !mapOptions.showUnitsTemporaryEngagementRings)} + > + {}}> + Show temporary engagement rings +
{ @@ -595,6 +617,14 @@ export abstract class Unit extends CustomMarker { case DataIndexes.callsign: this.#callsign = dataExtractor.extractString(); break; + case DataIndexes.unitID: + this.#unitID = dataExtractor.extractUInt8(); + updateMarker = true; + break; + case DataIndexes.groupID: + this.#groupID = dataExtractor.extractUInt8(); + updateMarker = true; + break; case DataIndexes.groupName: this.#groupName = dataExtractor.extractString(); updateMarker = true; @@ -835,6 +865,8 @@ export abstract class Unit extends CustomMarker { name: this.#name, unitName: this.#unitName, callsign: this.#callsign, + unitID: this.#unitID, + groupID: this.#groupID, groupName: this.#groupName, state: this.#state, task: this.#task, @@ -921,12 +953,14 @@ export abstract class Unit extends CustomMarker { /* If selected, update the marker to show the selected effects, else clear all the drawings that are only shown for selected units. */ if (selected) { this.#updateMarker(); + this.showTemporaryEngagementRing(); } else { this.#clearContacts(); this.#clearPath(); this.#clearTargetPosition(); this.#clearRacetrack(); this.#clearSpots(); + this.hideTemporaryEngagementRing(); } /* When the group leader is selected, if grouping is active, all the other group members are also selected */ @@ -1604,6 +1638,34 @@ export abstract class Unit extends CustomMarker { if (!this.#human) getApp().getServerManager().setRacetrack(this.ID, length, anchor, bearing, callback); } + /** Show temporary engagement ring when unit is selected */ + showTemporaryEngagementRing() { + if (!getApp().getMap().getOptions().showUnitsEngagementRings + && !getApp().getMap().getOptions().showUnitsAcquisitionRings + && getApp().getMap().getOptions().showUnitsTemporaryEngagementRings + && this.#engagementRange > 0) { + this.#temporaryEngagementCircle.setLatLng(this.getPosition()); + this.#temporaryEngagementCircle.setRadius(this.#engagementRange); + switch (this.getCoalition()) { + case "red": + this.#temporaryEngagementCircle.options.color = adjustBrightness(colors.RED_COALITION, -20); + break; + case "blue": + this.#temporaryEngagementCircle.options.color = adjustBrightness(colors.BLUE_COALITION, -20); + break; + default: + this.#temporaryEngagementCircle.options.color = adjustBrightness(colors.NEUTRAL_COALITION, -20); + break; + } + this.#temporaryEngagementCircle.addTo(getApp().getMap()); + } + } + + /** Hide temporary engagement ring */ + hideTemporaryEngagementRing() { + this.#temporaryEngagementCircle.removeFrom(getApp().getMap()); + } + /***********************************************/ onAdd(map: Map): this { super.onAdd(map); @@ -1680,8 +1742,6 @@ export abstract class Unit extends CustomMarker { if (this.#debounceTimeout) window.clearTimeout(this.#debounceTimeout); this.#debounceTimeout = window.setTimeout(() => { - console.log(`Left short click on ${this.getUnitName()}`); - if (getApp().getState() === OlympusState.UNIT_CONTROL && getApp().getMap().getContextAction()) { if (getApp().getMap().getContextAction()?.getTarget() === ContextActionTarget.UNIT) getApp().getMap().executeContextAction(this, null, e.originalEvent); else getApp().getMap().executeContextAction(null, this.getPosition(), e.originalEvent); @@ -1693,8 +1753,6 @@ export abstract class Unit extends CustomMarker { } #onLeftLongClick(e: any) { - console.log(`Left long click on ${this.getUnitName()}`); - if (getApp().getState() === OlympusState.IDLE) { this.setSelected(!this.getSelected()); @@ -1719,8 +1777,6 @@ export abstract class Unit extends CustomMarker { } #onRightShortClick(e: any) { - console.log(`Right short click on ${this.getUnitName()}`); - window.clearTimeout(this.#rightMouseDownTimeout); if ( getApp().getState() === OlympusState.UNIT_CONTROL && @@ -1731,7 +1787,7 @@ export abstract class Unit extends CustomMarker { } #onRightLongClick(e: any) { - console.log(`Right long click on ${this.getUnitName()}`); + // console.log(`Right long click on ${this.getUnitName()}`); } #onDoubleClick(e: any) { @@ -1739,8 +1795,6 @@ export abstract class Unit extends CustomMarker { DomEvent.preventDefault(e); e.originalEvent.stopImmediatePropagation(); - console.log(`Double click on ${this.getUnitName()}`); - if (this.#debounceTimeout) window.clearTimeout(this.#debounceTimeout); /* Select all matching units in the viewport */ diff --git a/scripts/lua/backend/OlympusCommand.lua b/scripts/lua/backend/OlympusCommand.lua index 2e3890ed..4161a48c 100644 --- a/scripts/lua/backend/OlympusCommand.lua +++ b/scripts/lua/backend/OlympusCommand.lua @@ -1249,6 +1249,7 @@ function Olympus.setUnitsData(arg, time) local airborne = unit:inAir() -- Fill the data table + table["unitID"] = unit:getID() table["name"] = unit:getTypeName() table["coalitionID"] = unit:getCoalition() table["position"] = {} @@ -1323,6 +1324,7 @@ function Olympus.setUnitsData(arg, time) end -- In case of AI units the callSign and the unitName will be the same table["callsign"] = unit:getName() + table["groupID"] = group:getID() table["groupName"] = group:getName() table["isHuman"] = (unit:getPlayerName() ~= nil) table["hasTask"] = controller:hasTask()