mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
655 lines
18 KiB
TypeScript
655 lines
18 KiB
TypeScript
import { Circle, LatLng, Polygon } from "leaflet";
|
|
import * as turf from "@turf/turf";
|
|
import { UnitDatabase } from "../unit/databases/unitdatabase";
|
|
import { aircraftDatabase } from "../unit/databases/aircraftdatabase";
|
|
import { helicopterDatabase } from "../unit/databases/helicopterdatabase";
|
|
import { groundUnitDatabase } from "../unit/databases/groundunitdatabase";
|
|
import {
|
|
ROEs,
|
|
emissionsCountermeasures,
|
|
reactionsToThreat,
|
|
states,
|
|
} from "../constants/constants";
|
|
import { navyUnitDatabase } from "../unit/databases/navyunitdatabase";
|
|
import { DateAndTime, UnitBlueprint } from "../interfaces";
|
|
import { Converter } from "usng";
|
|
import { MGRS } from "../types/types";
|
|
import { getApp } from "../olympusapp";
|
|
import { featureCollection } from "turf";
|
|
|
|
export function bearing(
|
|
lat1: number,
|
|
lon1: number,
|
|
lat2: number,
|
|
lon2: number
|
|
) {
|
|
const φ1 = deg2rad(lat1); // φ, λ in radians
|
|
const φ2 = deg2rad(lat2);
|
|
const λ1 = deg2rad(lon1); // φ, λ in radians
|
|
const λ2 = deg2rad(lon2);
|
|
const y = Math.sin(λ2 - λ1) * Math.cos(φ2);
|
|
const x =
|
|
Math.cos(φ1) * Math.sin(φ2) -
|
|
Math.sin(φ1) * Math.cos(φ2) * Math.cos(λ2 - λ1);
|
|
const θ = Math.atan2(y, x);
|
|
const brng = (rad2deg(θ) + 360) % 360; // in degrees
|
|
|
|
return brng;
|
|
}
|
|
|
|
export function distance(
|
|
lat1: number,
|
|
lon1: number,
|
|
lat2: number,
|
|
lon2: number
|
|
) {
|
|
const R = 6371e3; // metres
|
|
const φ1 = deg2rad(lat1); // φ, λ in radians
|
|
const φ2 = deg2rad(lat2);
|
|
const Δφ = deg2rad(lat2 - lat1);
|
|
const Δλ = deg2rad(lon2 - lon1);
|
|
|
|
const a =
|
|
Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
|
|
Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
|
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
|
|
const d = R * c; // in metres
|
|
|
|
return d;
|
|
}
|
|
|
|
export function bearingAndDistanceToLatLng(
|
|
lat: number,
|
|
lon: number,
|
|
brng: number,
|
|
dist: number
|
|
) {
|
|
const R = 6371e3; // metres
|
|
const φ1 = deg2rad(lat); // φ, λ in radians
|
|
const λ1 = deg2rad(lon);
|
|
const φ2 = Math.asin(
|
|
Math.sin(φ1) * Math.cos(dist / R) +
|
|
Math.cos(φ1) * Math.sin(dist / R) * Math.cos(brng)
|
|
);
|
|
const λ2 =
|
|
λ1 +
|
|
Math.atan2(
|
|
Math.sin(brng) * Math.sin(dist / R) * Math.cos(φ1),
|
|
Math.cos(dist / R) - Math.sin(φ1) * Math.sin(φ2)
|
|
);
|
|
|
|
return new LatLng(rad2deg(φ2), rad2deg(λ2));
|
|
}
|
|
|
|
export function ConvertDDToDMS(D: number, lng: boolean) {
|
|
var dir = D < 0 ? (lng ? "W" : "S") : lng ? "E" : "N";
|
|
var deg = 0 | (D < 0 ? (D = -D) : D);
|
|
var min = 0 | (((D += 1e-9) % 1) * 60);
|
|
var sec = (0 | (((D * 60) % 1) * 6000)) / 100;
|
|
var dec = Math.round((sec - Math.floor(sec)) * 100);
|
|
var sec = Math.floor(sec);
|
|
if (lng)
|
|
return (
|
|
dir +
|
|
zeroPad(deg, 3) +
|
|
"°" +
|
|
zeroPad(min, 2) +
|
|
"'" +
|
|
zeroPad(sec, 2) +
|
|
"." +
|
|
zeroPad(dec, 2) +
|
|
'"'
|
|
);
|
|
else
|
|
return (
|
|
dir +
|
|
zeroPad(deg, 2) +
|
|
"°" +
|
|
zeroPad(min, 2) +
|
|
"'" +
|
|
zeroPad(sec, 2) +
|
|
"." +
|
|
zeroPad(dec, 2) +
|
|
'"'
|
|
);
|
|
}
|
|
|
|
export function dataPointMap(container: HTMLElement, data: any) {
|
|
Object.keys(data).forEach((key) => {
|
|
const val = "" + data[key]; // Ensure a string
|
|
container.querySelectorAll(`[data-point="${key}"]`).forEach((el) => {
|
|
// We could probably have options here
|
|
if (el instanceof HTMLInputElement) {
|
|
el.value = val;
|
|
} else if (el instanceof HTMLElement) {
|
|
el.innerText = val;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
export function deg2rad(deg: number) {
|
|
var pi = Math.PI;
|
|
return deg * (pi / 180);
|
|
}
|
|
|
|
export function rad2deg(rad: number) {
|
|
var pi = Math.PI;
|
|
return rad / (pi / 180);
|
|
}
|
|
|
|
export function generateUUIDv4() {
|
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
var r = (Math.random() * 16) | 0,
|
|
v = c == "x" ? r : (r & 0x3) | 0x8;
|
|
return v.toString(16);
|
|
});
|
|
}
|
|
|
|
export function keyEventWasInInput(event: KeyboardEvent) {
|
|
const target = event.target;
|
|
return (
|
|
target instanceof HTMLElement &&
|
|
["INPUT", "TEXTAREA"].includes(target.nodeName)
|
|
);
|
|
}
|
|
|
|
export function reciprocalHeading(heading: number): number {
|
|
return heading > 180 ? heading - 180 : heading + 180;
|
|
}
|
|
|
|
/**
|
|
* Prepend numbers to the start of a string
|
|
*
|
|
* @param num <number> subject number
|
|
* @param places <number> places to pad
|
|
* @param decimal <boolean> whether this is a decimal number or not
|
|
*
|
|
* */
|
|
export const zeroAppend = function (
|
|
num: number,
|
|
places: number,
|
|
decimal: boolean = false
|
|
) {
|
|
var string = decimal ? num.toFixed(2) : String(num);
|
|
while (string.length < places) {
|
|
string = "0" + string;
|
|
}
|
|
return string;
|
|
};
|
|
|
|
export const zeroPad = function (num: number, places: number) {
|
|
var string = String(num);
|
|
while (string.length < places) {
|
|
string += "0";
|
|
}
|
|
return string;
|
|
};
|
|
|
|
export function similarity(s1: string, s2: string) {
|
|
var longer = s1;
|
|
var shorter = s2;
|
|
if (s1.length < s2.length) {
|
|
longer = s2;
|
|
shorter = s1;
|
|
}
|
|
var longerLength = longer.length;
|
|
if (longerLength == 0) {
|
|
return 1.0;
|
|
}
|
|
return (longerLength - editDistance(longer, shorter)) / longerLength;
|
|
}
|
|
|
|
export function editDistance(s1: string, s2: string) {
|
|
s1 = s1.toLowerCase();
|
|
s2 = s2.toLowerCase();
|
|
|
|
var costs = new Array();
|
|
for (var i = 0; i <= s1.length; i++) {
|
|
var lastValue = i;
|
|
for (var j = 0; j <= s2.length; j++) {
|
|
if (i == 0) costs[j] = j;
|
|
else {
|
|
if (j > 0) {
|
|
var newValue = costs[j - 1];
|
|
if (s1.charAt(i - 1) != s2.charAt(j - 1))
|
|
newValue = Math.min(Math.min(newValue, lastValue), costs[j]) + 1;
|
|
costs[j - 1] = lastValue;
|
|
lastValue = newValue;
|
|
}
|
|
}
|
|
}
|
|
if (i > 0) costs[s2.length] = lastValue;
|
|
}
|
|
return costs[s2.length];
|
|
}
|
|
|
|
export function latLngToMGRS(
|
|
lat: number,
|
|
lng: number,
|
|
precision: number = 4
|
|
): MGRS | false {
|
|
if (precision < 0 || precision > 6) {
|
|
console.error(
|
|
"latLngToMGRS: precision must be a number >= 0 and <= 6. Given precision: " +
|
|
precision
|
|
);
|
|
return false;
|
|
}
|
|
const mgrs = new Converter({}).LLtoMGRS(lat, lng, precision);
|
|
const match = mgrs.match(new RegExp(`^(\\d{2})([A-Z])([A-Z])([A-Z])(\\d+)$`));
|
|
const easting = match[5].substr(0, match[5].length / 2);
|
|
const northing = match[5].substr(match[5].length / 2);
|
|
|
|
let output: MGRS = {
|
|
bandLetter: match[2],
|
|
columnLetter: match[3],
|
|
groups: [match[1] + match[2], match[3] + match[4], easting, northing],
|
|
easting: easting,
|
|
northing: northing,
|
|
precision: precision,
|
|
rowLetter: match[4],
|
|
string: match[0],
|
|
zoneNumber: match[1],
|
|
};
|
|
|
|
return output;
|
|
}
|
|
|
|
export function latLngToUTM(lat: number, lng: number) {
|
|
return new Converter({}).LLtoUTM(lat, lng);
|
|
}
|
|
|
|
export function latLngToMercator(
|
|
lat: number,
|
|
lng: number
|
|
): { x: number; y: number } {
|
|
var rMajor = 6378137; //Equatorial Radius, WGS84
|
|
var shift = Math.PI * rMajor;
|
|
var x = (lng * shift) / 180;
|
|
var y = Math.log(Math.tan(((90 + lat) * Math.PI) / 360)) / (Math.PI / 180);
|
|
y = (y * shift) / 180;
|
|
|
|
return { x: x, y: y };
|
|
}
|
|
|
|
export function mercatorToLatLng(x: number, y: number) {
|
|
var rMajor = 6378137; //Equatorial Radius, WGS84
|
|
var shift = Math.PI * rMajor;
|
|
var lng = (x / shift) * 180.0;
|
|
var lat = (y / shift) * 180.0;
|
|
lat =
|
|
(180 / Math.PI) *
|
|
(2 * Math.atan(Math.exp((lat * Math.PI) / 180.0)) - Math.PI / 2.0);
|
|
|
|
return { lng: lng, lat: lat };
|
|
}
|
|
|
|
export function knotsToMs(knots: number) {
|
|
return knots / 1.94384;
|
|
}
|
|
|
|
export function msToKnots(ms: number) {
|
|
return ms * 1.94384;
|
|
}
|
|
|
|
export function ftToM(ft: number) {
|
|
return ft * 0.3048;
|
|
}
|
|
|
|
export function mToFt(m: number) {
|
|
return m / 0.3048;
|
|
}
|
|
|
|
export function mToNm(m: number) {
|
|
return m * 0.000539957;
|
|
}
|
|
|
|
export function nmToM(nm: number) {
|
|
return nm / 0.000539957;
|
|
}
|
|
|
|
export function nmToFt(nm: number) {
|
|
return nm * 6076.12;
|
|
}
|
|
|
|
export function areaContains(latlng: LatLng, area: Polygon | Circle) {
|
|
if (area instanceof Polygon) return polyContains(latlng, area);
|
|
else return circleContains(latlng, area);
|
|
}
|
|
|
|
export function polyContains(latlng: LatLng, polygon: Polygon) {
|
|
let coordinates = [
|
|
(polygon.getLatLngs()[0] as LatLng[]).map((latlng) => {
|
|
return [latlng.lng, latlng.lat];
|
|
}),
|
|
];
|
|
coordinates[0].push([
|
|
polygon.getLatLngs()[0][0].lng,
|
|
polygon.getLatLngs()[0][0].lat,
|
|
]);
|
|
const poly = turf.polygon(coordinates);
|
|
return turf.inside(turf.point([latlng.lng, latlng.lat]), poly);
|
|
}
|
|
|
|
export function circleContains(latlng: LatLng, circle: Circle) {
|
|
const poly = turf.circle(
|
|
turf.point([circle.getLatLng().lng, circle.getLatLng().lat]),
|
|
circle.getRadius() / 1000,
|
|
100,
|
|
"kilometers"
|
|
);
|
|
return turf.inside(turf.point([latlng.lng, latlng.lat]), poly);
|
|
}
|
|
|
|
export function polyCenter(polygon: Polygon) {
|
|
let coordinates = [
|
|
(polygon.getLatLngs()[0] as LatLng[]).map((latlng) => {
|
|
return [latlng.lng, latlng.lat];
|
|
}),
|
|
];
|
|
coordinates[0].push([
|
|
polygon.getLatLngs()[0][0].lng,
|
|
polygon.getLatLngs()[0][0].lat,
|
|
]);
|
|
const poly = turf.polygon(coordinates);
|
|
const center = turf.center(featureCollection([poly]));
|
|
return new LatLng(
|
|
center.geometry.coordinates[1],
|
|
center.geometry.coordinates[0]
|
|
);
|
|
}
|
|
|
|
export function randomPointInPoly(polygon: Polygon): LatLng {
|
|
var bounds = polygon.getBounds();
|
|
var x_min = bounds.getEast();
|
|
var x_max = bounds.getWest();
|
|
var y_min = bounds.getSouth();
|
|
var y_max = bounds.getNorth();
|
|
|
|
var lat = y_min + Math.random() * (y_max - y_min);
|
|
var lng = x_min + Math.random() * (x_max - x_min);
|
|
|
|
let coordinates = [
|
|
(polygon.getLatLngs()[0] as LatLng[]).map((latlng) => {
|
|
return [latlng.lng, latlng.lat];
|
|
}),
|
|
];
|
|
coordinates[0].push([
|
|
polygon.getLatLngs()[0][0].lng,
|
|
polygon.getLatLngs()[0][0].lat,
|
|
]);
|
|
const poly = turf.polygon(coordinates);
|
|
var inside = turf.inside(turf.point([lng, lat]), poly);
|
|
|
|
if (inside) {
|
|
return new LatLng(lat, lng);
|
|
} else {
|
|
return randomPointInPoly(polygon);
|
|
}
|
|
}
|
|
|
|
export function polygonArea(polygon: Polygon) {
|
|
var poly = polygon.toGeoJSON();
|
|
return turf.area(poly);
|
|
}
|
|
|
|
export function randomUnitBlueprint(
|
|
unitDatabase: UnitDatabase,
|
|
options: {
|
|
type?: string;
|
|
role?: string;
|
|
ranges?: string[];
|
|
eras?: string[];
|
|
coalition?: string;
|
|
}
|
|
) {
|
|
/* Start from all the unit blueprints in the database */
|
|
var unitBlueprints = Object.values(unitDatabase.getBlueprints());
|
|
|
|
/* If a specific type or role is provided, use only the blueprints of that type or role */
|
|
if (options.type && options.role) {
|
|
console.error(
|
|
"Can't create random unit if both type and role are provided. Either create by type or by role."
|
|
);
|
|
return null;
|
|
}
|
|
|
|
if (options.type) {
|
|
unitBlueprints = unitDatabase.getByType(options.type);
|
|
} else if (options.role) {
|
|
unitBlueprints = unitDatabase.getByType(options.role);
|
|
}
|
|
|
|
/* Keep only the units that have a range included in the requested values */
|
|
if (options.ranges) {
|
|
unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => {
|
|
var rangeType = "";
|
|
var range = unitBlueprint.acquisitionRange;
|
|
if (range !== undefined) {
|
|
if (range >= 0 && range < 10000) rangeType = "Short range";
|
|
else if (range >= 10000 && range < 100000) rangeType = "Medium range";
|
|
else if (range >= 100000 && range < 999999) rangeType = "Long range";
|
|
}
|
|
return options.ranges?.includes(rangeType);
|
|
});
|
|
}
|
|
|
|
/* Keep only the units that have an era included in the requested values */
|
|
if (options.eras) {
|
|
unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => {
|
|
return unitBlueprint.era
|
|
? options.eras?.includes(unitBlueprint.era)
|
|
: true;
|
|
});
|
|
}
|
|
|
|
/* Keep only the units that have the correct coalition, if selected */
|
|
if (options.coalition) {
|
|
unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => {
|
|
return unitBlueprint.coalition && unitBlueprint.coalition !== ""
|
|
? options.coalition === unitBlueprint.coalition
|
|
: true;
|
|
});
|
|
}
|
|
|
|
var index = Math.floor(Math.random() * unitBlueprints.length);
|
|
return unitBlueprints[index];
|
|
}
|
|
|
|
export function getMarkerCategoryByName(name: string) {
|
|
if (aircraftDatabase.getByName(name) != null) return "aircraft";
|
|
else if (helicopterDatabase.getByName(name) != null) return "helicopter";
|
|
else if (groundUnitDatabase.getByName(name) != null) {
|
|
var type = groundUnitDatabase.getByName(name)?.type ?? "";
|
|
if (/\bAAA|SAM\b/.test(type) || /\bmanpad|stinger\b/i.test(type))
|
|
return "groundunit-sam";
|
|
else return "groundunit-other";
|
|
} else if (navyUnitDatabase.getByName(name) != null) return "navyunit";
|
|
else return "aircraft"; // TODO add other unit types
|
|
}
|
|
|
|
export function getUnitDatabaseByCategory(category: string) {
|
|
if (category.toLowerCase() == "aircraft") return aircraftDatabase;
|
|
else if (category.toLowerCase() == "helicopter") return helicopterDatabase;
|
|
else if (category.toLowerCase().includes("groundunit"))
|
|
return groundUnitDatabase;
|
|
else if (category.toLowerCase().includes("navyunit")) return navyUnitDatabase;
|
|
else return null;
|
|
}
|
|
|
|
export function getUnitCategoryByBlueprint(blueprint: UnitBlueprint) {
|
|
for (let database of [
|
|
getApp()?.getAircraftDatabase(),
|
|
getApp()?.getHelicopterDatabase(),
|
|
getApp()?.getGroundUnitDatabase(),
|
|
getApp()?.getNavyUnitDatabase(),
|
|
]) {
|
|
if (blueprint.name in database.blueprints) return database.getCategory();
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
export function enumToState(state: number) {
|
|
if (state < states.length) return states[state];
|
|
else return states[0];
|
|
}
|
|
|
|
export function enumToROE(ROE: number) {
|
|
if (ROE < ROEs.length) return ROEs[ROE];
|
|
else return ROEs[0];
|
|
}
|
|
|
|
export function enumToReactionToThreat(reactionToThreat: number) {
|
|
if (reactionToThreat < reactionsToThreat.length)
|
|
return reactionsToThreat[reactionToThreat];
|
|
else return reactionsToThreat[0];
|
|
}
|
|
|
|
export function enumToEmissioNCountermeasure(emissionCountermeasure: number) {
|
|
if (emissionCountermeasure < emissionsCountermeasures.length)
|
|
return emissionsCountermeasures[emissionCountermeasure];
|
|
else return emissionsCountermeasures[0];
|
|
}
|
|
|
|
export function enumToCoalition(coalitionID: number) {
|
|
switch (coalitionID) {
|
|
case 0:
|
|
return "neutral";
|
|
case 1:
|
|
return "red";
|
|
case 2:
|
|
return "blue";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
export function coalitionToEnum(coalition: string) {
|
|
switch (coalition) {
|
|
case "neutral":
|
|
return 0;
|
|
case "red":
|
|
return 1;
|
|
case "blue":
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
export function convertDateAndTimeToDate(dateAndTime: DateAndTime) {
|
|
const date = dateAndTime.date;
|
|
const time = dateAndTime.time;
|
|
|
|
if (!date) {
|
|
return new Date();
|
|
}
|
|
|
|
let year = date.Year;
|
|
let month = date.Month - 1;
|
|
|
|
if (month < 0) {
|
|
month = 11;
|
|
year--;
|
|
}
|
|
|
|
return new Date(year, month, date.Day, time.h, time.m, time.s);
|
|
}
|
|
|
|
export function getGroundElevation(latlng: LatLng, callback: CallableFunction) {
|
|
/* Get the ground elevation from the server endpoint */
|
|
const xhr = new XMLHttpRequest();
|
|
xhr.open("GET", `api/elevation/${latlng.lat}/${latlng.lng}`, true);
|
|
xhr.timeout = 500; // ms
|
|
xhr.responseType = "json";
|
|
xhr.onload = () => {
|
|
var status = xhr?.status;
|
|
if (status === 200) {
|
|
callback(xhr.response);
|
|
}
|
|
};
|
|
xhr.send();
|
|
}
|
|
|
|
export function getWikipediaEntry(search: string, callback: CallableFunction) {
|
|
/* Get the ground elevation from the server endpoint */
|
|
const xhr = new XMLHttpRequest();
|
|
xhr.open(
|
|
"GET",
|
|
`https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts|pageimages&exintro&explaintext&generator=search&gsrsearch=intitle:${search}&gsrlimit=1&redirects=1&origin=*`,
|
|
true
|
|
);
|
|
xhr.timeout = 500; // ms
|
|
xhr.responseType = "json";
|
|
xhr.onload = () => {
|
|
var status = xhr?.status;
|
|
if (status === 200) {
|
|
callback(xhr.response);
|
|
}
|
|
};
|
|
xhr.send();
|
|
}
|
|
|
|
export function getFunctionArguments(func) {
|
|
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
|
|
var ARGUMENT_NAMES = /([^\s,]+)/g;
|
|
|
|
var fnStr = func.toString().replace(STRIP_COMMENTS, "");
|
|
var result = fnStr
|
|
.slice(fnStr.indexOf("(") + 1, fnStr.indexOf(")"))
|
|
.match(ARGUMENT_NAMES);
|
|
if (result === null) result = [];
|
|
return result;
|
|
}
|
|
|
|
export function filterBlueprintsByLabel(
|
|
blueprints: { [key: string]: UnitBlueprint },
|
|
filterString: string
|
|
) {
|
|
var filteredBlueprints: { [key: string]: UnitBlueprint } = {};
|
|
if (blueprints) {
|
|
Object.entries(blueprints).forEach(([key, value]) => {
|
|
if (
|
|
value.enabled &&
|
|
(filterString === "" || value.label.includes(filterString))
|
|
)
|
|
filteredBlueprints[key] = value;
|
|
});
|
|
}
|
|
return filteredBlueprints;
|
|
}
|
|
|
|
export function getUnitsByLabel(filterString: string) {
|
|
/* Filter aircrafts, helicopters, and navyunits */
|
|
const filteredAircraft = filterBlueprintsByLabel(
|
|
getApp()?.getAircraftDatabase()?.blueprints,
|
|
filterString
|
|
);
|
|
const filteredHelicopters = filterBlueprintsByLabel(
|
|
getApp()?.getHelicopterDatabase()?.blueprints,
|
|
filterString
|
|
);
|
|
const filteredNavyUnits = filterBlueprintsByLabel(
|
|
getApp()?.getNavyUnitDatabase()?.blueprints,
|
|
filterString
|
|
);
|
|
|
|
/* Split ground units between air defence and all others */
|
|
var filteredAirDefense: { [key: string]: UnitBlueprint } = {};
|
|
var filteredGroundUnits: { [key: string]: UnitBlueprint } = {};
|
|
Object.keys(getApp()?.getGroundUnitDatabase()?.blueprints ?? {}).forEach(
|
|
(key) => {
|
|
var blueprint = getApp()?.getGroundUnitDatabase()?.blueprints[key];
|
|
var type = blueprint.label;
|
|
if (/\bAAA|SAM\b/.test(type) || /\bmanpad|stinger\b/i.test(type)) {
|
|
filteredAirDefense[key] = blueprint;
|
|
} else {
|
|
filteredGroundUnits[key] = blueprint;
|
|
}
|
|
}
|
|
);
|
|
filteredAirDefense = filterBlueprintsByLabel(filteredAirDefense, filterString);
|
|
filteredGroundUnits = filterBlueprintsByLabel(filteredGroundUnits, filterString);
|
|
|
|
return [filteredAircraft, filteredHelicopters, filteredAirDefense, filteredGroundUnits, filteredNavyUnits]
|
|
} |