diff --git a/client/public/stylesheets/panels/unitcontrol.css b/client/public/stylesheets/panels/unitcontrol.css index c583dca4..770109f6 100644 --- a/client/public/stylesheets/panels/unitcontrol.css +++ b/client/public/stylesheets/panels/unitcontrol.css @@ -239,6 +239,8 @@ body.feature-forceShowUnitControlPanel #unit-control-panel { #unit-control-panel:not([data-show-roe]) #roe, #unit-control-panel:not([data-show-threat]) #threat, #unit-control-panel:not([data-show-emissions-countermeasures]) #emissions-countermeasures, +#unit-control-panel:not([data-show-shots-scatter]) #shots-scatter, +#unit-control-panel:not([data-show-shots-intensity]) #shots-intensity, #unit-control-panel:not([data-show-tanker-button]) #tanker-on, #unit-control-panel:not([data-show-AWACS-button]) #AWACS-on, #unit-control-panel:not([data-show-on-off]) #ai-on-off, diff --git a/client/public/stylesheets/style/style.css b/client/public/stylesheets/style/style.css index 37609fbd..0a49d7a1 100644 --- a/client/public/stylesheets/style/style.css +++ b/client/public/stylesheets/style/style.css @@ -640,7 +640,9 @@ nav.ol-panel> :last-child { #roe-buttons-container button, #reaction-to-threat-buttons-container button, -#emissions-countermeasures-buttons-container button { +#emissions-countermeasures-buttons-container button, +#shots-scatter-buttons-container button +#shots-intensity-buttons-container button { align-items: center; background-color: transparent; border: 1px solid var(--accent-light-blue); diff --git a/client/public/themes/olympus/images/buttons/intensity/1.svg b/client/public/themes/olympus/images/buttons/intensity/1.svg new file mode 100644 index 00000000..0846e4e6 --- /dev/null +++ b/client/public/themes/olympus/images/buttons/intensity/1.svg @@ -0,0 +1,44 @@ + + diff --git a/client/public/themes/olympus/images/buttons/intensity/2.svg b/client/public/themes/olympus/images/buttons/intensity/2.svg new file mode 100644 index 00000000..5c884188 --- /dev/null +++ b/client/public/themes/olympus/images/buttons/intensity/2.svg @@ -0,0 +1,43 @@ + + diff --git a/client/public/themes/olympus/images/buttons/intensity/3.svg b/client/public/themes/olympus/images/buttons/intensity/3.svg new file mode 100644 index 00000000..f07b2db6 --- /dev/null +++ b/client/public/themes/olympus/images/buttons/intensity/3.svg @@ -0,0 +1,43 @@ + + diff --git a/client/public/themes/olympus/images/buttons/scatter/1.svg b/client/public/themes/olympus/images/buttons/scatter/1.svg new file mode 100644 index 00000000..0846e4e6 --- /dev/null +++ b/client/public/themes/olympus/images/buttons/scatter/1.svg @@ -0,0 +1,44 @@ + + diff --git a/client/public/themes/olympus/images/buttons/scatter/2.svg b/client/public/themes/olympus/images/buttons/scatter/2.svg new file mode 100644 index 00000000..5c884188 --- /dev/null +++ b/client/public/themes/olympus/images/buttons/scatter/2.svg @@ -0,0 +1,43 @@ + + diff --git a/client/public/themes/olympus/images/buttons/scatter/3.svg b/client/public/themes/olympus/images/buttons/scatter/3.svg new file mode 100644 index 00000000..f07b2db6 --- /dev/null +++ b/client/public/themes/olympus/images/buttons/scatter/3.svg @@ -0,0 +1,43 @@ + + diff --git a/client/src/constants/constants.ts b/client/src/constants/constants.ts index e0547d43..b2c5834b 100644 --- a/client/src/constants/constants.ts +++ b/client/src/constants/constants.ts @@ -47,6 +47,18 @@ export const emissionsCountermeasuresDescriptions: string[] = [ "Always on (Radar and ECM always on)" ]; +export const shotsScatterDescriptions: string[] = [ + "Large scatter", + "Medium scatter", + "Small scatter (Radar guided units will track shots when the enemy unit is close)" +]; + +export const shotsIntensityDescriptions: string[] = [ + "Low intensity", + "Medium intensity", + "High intensity" +]; + export const minSpeedValues: { [key: string]: number } = { Aircraft: 100, Helicopter: 0, NavyUnit: 0, GroundUnit: 0 }; export const maxSpeedValues: { [key: string]: number } = { Aircraft: 800, Helicopter: 300, NavyUnit: 60, GroundUnit: 60 }; export const speedIncrements: { [key: string]: number } = { Aircraft: 25, Helicopter: 10, NavyUnit: 5, GroundUnit: 5 }; @@ -202,6 +214,8 @@ export enum DataIndexes { activePath, isLeader, operateAs, + shotsScatter, + shotsIntensity, endOfData = 255 }; diff --git a/client/src/interfaces.ts b/client/src/interfaces.ts index c5d45f36..61bd3409 100644 --- a/client/src/interfaces.ts +++ b/client/src/interfaces.ts @@ -177,6 +177,8 @@ export interface UnitData { activePath: LatLng[]; isLeader: boolean; operateAs: string; + shotsScatter: number; + shotsIntensity: number; } export interface LoadoutItemBlueprint { diff --git a/client/src/panels/unitcontrolpanel.ts b/client/src/panels/unitcontrolpanel.ts index c590c652..74e48944 100644 --- a/client/src/panels/unitcontrolpanel.ts +++ b/client/src/panels/unitcontrolpanel.ts @@ -6,7 +6,7 @@ import { aircraftDatabase } from "../unit/databases/aircraftdatabase"; import { Unit } from "../unit/unit"; import { Panel } from "./panel"; import { Switch } from "../controls/switch"; -import { ROEDescriptions, ROEs, altitudeIncrements, emissionsCountermeasures, emissionsCountermeasuresDescriptions, maxAltitudeValues, maxSpeedValues, minAltitudeValues, minSpeedValues, reactionsToThreat, reactionsToThreatDescriptions, speedIncrements } from "../constants/constants"; +import { ROEDescriptions, ROEs, altitudeIncrements, emissionsCountermeasures, emissionsCountermeasuresDescriptions, maxAltitudeValues, maxSpeedValues, minAltitudeValues, minSpeedValues, reactionsToThreat, reactionsToThreatDescriptions, shotsIntensityDescriptions, shotsScatterDescriptions, speedIncrements } from "../constants/constants"; import { ftToM, knotsToMs, mToFt, msToKnots } from "../other/utils"; import { GeneralSettings, Radio, TACAN } from "../interfaces"; @@ -56,9 +56,19 @@ export class UnitControlPanel extends Panel { return this.#createOptionButton(option, `emissions/${option.toLowerCase()}.svg`, emissionsCountermeasuresDescriptions[index],() => { getApp().getUnitsManager().selectedUnitsSetEmissionsCountermeasures(option); }); }); + this.#optionButtons["shotsScatter"] = [1, 2, 3].map((option: number, index: number) => { + return this.#createOptionButton(option.toString(), `scatter/${option.toString().toLowerCase()}.svg`, shotsScatterDescriptions[index],() => { getApp().getUnitsManager().selectedUnitsSetShotsScatter(option); }); + }); + + this.#optionButtons["shotsIntensity"] = [1, 2, 3].map((option: number, index: number) => { + return this.#createOptionButton(option.toString(), `intensity/${option.toString().toLowerCase()}.svg`, shotsIntensityDescriptions[index],() => { getApp().getUnitsManager().selectedUnitsSetShotsIntensity(option); }); + }); + this.getElement().querySelector("#roe-buttons-container")?.append(...this.#optionButtons["ROE"]); this.getElement().querySelector("#reaction-to-threat-buttons-container")?.append(...this.#optionButtons["reactionToThreat"]); this.getElement().querySelector("#emissions-countermeasures-buttons-container")?.append(...this.#optionButtons["emissionsCountermeasures"]); + this.getElement().querySelector("#shots-scatter-buttons-container")?.append(...this.#optionButtons["shotsScatter"]); + this.getElement().querySelector("#shots-intensity-buttons-container")?.append(...this.#optionButtons["shotsIntensity"]); /* Tanker */ this.#tankerSwitch = new Switch("tanker-on-switch", (value: boolean) => { @@ -195,6 +205,8 @@ export class UnitControlPanel extends Panel { element.toggleAttribute("data-show-roe", !isTanker && !isAWACS); element.toggleAttribute("data-show-threat", (this.#selectedUnitsTypes.includes("Aircraft") || this.#selectedUnitsTypes.includes("Helicopter")) && !(this.#selectedUnitsTypes.includes("GroundUnit") || this.#selectedUnitsTypes.includes("NavyUnit"))); element.toggleAttribute("data-show-emissions-countermeasures", (this.#selectedUnitsTypes.includes("Aircraft") || this.#selectedUnitsTypes.includes("Helicopter")) && !(this.#selectedUnitsTypes.includes("GroundUnit") || this.#selectedUnitsTypes.includes("NavyUnit"))); + element.toggleAttribute("data-show-shots-scatter", this.#selectedUnitsTypes.includes("GroundUnit")); //TODO: more refined + element.toggleAttribute("data-show-shots-intensity", this.#selectedUnitsTypes.includes("GroundUnit")); //TODO: more refined element.toggleAttribute("data-show-tanker-button", getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.isTanker();}) === true); element.toggleAttribute("data-show-AWACS-button", getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.isAWACS();}) === true); element.toggleAttribute("data-show-on-off", (this.#selectedUnitsTypes.includes("GroundUnit") || this.#selectedUnitsTypes.includes("NavyUnit")) && !(this.#selectedUnitsTypes.includes("Aircraft") || this.#selectedUnitsTypes.includes("Helicopter"))); @@ -256,6 +268,14 @@ export class UnitControlPanel extends Panel { button.classList.toggle("selected", this.#units.every((unit: Unit) => unit.getEmissionsCountermeasures() === button.value)) }); + this.#optionButtons["shotsScatter"].forEach((button: HTMLButtonElement) => { + button.classList.toggle("selected", this.#units.every((unit: Unit) => unit.getShotsScatter().toString() === button.value)) + }); + + this.#optionButtons["shotsIntensity"].forEach((button: HTMLButtonElement) => { + button.classList.toggle("selected", this.#units.every((unit: Unit) => unit.getShotsIntensity().toString() === button.value)) + }); + this.#tankerSwitch.setValue(isActiveTanker, false); this.#AWACSSwitch.setValue(isActiveAWACAS, false); this.#onOffSwitch.setValue(onOff, false); diff --git a/client/src/server/servermanager.ts b/client/src/server/servermanager.ts index c5321799..87a72599 100644 --- a/client/src/server/servermanager.ts +++ b/client/src/server/servermanager.ts @@ -357,6 +357,18 @@ export class ServerManager { this.PUT(data, callback); } + setShotsScatter(ID: number, shotsScatter: number, callback: CallableFunction = () => {}) { + var command = { "ID": ID, "shotsScatter": shotsScatter } + var data = { "setShotsScatter": command } + this.PUT(data, callback); + } + + setShotsIntensity(ID: number, shotsIntensity: number, callback: CallableFunction = () => {}) { + var command = { "ID": ID, "shotsIntensity": shotsIntensity } + var data = { "setShotsIntensity": command } + this.PUT(data, callback); + } + setAdvacedOptions(ID: number, isActiveTanker: boolean, isActiveAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings, callback: CallableFunction = () => {}) { var command = { "ID": ID, diff --git a/client/src/unit/unit.ts b/client/src/unit/unit.ts index b28e88aa..1679d1d2 100644 --- a/client/src/unit/unit.ts +++ b/client/src/unit/unit.ts @@ -78,6 +78,8 @@ export class Unit extends CustomMarker { #activePath: LatLng[] = []; #isLeader: boolean = false; #operateAs: string = "blue"; + #shotsScatter: number = 2; + #shotsIntensity: number = 2; #selectable: boolean; #selected: boolean = false; @@ -134,6 +136,8 @@ export class Unit extends CustomMarker { getActivePath() { return this.#activePath }; getIsLeader() { return this.#isLeader }; getOperateAs() { return this.#operateAs }; + getShotsScatter() { return this.#shotsScatter}; + getShotsIntensity() { return this.#shotsIntensity}; static getConstructor(type: string) { if (type === "GroundUnit") return GroundUnit; @@ -245,6 +249,8 @@ export class Unit extends CustomMarker { case DataIndexes.activePath: this.#activePath = dataExtractor.extractActivePath(); break; case DataIndexes.isLeader: this.#isLeader = dataExtractor.extractBool(); updateMarker = true; break; case DataIndexes.operateAs: this.#operateAs = enumToCoalition(dataExtractor.extractUInt8()); break; + case DataIndexes.shotsScatter: this.#shotsScatter = dataExtractor.extractUInt8(); break; + case DataIndexes.shotsIntensity: this.#shotsIntensity = dataExtractor.extractUInt8(); break; } } @@ -308,7 +314,9 @@ export class Unit extends CustomMarker { contacts: this.#contacts, activePath: this.#activePath, isLeader: this.#isLeader, - operateAs: this.#operateAs + operateAs: this.#operateAs, + shotsScatter: this.#shotsScatter, + shotsIntensity: this.#shotsIntensity } } @@ -813,6 +821,16 @@ export class Unit extends CustomMarker { getApp().getServerManager().landAtPoint(this.ID, latlng); } + setShotsScatter(shotsScatter: number) { + if (!this.#human) + getApp().getServerManager().setShotsScatter(this.ID, shotsScatter); + } + + setShotsIntensity(shotsIntensity: number) { + if (!this.#human) + getApp().getServerManager().setShotsIntensity(this.ID, shotsIntensity); + } + /***********************************************/ getActions(): { [key: string]: { text: string, tooltip: string, type: string } } { /* To be implemented by child classes */ // TODO make Unit an abstract class diff --git a/client/src/unit/unitsmanager.ts b/client/src/unit/unitsmanager.ts index 7e6fa97d..148b0ea2 100644 --- a/client/src/unit/unitsmanager.ts +++ b/client/src/unit/unitsmanager.ts @@ -687,6 +687,30 @@ export class UnitsManager { this.#showActionMessage(selectedUnits, `unit simulating fire fight`); } + /** Set a specific shots scatter to all the selected units + * + * @param shotsScatter Value to set + */ + selectedUnitsSetShotsScatter(shotsScatter: number) { + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + for (let idx in selectedUnits) { + selectedUnits[idx].setShotsScatter(shotsScatter); + } + this.#showActionMessage(selectedUnits, `shots scatter set to ${shotsScatter}`); + } + + /** Set a specific shots intensity to all the selected units + * + * @param shotsScatter Value to set + */ + selectedUnitsSetShotsIntensity(shotsIntensity: number) { + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + for (let idx in selectedUnits) { + selectedUnits[idx].setShotsIntensity(shotsIntensity); + } + this.#showActionMessage(selectedUnits, `shots intensity set to ${shotsIntensity}`); + } + /*********************** Control operations on selected units ************************/ /** See getUnitsCategories for more info * diff --git a/client/views/panels/unitcontrol.ejs b/client/views/panels/unitcontrol.ejs index aa48e8a4..0ff44f4f 100644 --- a/client/views/panels/unitcontrol.ejs +++ b/client/views/panels/unitcontrol.ejs @@ -61,6 +61,20 @@ +