diff --git a/backend/core/src/groundunit.cpp b/backend/core/src/groundunit.cpp index 3923ce03..5aa11bd6 100644 --- a/backend/core/src/groundunit.cpp +++ b/backend/core/src/groundunit.cpp @@ -402,6 +402,14 @@ void GroundUnit::AIloop() canAAA = databaseEntry[L"canAAA"].as_bool(); } + /* Recover the data from the database */ + bool flak = false; + if (database.has_object_field(to_wstring(name))) { + json::value databaseEntry = database[to_wstring(name)]; + if (databaseEntry.has_boolean_field(L"flak")) + flak = databaseEntry[L"flak"].as_bool(); + } + if (canAAA) { /* Only perform scenic functions when the scheduler is "free" */ /* Only run this when the internal counter reaches 0 to avoid excessive computations when no nearby target */ @@ -450,13 +458,18 @@ void GroundUnit::AIloop() } /* Else, do miss on purpose */ else { - /* Compute where the target will be in aimTime seconds, plus the effect of scatter. */ - double scatterDistance = distance * tan(shotsBaseScatter * (ShotsScatter::LOW - shotsScatter) / 57.29577) * (RANDOM_ZERO_TO_ONE - 0.1); - double aimDistance = target->getHorizontalVelocity() * correctedAimTime + scatterDistance; + /* Compute where the target will be in aimTime seconds. */ + double aimDistance = target->getHorizontalVelocity() * correctedAimTime; double aimLat = 0; double aimLng = 0; Geodesic::WGS84().Direct(target->getPosition().lat, target->getPosition().lng, target->getTrack() * 57.29577, aimDistance, aimLat, aimLng); /* TODO make util to convert degrees and radians function */ - double aimAlt = target->getPosition().alt + target->getVerticalVelocity() * correctedAimTime + distance * tan(shotsBaseScatter * (ShotsScatter::LOW - shotsScatter) / 57.29577) * RANDOM_ZERO_TO_ONE; // Force to always miss high never low + double aimAlt = target->getPosition().alt + target->getVerticalVelocity(); + + if (flak) { + aimLat += RANDOM_MINUS_ONE_TO_ONE * (ShotsScatter::LOW - shotsScatter) * 0.01; + aimLng += RANDOM_MINUS_ONE_TO_ONE * (ShotsScatter::LOW - shotsScatter) * 0.01; + aimAlt += RANDOM_MINUS_ONE_TO_ONE * (ShotsScatter::LOW - shotsScatter) * 1000; + } /* Send the command */ if (distance < engagementRange) { diff --git a/databases/units/groundunitdatabase.json b/databases/units/groundunitdatabase.json index 596feb34..90e54b8b 100644 --- a/databases/units/groundunitdatabase.json +++ b/databases/units/groundunitdatabase.json @@ -8625,8 +8625,8 @@ "abilities": "AA", "canTargetPoint": false, "canRearm": false, - "muzzleVelocity": 1000, - "aimTime": 25, + "muzzleVelocity": 700, + "aimTime": 50, "shotsToFire": 1, "barrelHeight": 5, "cost": null, @@ -10635,7 +10635,7 @@ "countries": "All" } }, - "aimTime": 15, + "aimTime": 50, "shotsToFire": 1, "acquisitionRange": 15000, "engagementRange": 12000, @@ -10643,7 +10643,7 @@ "abilities": "AA", "canTargetPoint": true, "canRearm": false, - "muzzleVelocity": 880, + "muzzleVelocity": 700, "barrelHeight": 2.1, "cost": 40000, "markerFile": "groundunit-aaa", diff --git a/frontend/react/public/images/markers/flak.svg b/frontend/react/public/images/markers/flak.svg new file mode 100644 index 00000000..146c0d87 --- /dev/null +++ b/frontend/react/public/images/markers/flak.svg @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/react/src/map/map.ts b/frontend/react/src/map/map.ts index bee24c70..4ec202ca 100644 --- a/frontend/react/src/map/map.ts +++ b/frontend/react/src/map/map.ts @@ -68,6 +68,7 @@ import { import { ContextActionSet } from "../unit/contextactionset"; import { SmokeMarker } from "./markers/smokemarker"; import { Measure } from "./measure"; +import { FlakMarker } from "./markers/flakmarker"; /* Register the handler for the box selection */ L.Map.addInitHook("addHandler", "boxSelect", BoxSelect); @@ -762,6 +763,12 @@ export class Map extends L.Map { return explosionMarker; } + addFlakMarker(latlng: L.LatLng) { + const explosionMarker = new FlakMarker(latlng, 10); + explosionMarker.addTo(this); + return explosionMarker; + } + addSmokeMarker(latlng: L.LatLng, color: string) { const smokeMarker = new SmokeMarker(latlng, color); smokeMarker.addTo(this); diff --git a/frontend/react/src/map/markers/flakmarker.ts b/frontend/react/src/map/markers/flakmarker.ts new file mode 100644 index 00000000..f2c581c1 --- /dev/null +++ b/frontend/react/src/map/markers/flakmarker.ts @@ -0,0 +1,39 @@ +import { CustomMarker } from "./custommarker"; +import { DivIcon, LatLng } from "leaflet"; +import { SVGInjector } from "@tanem/svg-injector"; +import { getApp } from "../../olympusapp"; + +export class FlakMarker extends CustomMarker { + #timer: number = 0; + #timeout: number = 0; + + constructor(latlng: LatLng, timeout?: number) { + super(latlng, { interactive: false }); + + if (timeout) { + this.#timeout = timeout; + + this.#timer = window.setTimeout(() => { + this.removeFrom(getApp().getMap()); + }, timeout * 1000); + } + } + + createIcon() { + /* Set the icon */ + this.setIcon( + new DivIcon({ + iconSize: [52, 52], + iconAnchor: [26, 26], + className: "leaflet-flak-marker", + }) + ); + var el = document.createElement("div"); + el.classList.add("ol-flak-icon"); + var img = document.createElement("img"); + img.src = "images/markers/flak.svg"; + img.onload = () => SVGInjector(img); + el.appendChild(img); + this.getElement()?.appendChild(el); + } +} diff --git a/frontend/react/src/weapon/weapon.ts b/frontend/react/src/weapon/weapon.ts index 0181c9e0..6b6f4b02 100644 --- a/frontend/react/src/weapon/weapon.ts +++ b/frontend/react/src/weapon/weapon.ts @@ -112,6 +112,9 @@ export abstract class Weapon extends CustomMarker { } setAlive(newAlive: boolean) { + if (this.#alive && !newAlive) { + getApp().getMap().addFlakMarker(this.getLatLng()); + } this.#alive = newAlive; }