feat: Added material to quick reference guide
BIN
frontend/react/public/images/training/step12.1.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
frontend/react/public/images/training/step12.gif
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
frontend/react/public/images/training/step13.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 5.2 MiB |
BIN
frontend/react/public/images/training/step3.1.gif
Normal file
|
After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 244 KiB |
BIN
frontend/react/public/images/training/step4.1.gif
Normal file
|
After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 409 KiB After Width: | Height: | Size: 3.3 MiB |
BIN
frontend/react/public/images/training/step8.gif
Normal file
|
After Width: | Height: | Size: 2.9 MiB |
@ -1,6 +1,6 @@
|
||||
import { LatLngExpression, Map, Circle, DivIcon, Marker, CircleOptions, LatLng } from "leaflet";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { DraggableHandle } from "./coalitionareahandle";
|
||||
import { DraggableHandle } from "../markers/draggablehandle";
|
||||
import { BLUE_COMMANDER, colors, RED_COMMANDER } from "../../constants/constants";
|
||||
import { Coalition } from "../../types/types";
|
||||
import * as turf from "@turf/turf";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { LatLng, LatLngExpression, Map, Point, Polygon, PolylineOptions, DivIcon, Marker } from "leaflet";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { DraggableHandle } from "./coalitionareahandle";
|
||||
import { DraggableHandle } from "../markers/draggablehandle";
|
||||
import { CoalitionAreaMiddleHandle } from "./coalitionareamiddlehandle";
|
||||
import { BLUE_COMMANDER, colors, RED_COMMANDER } from "../../constants/constants";
|
||||
import { Coalition } from "../../types/types";
|
||||
|
||||
@ -46,8 +46,8 @@ export class MapMouseHandler {
|
||||
|
||||
/* Events for touchscreen and mouse */
|
||||
if ("ontouchstart" in window) {
|
||||
DomEvent.on(this.#map.getContainer(), "touchstart", (e: any) => this.#onTouchDown(e), this);
|
||||
DomEvent.on(this.#map.getContainer(), "touchend", (e: any) => this.#onTouchUp(e), this);
|
||||
DomEvent.on(this.#map.getContainer(), "touchstart", (e: any) => this.#onTouchStart(e), this);
|
||||
DomEvent.on(this.#map.getContainer(), "touchend", (e: any) => this.#onTouchEnd(e), this);
|
||||
DomEvent.on(this.#map.getContainer(), "touchmove", (e: any) => this.#onTouchMove(e), this);
|
||||
} else {
|
||||
this.#map.on("mouseup", (e: any) => this.#onMouseUp(e));
|
||||
@ -162,7 +162,7 @@ export class MapMouseHandler {
|
||||
this.mouseWheel(e);
|
||||
};
|
||||
|
||||
#onTouchDown = (e: TouchEvent) => {
|
||||
#onTouchStart = (e: TouchEvent) => {
|
||||
let newEvent = {
|
||||
latlng: this.#map.containerPointToLatLng(this.#map.mouseEventToContainerPoint(e.changedTouches[0] as unknown as MouseEvent)),
|
||||
originalEvent: e,
|
||||
@ -177,7 +177,7 @@ export class MapMouseHandler {
|
||||
}, 300);
|
||||
};
|
||||
|
||||
#onTouchUp = (e: TouchEvent) => {
|
||||
#onTouchEnd = (e: TouchEvent) => {
|
||||
let newEvent = {
|
||||
latlng: this.#map.containerPointToLatLng(this.#map.mouseEventToContainerPoint(e.changedTouches[0] as unknown as MouseEvent)),
|
||||
originalEvent: e,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { DivIcon, DomEvent, LatLng } from "leaflet";
|
||||
import { CustomMarker } from "../markers/custommarker";
|
||||
import { CustomMarker } from "./custommarker";
|
||||
|
||||
export class DraggableHandle extends CustomMarker {
|
||||
constructor(latlng: LatLng) {
|
||||
@ -9,21 +9,10 @@ export class DraggableHandle extends CustomMarker {
|
||||
this.getElement()?.addEventListener("touchstart", (e) => e.stopPropagation());
|
||||
});
|
||||
|
||||
this.on("mousedown", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
|
||||
this.on("mouseup", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
|
||||
this.on("dragstart", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
|
||||
this.on("dragend", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
this.on("mousedown", (e) => DomEvent.stopPropagation(e));
|
||||
this.on("mouseup", (e) => DomEvent.stopPropagation(e));
|
||||
this.on("dragstart", (e) => DomEvent.stopPropagation(e));
|
||||
this.on("dragend", (e) => DomEvent.stopPropagation(e));
|
||||
}
|
||||
|
||||
createIcon() {
|
||||
@ -1,15 +1,32 @@
|
||||
import { DivIcon, LatLngExpression, Map, MarkerOptions } from "leaflet";
|
||||
import { DivIcon, DomEvent, LatLngExpression, Map, MarkerOptions } from "leaflet";
|
||||
import { CustomMarker } from "./custommarker";
|
||||
import { SVGInjector } from "@tanem/svg-injector";
|
||||
|
||||
export class MeasureEndMarker extends CustomMarker {
|
||||
#rotationAngle: number = 0;
|
||||
#moving: boolean = true;
|
||||
|
||||
constructor(latlng: LatLngExpression, options?: MarkerOptions) {
|
||||
super(latlng, options);
|
||||
this.options.interactive = true;
|
||||
this.options.draggable = true;
|
||||
this.setZIndexOffset(9999);
|
||||
|
||||
this.on("mousedown", (e) => {
|
||||
if (!this.#moving) DomEvent.stopPropagation(e);
|
||||
});
|
||||
|
||||
this.on("mouseup", (e) => {
|
||||
if (!this.#moving) DomEvent.stopPropagation(e);
|
||||
});
|
||||
|
||||
this.on("dragstart", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
|
||||
this.on("dragend", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
}
|
||||
|
||||
createIcon() {
|
||||
@ -27,7 +44,7 @@ export class MeasureEndMarker extends CustomMarker {
|
||||
img.onload = () => {
|
||||
SVGInjector(img);
|
||||
this.#applyRotation();
|
||||
}
|
||||
};
|
||||
el.appendChild(img);
|
||||
this.getElement()?.appendChild(el);
|
||||
}
|
||||
@ -47,6 +64,14 @@ export class MeasureEndMarker extends CustomMarker {
|
||||
return this;
|
||||
}
|
||||
|
||||
setMoving(moving: boolean) {
|
||||
this.#moving = moving;
|
||||
}
|
||||
|
||||
getMoving() {
|
||||
return this.#moving;
|
||||
}
|
||||
|
||||
#applyRotation() {
|
||||
const element = this.getElement();
|
||||
if (element) {
|
||||
|
||||
@ -1,14 +1,29 @@
|
||||
import { DivIcon, LatLngExpression, MarkerOptions } from "leaflet";
|
||||
import { DivIcon, DomEvent, LatLngExpression, MarkerOptions } from "leaflet";
|
||||
import { CustomMarker } from "./custommarker";
|
||||
import { SVGInjector } from "@tanem/svg-injector";
|
||||
|
||||
export class MeasureStartMarker extends CustomMarker {
|
||||
|
||||
constructor(latlng: LatLngExpression, options?: MarkerOptions) {
|
||||
constructor(latlng: LatLngExpression, options?: MarkerOptions) {
|
||||
super(latlng, options);
|
||||
this.options.interactive = true;
|
||||
this.options.draggable = true;
|
||||
this.setZIndexOffset(9999);
|
||||
|
||||
this.on("mousedown", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
|
||||
this.on("mouseup", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
|
||||
this.on("dragstart", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
|
||||
this.on("dragend", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
}
|
||||
|
||||
createIcon() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { DivIcon, LatLngExpression, MarkerOptions } from "leaflet";
|
||||
import { DivIcon, DomEvent, LatLngExpression, MarkerOptions } from "leaflet";
|
||||
import { CustomMarker } from "./custommarker";
|
||||
|
||||
export class SpotMarker extends CustomMarker {
|
||||
@ -7,6 +7,22 @@ export class SpotMarker extends CustomMarker {
|
||||
this.options.interactive = true;
|
||||
this.options.draggable = true;
|
||||
this.setZIndexOffset(9999);
|
||||
|
||||
this.on("mousedown", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
|
||||
this.on("mouseup", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
|
||||
this.on("dragstart", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
|
||||
this.on("dragend", (e) => {
|
||||
DomEvent.stopPropagation(e);
|
||||
});
|
||||
}
|
||||
|
||||
createIcon() {
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
import { LatLng, LeafletMouseEvent, Polyline } from "leaflet";
|
||||
import { LatLng, Polyline } from "leaflet";
|
||||
import { Map } from "./map";
|
||||
import { MeasureMarker } from "./markers/measuremarker";
|
||||
import { MeasureStartMarker } from "./markers/measurestartmarker";
|
||||
import { MeasureEndMarker } from "./markers/measureendmarker";
|
||||
import { bearing, deg2rad, midpoint, mToFt, mToNm, nmToM, rad2deg } from "../other/utils";
|
||||
import { AppStateChangedEvent } from "../events";
|
||||
import { OlympusState } from "../constants/constants";
|
||||
|
||||
export class Measure {
|
||||
#active: boolean = false;
|
||||
@ -76,6 +74,7 @@ export class Measure {
|
||||
|
||||
finish() {
|
||||
this.#active = false;
|
||||
this.#endMarker.setMoving(false);
|
||||
}
|
||||
|
||||
isActive() {
|
||||
|
||||
@ -40,17 +40,15 @@ export class Airbase extends CustomMarker {
|
||||
(this.getElement()?.querySelector(".airbase-icon") as HTMLElement).dataset.selected = `${this.#selected}`;
|
||||
});
|
||||
|
||||
this.addEventListener("mousedown", (ev) => {
|
||||
this.on("mousedown", (ev) => {
|
||||
if (getApp().getState() === OlympusState.IDLE || getApp().getState() === OlympusState.AIRBASE) {
|
||||
DomEvent.stop(ev);
|
||||
ev.originalEvent.stopImmediatePropagation();
|
||||
}
|
||||
});
|
||||
|
||||
this.addEventListener("mouseup", (ev) => {
|
||||
this.on("mouseup", (ev) => {
|
||||
if (getApp().getState() === OlympusState.IDLE || getApp().getState() === OlympusState.AIRBASE) {
|
||||
DomEvent.stop(ev);
|
||||
ev.originalEvent.stopImmediatePropagation();
|
||||
getApp().setState(OlympusState.AIRBASE);
|
||||
AirbaseSelectedEvent.dispatch(this);
|
||||
}
|
||||
|
||||
@ -2,70 +2,116 @@ import React, { useState } from "react";
|
||||
import { Modal } from "./components/modal";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faArrowLeft, faArrowRight } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FaCaretRight, FaLink } from "react-icons/fa6";
|
||||
import { FaLink } from "react-icons/fa6";
|
||||
import { FaQuestionCircle } from "react-icons/fa";
|
||||
|
||||
const MAX_STEPS = 10;
|
||||
const MAX_STEPS = 15;
|
||||
|
||||
export function TrainingModal(props: { open: boolean }) {
|
||||
const [step, setStep] = useState(0);
|
||||
|
||||
return (
|
||||
<Modal open={props.open}>
|
||||
<div>
|
||||
<h1 className={`text-2xl font-semibold text-white`}>DCS Olympus guided tour</h1>
|
||||
|
||||
</div>
|
||||
<Modal open={props.open} size="lg">
|
||||
<>
|
||||
{step === 0 && (
|
||||
<div>
|
||||
<h1 className={`text-3xl font-semibold text-gray-200`}>DCS Olympus quick reference guide</h1>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
<>
|
||||
{step === 0 && (
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-16">
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<img
|
||||
src="images/olympus-500x500.png"
|
||||
className={`my-auto h-40 w-40 rounded-xl`}
|
||||
className={`
|
||||
pointer-events-none absolute left-[50%] top-[50%] my-auto
|
||||
aspect-square h-[80%] translate-x-[-50%] translate-y-[-50%]
|
||||
rounded-xl opacity-[2%]
|
||||
`}
|
||||
/>
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<div className="flex flex-col gap-4 gap-x-10 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>Home</h2>
|
||||
<p>
|
||||
Welcome to the Olympus quick start guide! This tour will guide you through the basics of DCS Olympus. You can navigate through the steps using
|
||||
the "Next" and "Previous" buttons at the bottom of the screen, or select a topic from the list below.
|
||||
</p>
|
||||
<div className="flex flex-col flex-wrap gap-2">
|
||||
<div className={`
|
||||
flex w-fit flex-col flex-wrap gap-2
|
||||
md:h-32
|
||||
`}>
|
||||
<div className="flex gap-2">
|
||||
<FaLink className="my-auto" />
|
||||
<a href="#" className={`text-blue-400`} onClick={() => setStep(1)}>
|
||||
<div className={`cursor-pointer text-blue-400`} onClick={() => setStep(1)}>
|
||||
Main navbar
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<FaLink className="my-auto" />
|
||||
<a href="#" className={`text-blue-400`} onClick={() => setStep(2)}>
|
||||
<div className={`cursor-pointer text-blue-400`} onClick={() => setStep(2)}>
|
||||
Spawning units
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<FaLink className="my-auto" />
|
||||
<a href="#" className={`text-blue-400`} onClick={() => setStep(5)}>
|
||||
<div className={`cursor-pointer text-blue-400`} onClick={() => setStep(5)}>
|
||||
Controlling units
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<FaLink className="my-auto" />
|
||||
<a href="#" className={`text-blue-400`} onClick={() => setStep(9)}>
|
||||
<div className={`cursor-pointer text-blue-400`} onClick={() => setStep(9)}>
|
||||
The unit marker
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Every panel has a dedicated integrated wiki. Click on the{" "}
|
||||
<span
|
||||
className={`
|
||||
mt-[-7px] inline-block translate-y-2 rounded-full p-1
|
||||
`}
|
||||
>
|
||||
<FaQuestionCircle />
|
||||
</span>{" "}
|
||||
symbol to access it. Moreover, most clickable content has tooltips providing info about their function.
|
||||
<div className="flex gap-2">
|
||||
<FaLink className="my-auto" />
|
||||
<div className={`cursor-pointer text-blue-400`} onClick={() => setStep(11)}>
|
||||
Interacting with the map
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<FaLink className="my-auto" />
|
||||
<div className={`cursor-pointer text-blue-400`} onClick={() => setStep(13)}>
|
||||
Mission drawings
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<FaLink className="my-auto" />
|
||||
<div className={`cursor-pointer text-blue-400`} onClick={() => setStep(14)}>
|
||||
The audio backend
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<FaLink className="my-auto" />
|
||||
<div className={`cursor-pointer text-blue-400`} onClick={() => setStep(15)}>
|
||||
Game master mode
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<FaLink className="my-auto" />
|
||||
<div className={`cursor-pointer text-blue-400`} onClick={() => {}}>
|
||||
Advanced topics
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Every panel has a dedicated integrated wiki. Click on the{" "}
|
||||
<span
|
||||
className={`
|
||||
mt-[-7px] inline-block translate-y-2 rounded-full p-1
|
||||
`}
|
||||
>
|
||||
<FaQuestionCircle />
|
||||
</span>{" "}
|
||||
symbol to access it. Moreover, most clickable content has tooltips providing info about their function.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -74,18 +120,23 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
|
||||
<>
|
||||
{step === 1 && (
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-16">
|
||||
<img
|
||||
src="images/training/step1.gif"
|
||||
className={`h-72 w-72 rounded-xl`}
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col content-center gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<img src="images/training/step1.gif" className={`
|
||||
h-96 w-96 rounded-xl
|
||||
`} />
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>Main navbar</h2>
|
||||
<p>
|
||||
The main functions of DCS Olympus are accessible from the main navbar. You can access the spawn tool, the unit selection and control tool, the
|
||||
drawings tool, the audio/radio tool, and the game master options from here.
|
||||
</p>
|
||||
<p>On the bottom left corner, you can find the DCS Olympus options tool.</p>
|
||||
<p>On the bottom left corner, you can find the DCS Olympus options tool and the button to access this quick reference guide.</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@ -93,11 +144,16 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
|
||||
<>
|
||||
{step === 2 && (
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-16">
|
||||
<img
|
||||
src="images/training/step2.gif"
|
||||
className={`h-72 w-72 rounded-xl`}
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<img src="images/training/step2.gif" className={`
|
||||
h-96 w-96 rounded-xl
|
||||
`} />
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>Spawning units (1 of 3)</h2>
|
||||
<p>
|
||||
@ -116,18 +172,29 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
|
||||
<>
|
||||
{step === 3 && (
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-16">
|
||||
<img
|
||||
src="images/training/step3.gif"
|
||||
className={`h-72 w-72 rounded-xl`}
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<img src="images/training/step3.gif" className={`
|
||||
h-96 w-96 rounded-xl
|
||||
`} />
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>Spawning units (2 of 3)</h2>
|
||||
<p>
|
||||
You can also spawn units directly on the map by right clicking on it and selecting the unit you want to spawn. This will spawn the unit at the
|
||||
clicked location.
|
||||
</p>
|
||||
<p>You can edit the unit properties like in the previous method.</p>
|
||||
<p>You can edit the unit properties like in the previous method. Remember you can open the unit summary section to get more info on the unit.</p>
|
||||
<div className="flex gap-4">
|
||||
<img src="images/training/step3.1.gif" className={`
|
||||
h-32 w-32 rounded-xl
|
||||
`} />
|
||||
You can change the spawn heading of the unit by dragging the arrow on the map. This will also change the spawn heading in the unit properties.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@ -135,11 +202,16 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
|
||||
<>
|
||||
{step === 4 && (
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-16">
|
||||
<img
|
||||
src="images/training/step4.gif"
|
||||
className={`h-72 w-72 rounded-xl`}
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<img src="images/training/step4.gif" className={`
|
||||
h-96 w-96 rounded-xl
|
||||
`} />
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>Spawning units (3 of 3)</h2>
|
||||
<p>
|
||||
@ -154,11 +226,16 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
|
||||
<>
|
||||
{step === 5 && (
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-16">
|
||||
<img
|
||||
src="images/training/step5.gif"
|
||||
className={`h-72 w-72 rounded-xl`}
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<img src="images/training/step5.gif" className={`
|
||||
h-96 w-96 rounded-xl
|
||||
`} />
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>Controlling units (1 of 4)</h2>
|
||||
<p>
|
||||
@ -170,6 +247,13 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
Previously created destinations can be moved by dragging the marker on the map. If multiple units are selected when creating the path,
|
||||
destinations will be shared between them.
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
<img src="images/training/step4.1.gif" className={`
|
||||
h-40 w-40 rounded-xl
|
||||
`} />
|
||||
Holding down the right mouse button enters "group movement" mode. The units will hold their relative positions and move as a formation. Move the
|
||||
mouse to choose the formation heading. Ctrl can be pressed to create a path.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@ -177,16 +261,31 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
|
||||
<>
|
||||
{step === 6 && (
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-16">
|
||||
<img
|
||||
src="images/training/step6.gif"
|
||||
className={`h-72 w-72 rounded-xl`}
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<img src="images/training/step6.gif" className={`
|
||||
h-96 w-96 rounded-xl
|
||||
`} />
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>Controlling units (2 of 4)</h2>
|
||||
<p>
|
||||
To issue an instruction to a unit, long press the right mouse button on the map. This will allow you to select an action, depending on what you
|
||||
clicked on.
|
||||
To issue an instruction to a unit, long press the left mouse button on the map. This will open a context menu allowing you to select an action,
|
||||
depending on what you clicked on. The same can be done by clicking a unit marker, which will show the unit's context menu.{" "}
|
||||
</p>
|
||||
<p>
|
||||
Actions are color coded:
|
||||
<ul className="list-inside list-disc">
|
||||
<li className="text-white">White: Movement</li>
|
||||
<li className="text-green-400">Green: Miscellaneous (group, center on map, etc)</li>
|
||||
<li className="text-purple-400">Purple: Admin (AAR, land, etc)</li>
|
||||
<li className="cursor-pointer text-blue-400">Blue: Attack or engage</li>
|
||||
<li className="text-red-400">Red: Delete or destroy</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p></p>
|
||||
</div>
|
||||
@ -196,11 +295,16 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
|
||||
<>
|
||||
{step === 7 && (
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-16">
|
||||
<img
|
||||
src="images/training/step7.gif"
|
||||
className={`h-72 w-72 rounded-xl`}
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<img src="images/training/step7.gif" className={`
|
||||
h-96 w-96 rounded-xl
|
||||
`} />
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>Controlling units (3 of 4)</h2>
|
||||
<p>
|
||||
@ -211,6 +315,7 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
Tools can be enabled using keyboard shortcuts. To learn what a tool does and what shortcut enables it, place your cursor over the corresponding
|
||||
button.{" "}
|
||||
</p>
|
||||
<p>You can hold the left mouse button down to enter "group" mode just like for movement. Moving the mouse allows to rotate the tool patterns.</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@ -218,11 +323,16 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
|
||||
<>
|
||||
{step === 8 && (
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-16">
|
||||
<img
|
||||
src="images/training/step8.gif"
|
||||
className={`h-72 w-72 rounded-xl`}
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<img src="images/training/step8.gif" className={`
|
||||
h-96 w-96 rounded-xl
|
||||
`} />
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>Controlling units (4 of 4)</h2>
|
||||
<p>
|
||||
@ -230,7 +340,16 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
Unit properties are set using the unit control menu, which opens automatically when a unit is selected. Here, depending on the selected unit,
|
||||
you can set altitude and speed, Rules Of Engagement, reaction to threat, as well as advanced settings like AWACS frequencies and so on.{" "}
|
||||
</p>
|
||||
<p> </p>
|
||||
<p>
|
||||
{" "}
|
||||
Available options will change depending on selected unit type and number. If different types of units are selected at the same time, only common
|
||||
properties will be editable. Use the units list on the upper part of the panel to refine your choice.{" "}
|
||||
</p>
|
||||
<p>
|
||||
{" "}
|
||||
If the selected units have properties set differently, the relevant inputs will show a "Different value" state, or show no state at all until
|
||||
the same value is set for all units.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@ -238,11 +357,16 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
|
||||
<>
|
||||
{step === 9 && (
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-16">
|
||||
<img
|
||||
src="images/training/unitmarker.png"
|
||||
className={`max-h-34 max-w-34 my-auto rounded-xl`}
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<img src="images/training/unitmarker.png" className={`
|
||||
max-h-34 max-w-34 my-auto rounded-xl
|
||||
`} />
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>The unit marker (1 of 2)</h2>
|
||||
<p>
|
||||
@ -353,11 +477,22 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
|
||||
<>
|
||||
{step === 10 && (
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-16">
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>The unit marker (2 of 2)</h2>
|
||||
<p>The unit marker has a symbol showing the unit state, i.e. what instruction it is performing. These are all the possible values:</p>
|
||||
<div className="flex sm:max-h-64 flex-col flex-wrap gap-4">
|
||||
<div
|
||||
className={`
|
||||
flex flex-col flex-wrap gap-4
|
||||
sm:max-h-64
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<p className="flex gap-2">
|
||||
<img src="images/states/attack.svg" />
|
||||
@ -454,6 +589,131 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
)}
|
||||
</>
|
||||
|
||||
<>
|
||||
{step === 11 && (
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>Interacting with the map (1 of 2)</h2>
|
||||
<p>
|
||||
Moving the map is done in a similar way to other map applications. You can use the mouse wheel to zoom in and out, and click and drag to move
|
||||
the map. On mobile, you can use two fingers to zoom in and out, and one finger to move the map.
|
||||
</p>
|
||||
<p>
|
||||
You have multiple options for selecting units. Clicking on a unit will select it. If you want to select multiple units, you can hold the ctrl
|
||||
key and click on them. A double click on the unit will perform the same operations, but on all currently visible units of the same type. This is
|
||||
useful for selecting all units of a certain type, like all F-16s.
|
||||
</p>
|
||||
<p>
|
||||
Box selection mode can be accessed in three different ways: by long pressing on the map with left mouse button (but only if no unit is currently
|
||||
selected), by holding the alt key and clicking on the map, or by using the box selection tool. This will select all units inside the box.
|
||||
</p>
|
||||
<p>
|
||||
Ctrl + A will select all the visible units on the map. Toggling the visibility of unwanted units and using this shortcut is a good way to select
|
||||
all units of a certain coalition or type. A more advanced selection tool can be accessed by clicking on the unit selection tool on the main
|
||||
navbar.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
<>
|
||||
{step === 12 && (
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<img src="images/training/step12.gif" className={`
|
||||
h-96 w-96 rounded-xl
|
||||
`} />
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>Interacting with the map (2 of 2)</h2>
|
||||
<p>
|
||||
Measurements can be perfomed on the map. To enter measure mode you can either use the measure tool, or click the mouse wheel. Subsequent clicks
|
||||
on the map will add additional points. You will be able to read the leg length and bearing, as well as the overall length of the line.
|
||||
</p>
|
||||
<div
|
||||
className={`
|
||||
flex w-full flex-col content-center justify-center gap-4
|
||||
`}
|
||||
>
|
||||
<img src="images/training/step12.1.png" className={`
|
||||
mx-auto w-40 rounded-xl
|
||||
`} />
|
||||
On the bottom right corner of the map, you can find the coordinates panel, providing the coordinates of the mouse cursor, as well as its
|
||||
bullseye position and the ground elevation. Click on the coordinates to rotate format.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
<>
|
||||
{step === 13 && (
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<img src="images/training/step13.png" className={`h-96 rounded-xl`} />
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>Mission drawings</h2>
|
||||
<p>Mission drawings are useful to provide information from the mission creator. They can define borders, Areas of Operation, navigational points and more. </p>
|
||||
<p>Mission drawings are automatically imported from the mission and can be shown using the drawing menu. You can enable or disable different sections, change the opacity, and look for specific drawings or navpoint using the shearch bar.</p>
|
||||
<p>You can also define your own drawings, which can be useful as reference, or as a way to automatically create IADS.</p>
|
||||
<p>Use the <FaQuestionCircle className="inline"/> button on the upper right of the drawings panel for more info. </p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
<>
|
||||
{step === 14 && (
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>The audio backend</h2>
|
||||
<p></p>
|
||||
<p></p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
<>
|
||||
{step === 15 && (
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-4
|
||||
md:flex-row
|
||||
sm:gap-16
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-col gap-4 text-gray-400">
|
||||
<h2 className={`text-xl font-semibold text-white`}>Game master mode</h2>
|
||||
<p></p>
|
||||
<p></p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
<div className="mt-auto flex justify-between">
|
||||
{step > 0 ? (
|
||||
<button
|
||||
@ -475,15 +735,22 @@ export function TrainingModal(props: { open: boolean }) {
|
||||
)}
|
||||
|
||||
{step > 0 && (
|
||||
<div className="my-auto gap-2 hidden sm:flex">
|
||||
<div
|
||||
className={`
|
||||
my-auto hidden gap-2
|
||||
sm:flex
|
||||
`}
|
||||
>
|
||||
{[...Array(MAX_STEPS).keys()].map((i) => (
|
||||
<div
|
||||
key={i + 1}
|
||||
className={`
|
||||
h-4 w-4 rounded-full
|
||||
${i + 1 === step ? "bg-blue-700 shadow-white" : `
|
||||
bg-gray-300/10
|
||||
`}
|
||||
${
|
||||
i + 1 === step
|
||||
? "bg-blue-700 shadow-white"
|
||||
: `bg-gray-300/10`
|
||||
}
|
||||
`}
|
||||
/>
|
||||
))}
|
||||
|
||||
@ -164,7 +164,7 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
|
||||
<FontAwesomeIcon
|
||||
icon={faMapLocation}
|
||||
className={`
|
||||
ml-auto cusor-pointer transition-transform
|
||||
ml-auto cursor-pointer transition-transform
|
||||
hover:scale-125
|
||||
`}
|
||||
onClick={() => {
|
||||
|
||||
@ -22,6 +22,13 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { FaQuestionCircle } from "react-icons/fa";
|
||||
import { OlExpandingTooltip } from "../components/olexpandingtooltip";
|
||||
|
||||
enum OpenAccordion {
|
||||
NONE,
|
||||
LOADOUT,
|
||||
UNIT_SUMMARY,
|
||||
ADVANCED_OPTIONS,
|
||||
}
|
||||
|
||||
export function UnitSpawnMenu(props: {
|
||||
visible: boolean;
|
||||
compact: boolean;
|
||||
@ -49,13 +56,13 @@ export function UnitSpawnMenu(props: {
|
||||
const [spawnAltitudeType, setSpawnAltitudeType] = useState(false);
|
||||
const [spawnLiveryID, setSpawnLiveryID] = useState("");
|
||||
const [spawnSkill, setSpawnSkill] = useState("High");
|
||||
const [showLoadout, setShowLoadout] = useState(false);
|
||||
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
|
||||
const [showUnitSummary, setShowUnitSummary] = useState(false);
|
||||
const [quickAccessName, setQuickAccessName] = useState("Preset 1");
|
||||
const [key, setKey] = useState("");
|
||||
const [spawnRequestTable, setSpawnRequestTable] = useState(null as null | SpawnRequestTable);
|
||||
|
||||
const [openAccordion, setOpenAccordion] = useState(OpenAccordion.NONE);
|
||||
const [showLoadout, setShowLoadout] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setAppState(getApp()?.getState());
|
||||
AppStateChangedEvent.on((state, subState) => setAppState(state));
|
||||
@ -391,9 +398,9 @@ export function UnitSpawnMenu(props: {
|
||||
)}
|
||||
<OlAccordion
|
||||
onClick={() => {
|
||||
setShowAdvancedOptions(!showAdvancedOptions);
|
||||
setOpenAccordion(openAccordion === OpenAccordion.ADVANCED_OPTIONS ? OpenAccordion.NONE : OpenAccordion.ADVANCED_OPTIONS);
|
||||
}}
|
||||
open={showAdvancedOptions}
|
||||
open={openAccordion === OpenAccordion.ADVANCED_OPTIONS}
|
||||
title="Advanced options"
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
@ -441,9 +448,10 @@ export function UnitSpawnMenu(props: {
|
||||
`}
|
||||
>
|
||||
{props.blueprint?.liveries && props.blueprint?.liveries[id].countries.length == 1 && (
|
||||
<img src={`images/countries/${country?.flagCode.toLowerCase()}.svg`} className={`
|
||||
h-6
|
||||
`} />
|
||||
<img
|
||||
src={`images/countries/${country?.flagCode.toLowerCase()}.svg`}
|
||||
className={`h-6`}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="my-auto truncate">
|
||||
@ -509,9 +517,12 @@ export function UnitSpawnMenu(props: {
|
||||
<div className="my-auto flex flex-col gap-2">
|
||||
<span>Spawn heading</span>
|
||||
<div className="flex gap-1 text-sm text-gray-400">
|
||||
<FaQuestionCircle className={`my-auto`} /> <div className={`
|
||||
my-auto
|
||||
`}>Drag to change</div>
|
||||
<FaQuestionCircle className={`my-auto`} />{" "}
|
||||
<div
|
||||
className={`my-auto`}
|
||||
>
|
||||
Drag to change
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -558,9 +569,9 @@ export function UnitSpawnMenu(props: {
|
||||
</div>
|
||||
<OlAccordion
|
||||
onClick={() => {
|
||||
setShowUnitSummary(!showUnitSummary);
|
||||
setOpenAccordion(openAccordion === OpenAccordion.UNIT_SUMMARY ? OpenAccordion.NONE : OpenAccordion.UNIT_SUMMARY);
|
||||
}}
|
||||
open={showUnitSummary}
|
||||
open={openAccordion === OpenAccordion.UNIT_SUMMARY}
|
||||
title="Unit summary"
|
||||
>
|
||||
{props.blueprint ? <OlUnitSummary blueprint={props.blueprint} coalition={spawnCoalition} /> : <span></span>}
|
||||
@ -568,9 +579,9 @@ export function UnitSpawnMenu(props: {
|
||||
{spawnLoadout && spawnLoadout.items.length > 0 && (
|
||||
<OlAccordion
|
||||
onClick={() => {
|
||||
setShowLoadout(!showLoadout);
|
||||
setOpenAccordion(openAccordion === OpenAccordion.LOADOUT ? OpenAccordion.NONE : OpenAccordion.LOADOUT);
|
||||
}}
|
||||
open={showLoadout}
|
||||
open={openAccordion === OpenAccordion.LOADOUT}
|
||||
title="Loadout"
|
||||
>
|
||||
{spawnLoadout.items.map((item) => {
|
||||
@ -897,10 +908,9 @@ export function UnitSpawnMenu(props: {
|
||||
`}
|
||||
>
|
||||
{props.blueprint?.liveries && props.blueprint?.liveries[id].countries.length == 1 && (
|
||||
<img
|
||||
src={`images/countries/${country?.flagCode.toLowerCase()}.svg`}
|
||||
className={`h-6`}
|
||||
/>
|
||||
<img src={`images/countries/${country?.flagCode.toLowerCase()}.svg`} className={`
|
||||
h-6
|
||||
`} />
|
||||
)}
|
||||
|
||||
<div className="my-auto truncate">
|
||||
@ -966,9 +976,12 @@ export function UnitSpawnMenu(props: {
|
||||
<div className="my-auto flex flex-col gap-2">
|
||||
<span className="text-white">Spawn heading</span>
|
||||
<div className="flex gap-1 text-sm text-gray-400">
|
||||
<FaQuestionCircle className={`my-auto`} /> <div className={`
|
||||
my-auto
|
||||
`}>Drag to change</div>
|
||||
<FaQuestionCircle className={`my-auto`} />{" "}
|
||||
<div
|
||||
className={`my-auto`}
|
||||
>
|
||||
Drag to change
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -70,7 +70,7 @@ import {
|
||||
UnitSelectedEvent,
|
||||
UnitUpdatedEvent,
|
||||
} from "../events";
|
||||
import { DraggableHandle } from "../map/coalitionarea/coalitionareahandle";
|
||||
import { DraggableHandle } from "../map/markers/draggablehandle";
|
||||
import { ArrowMarker } from "../map/markers/arrowmarker";
|
||||
import { Spot } from "../mission/spot";
|
||||
import { SpotEditMarker } from "../map/markers/spoteditmarker";
|
||||
|
||||
24
notes.txt
@ -1,3 +1,26 @@
|
||||
v2.0.1 ====================
|
||||
Changes:
|
||||
feat: Added starred spawns to spawn menu and ability to remove starred spawns
|
||||
feat: Added selection tools to unit list in unit control menu
|
||||
fix: Removed accidental development feature in unit spawn men
|
||||
feat: Added nightly link to manager to update without github account
|
||||
feat: Multiple improvements to audio backend
|
||||
feat: Added ability to change command mode, improved local connection detection
|
||||
fix: Drawings filtering utility made case insensitive
|
||||
feat: Added drawings filtering by string and go to drawing feature
|
||||
feat: Navpoints settings saved in session data
|
||||
feat: Navpoints separated from Drawings
|
||||
refactor: Changed styles for visible / hidden drawing layers
|
||||
feat: Added + and - buttons to control map zoom level
|
||||
fix: "Don't show again" tickbox not being respected
|
||||
fix: Manager not respecting autoconnectwhentrue and srsport settings
|
||||
fix: Aligned airbase spawn menu to other menus
|
||||
fix: Context menu shown when dragging handles
|
||||
fix: Aligned tanker orbit behaviour
|
||||
fix: Quick box selection causes units to be immediately deselected
|
||||
fix: Unable to clone units if game master with spawn restrictions on
|
||||
feat: Added support for callsigns
|
||||
|
||||
v2.0.0 ====================
|
||||
Changes:
|
||||
1) completely redone UI using React
|
||||
@ -5,7 +28,6 @@ Changes:
|
||||
3) added Mission Editor drawings in DCS
|
||||
4) multiple enhancements to control scheme
|
||||
|
||||
|
||||
v1.0.4 ====================
|
||||
Changes:
|
||||
1) Added Olympus Manager for unified configuration management;
|
||||
|
||||