Added unit marker to database and implemented better grouping
3
client/@types/olympus/index.d.ts
vendored
@ -590,6 +590,7 @@ declare module "interfaces" {
|
||||
canAAA?: boolean;
|
||||
indirectFire?: boolean;
|
||||
markerFile?: string;
|
||||
unitWhenGrouped?: string;
|
||||
}
|
||||
export interface UnitSpawnOptions {
|
||||
roleType: string;
|
||||
@ -773,7 +774,7 @@ declare module "other/utils" {
|
||||
ranges?: string[];
|
||||
eras?: string[];
|
||||
}): UnitBlueprint | null;
|
||||
export function getMarkerCategoryByName(name: string): "aircraft" | "helicopter" | "groundunit-sam" | "groundunit-other" | "groundunit-sam-radar" | "groundunit-sam-launcher" | "groundunit-ewr";
|
||||
export function getMarkerCategoryByName(name: string): "aircraft" | "helicopter" | "groundunit-other" | "navyunit" | "groundunit";
|
||||
export function getUnitDatabaseByCategory(category: string): import("unit/databases/aircraftdatabase").AircraftDatabase | import("unit/databases/helicopterdatabase").HelicopterDatabase | import("unit/databases/groundunitdatabase").GroundUnitDatabase | import("unit/databases/navyunitdatabase").NavyUnitDatabase | null;
|
||||
export function base64ToBytes(base64: string): ArrayBufferLike;
|
||||
export function enumToState(state: number): string;
|
||||
|
||||
@ -10,7 +10,7 @@ const navyUnitDatabase = require('./public/databases/units/navyUnitDatabase.json
|
||||
const DEMO_UNIT_DATA = {}
|
||||
|
||||
const DEMO_WEAPONS_DATA = {
|
||||
["1001"]:{ category: "Missile", alive: true, coalition: 2, name: "", position: { lat: 37.1, lng: -116, alt: 1000 }, speed: 200, heading: 45 * Math.PI / 180 },
|
||||
/*["1001"]:{ category: "Missile", alive: true, coalition: 2, name: "", position: { lat: 37.1, lng: -116, alt: 1000 }, speed: 200, heading: 45 * Math.PI / 180 }, */
|
||||
}
|
||||
|
||||
class DemoDataGenerator {
|
||||
@ -52,6 +52,10 @@ class DemoDataGenerator {
|
||||
isLeader: true
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
UNCOMMENT TO TEST ALL UNITS
|
||||
|
||||
var databases = Object.assign({}, aircraftDatabase, helicopterDatabase, groundUnitDatabase, navyUnitDatabase);
|
||||
var t = Object.keys(databases).length;
|
||||
var l = Math.floor(Math.sqrt(t));
|
||||
@ -60,28 +64,57 @@ class DemoDataGenerator {
|
||||
let idx = 1;
|
||||
console.log(l)
|
||||
for (let name in databases) {
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = name;
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group-${idx}`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += latIdx / 5;
|
||||
DEMO_UNIT_DATA[idx].position.lng += lngIdx / 5;
|
||||
|
||||
latIdx += 1;
|
||||
if (latIdx === l) {
|
||||
latIdx = 0;
|
||||
lngIdx += 1;
|
||||
}
|
||||
if (databases[name].enabled) {
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = name;
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group-${idx}`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += latIdx / 5;
|
||||
DEMO_UNIT_DATA[idx].position.lng += lngIdx / 5;
|
||||
|
||||
latIdx += 1;
|
||||
if (latIdx === l) {
|
||||
latIdx = 0;
|
||||
lngIdx += 1;
|
||||
}
|
||||
|
||||
if (name in aircraftDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "Aircraft";
|
||||
else if (name in helicopterDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "Helicopter";
|
||||
else if (name in groundUnitDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
else if (name in navyUnitDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "NavyUnit";
|
||||
idx += 1;
|
||||
if (name in aircraftDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "Aircraft";
|
||||
else if (name in helicopterDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "Helicopter";
|
||||
else if (name in groundUnitDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
else if (name in navyUnitDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "NavyUnit";
|
||||
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
let idx = 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = "S_75M_Volhov";
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += idx / 100;
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
DEMO_UNIT_DATA[idx].isLeader = true;
|
||||
|
||||
idx += 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = "SNR_75V";
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += idx / 100;
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
DEMO_UNIT_DATA[idx].isLeader = false;
|
||||
|
||||
idx += 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = "Ural-4320 APA-5D";
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += idx / 100;
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
DEMO_UNIT_DATA[idx].isLeader = false;
|
||||
|
||||
this.startTime = Date.now();
|
||||
}
|
||||
|
||||
|
||||
@ -508,7 +508,7 @@ class GroundUnitEditor extends uniteditor_1.UnitEditor {
|
||||
* @param blueprint The blueprint to edit
|
||||
*/
|
||||
setBlueprint(blueprint) {
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
|
||||
__classPrivateFieldSet(this, _GroundUnitEditor_blueprint, blueprint, "f");
|
||||
if (__classPrivateFieldGet(this, _GroundUnitEditor_blueprint, "f") !== null) {
|
||||
this.contentDiv2.replaceChildren();
|
||||
@ -519,28 +519,29 @@ class GroundUnitEditor extends uniteditor_1.UnitEditor {
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Label", blueprint.label, "text", (value) => { blueprint.label = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Short label", blueprint.shortLabel, "text", (value) => { blueprint.shortLabel = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Type", (_a = blueprint.type) !== null && _a !== void 0 ? _a : "", "text", (value) => { blueprint.type = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Unit when grouped", (_b = blueprint.unitWhenGrouped) !== null && _b !== void 0 ? _b : "", "text", (value) => { blueprint.unitWhenGrouped = value; });
|
||||
(0, utils_1.addDropdownInput)(this.contentDiv2, "Coalition", blueprint.coalition, ["", "blue", "red"], (value) => { blueprint.coalition = value; });
|
||||
(0, utils_1.addDropdownInput)(this.contentDiv2, "Era", blueprint.era, ["WW2", "Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], (value) => { blueprint.era = value; });
|
||||
//addStringInput(this.contentDiv2, "Filename", blueprint.filename?? "", "text", (value: string) => {blueprint.filename = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Cost", (_b = String(blueprint.cost)) !== null && _b !== void 0 ? _b : "", "number", (value) => { blueprint.cost = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Acquisition range [m]", (_c = String(blueprint.acquisitionRange)) !== null && _c !== void 0 ? _c : "", "number", (value) => { blueprint.acquisitionRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Engagement range [m]", (_d = String(blueprint.engagementRange)) !== null && _d !== void 0 ? _d : "", "number", (value) => { blueprint.engagementRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Targeting range [m]", (_e = String(blueprint.targetingRange)) !== null && _e !== void 0 ? _e : "", "number", (value) => { blueprint.targetingRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Aim method range [m]", (_f = String(blueprint.aimMethodRange)) !== null && _f !== void 0 ? _f : "", "number", (value) => { blueprint.aimMethodRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Barrel height [m]", (_g = String(blueprint.barrelHeight)) !== null && _g !== void 0 ? _g : "", "number", (value) => { blueprint.barrelHeight = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Muzzle velocity [m/s]", (_h = String(blueprint.muzzleVelocity)) !== null && _h !== void 0 ? _h : "", "number", (value) => { blueprint.muzzleVelocity = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Aim time [s]", (_j = String(blueprint.aimTime)) !== null && _j !== void 0 ? _j : "", "number", (value) => { blueprint.aimTime = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Shots to fire", (_k = String(blueprint.shotsToFire)) !== null && _k !== void 0 ? _k : "", "number", (value) => { blueprint.shotsToFire = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Shots base interval [s]", (_l = String(blueprint.shotsBaseInterval)) !== null && _l !== void 0 ? _l : "", "number", (value) => { blueprint.shotsBaseInterval = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Shots base scatter [°]", (_m = String(blueprint.shotsBaseScatter)) !== null && _m !== void 0 ? _m : "", "number", (value) => { blueprint.shotsBaseScatter = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Alertness time constant [s]", (_o = String(blueprint.alertnessTimeConstant)) !== null && _o !== void 0 ? _o : "", "number", (value) => { blueprint.alertnessTimeConstant = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Can target point", (_p = blueprint.canTargetPoint) !== null && _p !== void 0 ? _p : false, (value) => { blueprint.canTargetPoint = value; });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Can rearm", (_q = blueprint.canRearm) !== null && _q !== void 0 ? _q : false, (value) => { blueprint.canRearm = value; });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Can operate as AAA", (_r = blueprint.canAAA) !== null && _r !== void 0 ? _r : false, (value) => { blueprint.canAAA = value; });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Indirect fire (e.g. mortar)", (_s = blueprint.indirectFire) !== null && _s !== void 0 ? _s : false, (value) => { blueprint.indirectFire = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Description", (_t = blueprint.description) !== null && _t !== void 0 ? _t : "", "text", (value) => { blueprint.description = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Tags", (_u = blueprint.tags) !== null && _u !== void 0 ? _u : "", "text", (value) => { blueprint.tags = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Marker file", (_v = blueprint.markerFile) !== null && _v !== void 0 ? _v : "", "text", (value) => { blueprint.markerFile = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Cost", (_c = String(blueprint.cost)) !== null && _c !== void 0 ? _c : "", "number", (value) => { blueprint.cost = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Acquisition range [m]", (_d = String(blueprint.acquisitionRange)) !== null && _d !== void 0 ? _d : "", "number", (value) => { blueprint.acquisitionRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Engagement range [m]", (_e = String(blueprint.engagementRange)) !== null && _e !== void 0 ? _e : "", "number", (value) => { blueprint.engagementRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Targeting range [m]", (_f = String(blueprint.targetingRange)) !== null && _f !== void 0 ? _f : "", "number", (value) => { blueprint.targetingRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Aim method range [m]", (_g = String(blueprint.aimMethodRange)) !== null && _g !== void 0 ? _g : "", "number", (value) => { blueprint.aimMethodRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Barrel height [m]", (_h = String(blueprint.barrelHeight)) !== null && _h !== void 0 ? _h : "", "number", (value) => { blueprint.barrelHeight = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Muzzle velocity [m/s]", (_j = String(blueprint.muzzleVelocity)) !== null && _j !== void 0 ? _j : "", "number", (value) => { blueprint.muzzleVelocity = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Aim time [s]", (_k = String(blueprint.aimTime)) !== null && _k !== void 0 ? _k : "", "number", (value) => { blueprint.aimTime = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Shots to fire", (_l = String(blueprint.shotsToFire)) !== null && _l !== void 0 ? _l : "", "number", (value) => { blueprint.shotsToFire = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Shots base interval [s]", (_m = String(blueprint.shotsBaseInterval)) !== null && _m !== void 0 ? _m : "", "number", (value) => { blueprint.shotsBaseInterval = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Shots base scatter [°]", (_o = String(blueprint.shotsBaseScatter)) !== null && _o !== void 0 ? _o : "", "number", (value) => { blueprint.shotsBaseScatter = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Alertness time constant [s]", (_p = String(blueprint.alertnessTimeConstant)) !== null && _p !== void 0 ? _p : "", "number", (value) => { blueprint.alertnessTimeConstant = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Can target point", (_q = blueprint.canTargetPoint) !== null && _q !== void 0 ? _q : false, (value) => { blueprint.canTargetPoint = value; });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Can rearm", (_r = blueprint.canRearm) !== null && _r !== void 0 ? _r : false, (value) => { blueprint.canRearm = value; });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Can operate as AAA", (_s = blueprint.canAAA) !== null && _s !== void 0 ? _s : false, (value) => { blueprint.canAAA = value; });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Indirect fire (e.g. mortar)", (_t = blueprint.indirectFire) !== null && _t !== void 0 ? _t : false, (value) => { blueprint.indirectFire = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Description", (_u = blueprint.description) !== null && _u !== void 0 ? _u : "", "text", (value) => { blueprint.description = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Tags", (_v = blueprint.tags) !== null && _v !== void 0 ? _v : "", "text", (value) => { blueprint.tags = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Marker file", (_w = blueprint.markerFile) !== null && _w !== void 0 ? _w : "", "text", (value) => { blueprint.markerFile = value; });
|
||||
}
|
||||
}
|
||||
/** Add a new empty blueprint
|
||||
@ -998,8 +999,8 @@ function addBlueprintsScroll(div, database, filter, callback) {
|
||||
if (addKey) {
|
||||
var rowDiv = document.createElement("div");
|
||||
scrollDiv.appendChild(rowDiv);
|
||||
let text = document.createElement("label");
|
||||
text.textContent = key;
|
||||
let text = document.createElement("div");
|
||||
text.innerHTML = `<div>${key}</div> <div>${blueprints[key].label}</div>`;
|
||||
text.onclick = () => {
|
||||
callback(key);
|
||||
const collection = document.getElementsByClassName("blueprint-selected");
|
||||
|
||||
@ -30,6 +30,7 @@ export class GroundUnitEditor extends UnitEditor {
|
||||
addStringInput(this.contentDiv2, "Label", blueprint.label, "text", (value: string) => {blueprint.label = value; });
|
||||
addStringInput(this.contentDiv2, "Short label", blueprint.shortLabel, "text", (value: string) => {blueprint.shortLabel = value; });
|
||||
addStringInput(this.contentDiv2, "Type", blueprint.type?? "", "text", (value: string) => {blueprint.type = value; });
|
||||
addStringInput(this.contentDiv2, "Unit when grouped", blueprint.unitWhenGrouped?? "", "text", (value: string) => {blueprint.unitWhenGrouped = value; });
|
||||
addDropdownInput(this.contentDiv2, "Coalition", blueprint.coalition, ["", "blue", "red"], (value: string) => {blueprint.coalition = value; });
|
||||
addDropdownInput(this.contentDiv2, "Era", blueprint.era, ["WW2", "Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], (value: string) => {blueprint.era = value; });
|
||||
//addStringInput(this.contentDiv2, "Filename", blueprint.filename?? "", "text", (value: string) => {blueprint.filename = value; });
|
||||
|
||||
@ -198,8 +198,8 @@ export function addBlueprintsScroll(div: HTMLElement, database: {blueprints: {[k
|
||||
var rowDiv = document.createElement("div");
|
||||
scrollDiv.appendChild(rowDiv);
|
||||
|
||||
let text = document.createElement("label");
|
||||
text.textContent = key;
|
||||
let text = document.createElement("div");
|
||||
text.innerHTML = `<div>${key}</div> <div>${blueprints[key].label}</div>`;
|
||||
text.onclick = () => {
|
||||
callback(key);
|
||||
const collection = document.getElementsByClassName("blueprint-selected");
|
||||
|
||||
@ -73,7 +73,7 @@
|
||||
}
|
||||
|
||||
.dm-content-container:nth-of-type(1) {
|
||||
width: 300px;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.dm-content-container:nth-of-type(2) {
|
||||
@ -153,12 +153,29 @@
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div>div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div>button {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div>div>div:nth-child(1) {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div>div>div:nth-child(2) {
|
||||
overflow: hidden;
|
||||
text-wrap: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.input-row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
@ -97,6 +97,10 @@
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
[data-object|="unit-groundunit"] .unit-short-label {
|
||||
transform: translateY(7px);
|
||||
}
|
||||
|
||||
/*** Health indicator ***/
|
||||
[data-object|="unit"] .unit-health {
|
||||
background: white;
|
||||
@ -277,7 +281,8 @@
|
||||
background-image: url("/resources/theme/images/states/idle.svg");
|
||||
}
|
||||
|
||||
[data-object*="groundunit"][data-state="idle"] .unit-state {
|
||||
[data-object*="groundunit"][data-state="idle"] .unit-state,
|
||||
[data-object*="navyunit"][data-state="idle"] .unit-state {
|
||||
background-image: url(""); /* To avoid clutter, dont show the idle state for non flying units */
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="50" height="50" version="1.1" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m45.773 41.342-19.825-33.703c-0.4253-0.72303-1.4709-0.72305-1.8962 0l-19.825 33.703c-0.43135 0.7333 0.09738 1.6577 0.94813 1.6577h39.65c0.8507 0 1.3794-0.9244 0.9481-1.6577z" fill="#3BB9FF" stroke-width="2"/>
|
||||
<text x="21.590775" y="28.910091" fill="#082e44" font-family="sans-serif" font-size="8.838px" font-weight="bold" stroke="#082e44" stroke-width=".514" style="line-height:1.25" xml:space="preserve"><tspan x="21.590775" y="28.910091" font-family="sans-serif" font-size="8.838px" font-weight="bold" stroke="#082e44" stroke-width=".514">A</tspan></text>
|
||||
<path d="M6.74842 41L25 9.97231L43.2516 41H6.74842Z" fill="none" stroke="#082E44" stroke-width="2"/>
|
||||
<text x="16.718756" y="38.373253" fill="#082e44" font-family="sans-serif" font-size="21.333px" style="line-height:1.25" xml:space="preserve"><tspan x="16.718756" y="38.373253" font-family="sans-serif" font-size="21.333px" font-weight="bold">A</tspan></text>
|
||||
<text x="16.189293" y="36.063168" fill="#082e44" font-family="sans-serif" font-size="8.838px" font-weight="bold" stroke="#082e44" stroke-width=".514" style="line-height:1.25" xml:space="preserve"><tspan x="16.189293" y="36.063168" font-family="sans-serif" font-size="8.838px" font-weight="bold" stroke="#082e44" stroke-width=".514">A</tspan></text>
|
||||
<text x="27.098772" y="36.063168" fill="#082e44" font-family="sans-serif" font-size="8.838px" font-weight="bold" stroke="#082e44" stroke-width=".514" style="line-height:1.25" xml:space="preserve"><tspan x="27.098772" y="36.063168" font-family="sans-serif" font-size="8.838px" font-weight="bold" stroke="#082e44" stroke-width=".514">A</tspan></text>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 724 B After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="50" height="50" version="1.1" viewBox="0 0 50 50" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect transform="rotate(45 25 2.3724)" x="25" y="2.3724" width="32" height="32" rx="1.1" fill="white" stroke-width="2"/><rect transform="rotate(45 25 5.2008)" x="25" y="5.2008" width="28" height="28" fill="none" stroke="#082e44" stroke-width="2"/><path d="m25 14.598c0.71918 0 1.3002 0.58104 1.3002 1.3002v0.42257c3.8072 0.56478 6.814 3.5756 7.3787 7.3787h0.42257c0.71918 0 1.3002 0.58104 1.3002 1.3002s-0.58104 1.3002-1.3002 1.3002h-0.42257c-0.56478 3.8072-3.5756 6.814-7.3787 7.3787v0.42257c0 0.71918-0.58104 1.3002-1.3002 1.3002s-1.3002-0.58104-1.3002-1.3002v-0.42257c-3.8072-0.56478-6.814-3.5715-7.3787-7.3787h-0.42257c-0.71918 0-1.3002-0.58104-1.3002-1.3002s0.58104-1.3002 1.3002-1.3002h0.42257c0.56478-3.8072 3.5715-6.814 7.3787-7.3787v-0.42257c0-0.71918 0.58104-1.3002 1.3002-1.3002zm-6.0379 11.702c0.5079 2.3688 2.3729 4.2298 4.7377 4.7377v-0.83702c0-0.71918 0.58104-1.3002 1.3002-1.3002s1.3002 0.58104 1.3002 1.3002v0.83702c2.3688-0.5079 4.2298-2.3729 4.7377-4.7377h-0.83702c-0.71918 0-1.3002-0.58104-1.3002-1.3002s0.58104-1.3002 1.3002-1.3002h0.83702c-0.5079-2.3688-2.3688-4.2298-4.7377-4.7377v0.83702c0 0.71918-0.58104 1.3002-1.3002 1.3002s-1.3002-0.58104-1.3002-1.3002v-0.83702c-2.3688 0.5079-4.2298 2.3688-4.7377 4.7377h0.83702c0.71918 0 1.3002 0.58104 1.3002 1.3002s-0.58104 1.3002-1.3002 1.3002zm6.0379-2.6004a1.3002 1.3002 0 1 1 0 2.6004 1.3002 1.3002 0 1 1 0-2.6004z" fill="#082e44" stroke="#082e44" stroke-width=".040632"/></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="50" height="50" version="1.1" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect transform="rotate(45 25 2.3724)" x="25" y="2.3724" width="32" height="32" rx="1.1" fill="white" stroke-width="2"/>
|
||||
<rect transform="rotate(45 25 5.2008)" x="25" y="5.2008" width="28" height="28" fill="none" stroke="#082E44" stroke-width="2"/>
|
||||
<path d="m24.581 23.186c0.72013 0 1.4119 0.2013 2.0073 0.56419v8.5083h-5.4435v-4.9416l-1.5196 2.5686c-0.31753 0.53868-1.015 0.71729-1.5537 0.39976-0.53868-0.31754-0.71729-1.015-0.39975-1.5537l2.1604-3.6517c0.69461-1.1737 1.9562-1.8939 3.3199-1.8939zm-2.9826-3.1754a2.2681 2.2681 0 1 1 4.5362 0 2.2681 2.2681 0 1 1-4.5362 0zm8.1652-2.2681c0.24949 0 0.45362 0.20413 0.45362 0.45362v3.2973c0.27218 0.15594 0.45362 0.45079 0.45362 0.78533v3.0988l0.45362-0.15026v-1.5877c0-0.24949 0.20413-0.45362 0.45362-0.45362h0.45362c0.24949 0 0.45362 0.20413 0.45362 0.45362v2.3957c0 0.19562-0.12474 0.36857-0.30903 0.43094l-1.5055 0.49898v0.75698h1.3609c0.24949 0 0.45362 0.20413 0.45362 0.45362v0.45362c0 0.24949-0.20413 0.45362-0.45362 0.45362h-1.2475l0.65208 2.6112c0.07088 0.28635-0.1446 0.56419-0.43945 0.56419h-1.6869c-0.24949 0-0.45362-0.20413-0.45362-0.45362v-2.7217h-0.45362c-0.50182 0-0.90724-0.40542-0.90724-0.90724v-4.0826c0-0.50182 0.40542-0.90724 0.90724-0.90724v-0.90725c0-0.33454 0.18145-0.6294 0.45362-0.78533v-2.8436c-0.24949 0-0.45362-0.20413-0.45362-0.45362 0-0.2495 0.20413-0.45362 0.45362-0.45362h0.45362z" fill="none" stroke="#082e44"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="50" height="50" version="1.1" viewBox="0 0 50 50" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect transform="rotate(45 25 2.3724)" x="25" y="2.3724" width="32" height="32" rx="1.1" fill="white" stroke-width="2"/><rect transform="rotate(45 25 5.2008)" x="25" y="5.2008" width="28" height="28" fill="none" stroke="#082E44" stroke-width="2"/><path d="m22.431 22.053-0.55497 1.5863h6.2472l-0.55497-1.5863c-0.09568-0.27217-0.35297-0.45504-0.64216-0.45504h-3.8529c-0.28918 0-0.54647 0.18287-0.64216 0.45504zm-2.0328 1.6883 0.74847-2.137c0.28706-0.81864 1.0589-1.3672 1.9265-1.3672h3.8529c0.86754 0 1.6394 0.5486 1.9265 1.3672l0.74847 2.137c0.49331 0.20412 0.84203 0.69106 0.84203 1.2588v4.0826c0 0.37636-0.30407 0.68042-0.68043 0.68042h-0.68042c-0.37636 0-0.68043-0.30407-0.68043-0.68042v-1.0206h-6.8043v1.0206c0 0.37636-0.30407 0.68042-0.68043 0.68042h-0.68042c-0.37636 0-0.68043-0.30407-0.68043-0.68042v-4.0826c0-0.56773 0.34872-1.0547 0.84203-1.2588zm1.8797 1.9392a0.68043 0.68043 0 1 0-1.3609 0 0.68043 0.68043 0 1 0 1.3609 0zm6.1239 0.68043a0.68043 0.68043 0 1 0 0-1.3609 0.68043 0.68043 0 1 0 0 1.3609z" fill="none" stroke="#082e44"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="50" height="50" version="1.1" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect transform="rotate(45 25 2.3724)" x="25" y="2.3724" width="32" height="32" rx="1.1" fill="white" stroke-width="2"/>
|
||||
<rect transform="rotate(45 25 5.2008)" x="25" y="5.2008" width="28" height="28" fill="none" stroke="#082E44" stroke-width="2"/>
|
||||
<path d="m19.209 19.55c-0.56415 0-1.0219 0.45771-1.0219 1.0219v6.8124c0 0.56415 0.45771 1.0219 1.0219 1.0219h0.34062c0 1.1283 0.91541 2.0437 2.0437 2.0437 1.1283 0 2.0437-0.91541 2.0437-2.0437h2.725c0 1.1283 0.91542 2.0437 2.0437 2.0437 1.1283 0 2.0437-0.91541 2.0437-2.0437h0.68124c0.37681 0 0.68124-0.30443 0.68124-0.68124s-0.30443-0.68124-0.68124-0.68124v-2.4418c0-0.36191-0.14264-0.70892-0.3981-0.96438l-1.6456-1.6456c-0.25546-0.25546-0.60247-0.3981-0.96438-0.3981h-1.0793v-1.0219c0-0.56415-0.45771-1.0219-1.0219-1.0219zm7.8342 3.4062h1.0793l1.6456 1.6456v0.3981h-2.725zm-6.4718 5.4499a1.0219 1.0219 0 1 1 2.0437 0 1.0219 1.0219 0 1 1-2.0437 0zm7.8342-1.0219a1.0219 1.0219 0 1 1 0 2.0437 1.0219 1.0219 0 1 1 0-2.0437z" fill="none" stroke="#082e44"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 447 B After Width: | Height: | Size: 447 B |
@ -273,4 +273,6 @@ export const MGRS_PRECISION_10M = 5;
|
||||
export const MGRS_PRECISION_1M = 6;
|
||||
|
||||
export const DELETE_CYCLE_TIME = 0.05;
|
||||
export const DELETE_SLOW_THRESHOLD = 50;
|
||||
export const DELETE_SLOW_THRESHOLD = 50;
|
||||
|
||||
export const GROUPING_ZOOM_TRANSITION = 13;
|
||||
@ -231,6 +231,7 @@ export interface UnitBlueprint {
|
||||
canAAA?: boolean;
|
||||
indirectFire?: boolean;
|
||||
markerFile?: string;
|
||||
unitWhenGrouped?: string;
|
||||
}
|
||||
|
||||
export interface UnitSpawnOptions {
|
||||
|
||||
@ -68,6 +68,7 @@ export class Map extends L.Map {
|
||||
#temporaryMarkers: TemporaryUnitMarker[] = [];
|
||||
#selecting: boolean = false;
|
||||
#isZooming: boolean = false;
|
||||
#previousZoom: number = 0;
|
||||
|
||||
#destinationGroupRotation: number = 0;
|
||||
#computeDestinationRotation: boolean = false;
|
||||
@ -501,6 +502,10 @@ export class Map extends L.Map {
|
||||
return this.#visibilityOptions;
|
||||
}
|
||||
|
||||
getPreviousZoom() {
|
||||
return this.#previousZoom;
|
||||
}
|
||||
|
||||
/* Event handlers */
|
||||
#onClick(e: any) {
|
||||
if (!this.#preventLeftClick) {
|
||||
@ -701,6 +706,7 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
#onZoomStart(e: any) {
|
||||
this.#previousZoom = this.getZoom();
|
||||
if (this.#centerUnit != null)
|
||||
this.#panToUnit(this.#centerUnit);
|
||||
this.#isZooming = true;
|
||||
|
||||
@ -5,7 +5,7 @@ import { CustomMarker } from '../map/markers/custommarker';
|
||||
import { SVGInjector } from '@tanem/svg-injector';
|
||||
import { UnitDatabase } from './databases/unitdatabase';
|
||||
import { TargetMarker } from '../map/markers/targetmarker';
|
||||
import { DLINK, DataIndexes, GAME_MASTER, HIDE_GROUP_MEMBERS, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, SHOW_UNIT_CONTACTS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING } from '../constants/constants';
|
||||
import { DLINK, DataIndexes, GAME_MASTER, HIDE_GROUP_MEMBERS, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, SHOW_UNIT_CONTACTS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, GROUPING_ZOOM_TRANSITION } from '../constants/constants';
|
||||
import { DataExtractor } from '../server/dataextractor';
|
||||
import { groundUnitDatabase } from './databases/groundunitdatabase';
|
||||
import { navyUnitDatabase } from './databases/navyunitdatabase';
|
||||
@ -21,12 +21,11 @@ var pathIcon = new Icon({
|
||||
|
||||
/**
|
||||
* Unit class which controls unit behaviour
|
||||
*
|
||||
* Just about everything is a unit - even missiles!
|
||||
*/
|
||||
export class Unit extends CustomMarker {
|
||||
export abstract class Unit extends CustomMarker {
|
||||
ID: number;
|
||||
|
||||
/* Data controlled directly by the backend. No setters are provided to avoid misalignments */
|
||||
#alive: boolean = false;
|
||||
#human: boolean = false;
|
||||
#controlled: boolean = false;
|
||||
@ -90,6 +89,7 @@ export class Unit extends CustomMarker {
|
||||
#shotsIntensity: number = 2;
|
||||
#health: number = 100;
|
||||
|
||||
/* Other members used to draw the unit, mostly ancillary stuff like targets, ranges and so on */
|
||||
#selectable: boolean;
|
||||
#selected: boolean = false;
|
||||
#hidden: boolean = false;
|
||||
@ -107,6 +107,7 @@ export class Unit extends CustomMarker {
|
||||
#hotgroup: number | null = null;
|
||||
#detectionMethods: number[] = [];
|
||||
|
||||
/* Getters for backend driven data */
|
||||
getAlive() { return this.#alive };
|
||||
getHuman() { return this.#human };
|
||||
getControlled() { return this.#controlled };
|
||||
@ -171,6 +172,7 @@ export class Unit extends CustomMarker {
|
||||
this.#engagementCircle = new RangeCircle(this.getPosition(), { radius: 0, weight: 4, opacity: 1, fillOpacity: 0, dashArray: "4 8", interactive: false, bubblingMouseEvents: false });
|
||||
this.#acquisitionCircle = new RangeCircle(this.getPosition(), { radius: 0, weight: 2, opacity: 1, fillOpacity: 0, dashArray: "8 12", interactive: false, bubblingMouseEvents: false });
|
||||
|
||||
/* Leaflet events listeners */
|
||||
this.on('click', (e) => this.#onClick(e));
|
||||
this.on('dblclick', (e) => this.#onDoubleClick(e));
|
||||
this.on('contextmenu', (e) => this.#onContextMenu(e));
|
||||
@ -184,7 +186,7 @@ export class Unit extends CustomMarker {
|
||||
this.setHighlighted(false);
|
||||
document.dispatchEvent(new CustomEvent("unitMouseout", { detail: this }));
|
||||
});
|
||||
getApp().getMap().on("zoomend", () => { this.#onZoom(); })
|
||||
getApp().getMap().on("zoomend", (e: any) => { this.#onZoom(e); })
|
||||
|
||||
/* Deselect units if they are hidden */
|
||||
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
|
||||
@ -195,6 +197,7 @@ export class Unit extends CustomMarker {
|
||||
window.setTimeout(() => { this.setSelected(this.getSelected() && !this.getHidden()) }, 300);
|
||||
});
|
||||
|
||||
/* Update the marker when the visibility options change */
|
||||
document.addEventListener("mapVisibilityOptionsChanged", (ev: CustomEventInit) => {
|
||||
this.#updateMarker();
|
||||
|
||||
@ -209,13 +212,33 @@ export class Unit extends CustomMarker {
|
||||
});
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
// Overloaded by child classes
|
||||
return "";
|
||||
}
|
||||
/********************** Abstract methods *************************/
|
||||
/** Get the unit category string
|
||||
*
|
||||
* @returns string The unit category
|
||||
*/
|
||||
abstract getCategory(): string;
|
||||
|
||||
/** Get the icon options
|
||||
* Used to configure how the marker appears on the map
|
||||
*
|
||||
* @returns ObjectIconOptions
|
||||
*/
|
||||
abstract getIconOptions(): ObjectIconOptions;
|
||||
|
||||
/** Get the actions that this unit can perform
|
||||
*
|
||||
* @returns Object containing the available actions
|
||||
*/
|
||||
abstract getActions(): {[key: string]: { text: string, tooltip: string, type: string}};
|
||||
|
||||
/********************** Unit data *************************/
|
||||
/** This function is called by the units manager to update all the data coming from the backend. It reads the binary raw data using a DataExtractor
|
||||
*
|
||||
* @param dataExtractor The DataExtractor object pointing to the binary buffer which contains the raw data coming from the backend
|
||||
*/
|
||||
setData(dataExtractor: DataExtractor) {
|
||||
/* This variable controls if the marker must be updated. This is not always true since not all variables have an effect on the marker*/
|
||||
var updateMarker = !getApp().getMap().hasLayer(this);
|
||||
|
||||
var datumIndex = 0;
|
||||
@ -226,7 +249,7 @@ export class Unit extends CustomMarker {
|
||||
case DataIndexes.alive: this.setAlive(dataExtractor.extractBool()); updateMarker = true; break;
|
||||
case DataIndexes.human: this.#human = dataExtractor.extractBool(); break;
|
||||
case DataIndexes.controlled: this.#controlled = dataExtractor.extractBool(); updateMarker = true; break;
|
||||
case DataIndexes.coalition: let newCoalition = enumToCoalition(dataExtractor.extractUInt8()); updateMarker = true; if (newCoalition != this.#coalition) this.#clearRanges(); this.#coalition = newCoalition; break;
|
||||
case DataIndexes.coalition: let newCoalition = enumToCoalition(dataExtractor.extractUInt8()); updateMarker = true; if (newCoalition != this.#coalition) this.#clearRanges(); this.#coalition = newCoalition; break; // If the coalition has changed, redraw the range circles to update the colour
|
||||
case DataIndexes.country: this.#country = dataExtractor.extractUInt8(); break;
|
||||
case DataIndexes.name: this.#name = dataExtractor.extractString(); break;
|
||||
case DataIndexes.unitName: this.#unitName = dataExtractor.extractString(); break;
|
||||
@ -269,25 +292,18 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
}
|
||||
|
||||
/* Dead units can't be selected */
|
||||
/* Dead and hidden units can't be selected */
|
||||
this.setSelected(this.getSelected() && this.#alive && !this.getHidden())
|
||||
|
||||
/* Update the marker if required */
|
||||
if (updateMarker)
|
||||
this.#updateMarker();
|
||||
|
||||
/* If the unit is selected or if the view is centered on this unit, sent the update signal so that other elements like the UnitControlPanel can be updated. */
|
||||
if (this.getSelected() || getApp().getMap().getCenterUnit() === this)
|
||||
document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this }));
|
||||
}
|
||||
|
||||
drawLines() {
|
||||
/* Leaflet does not like it when you change coordinates when the map is zooming */
|
||||
if (!getApp().getMap().isZooming()) {
|
||||
this.#drawPath();
|
||||
this.#drawContacts();
|
||||
this.#drawTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/** Get unit data collated into an object
|
||||
*
|
||||
* @returns object populated by unit information which can also be retrieved using getters
|
||||
@ -358,28 +374,6 @@ export class Unit extends CustomMarker {
|
||||
return getUnitDatabaseByCategory(this.getMarkerCategory());
|
||||
}
|
||||
|
||||
/** Get the icon options
|
||||
* Used to configure how the marker appears on the map
|
||||
*
|
||||
* @returns ObjectIconOptions
|
||||
*/
|
||||
getIconOptions(): ObjectIconOptions {
|
||||
// Default values, overloaded by child classes if needed
|
||||
return {
|
||||
showState: false,
|
||||
showVvi: false,
|
||||
showHealth: true,
|
||||
showHotgroup: false,
|
||||
showUnitIcon: true,
|
||||
showShortLabel: false,
|
||||
showFuel: false,
|
||||
showAmmo: false,
|
||||
showSummary: true,
|
||||
showCallsign: true,
|
||||
rotateToHeading: false
|
||||
}
|
||||
}
|
||||
|
||||
/** Set the unit as alive or dead
|
||||
*
|
||||
* @param newAlive (boolean) true = alive, false = dead
|
||||
@ -395,16 +389,11 @@ export class Unit extends CustomMarker {
|
||||
* @param selected (boolean)
|
||||
*/
|
||||
setSelected(selected: boolean) {
|
||||
/* Only alive units can be selected. Some units are not selectable (weapons) */
|
||||
if ((this.#alive || !selected) && this.getSelectable() && this.getSelected() != selected && this.belongsToCommandedCoalition()) {
|
||||
/* Only alive units can be selected that belong to the commanded coalition can be selected */
|
||||
if ((this.#alive || !selected) && this.belongsToCommandedCoalition() && this.getSelected() != selected) {
|
||||
this.#selected = selected;
|
||||
|
||||
/* Circles don't like to be updated when the map is zooming */
|
||||
if (!getApp().getMap().isZooming())
|
||||
this.#drawRanges();
|
||||
else
|
||||
this.once("zoomend", () => { this.#drawRanges(); })
|
||||
|
||||
/* 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();
|
||||
}
|
||||
@ -414,21 +403,27 @@ export class Unit extends CustomMarker {
|
||||
this.#clearTarget();
|
||||
}
|
||||
|
||||
this.getElement()?.querySelector(`.unit`)?.toggleAttribute("data-is-selected", selected);
|
||||
if (this.getCategory() === "GroundUnit" && getApp().getMap().getZoom() < 13) {
|
||||
if (this.#isLeader)
|
||||
/* When the group leader is selected, if grouping is active, all the other group members are also selected */
|
||||
if (this.getCategory() === "GroundUnit" && getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION) {
|
||||
if (this.#isLeader) {
|
||||
/* Redraw the marker in case the leader unit was replaced by a group marker, like for SAM Sites */
|
||||
this.#redrawMarker();
|
||||
this.getGroupMembers().forEach((unit: Unit) => unit.setSelected(selected));
|
||||
else
|
||||
}
|
||||
else {
|
||||
this.#updateMarker();
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger events after all (de-)selecting has been done
|
||||
/* Activate the selection effects on the marker */
|
||||
this.getElement()?.querySelector(`.unit`)?.toggleAttribute("data-is-selected", selected);
|
||||
|
||||
/* Trigger events after all (de-)selecting has been done */
|
||||
if (selected) {
|
||||
document.dispatchEvent(new CustomEvent("unitSelection", { detail: this }));
|
||||
} else {
|
||||
document.dispatchEvent(new CustomEvent("unitDeselection", { detail: this }));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -440,22 +435,6 @@ export class Unit extends CustomMarker {
|
||||
return this.#selected;
|
||||
}
|
||||
|
||||
/** Set whether this unit is selectable
|
||||
*
|
||||
* @param selectable (boolean)
|
||||
*/
|
||||
setSelectable(selectable: boolean) {
|
||||
this.#selectable = selectable;
|
||||
}
|
||||
|
||||
/** Get whether this unit is selectable
|
||||
*
|
||||
* @returns boolean
|
||||
*/
|
||||
getSelectable() {
|
||||
return this.#selectable;
|
||||
}
|
||||
|
||||
/** Set the number of the hotgroup to which the unit belongs
|
||||
*
|
||||
* @param hotgroup (number)
|
||||
@ -478,9 +457,9 @@ export class Unit extends CustomMarker {
|
||||
* @param highlighted (boolean)
|
||||
*/
|
||||
setHighlighted(highlighted: boolean) {
|
||||
if (this.getSelectable() && this.#highlighted != highlighted) {
|
||||
this.getElement()?.querySelector(`[data-object|="unit"]`)?.toggleAttribute("data-is-highlighted", highlighted);
|
||||
if (this.#highlighted != highlighted) {
|
||||
this.#highlighted = highlighted;
|
||||
this.getElement()?.querySelector(`[data-object|="unit"]`)?.toggleAttribute("data-is-highlighted", highlighted);
|
||||
this.getGroupMembers().forEach((unit: Unit) => unit.setHighlighted(highlighted));
|
||||
}
|
||||
}
|
||||
@ -517,10 +496,25 @@ export class Unit extends CustomMarker {
|
||||
return this.getDatabase()?.getSpawnPointsByName(this.getName());
|
||||
}
|
||||
|
||||
getDatabaseEntry() {
|
||||
return this.getDatabase()?.getByName(this.#name);
|
||||
}
|
||||
|
||||
drawLines() {
|
||||
/* Leaflet does not like it when you change coordinates when the map is zooming */
|
||||
if (!getApp().getMap().isZooming()) {
|
||||
this.#drawPath();
|
||||
this.#drawContacts();
|
||||
this.#drawTarget();
|
||||
}
|
||||
}
|
||||
|
||||
checkRedraw() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/********************** Icon *************************/
|
||||
createIcon(): void {
|
||||
const databaseEntry = this.getDatabase()?.getByName(this.#name);
|
||||
|
||||
/* Set the icon */
|
||||
var icon = new DivIcon({
|
||||
className: 'leaflet-unit-icon',
|
||||
@ -529,6 +523,7 @@ export class Unit extends CustomMarker {
|
||||
});
|
||||
this.setIcon(icon);
|
||||
|
||||
/* Create the base element */
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("unit");
|
||||
el.setAttribute("data-object", `unit-${this.getMarkerCategory()}`);
|
||||
@ -536,8 +531,8 @@ export class Unit extends CustomMarker {
|
||||
|
||||
var iconOptions = this.getIconOptions();
|
||||
|
||||
// Generate and append elements depending on active options
|
||||
// Velocity vector
|
||||
/* Generate and append elements depending on active options */
|
||||
/* Velocity vector */
|
||||
if (iconOptions.showVvi) {
|
||||
var vvi = document.createElement("div");
|
||||
vvi.classList.add("unit-vvi");
|
||||
@ -545,7 +540,7 @@ export class Unit extends CustomMarker {
|
||||
el.append(vvi);
|
||||
}
|
||||
|
||||
// Hotgroup indicator
|
||||
/* Hotgroup indicator */
|
||||
if (iconOptions.showHotgroup) {
|
||||
var hotgroup = document.createElement("div");
|
||||
hotgroup.classList.add("unit-hotgroup");
|
||||
@ -555,42 +550,42 @@ export class Unit extends CustomMarker {
|
||||
el.append(hotgroup);
|
||||
}
|
||||
|
||||
// Main icon
|
||||
/* Main icon */
|
||||
if (iconOptions.showUnitIcon) {
|
||||
var unitIcon = document.createElement("div");
|
||||
unitIcon.classList.add("unit-icon");
|
||||
var img = document.createElement("img");
|
||||
|
||||
var marker;
|
||||
/* If a unit does not belong to the commanded coalition or it is not visually detected, show it with the generic aircraft square */
|
||||
if (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)))
|
||||
marker = databaseEntry?.markerFile ?? this.getMarkerCategory();
|
||||
var marker;
|
||||
if (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)))
|
||||
marker = this.getDatabaseEntry()?.markerFile ?? this.getMarkerCategory();
|
||||
else
|
||||
marker = "aircraft";
|
||||
|
||||
img.src = `/resources/theme/images/units/${marker}.svg`;
|
||||
img.onload = () => SVGInjector(img);
|
||||
unitIcon.appendChild(img);
|
||||
|
||||
unitIcon.toggleAttribute("data-rotate-to-heading", iconOptions.rotateToHeading);
|
||||
el.append(unitIcon);
|
||||
}
|
||||
|
||||
// State icon
|
||||
/* State icon */
|
||||
if (iconOptions.showState) {
|
||||
var state = document.createElement("div");
|
||||
state.classList.add("unit-state");
|
||||
el.appendChild(state);
|
||||
}
|
||||
|
||||
// Short label
|
||||
/* Short label */
|
||||
if (iconOptions.showShortLabel) {
|
||||
var shortLabel = document.createElement("div");
|
||||
shortLabel.classList.add("unit-short-label");
|
||||
shortLabel.innerText = databaseEntry?.shortLabel || "";
|
||||
shortLabel.innerText = this.getDatabaseEntry()?.shortLabel || "";
|
||||
el.append(shortLabel);
|
||||
}
|
||||
|
||||
// Fuel indicator
|
||||
/* Fuel indicator */
|
||||
if (iconOptions.showFuel) {
|
||||
var fuelIndicator = document.createElement("div");
|
||||
fuelIndicator.classList.add("unit-fuel");
|
||||
@ -600,7 +595,7 @@ export class Unit extends CustomMarker {
|
||||
el.append(fuelIndicator);
|
||||
}
|
||||
|
||||
// Health indicator
|
||||
/* Health indicator */
|
||||
if (iconOptions.showHealth) {
|
||||
var healthIndicator = document.createElement("div");
|
||||
healthIndicator.classList.add("unit-health");
|
||||
@ -610,7 +605,7 @@ export class Unit extends CustomMarker {
|
||||
el.append(healthIndicator);
|
||||
}
|
||||
|
||||
// Ammo indicator
|
||||
/* Ammo indicator */
|
||||
if (iconOptions.showAmmo) {
|
||||
var ammoIndicator = document.createElement("div");
|
||||
ammoIndicator.classList.add("unit-ammo");
|
||||
@ -619,7 +614,7 @@ export class Unit extends CustomMarker {
|
||||
el.append(ammoIndicator);
|
||||
}
|
||||
|
||||
// Unit summary
|
||||
/* Unit summary */
|
||||
if (iconOptions.showSummary) {
|
||||
var summary = document.createElement("div");
|
||||
summary.classList.add("unit-summary");
|
||||
@ -637,25 +632,29 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
this.getElement()?.appendChild(el);
|
||||
|
||||
/* Circles don't like to be updated when the map is zooming */
|
||||
if (!getApp().getMap().isZooming())
|
||||
this.#drawRanges();
|
||||
else
|
||||
this.once("zoomend", () => { this.#drawRanges(); })
|
||||
}
|
||||
|
||||
/********************** Visibility *************************/
|
||||
updateVisibility() {
|
||||
const hiddenUnits = getApp().getMap().getHiddenTypes();
|
||||
var hidden = ((this.#human && hiddenUnits.includes("human")) ||
|
||||
(this.#controlled == false && hiddenUnits.includes("dcs")) ||
|
||||
(hiddenUnits.includes(this.getMarkerCategory())) ||
|
||||
(hiddenUnits.includes(this.#coalition)) ||
|
||||
const hiddenTypes = getApp().getMap().getHiddenTypes();
|
||||
var hidden = (
|
||||
/* Hide the unit if it is a human and humans are hidden */
|
||||
(this.#human && hiddenTypes.includes("human")) ||
|
||||
/* Hide the unit if it is DCS controlled and DCS controlled units are hidden */
|
||||
(this.#controlled == false && hiddenTypes.includes("dcs")) ||
|
||||
/* Hide the unit if this specific category is hidden */
|
||||
(hiddenTypes.includes(this.getMarkerCategory())) ||
|
||||
/* Hide the unit if this coalition is hidden */
|
||||
(hiddenTypes.includes(this.#coalition)) ||
|
||||
/* Hide the unit if it does not belong to the commanded coalition and it is not detected by a method that can pinpoint its location (RWR does not count) */
|
||||
(!this.belongsToCommandedCoalition() && (this.#detectionMethods.length == 0 || (this.#detectionMethods.length == 1 && this.#detectionMethods[0] === RWR))) ||
|
||||
(getApp().getMap().getVisibilityOptions()[HIDE_GROUP_MEMBERS] && !this.#isLeader && this.getCategory() == "GroundUnit" && getApp().getMap().getZoom() < 13 && (this.belongsToCommandedCoalition() || (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0)))) &&
|
||||
!(this.getSelected());
|
||||
/* Hide the unit if grouping is activated, the unit is not the group leader, it is not selected, and the zoom is higher than the grouping threshold */
|
||||
(getApp().getMap().getVisibilityOptions()[HIDE_GROUP_MEMBERS] && !this.#isLeader && this.getCategory() == "GroundUnit" && getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION &&
|
||||
(this.belongsToCommandedCoalition() || (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0)))) &&
|
||||
!(this.getSelected()
|
||||
);
|
||||
|
||||
/* Force dead units to be hidden */
|
||||
this.setHidden(hidden || !this.#alive);
|
||||
}
|
||||
|
||||
@ -675,6 +674,7 @@ export class Unit extends CustomMarker {
|
||||
getApp().getMap().removeLayer(this);
|
||||
}
|
||||
|
||||
/* Draw the range circles if the unit is not hidden */
|
||||
if (!this.getHidden()) {
|
||||
/* Circles don't like to be updated when the map is zooming */
|
||||
if (!getApp().getMap().isZooming())
|
||||
@ -714,7 +714,7 @@ export class Unit extends CustomMarker {
|
||||
if (typeof (roles) === "string")
|
||||
roles = [roles];
|
||||
|
||||
var loadouts = this.getDatabase()?.getByName(this.#name)?.loadouts;
|
||||
var loadouts = this.getDatabaseEntry()?.loadouts;
|
||||
if (loadouts) {
|
||||
return loadouts.some((loadout: LoadoutBlueprint) => {
|
||||
return (roles as string[]).some((role: string) => { return loadout.roles.includes(role) });
|
||||
@ -728,11 +728,11 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
canTargetPoint() {
|
||||
return this.getDatabase()?.getByName(this.#name)?.canTargetPoint === true;
|
||||
return this.getDatabaseEntry()?.canTargetPoint === true;
|
||||
}
|
||||
|
||||
canRearm() {
|
||||
return this.getDatabase()?.getByName(this.#name)?.canRearm === true;
|
||||
return this.getDatabaseEntry()?.canRearm === true;
|
||||
}
|
||||
|
||||
canLandAtPoint() {
|
||||
@ -740,11 +740,11 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
canAAA() {
|
||||
return this.getDatabase()?.getByName(this.#name)?.canAAA === true;
|
||||
return this.getDatabaseEntry()?.canAAA === true;
|
||||
}
|
||||
|
||||
indirectFire() {
|
||||
return this.getDatabase()?.getByName(this.#name)?.indirectFire === true;
|
||||
return this.getDatabaseEntry()?.indirectFire === true;
|
||||
}
|
||||
|
||||
isTanker() {
|
||||
@ -933,11 +933,6 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
/***********************************************/
|
||||
getActions(): { [key: string]: { text: string, tooltip: string, type: string } } {
|
||||
/* To be implemented by child classes */ // TODO make Unit an abstract class
|
||||
return {};
|
||||
}
|
||||
|
||||
executeAction(e: any, action: string) {
|
||||
if (action === "center-map")
|
||||
getApp().getMap().centerOnUnit(this.ID);
|
||||
@ -963,18 +958,15 @@ export class Unit extends CustomMarker {
|
||||
|
||||
/***********************************************/
|
||||
#onClick(e: any) {
|
||||
|
||||
// Exit if we were waiting for a doubleclick
|
||||
/* Exit if we were waiting for a doubleclick */
|
||||
if (this.#waitingForDoubleClick) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We'll wait for a doubleclick
|
||||
/* We'll wait for a doubleclick */
|
||||
this.#waitingForDoubleClick = true;
|
||||
|
||||
this.#doubleClickTimer = window.setTimeout(() => {
|
||||
|
||||
// Still waiting so no doubleclick; do the click action
|
||||
/* Still waiting so no doubleclick; do the click action */
|
||||
if (this.#waitingForDoubleClick) {
|
||||
if (getApp().getMap().getState() === IDLE || getApp().getMap().getState() === MOVE_UNIT || e.originalEvent.ctrlKey) {
|
||||
if (!e.originalEvent.ctrlKey)
|
||||
@ -984,17 +976,17 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
}
|
||||
|
||||
// No longer waiting for a doubleclick
|
||||
/* No longer waiting for a doubleclick */
|
||||
this.#waitingForDoubleClick = false;
|
||||
}, 200);
|
||||
}
|
||||
|
||||
#onDoubleClick(e: any) {
|
||||
// Let single clicks work again
|
||||
/* Let single clicks work again */
|
||||
this.#waitingForDoubleClick = false;
|
||||
clearTimeout(this.#doubleClickTimer);
|
||||
|
||||
// Select all matching units in the viewport
|
||||
/* Select all matching units in the viewport */
|
||||
const unitsManager = getApp().getUnitsManager();
|
||||
Object.values(unitsManager.getUnits()).forEach((unit: Unit) => {
|
||||
if (unit.getAlive() === true && unit.getName() === this.getName() && unit.isInViewport())
|
||||
@ -1226,6 +1218,11 @@ export class Unit extends CustomMarker {
|
||||
if (hotgroupEl)
|
||||
hotgroupEl.innerText = String(this.#hotgroup);
|
||||
}
|
||||
|
||||
/* If the unit is a leader of a SAM Site, show the group as a SAM with the appropriate short label */
|
||||
if (this.#isLeader) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Set vertical offset for altitude stacking */
|
||||
@ -1234,6 +1231,11 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
}
|
||||
|
||||
#redrawMarker() {
|
||||
this.removeFrom(getApp().getMap());
|
||||
this.#updateMarker();
|
||||
}
|
||||
|
||||
#drawPath() {
|
||||
if (this.#activePath != undefined && getApp().getMap().getVisibilityOptions()[SHOW_UNIT_PATHS]) {
|
||||
var points = [];
|
||||
@ -1446,12 +1448,21 @@ export class Unit extends CustomMarker {
|
||||
this.#targetPositionPolyline.removeFrom(getApp().getMap());
|
||||
}
|
||||
|
||||
#onZoom() {
|
||||
#onZoom(e: any) {
|
||||
if (this.checkRedraw()) {
|
||||
this.#redrawMarker();
|
||||
|
||||
/* If the marker has been redrawn, reapply selection */
|
||||
if (this.getSelected()) {
|
||||
this.setSelected(false);
|
||||
this.setSelected(true);
|
||||
}
|
||||
}
|
||||
this.#updateMarker();
|
||||
}
|
||||
}
|
||||
|
||||
export class AirUnit extends Unit {
|
||||
export abstract class AirUnit extends Unit {
|
||||
getIconOptions() {
|
||||
var belongsToCommandedCoalition = this.belongsToCommandedCoalition();
|
||||
return {
|
||||
@ -1534,7 +1545,7 @@ export class GroundUnit extends Unit {
|
||||
showHealth: true,
|
||||
showHotgroup: belongsToCommandedCoalition,
|
||||
showUnitIcon: (belongsToCommandedCoalition || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showShortLabel: false,
|
||||
showShortLabel: this.getDatabaseEntry()?.type === "SAM Site",
|
||||
showFuel: false,
|
||||
showAmmo: false,
|
||||
showSummary: false,
|
||||
@ -1584,6 +1595,31 @@ export class GroundUnit extends Unit {
|
||||
var blueprint = groundUnitDatabase.getByName(this.getName());
|
||||
return blueprint?.type ? blueprint.type : "";
|
||||
}
|
||||
|
||||
/* When a unit is a leader of a group, the map is zoomed out and grouping when zoomed out is enabled, check if the unit should be shown as a specific group. This is used to show a SAM battery instead of the group leader */
|
||||
getDatabaseEntry() {
|
||||
let unitWhenGrouped = null;
|
||||
if (!this.getSelected() && this.getIsLeader() && getApp().getMap().getVisibilityOptions()[HIDE_GROUP_MEMBERS] && getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION) {
|
||||
unitWhenGrouped = this.getDatabase()?.getByName(this.getName())?.unitWhenGrouped ?? null;
|
||||
let member = this.getGroupMembers().reduce((prev: Unit | null, unit: Unit, index: number) => {
|
||||
if (unit.getDatabaseEntry()?.unitWhenGrouped != undefined)
|
||||
return unit
|
||||
return prev;
|
||||
}, null);
|
||||
unitWhenGrouped == member !== null ? member?.getDatabaseEntry()?.unitWhenGrouped : unitWhenGrouped;
|
||||
}
|
||||
if (unitWhenGrouped !== null)
|
||||
return this.getDatabase()?.getByName(unitWhenGrouped);
|
||||
else
|
||||
return this.getDatabase()?.getByName(this.getName());
|
||||
}
|
||||
|
||||
/* When we zoom past the grouping limit, grouping is enabled and the unit is a leader, we redraw the unit to apply any possible grouped marker */
|
||||
checkRedraw(): boolean {
|
||||
return (this.getIsLeader() && getApp().getMap().getVisibilityOptions()[HIDE_GROUP_MEMBERS] &&
|
||||
(getApp().getMap().getZoom() >= GROUPING_ZOOM_TRANSITION && getApp().getMap().getPreviousZoom() < GROUPING_ZOOM_TRANSITION ||
|
||||
getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION && getApp().getMap().getPreviousZoom() >= GROUPING_ZOOM_TRANSITION))
|
||||
}
|
||||
}
|
||||
|
||||
export class NavyUnit extends Unit {
|
||||
|
||||