From edc3529b677193b4d09bef0bc78370081422fe4c Mon Sep 17 00:00:00 2001 From: MarcoJayUsai Date: Tue, 15 Apr 2025 22:20:15 +0200 Subject: [PATCH 1/4] feature(map): showing ground units engagement rings when selected and no other global rings are selected --- backend/core/src/groundunit.cpp | 20 +++++++++--------- backend/core/src/navyunit.cpp | 25 ++++++++++++++++++++++ frontend/react/src/index.css | 4 ++++ frontend/react/src/unit/unit.ts | 37 +++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 10 deletions(-) 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/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/unit/unit.ts b/frontend/react/src/unit/unit.ts index ec4a0a0d..dbd588a7 100644 --- a/frontend/react/src/unit/unit.ts +++ b/frontend/react/src/unit/unit.ts @@ -170,6 +170,7 @@ export abstract class Unit extends CustomMarker { #contactsPolylines: Polyline[] = []; #engagementCircle: RangeCircle; #acquisitionCircle: RangeCircle; + #temporaryEngagementCircle: RangeCircle; #miniMapMarker: CircleMarker | null = null; #targetPositionMarker: TargetMarker; #targetPositionPolyline: Polyline; @@ -442,6 +443,17 @@ export abstract class Unit extends CustomMarker { interactive: false, bubblingMouseEvents: false, }); + this.#temporaryEngagementCircle = new RangeCircle(this.getPosition(), { + radius: 0, + weight: 3, + opacity: 0.8, + fillOpacity: 0.02, + dashArray: "4 8", + interactive: false, + bubblingMouseEvents: false, + color: '#f6b13b', + stroke: true + }); this.#racetrackPolylines = [ new Polyline([], { color: colors.WHITE, weight: 3, smoothFactor: 1, dashArray: "5, 5" }), @@ -511,6 +523,10 @@ export abstract class Unit extends CustomMarker { }); if (this.getSelected()) this.drawLines(); + + if (mapOptions.showUnitsEngagementRings || mapOptions.showUnitsAcquisitionRings) { + this.hideTemporaryEngagementRing(); + } }); CommandModeOptionsChangedEvent.on((commandModeOptions) => { @@ -921,12 +937,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 +1622,25 @@ 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() { + console.log(`Show temporary engagement ring for ${this.getUnitName()}, engagement range: ${this.#engagementRange}`); + + + if (!getApp().getMap().getOptions().showUnitsEngagementRings + && !getApp().getMap().getOptions().showUnitsAcquisitionRings + && this.#engagementRange > 0) { + this.#temporaryEngagementCircle.setLatLng(this.getPosition()); + this.#temporaryEngagementCircle.setRadius(this.#engagementRange); + this.#temporaryEngagementCircle.addTo(getApp().getMap()); + } + } + + /** Hide temporary engagement ring */ + hideTemporaryEngagementRing() { + this.#temporaryEngagementCircle.removeFrom(getApp().getMap()); + } + /***********************************************/ onAdd(map: Map): this { super.onAdd(map); From 5237dc688a92c982b9c8e9ad9ed602d1c2148350 Mon Sep 17 00:00:00 2001 From: MarcoJayUsai Date: Mon, 28 Apr 2025 16:14:07 +0200 Subject: [PATCH 2/4] refactor: adding back country, group id and original unit id to the unit data for future uses --- backend/core/include/datatypes.h | 2 ++ backend/core/include/unit.h | 6 +++++ backend/core/src/unit.cpp | 11 ++++++-- frontend/react/src/constants/constants.ts | 2 ++ frontend/react/src/interfaces.ts | 2 ++ frontend/react/src/unit/unit.ts | 31 ++++++++++++++--------- scripts/lua/backend/OlympusCommand.lua | 7 +++-- 7 files changed, 45 insertions(+), 16 deletions(-) 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/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..cfda3ea7 100644 --- a/frontend/react/src/constants/constants.ts +++ b/frontend/react/src/constants/constants.ts @@ -462,6 +462,8 @@ export enum DataIndexes { name, unitName, callsign, + unitID, + groupID, groupName, state, task, 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/unit/unit.ts b/frontend/react/src/unit/unit.ts index dbd588a7..02708259 100644 --- a/frontend/react/src/unit/unit.ts +++ b/frontend/react/src/unit/unit.ts @@ -97,6 +97,8 @@ export abstract class Unit extends CustomMarker { #name: string = ""; #unitName: string = ""; #callsign: string = ""; + #unitID: number = 0; + #groupID: number = 0; #groupName: string = ""; #state: string = states[0]; #task: string = ""; @@ -236,6 +238,12 @@ export abstract class Unit extends CustomMarker { getCallsign() { return this.#callsign; } + getUnitID() { + return this.#unitID; + } + getGroupID() { + return this.#groupID; + } getGroupName() { return this.#groupName; } @@ -611,6 +619,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; @@ -851,6 +867,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, @@ -1624,9 +1642,6 @@ export abstract class Unit extends CustomMarker { /** Show temporary engagement ring when unit is selected */ showTemporaryEngagementRing() { - console.log(`Show temporary engagement ring for ${this.getUnitName()}, engagement range: ${this.#engagementRange}`); - - if (!getApp().getMap().getOptions().showUnitsEngagementRings && !getApp().getMap().getOptions().showUnitsAcquisitionRings && this.#engagementRange > 0) { @@ -1717,8 +1732,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); @@ -1730,8 +1743,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()); @@ -1756,8 +1767,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 && @@ -1768,7 +1777,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) { @@ -1776,8 +1785,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 aeed0959..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"] = {} @@ -1276,13 +1277,14 @@ function Olympus.setUnitsData(arg, time) table["isAlive"] = unit:isExist() and unit:isActive() and unit:getLife() >= 1 - if unit:isActive() and unit:hasSensors(Unit.SensorType.RADAR) then + --[[ COMMENTING OUT BECAUSE OF CRASHES -- TO BE INVESTIGATED LATER ON ]]-- + --[[ if unit:isActive() and unit:hasSensors(Unit.SensorType.RADAR) then if unit:getRadar() then table["radarState"] = true else table["radarState"] = false end - end + end ]] local group = unit:getGroup() if group ~= nil then @@ -1322,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() From 600b649449cb99c44f29b167551798e00994794c Mon Sep 17 00:00:00 2001 From: MarcoJayUsai Date: Mon, 28 Apr 2025 16:22:51 +0200 Subject: [PATCH 3/4] feat(map): added option to toggle display of temporary engagement rings --- frontend/react/src/constants/constants.ts | 1 + frontend/react/src/types/types.ts | 1 + frontend/react/src/ui/panels/optionsmenu.tsx | 11 +++++++++++ frontend/react/src/unit/unit.ts | 1 + 4 files changed, 14 insertions(+) diff --git a/frontend/react/src/constants/constants.ts b/frontend/react/src/constants/constants.ts index cfda3ea7..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, 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 +
0) { this.#temporaryEngagementCircle.setLatLng(this.getPosition()); this.#temporaryEngagementCircle.setRadius(this.#engagementRange); From 36a80c870804a4e5b2e31efe01893bd763284662 Mon Sep 17 00:00:00 2001 From: MarcoJayUsai Date: Tue, 29 Apr 2025 13:36:40 +0200 Subject: [PATCH 4/4] refactor: temporary engagement ring color is now the same as fixed one --- frontend/react/src/unit/unit.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/frontend/react/src/unit/unit.ts b/frontend/react/src/unit/unit.ts index c94a62d8..e761f6e0 100644 --- a/frontend/react/src/unit/unit.ts +++ b/frontend/react/src/unit/unit.ts @@ -453,14 +453,12 @@ export abstract class Unit extends CustomMarker { }); this.#temporaryEngagementCircle = new RangeCircle(this.getPosition(), { radius: 0, - weight: 3, - opacity: 0.8, - fillOpacity: 0.02, + weight: 4, + opacity: 1, + fillOpacity: 0, dashArray: "4 8", interactive: false, bubblingMouseEvents: false, - color: '#f6b13b', - stroke: true }); this.#racetrackPolylines = [ @@ -1648,6 +1646,17 @@ export abstract class Unit extends CustomMarker { && 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()); } }