mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
feat: Added unit spawn heading selection
This commit is contained in:
parent
6074367300
commit
d1d4116e66
BIN
frontend/react/public/images/others/arrow.png
Normal file
BIN
frontend/react/public/images/others/arrow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
39
frontend/react/public/images/others/arrow.svg
Normal file
39
frontend/react/public/images/others/arrow.svg
Normal file
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 40 40"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="arrow.svg"
|
||||
width="40"
|
||||
height="40"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="8.9824657"
|
||||
inkscape:cx="20.985329"
|
||||
inkscape:cy="21.430641"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1009"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.-->
|
||||
<path
|
||||
d="M 23.307123,3.5189202 C 22.873778,2.4788845 21.852318,1.8041019 20.725614,1.8041019 c -1.126705,0 -2.14817,0.6747826 -2.581515,1.7148183 L 5.2674896,34.22469 c -0.5200196,1.238134 -0.086679,2.661992 1.0214597,3.411065 1.1081335,0.749072 2.5938946,0.600492 3.5410687,-0.346667 L 20.725614,26.393493 31.621209,37.289088 c 0.947171,0.947172 2.426747,1.089559 3.541067,0.346667 1.114324,-0.742886 1.541478,-2.172931 1.021461,-3.411065 z"
|
||||
id="path1"
|
||||
style="stroke:#262626;stroke-width:2.30583;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
BIN
frontend/react/public/images/others/arrow_background.png
Normal file
BIN
frontend/react/public/images/others/arrow_background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
@ -558,6 +558,23 @@ export class SpawnContextMenuRequestEvent {
|
||||
}
|
||||
}
|
||||
|
||||
export class SpawnHeadingChangedEvent {
|
||||
static on(callback: (heading: number) => void, singleShot = false) {
|
||||
document.addEventListener(
|
||||
this.name,
|
||||
(ev: CustomEventInit) => {
|
||||
callback(ev.detail.heading);
|
||||
},
|
||||
{ once: singleShot }
|
||||
);
|
||||
}
|
||||
|
||||
static dispatch(heading: number) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { heading } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
|
||||
export class HotgroupsChangedEvent {
|
||||
static on(callback: (hotgroups: { [key: number]: Unit[] }) => void, singleShot = false) {
|
||||
document.addEventListener(
|
||||
|
||||
@ -142,6 +142,7 @@ export interface UnitSpawnTable {
|
||||
liveryID: string;
|
||||
altitude?: number;
|
||||
loadout?: string;
|
||||
heading?: number;
|
||||
}
|
||||
|
||||
export interface ObjectIconOptions {
|
||||
|
||||
@ -56,6 +56,7 @@ import {
|
||||
SelectionEnabledChangedEvent,
|
||||
SessionDataLoadedEvent,
|
||||
SpawnContextMenuRequestEvent,
|
||||
SpawnHeadingChangedEvent,
|
||||
StarredSpawnsChangedEvent,
|
||||
UnitDeselectedEvent,
|
||||
UnitSelectedEvent,
|
||||
@ -144,6 +145,7 @@ export class Map extends L.Map {
|
||||
#temporaryMarkers: TemporaryUnitMarker[] = [];
|
||||
#currentSpawnMarker: TemporaryUnitMarker | null = null;
|
||||
#currentEffectMarker: ExplosionMarker | SmokeMarker | null = null;
|
||||
#spawnHeading: number = 0;
|
||||
|
||||
/* JTAC tools */
|
||||
#ECHOPoint: TextMarker | null = null;
|
||||
@ -565,6 +567,19 @@ export class Map extends L.Map {
|
||||
this.#currentSpawnMarker = this.addTemporaryMarker(spawnRequestTable.unit.location, spawnRequestTable.unit.unitType, spawnRequestTable.coalition, true);
|
||||
}
|
||||
|
||||
getSpawnRequestTable() {
|
||||
return this.#spawnRequestTable;
|
||||
}
|
||||
|
||||
setSpawnHeading(heading: number) {
|
||||
this.#spawnHeading = heading;
|
||||
SpawnHeadingChangedEvent.dispatch(heading);
|
||||
}
|
||||
|
||||
getSpawnHeading() {
|
||||
return this.#spawnHeading;
|
||||
}
|
||||
|
||||
addStarredSpawnRequestTable(key, spawnRequestTable: SpawnRequestTable, quickAccessName: string) {
|
||||
this.#starredSpawnRequestTables[key] = spawnRequestTable;
|
||||
this.#starredSpawnRequestTables[key].quickAccessName = quickAccessName;
|
||||
@ -833,7 +848,7 @@ export class Map extends L.Map {
|
||||
new L.LatLng(0, 0),
|
||||
this.#spawnRequestTable?.unit.unitType ?? "",
|
||||
this.#spawnRequestTable?.coalition ?? "neutral",
|
||||
false
|
||||
true
|
||||
);
|
||||
this.#currentSpawnMarker.addTo(this);
|
||||
} else if (subState === SpawnSubState.SPAWN_EFFECT) {
|
||||
@ -923,6 +938,7 @@ export class Map extends L.Map {
|
||||
if (getApp().getSubState() === SpawnSubState.SPAWN_UNIT) {
|
||||
if (this.#spawnRequestTable !== null) {
|
||||
this.#spawnRequestTable.unit.location = e.latlng;
|
||||
this.#spawnRequestTable.unit.heading = deg2rad(this.#spawnHeading);
|
||||
getApp()
|
||||
.getUnitsManager()
|
||||
.spawnUnits(
|
||||
@ -937,6 +953,7 @@ export class Map extends L.Map {
|
||||
e.latlng,
|
||||
this.#spawnRequestTable?.unit.unitType ?? "unknown",
|
||||
this.#spawnRequestTable?.coalition ?? "blue",
|
||||
false,
|
||||
hash
|
||||
);
|
||||
}
|
||||
|
||||
@ -421,7 +421,7 @@
|
||||
color: var(--secondary-blue-text);
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-coalition="blue"][data-is-selected] path {
|
||||
[data-object|="unit"][data-coalition="blue"][data-is-selected] path:nth-child(1) {
|
||||
fill: var(--secondary-blue-text);
|
||||
}
|
||||
|
||||
@ -577,10 +577,41 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ol-temporary-marker {
|
||||
.ol-temporary-marker .unit-icon {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.ol-temporary-marker .unit-short-label {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.ol-temporary-marker .heading-handle {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
position: absolute;
|
||||
top: -40px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
cursor: move;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.ol-temporary-marker .heading-handle svg {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ol-temporary-marker [data-coalition="blue"] .heading-handle svg {
|
||||
fill: var(--unit-background-blue);
|
||||
}
|
||||
|
||||
.ol-temporary-marker [data-coalition="red"] .heading-handle svg {
|
||||
fill: var(--unit-background-red);
|
||||
}
|
||||
|
||||
.ol-temporary-marker [data-coalition="neutral"] .heading-handle svg {
|
||||
fill: var(--unit-background-neutral);
|
||||
}
|
||||
.unit-bullseye,
|
||||
.unit-braa {
|
||||
width: 50%;
|
||||
|
||||
@ -3,6 +3,8 @@ import { DivIcon, LatLng } from "leaflet";
|
||||
import { SVGInjector } from "@tanem/svg-injector";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { UnitBlueprint } from "../../interfaces";
|
||||
import { deg2rad, normalizeAngle, rad2deg } from "../../other/utils";
|
||||
import { SpawnHeadingChangedEvent } from "../../events";
|
||||
|
||||
export class TemporaryUnitMarker extends CustomMarker {
|
||||
#name: string;
|
||||
@ -72,6 +74,50 @@ export class TemporaryUnitMarker extends CustomMarker {
|
||||
el.append(shortLabel);
|
||||
}
|
||||
|
||||
// Heading handle
|
||||
if (this.#headingHandle) {
|
||||
var handle = document.createElement("div");
|
||||
var handleImg = document.createElement("img");
|
||||
handleImg.src = "/images/others/arrow.svg";
|
||||
handleImg.onload = () => SVGInjector(handleImg);
|
||||
handle.classList.add("heading-handle");
|
||||
el.append(handle);
|
||||
|
||||
handle.append(handleImg);
|
||||
|
||||
const rotateHandle = (heading) => {
|
||||
el.style.transform = `rotate(${heading}deg)`;
|
||||
unitIcon.style.transform = `rotate(-${heading}deg)`;
|
||||
shortLabel.style.transform = `rotate(-${heading}deg)`;
|
||||
};
|
||||
|
||||
SpawnHeadingChangedEvent.on((heading) => rotateHandle(heading));
|
||||
rotateHandle(getApp().getMap().getSpawnHeading());
|
||||
|
||||
// Add drag and rotate functionality
|
||||
handle.addEventListener("mousedown", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const onMouseMove = (e) => {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
let angle = rad2deg(Math.atan2(e.clientY - centerY, e.clientX - centerX)) + 90;
|
||||
angle = normalizeAngle(angle);
|
||||
getApp().getMap().setSpawnHeading(angle);
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
document.removeEventListener("mousemove", onMouseMove);
|
||||
document.removeEventListener("mouseup", onMouseUp);
|
||||
};
|
||||
|
||||
document.addEventListener("mousemove", onMouseMove);
|
||||
document.addEventListener("mouseup", onMouseUp);
|
||||
});
|
||||
}
|
||||
|
||||
this.getElement()?.appendChild(el);
|
||||
this.getElement()?.classList.add("ol-temporary-marker");
|
||||
}
|
||||
|
||||
@ -600,4 +600,18 @@ export function computeBrightness(color) {
|
||||
let brightness = 0.299 * r + 0.587 * g + 0.114 * b;
|
||||
|
||||
return brightness;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes an angle to be within the range of 0 to 360 degrees.
|
||||
* @param {number} angle - The angle to normalize.
|
||||
* @returns {number} - The normalized angle.
|
||||
*/
|
||||
export function normalizeAngle(angle: number): number {
|
||||
// Ensure the angle is within the range of 0 to 360 degrees
|
||||
angle = angle % 360;
|
||||
if (angle < 0) {
|
||||
angle += 360;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { BLUE_COMMANDER, colors, COMMAND_MODE_OPTIONS_DEFAULTS, GAME_MASTER, NO_SUBSTATE, OlympusState, OlympusSubState } from "../../constants/constants";
|
||||
import { LatLng } from "leaflet";
|
||||
import {
|
||||
@ -10,7 +10,7 @@ import {
|
||||
} from "../../events";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { SpawnRequestTable, UnitBlueprint } from "../../interfaces";
|
||||
import { faArrowLeft, faEllipsisVertical, faExplosion, faListDots, faSearch, faSmog, faStar } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faEllipsisVertical, faExplosion, faSearch, faSmog, faStar } from "@fortawesome/free-solid-svg-icons";
|
||||
import { EffectSpawnMenu } from "../panels/effectspawnmenu";
|
||||
import { UnitSpawnMenu } from "../panels/unitspawnmenu";
|
||||
import { OlEffectListEntry } from "../components/oleffectlistentry";
|
||||
@ -28,6 +28,7 @@ import { OlDropdownItem } from "../components/oldropdown";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { OlCoalitionToggle } from "../components/olcoalitiontoggle";
|
||||
import { Coalition } from "../../types/types";
|
||||
import { spawn } from "child_process";
|
||||
|
||||
enum CategoryGroup {
|
||||
NONE,
|
||||
@ -62,6 +63,7 @@ export function SpawnContextMenu(props: {}) {
|
||||
const [spawnCoalition, setSpawnCoalition] = useState("blue" as Coalition);
|
||||
const [showMore, setShowMore] = useState(false);
|
||||
const [height, setHeight] = useState(0);
|
||||
const [translated, setTranslated] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedRole) setBlueprints(getApp()?.getUnitsManager().getDatabase().getByRole(selectedRole));
|
||||
@ -110,6 +112,19 @@ export function SpawnContextMenu(props: {}) {
|
||||
setSelectedRole(null);
|
||||
}, [openAccordion]);
|
||||
|
||||
const translateMenu = useCallback(() => {
|
||||
if (blueprint && !translated) {
|
||||
setTranslated(true);
|
||||
setXPosition(xPosition + 60);
|
||||
setYPosition(yPosition + 40);
|
||||
} else if (!blueprint && translated) {
|
||||
setTranslated(false);
|
||||
setXPosition(xPosition - 60);
|
||||
setYPosition(yPosition - 40);
|
||||
}
|
||||
}, [blueprint, translated])
|
||||
useEffect(translateMenu, [blueprint, translated])
|
||||
|
||||
/* Filter the blueprints according to the label */
|
||||
const filteredBlueprints: UnitBlueprint[] = [];
|
||||
if (blueprints && filterString !== "") {
|
||||
@ -131,6 +146,7 @@ export function SpawnContextMenu(props: {}) {
|
||||
const containerPoint = getApp().getMap().latLngToContainerPoint(latlng);
|
||||
setXPosition(getApp().getMap().getContainer().offsetLeft + containerPoint.x);
|
||||
setYPosition(getApp().getMap().getContainer().offsetTop + containerPoint.y);
|
||||
setTranslated(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Menu } from "./components/menu";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { FaPlus, FaPlusCircle, FaQuestionCircle } from "react-icons/fa";
|
||||
import { FaPlus, FaQuestionCircle } from "react-icons/fa";
|
||||
import { AudioSourcePanel } from "./components/sourcepanel";
|
||||
import { AudioSource } from "../../audio/audiosource";
|
||||
import { RadioSinkPanel } from "./components/radiosinkpanel";
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import React, { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { OlUnitSummary } from "../components/olunitsummary";
|
||||
import { OlCoalitionToggle } from "../components/olcoalitiontoggle";
|
||||
import { OlNumberInput } from "../components/olnumberinput";
|
||||
@ -9,7 +9,7 @@ import { LoadoutBlueprint, SpawnRequestTable, UnitBlueprint } from "../../interf
|
||||
import { OlStateButton } from "../components/olstatebutton";
|
||||
import { Coalition } from "../../types/types";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { deepCopyTable, ftToM, hash, mode } from "../../other/utils";
|
||||
import { deepCopyTable, deg2rad, ftToM, hash, mode, normalizeAngle } from "../../other/utils";
|
||||
import { LatLng } from "leaflet";
|
||||
import { Airbase } from "../../mission/airbase";
|
||||
import { altitudeIncrements, groupUnitCount, maxAltitudeValues, minAltitudeValues, OlympusState, SpawnSubState } from "../../constants/constants";
|
||||
@ -17,8 +17,9 @@ import { faArrowLeft, faStar } from "@fortawesome/free-solid-svg-icons";
|
||||
import { OlStringInput } from "../components/olstringinput";
|
||||
import { countryCodes } from "../data/codes";
|
||||
import { OlAccordion } from "../components/olaccordion";
|
||||
import { AppStateChangedEvent } from "../../events";
|
||||
import { AppStateChangedEvent, SpawnHeadingChangedEvent } from "../../events";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { FaQuestionCircle } from "react-icons/fa";
|
||||
|
||||
export function UnitSpawnMenu(props: {
|
||||
visible: boolean;
|
||||
@ -123,6 +124,46 @@ export function UnitSpawnMenu(props: {
|
||||
if (props.coalition) setSpawnCoalition(props.coalition);
|
||||
}, [props.coalition]);
|
||||
|
||||
/* Heading compass */
|
||||
const [compassAngle, setCompassAngle] = useState(0);
|
||||
const compassRef = useRef<HTMLImageElement>(null);
|
||||
|
||||
const updateSpawnRequestTableHeading = useCallback(() => {
|
||||
getApp()?.getMap().setSpawnHeading(compassAngle);
|
||||
}, [compassAngle]);
|
||||
useEffect(updateSpawnRequestTableHeading, [compassAngle]);
|
||||
|
||||
useEffect(() => {
|
||||
SpawnHeadingChangedEvent.on((heading) => {
|
||||
setCompassAngle(heading);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setCompassAngle(getApp()?.getMap().getSpawnHeading() ?? 0);
|
||||
}, [appState]);
|
||||
|
||||
const handleMouseDown = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
const onMouseMove = (e: MouseEvent) => {
|
||||
if (compassRef.current) {
|
||||
const rect = compassRef.current.getBoundingClientRect();
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const angle = Math.atan2(e.clientY - centerY, e.clientX - centerX) * (180 / Math.PI);
|
||||
setCompassAngle(Math.round(normalizeAngle(angle + 90)));
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
document.removeEventListener("mousemove", onMouseMove);
|
||||
document.removeEventListener("mouseup", onMouseUp);
|
||||
};
|
||||
|
||||
document.addEventListener("mousemove", onMouseMove);
|
||||
document.addEventListener("mouseup", onMouseUp);
|
||||
};
|
||||
|
||||
/* Get a list of all the roles */
|
||||
const roles: string[] = [];
|
||||
props.blueprint?.loadouts?.forEach((loadout) => {
|
||||
@ -198,9 +239,11 @@ export function UnitSpawnMenu(props: {
|
||||
/>
|
||||
<OlStateButton
|
||||
onClick={() => {
|
||||
if (spawnRequestTable)
|
||||
if (spawnRequestTable) {
|
||||
spawnRequestTable.unit.heading = compassAngle;
|
||||
if (key in props.starredSpawns) getApp().getMap().removeStarredSpawnRequestTable(key);
|
||||
else getApp().getMap().addStarredSpawnRequestTable(key, spawnRequestTable, quickAccessName);
|
||||
}
|
||||
}}
|
||||
tooltip="Save this spawn for quick access"
|
||||
checked={key in props.starredSpawns}
|
||||
@ -355,10 +398,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">
|
||||
@ -410,6 +452,48 @@ export function UnitSpawnMenu(props: {
|
||||
</OlDropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-5 flex justify-between">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<OlNumberInput
|
||||
className={"my-auto"}
|
||||
min={0}
|
||||
max={360}
|
||||
onChange={(ev) => {
|
||||
setCompassAngle(Number(ev.target.value));
|
||||
}}
|
||||
onDecrease={() => {
|
||||
setCompassAngle(normalizeAngle(compassAngle - 1));
|
||||
}}
|
||||
onIncrease={() => {
|
||||
setCompassAngle(normalizeAngle(compassAngle + 1));
|
||||
}}
|
||||
value={compassAngle}
|
||||
/>
|
||||
|
||||
<div className={`relative mr-3 h-[60px] w-[60px]`}>
|
||||
<img className="absolute" ref={compassRef} onMouseDown={handleMouseDown} src={"/images/others/arrow_background.png"}></img>
|
||||
<img
|
||||
className="absolute left-0"
|
||||
ref={compassRef}
|
||||
onMouseDown={handleMouseDown}
|
||||
src={"/images/others/arrow.png"}
|
||||
style={{
|
||||
width: "60px",
|
||||
height: "60px",
|
||||
transform: `rotate(${compassAngle}deg)`,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
></img>
|
||||
</div>
|
||||
</div>
|
||||
</OlAccordion>
|
||||
</div>
|
||||
<OlAccordion
|
||||
@ -467,7 +551,8 @@ export function UnitSpawnMenu(props: {
|
||||
focus:outline-none focus:ring-4
|
||||
`}
|
||||
onClick={() => {
|
||||
if (spawnRequestTable)
|
||||
if (spawnRequestTable){
|
||||
spawnRequestTable.unit.heading = deg2rad(compassAngle);
|
||||
getApp()
|
||||
.getUnitsManager()
|
||||
.spawnUnits(
|
||||
@ -477,6 +562,7 @@ export function UnitSpawnMenu(props: {
|
||||
false,
|
||||
props.airbase?.getName() ?? undefined
|
||||
);
|
||||
}
|
||||
|
||||
getApp().setState(OlympusState.IDLE);
|
||||
}}
|
||||
@ -694,9 +780,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">
|
||||
@ -748,6 +835,48 @@ export function UnitSpawnMenu(props: {
|
||||
})}
|
||||
</OlDropdown>
|
||||
</div>
|
||||
<div className="my-5 flex justify-between">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<OlNumberInput
|
||||
className={"my-auto"}
|
||||
min={0}
|
||||
max={360}
|
||||
onChange={(ev) => {
|
||||
setCompassAngle(Number(ev.target.value));
|
||||
}}
|
||||
onDecrease={() => {
|
||||
setCompassAngle(normalizeAngle(compassAngle - 1));
|
||||
}}
|
||||
onIncrease={() => {
|
||||
setCompassAngle(normalizeAngle(compassAngle + 1));
|
||||
}}
|
||||
value={compassAngle}
|
||||
/>
|
||||
|
||||
<div className={`relative mr-3 h-[60px] w-[60px]`}>
|
||||
<img className="absolute" ref={compassRef} onMouseDown={handleMouseDown} src={"/images/others/arrow_background.png"}></img>
|
||||
<img
|
||||
className="absolute left-0"
|
||||
ref={compassRef}
|
||||
onMouseDown={handleMouseDown}
|
||||
src={"/images/others/arrow.png"}
|
||||
style={{
|
||||
width: "60px",
|
||||
height: "60px",
|
||||
transform: `rotate(${compassAngle}deg)`,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
></img>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{spawnLoadout && spawnLoadout.items.length > 0 && (
|
||||
<div
|
||||
@ -802,7 +931,7 @@ export function UnitSpawnMenu(props: {
|
||||
hover:bg-blue-800
|
||||
`}
|
||||
onClick={() => {
|
||||
if (spawnRequestTable)
|
||||
if (spawnRequestTable) {
|
||||
getApp()
|
||||
.getUnitsManager()
|
||||
.spawnUnits(
|
||||
@ -812,6 +941,7 @@ export function UnitSpawnMenu(props: {
|
||||
false,
|
||||
props.airbase?.getName()
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Spawn
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user