Added ability to rotate movement path

This commit is contained in:
Pax1601 2024-11-08 19:55:37 +01:00
parent 644404c4e6
commit 4e84a97367
4 changed files with 135 additions and 41 deletions

View File

@ -49,6 +49,7 @@ import {
MapOptionsChangedEvent,
MapSourceChangedEvent,
SelectionClearedEvent,
UnitDeselectedEvent,
UnitSelectedEvent,
UnitUpdatedEvent,
} from "../events";
@ -122,6 +123,7 @@ export class Map extends L.Map {
/* Units movement */
#destinationPreviewMarkers: { [key: number]: TemporaryUnitMarker | TargetMarker } = {};
#destinationRotation: number = 0;
#isRotatingDestination: boolean = false;
/* Unit context actions */
#contextActionSet: null | ContextActionSet = null;
@ -197,7 +199,6 @@ export class Map extends L.Map {
/* Custom touch events for touchscreen support */
L.DomEvent.on(this.getContainer(), "touchstart", this.#onMouseDown, this);
L.DomEvent.on(this.getContainer(), "touchend", this.#onMouseUp, this);
L.DomEvent.on(this.getContainer(), "wheel", this.#onWheel, this);
/* Event listeners */
AppStateChangedEvent.on((state, subState) => this.#onStateChanged(state, subState));
@ -264,8 +265,18 @@ export class Map extends L.Map {
}
});
UnitSelectedEvent.on((unit) => this.#updateDestinationPreviewMarkers());
SelectionClearedEvent.on(() => this.#updateDestinationPreviewMarkers());
UnitSelectedEvent.on((unit) => {
this.#updateDestinationPreviewMarkers();
this.#destinationRotation = 0;
});
UnitDeselectedEvent.on(() => {
this.#updateDestinationPreviewMarkers();
this.#destinationRotation = 0;
});
SelectionClearedEvent.on(() => {
this.#updateDestinationPreviewMarkers();
this.#destinationRotation = 0;
});
ContextActionChangedEvent.on((contextAction) => this.#updateDestinationPreviewMarkers());
MapOptionsChangedEvent.on((mapOptions) => this.#moveDestinationPreviewMarkers());
@ -624,6 +635,10 @@ export class Map extends L.Map {
return this.#lastMouseCoordinates;
}
getDestinationRotation() {
return this.#destinationRotation;
}
centerOnUnit(unit: Unit | null) {
if (unit !== null) {
this.options.scrollWheelZoom = "center";
@ -900,7 +915,9 @@ export class Map extends L.Map {
window.clearTimeout(this.#longPressTimer);
this.scrollWheelZoom.enable();
this.dragging.enable();
this.#isRotatingDestination = false;
this.#isMouseOnCooldown = true;
this.#mouseCooldownTimer = window.setTimeout(() => {
this.#isMouseOnCooldown = false;
@ -914,6 +931,7 @@ export class Map extends L.Map {
return;
}
if (e.originalEvent.button === 2) this.#isRotatingDestination = true;
this.scrollWheelZoom.disable();
this.#shortPressTimer = window.setTimeout(() => {
@ -927,11 +945,6 @@ export class Map extends L.Map {
}, 350);
}
#onWheel(e: any) {
//this.#destinationRotation += e.deltaY / 25;
//this.#moveDestinationPreviewMarkers();
}
#onDoubleClick(e: any) {
console.log(`Double click at ${e.latlng}`);
@ -1020,6 +1033,7 @@ export class Map extends L.Map {
this.executeDefaultContextAction(null, pressLocation, e.originalEvent);
}
} else if (getApp().getState() === OlympusState.JTAC) {
// TODO less redundant way to do this
if (getApp().getSubState() === JTACSubState.SELECT_TARGET) {
if (!this.#targetPoint) {
this.#targetPoint = new TextMarker(pressLocation, "BP", "rgb(37 99 235)", { interactive: true, draggable: true });
@ -1089,8 +1103,13 @@ export class Map extends L.Map {
getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.MAP_CONTEXT_MENU);
MapContextMenuRequestEvent.dispatch(pressLocation);
} else {
if (e.type === "touchstart") document.dispatchEvent(new CustomEvent("forceboxselect", { detail: e }));
else document.dispatchEvent(new CustomEvent("forceboxselect", { detail: e.originalEvent }));
if (this.#contextAction?.getTarget() === "position") {
this.dragging.disable();
this.#isRotatingDestination = true;
} else {
if (e.type === "touchstart") document.dispatchEvent(new CustomEvent("forceboxselect", { detail: e }));
else document.dispatchEvent(new CustomEvent("forceboxselect", { detail: e.originalEvent }));
}
}
}
}
@ -1099,12 +1118,16 @@ export class Map extends L.Map {
#onMouseMove(e: any) {
window.clearTimeout(this.#longPressTimer);
this.#lastMousePosition.x = e.originalEvent.x;
this.#lastMousePosition.y = e.originalEvent.y;
this.#lastMouseCoordinates = e.latlng;
if (!this.#isRotatingDestination) {
this.#lastMousePosition.x = e.originalEvent.x;
this.#lastMousePosition.y = e.originalEvent.y;
this.#lastMouseCoordinates = e.latlng;
if (this.#currentSpawnMarker) this.#currentSpawnMarker.setLatLng(e.latlng);
if (this.#currentEffectMarker) this.#currentEffectMarker.setLatLng(e.latlng);
if (this.#currentSpawnMarker) this.#currentSpawnMarker.setLatLng(e.latlng);
if (this.#currentEffectMarker) this.#currentEffectMarker.setLatLng(e.latlng);
} else {
this.#destinationRotation -= e.originalEvent.movementX;
}
this.#moveDestinationPreviewMarkers();
}

View File

@ -7,7 +7,7 @@ import { FaLock } from "react-icons/fa6";
import { getApp } from "../../olympusapp";
import { OlympusState } from "../../constants/constants";
export function ProtectionPrompt(props: { }) {
export function ProtectionPrompt(props: {}) {
return (
<Modal
className={`
@ -49,16 +49,25 @@ export function ProtectionPrompt(props: { }) {
dark:text-gray-500
`}
>
To disable this warning, press on the <span className={`
inline-block translate-y-3 rounded-full border-[1px]
border-gray-900 bg-red-500 p-2 text-olympus-900
`}><FaLock/></span> button
To disable this warning, press on the{" "}
<span
className={`
inline-block translate-y-3 rounded-full border-[1px]
border-gray-900 bg-red-500 p-2 text-olympus-900
`}
>
<FaLock />
</span>{" "}
button
</span>
</div>
<div className="flex">
<button
type="button"
onClick={() => getApp().getUnitsManager().executeProtectionCallback()}
onClick={() => {
getApp().getUnitsManager().executeProtectionCallback();
getApp().setState(OlympusState.UNIT_CONTROL);
}}
className={`
mb-2 me-2 ml-auto flex content-center items-center gap-2
rounded-sm bg-blue-700 px-5 py-2.5 text-sm font-medium text-white

View File

@ -68,7 +68,18 @@ import {
faXmarksLines,
} from "@fortawesome/free-solid-svg-icons";
import { Carrier } from "../mission/carrier";
import { ContactsUpdatedEvent, FormationCreationRequestEvent, HiddenTypesChangedEvent, MapOptionsChangedEvent, UnitContextMenuRequestEvent, UnitDeadEvent, UnitDeselectedEvent, UnitExplosionRequestEvent, UnitSelectedEvent, UnitUpdatedEvent } from "../events";
import {
ContactsUpdatedEvent,
FormationCreationRequestEvent,
HiddenTypesChangedEvent,
MapOptionsChangedEvent,
UnitContextMenuRequestEvent,
UnitDeadEvent,
UnitDeselectedEvent,
UnitExplosionRequestEvent,
UnitSelectedEvent,
UnitUpdatedEvent,
} from "../events";
var pathIcon = new Icon({
iconUrl: "/vite/images/markers/marker-icon.png",
@ -835,7 +846,7 @@ export abstract class Unit extends CustomMarker {
faHand,
null,
(units: Unit[], _1, _2) => {
getApp().getUnitsManager().clearDestinations(units);
getApp().getUnitsManager().stop(units);
},
{
executeImmediately: true,
@ -852,7 +863,10 @@ export abstract class Unit extends CustomMarker {
"position",
(units: Unit[], _, targetPosition, originalEvent) => {
if (!originalEvent?.ctrlKey) getApp().getUnitsManager().clearDestinations(units);
if (targetPosition) getApp().getUnitsManager().addDestination(targetPosition, getApp().getMap().getOptions().keepRelativePositions, 0, units);
if (targetPosition)
getApp()
.getUnitsManager()
.addDestination(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units);
},
{ type: ContextActionType.MOVE }
);
@ -865,7 +879,10 @@ export abstract class Unit extends CustomMarker {
faRoute,
"position",
(units: Unit[], _, targetPosition) => {
if (targetPosition) getApp().getUnitsManager().addDestination(targetPosition, getApp().getMap().getOptions().keepRelativePositions, 0, units);
if (targetPosition)
getApp()
.getUnitsManager()
.addDestination(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units);
},
{ type: ContextActionType.MOVE }
);
@ -894,8 +911,8 @@ export abstract class Unit extends CustomMarker {
faExplosion,
null,
(units: Unit[], _1, _2) => {
getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.UNIT_EXPLOSION_MENU)
UnitExplosionRequestEvent.dispatch(units)
getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.UNIT_EXPLOSION_MENU);
UnitExplosionRequestEvent.dispatch(units);
},
{
executeImmediately: true,
@ -903,12 +920,20 @@ export abstract class Unit extends CustomMarker {
}
);
contextActionSet.addDefaultContextAction(this, "default", "Set destination", "", faRoute, null, (units: Unit[], targetUnit, targetPosition, originalEvent) => {
if (targetPosition) {
if (!originalEvent?.ctrlKey) getApp().getUnitsManager().clearDestinations(units);
getApp().getUnitsManager().addDestination(targetPosition, false, 0, units);
contextActionSet.addDefaultContextAction(
this,
"default",
"Set destination",
"",
faRoute,
null,
(units: Unit[], targetUnit, targetPosition, originalEvent) => {
if (targetPosition) {
if (!originalEvent?.ctrlKey) getApp().getUnitsManager().clearDestinations(units);
getApp().getUnitsManager().addDestination(targetPosition, false, 0, units);
}
}
});
);
}
drawLines() {
@ -1191,7 +1216,6 @@ export abstract class Unit extends CustomMarker {
clearDestinations() {
if (!this.#human) this.#activePath = [];
getApp().getServerManager().addDestination(this.ID, []);
}
updatePathFromMarkers() {
@ -1429,7 +1453,7 @@ export abstract class Unit extends CustomMarker {
console.log(`Long press on ${this.getUnitName()}`);
if (e.originalEvent.button === 2) {
getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.UNIT_CONTEXT_MENU)
getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.UNIT_CONTEXT_MENU);
UnitContextMenuRequestEvent.dispatch(this);
}
}
@ -1894,7 +1918,10 @@ export abstract class AirUnit extends Unit {
(units: Unit[], targetUnit: Unit | null, _) => {
if (targetUnit) {
getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.FORMATION);
FormationCreationRequestEvent.dispatch(targetUnit, units.filter((unit) => unit !== targetUnit))
FormationCreationRequestEvent.dispatch(
targetUnit,
units.filter((unit) => unit !== targetUnit)
);
}
},
{ type: ContextActionType.ADMIN }
@ -1910,7 +1937,10 @@ export abstract class AirUnit extends Unit {
faLocationCrosshairs,
"position",
(units: Unit[], _, targetPosition: LatLng | null) => {
if (targetPosition) getApp().getUnitsManager().bombPoint(targetPosition, getApp().getMap().getOptions().keepRelativePositions, 0, units);
if (targetPosition)
getApp()
.getUnitsManager()
.bombPoint(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units);
},
{ type: ContextActionType.ENGAGE }
);
@ -1923,7 +1953,10 @@ export abstract class AirUnit extends Unit {
faXmarksLines,
"position",
(units: Unit[], _, targetPosition: LatLng | null) => {
if (targetPosition) getApp().getUnitsManager().carpetBomb(targetPosition, getApp().getMap().getOptions().keepRelativePositions, 0, units);
if (targetPosition)
getApp()
.getUnitsManager()
.carpetBomb(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units);
},
{ type: ContextActionType.ENGAGE }
);
@ -1985,7 +2018,10 @@ export class Helicopter extends AirUnit {
olButtonsContextLandAtPoint,
"position",
(units: Unit[], _, targetPosition: LatLng | null) => {
if (targetPosition) getApp().getUnitsManager().landAtPoint(targetPosition, getApp().getMap().getOptions().keepRelativePositions, 0, units);
if (targetPosition)
getApp()
.getUnitsManager()
.landAtPoint(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units);
},
{ type: ContextActionType.ADMIN }
);
@ -2075,7 +2111,10 @@ export class GroundUnit extends Unit {
faLocationCrosshairs,
"position",
(units: Unit[], _, targetPosition: LatLng | null) => {
if (targetPosition) getApp().getUnitsManager().fireAtArea(targetPosition, getApp().getMap().getOptions().keepRelativePositions, 0, units);
if (targetPosition)
getApp()
.getUnitsManager()
.fireAtArea(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units);
},
{ type: ContextActionType.ENGAGE }
);
@ -2087,7 +2126,10 @@ export class GroundUnit extends Unit {
olButtonsContextSimulateFireFight,
"position",
(units: Unit[], _, targetPosition: LatLng | null) => {
if (targetPosition) getApp().getUnitsManager().simulateFireFight(targetPosition, getApp().getMap().getOptions().keepRelativePositions, 0, units);
if (targetPosition)
getApp()
.getUnitsManager()
.simulateFireFight(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units);
},
{ type: ContextActionType.ADMIN }
);
@ -2198,7 +2240,10 @@ export class NavyUnit extends Unit {
faLocationCrosshairs,
"position",
(units: Unit[], _, targetPosition: LatLng | null) => {
if (targetPosition) getApp().getUnitsManager().fireAtArea(targetPosition, getApp().getMap().getOptions().keepRelativePositions, 0, units);
if (targetPosition)
getApp()
.getUnitsManager()
.fireAtArea(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units);
},
{ type: ContextActionType.ENGAGE }
);

View File

@ -394,6 +394,23 @@ export class UnitsManager {
} else callback(units);
}
stop(units: Unit[] | null = null) {
if (units === null) units = this.getSelectedUnits();
units = units.filter((unit) => !unit.getHuman());
let callback = (units: Unit[]) => {
for (let idx in units) {
getApp().getServerManager().addDestination(units[idx].ID, []);
}
this.#showActionMessage(units, " stopped");
};
if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus())) {
getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.PROTECTION);
this.#protectionCallback = callback;
} else callback(units);
}
/** Instruct all the selected units to land at a specific location
*
* @param latlng Location where to land at