import React, { useEffect, useRef, useState } from "react";
import { BLUE_COMMANDER, COMMAND_MODE_OPTIONS_DEFAULTS, GAME_MASTER, NO_SUBSTATE, OlympusState, OlympusSubState } from "../../constants/constants";
import { LatLng } from "leaflet";
import {
AppStateChangedEvent,
CommandModeOptionsChangedEvent,
SpawnContextMenuRequestEvent,
StarredSpawnsChangedEvent,
UnitDatabaseLoadedEvent,
} 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 { EffectSpawnMenu } from "../panels/effectspawnmenu";
import { UnitSpawnMenu } from "../panels/unitspawnmenu";
import { OlEffectListEntry } from "../components/oleffectlistentry";
import {
olButtonsVisibilityAircraft,
olButtonsVisibilityGroundunit,
olButtonsVisibilityGroundunitSam,
olButtonsVisibilityHelicopter,
olButtonsVisibilityNavyunit,
} from "../components/olicons";
import { OlUnitListEntry } from "../components/olunitlistentry";
import { OlSearchBar } from "../components/olsearchbar";
import { OlStateButton } from "../components/olstatebutton";
import { OlDropdownItem } from "../components/oldropdown";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { OlCoalitionToggle } from "../components/olcoalitiontoggle";
import { Coalition } from "../../types/types";
import { CompactEffectSpawnMenu } from "../panels/compacteffectspawnmenu";
enum CategoryGroup {
NONE,
AIRCRAFT,
HELICOPTER,
AIR_DEFENCE,
GROUND_UNIT,
NAVY_UNIT,
EFFECT,
SEARCH,
STARRED,
}
export function SpawnContextMenu(props: {}) {
const [appState, setAppState] = useState(OlympusState.NOT_INITIALIZED);
const [appSubState, setAppSubState] = useState(NO_SUBSTATE as OlympusSubState);
const [xPosition, setXPosition] = useState(0);
const [yPosition, setYPosition] = useState(0);
const [latlng, setLatLng] = useState(null as null | LatLng);
const [starredSpawns, setStarredSpawns] = useState({} as { [key: string]: SpawnRequestTable });
const [openAccordion, setOpenAccordion] = useState(CategoryGroup.NONE);
const [blueprint, setBlueprint] = useState(null as null | UnitBlueprint);
const [effect, setEffect] = useState(null as null | string);
const [filterString, setFilterString] = useState("");
const [selectedRole, setSelectedRole] = useState(null as null | string);
const [selectedType, setSelectedType] = useState(null as null | string);
const [blueprints, setBlueprints] = useState([] as UnitBlueprint[]);
const [roles, setRoles] = useState({ aircraft: [] as string[], helicopter: [] as string[] });
const [types, setTypes] = useState({ groundunit: [] as string[], navyunit: [] as string[] });
const [commandModeOptions, setCommandModeOptions] = useState(COMMAND_MODE_OPTIONS_DEFAULTS);
const [showCost, setShowCost] = useState(false);
const [spawnCoalition, setSpawnCoalition] = useState("blue" as Coalition);
const [showMore, setShowMore] = useState(false);
const [height, setHeight] = useState(0);
useEffect(() => {
if (selectedRole) setBlueprints(getApp()?.getUnitsManager().getDatabase().getByRole(selectedRole));
else if (selectedType) setBlueprints(getApp()?.getUnitsManager().getDatabase().getByType(selectedType));
else setBlueprints(getApp()?.getUnitsManager().getDatabase().getBlueprints());
}, [selectedRole, selectedType, openAccordion]);
useEffect(() => {
UnitDatabaseLoadedEvent.on(() => {
setRoles({
aircraft: getApp()
?.getUnitsManager()
.getDatabase()
.getRoles((unit) => unit.category === "aircraft"),
helicopter: getApp()
?.getUnitsManager()
.getDatabase()
.getRoles((unit) => unit.category === "helicopter"),
});
setTypes({
groundunit: getApp()
?.getUnitsManager()
.getDatabase()
.getTypes((unit) => unit.category === "groundunit"),
navyunit: getApp()
?.getUnitsManager()
.getDatabase()
.getTypes((unit) => unit.category === "navyunit"),
});
});
CommandModeOptionsChangedEvent.on((commandModeOptions) => {
setCommandModeOptions(commandModeOptions);
setShowCost(!(commandModeOptions.commandMode == GAME_MASTER || !commandModeOptions.restrictSpawns));
setOpenAccordion(CategoryGroup.NONE);
});
StarredSpawnsChangedEvent.on((starredSpawns) => setStarredSpawns({ ...starredSpawns }));
}, []);
useEffect(() => {
setBlueprint(null);
setEffect(null);
setSelectedType(null);
setSelectedRole(null);
}, [openAccordion]);
/* Filter the blueprints according to the label */
const filteredBlueprints: UnitBlueprint[] = [];
if (blueprints && filterString !== "") {
blueprints.forEach((blueprint) => {
if (blueprint.enabled && (filterString === "" || blueprint.label.toLowerCase().includes(filterString.toLowerCase()))) filteredBlueprints.push(blueprint);
});
}
var contentRef = useRef(null);
useEffect(() => {
AppStateChangedEvent.on((state, subState) => {
setAppState(state);
setAppSubState(subState);
});
StarredSpawnsChangedEvent.on((starredSpawns) => setStarredSpawns({ ...starredSpawns }));
SpawnContextMenuRequestEvent.on((latlng) => {
setLatLng(latlng);
const containerPoint = getApp().getMap().latLngToContainerPoint(latlng);
setXPosition(getApp().getMap().getContainer().offsetLeft + containerPoint.x);
setYPosition(getApp().getMap().getContainer().offsetTop + containerPoint.y);
});
}, []);
useEffect(() => {
if (contentRef.current) {
const content = contentRef.current as HTMLDivElement;
content.style.left = `${xPosition}px`;
content.style.top = `${yPosition}px`;
let newXPosition = xPosition;
let newYposition = yPosition;
let [cxr, cyb] = [content.getBoundingClientRect().x + content.clientWidth, content.getBoundingClientRect().y + content.clientHeight];
/* Try and move the content so it is inside the screen */
if (cxr > window.innerWidth) newXPosition -= cxr - window.innerWidth;
if (cyb > window.innerHeight) newYposition -= cyb - window.innerHeight;
content.style.left = `${newXPosition}px`;
content.style.top = `${newYposition}px`;
const resizeObserver = new ResizeObserver(() => {
setHeight(content.clientHeight);
});
resizeObserver.observe(content);
return () => resizeObserver.disconnect(); // clean up
}
});
// TODO fix button being moved if overflowing
return (
<>
{
spawnCoalition === "blue" && setSpawnCoalition("neutral");
spawnCoalition === "neutral" && setSpawnCoalition("red");
spawnCoalition === "red" && setSpawnCoalition("blue");
}}
/>
(openAccordion !== CategoryGroup.AIRCRAFT ? setOpenAccordion(CategoryGroup.AIRCRAFT) : setOpenAccordion(CategoryGroup.NONE))}
icon={olButtonsVisibilityAircraft}
tooltip="Show aircraft units"
buttonColor={spawnCoalition === "blue" ? "#2563eb" : spawnCoalition === "neutral" ? "#9ca3af" : "#ef4444"}
/>
(openAccordion !== CategoryGroup.HELICOPTER ? setOpenAccordion(CategoryGroup.HELICOPTER) : setOpenAccordion(CategoryGroup.NONE))}
icon={olButtonsVisibilityHelicopter}
tooltip="Show helicopter units"
buttonColor={spawnCoalition === "blue" ? "#2563eb" : spawnCoalition === "neutral" ? "#9ca3af" : "#ef4444"}
/>
(openAccordion !== CategoryGroup.AIR_DEFENCE ? setOpenAccordion(CategoryGroup.AIR_DEFENCE) : setOpenAccordion(CategoryGroup.NONE))}
icon={olButtonsVisibilityGroundunitSam}
tooltip="Show air defence units"
buttonColor={spawnCoalition === "blue" ? "#2563eb" : spawnCoalition === "neutral" ? "#9ca3af" : "#ef4444"}
/>
(openAccordion !== CategoryGroup.GROUND_UNIT ? setOpenAccordion(CategoryGroup.GROUND_UNIT) : setOpenAccordion(CategoryGroup.NONE))}
icon={olButtonsVisibilityGroundunit}
tooltip="Show ground units"
buttonColor={spawnCoalition === "blue" ? "#2563eb" : spawnCoalition === "neutral" ? "#9ca3af" : "#ef4444"}
/>
(openAccordion !== CategoryGroup.NAVY_UNIT ? setOpenAccordion(CategoryGroup.NAVY_UNIT) : setOpenAccordion(CategoryGroup.NONE))}
icon={olButtonsVisibilityNavyunit}
tooltip="Show navy units"
buttonColor={spawnCoalition === "blue" ? "#2563eb" : spawnCoalition === "neutral" ? "#9ca3af" : "#ef4444"}
/>
setShowMore(!showMore)} icon={faEllipsisVertical} tooltip="Show more options" />
{showMore && (
<>
(openAccordion !== CategoryGroup.EFFECT ? setOpenAccordion(CategoryGroup.EFFECT) : setOpenAccordion(CategoryGroup.NONE))}
icon={faExplosion}
tooltip="Show effects"
className="ml-auto"
/>
(openAccordion !== CategoryGroup.SEARCH ? setOpenAccordion(CategoryGroup.SEARCH) : setOpenAccordion(CategoryGroup.NONE))}
icon={faSearch}
tooltip="Search unit"
/>
(openAccordion !== CategoryGroup.STARRED ? setOpenAccordion(CategoryGroup.STARRED) : setOpenAccordion(CategoryGroup.NONE))}
icon={faStar}
tooltip="Show starred spanws"
/>
>
)}
{blueprint === null && effect === null && openAccordion !== CategoryGroup.NONE && (
<>
<>
{openAccordion === CategoryGroup.AIRCRAFT && (
<>
{roles.aircraft.sort().map((role) => {
return (
{
selectedRole === role ? setSelectedRole(null) : setSelectedRole(role);
}}
>
{role}
);
})}
{blueprints
?.sort((a, b) => (a.label > b.label ? 1 : -1))
.filter((blueprint) => blueprint.category === "aircraft")
.map((blueprint) => {
return (
setBlueprint(blueprint)}
showCost={showCost}
cost={getApp().getUnitsManager().getDatabase().getSpawnPointsByName(blueprint.name)}
/>
);
})}
>
)}
{openAccordion === CategoryGroup.HELICOPTER && (
<>
{roles.helicopter.sort().map((role) => {
return (
{
selectedRole === role ? setSelectedRole(null) : setSelectedRole(role);
}}
>
{role}
);
})}
{blueprints
?.sort((a, b) => (a.label > b.label ? 1 : -1))
.filter((blueprint) => blueprint.category === "helicopter")
.map((blueprint) => {
return (
setBlueprint(blueprint)}
showCost={showCost}
cost={getApp().getUnitsManager().getDatabase().getSpawnPointsByName(blueprint.name)}
/>
);
})}
>
)}
{openAccordion === CategoryGroup.AIR_DEFENCE && (
<>
{types.groundunit
.sort()
?.filter((type) => type === "SAM Site" || type === "AAA")
.map((type) => {
return (
{
selectedType === type ? setSelectedType(null) : setSelectedType(type);
}}
>
{type}
);
})}
{blueprints
?.sort((a, b) => (a.label > b.label ? 1 : -1))
.filter((blueprint) => blueprint.category === "groundunit" && (blueprint.type === "SAM Site" || blueprint.type === "AAA"))
.map((blueprint) => {
return (
setBlueprint(blueprint)}
showCost={showCost}
cost={getApp().getUnitsManager().getDatabase().getSpawnPointsByName(blueprint.name)}
/>
);
})}
>
)}
{openAccordion === CategoryGroup.GROUND_UNIT && (
<>
{types.groundunit
.sort()
?.filter((type) => type !== "SAM Site" && type !== "AAA")
.map((type) => {
return (
{
selectedType === type ? setSelectedType(null) : setSelectedType(type);
}}
>
{type}
);
})}
{blueprints
?.sort((a, b) => (a.label > b.label ? 1 : -1))
.filter((blueprint) => blueprint.category === "groundunit" && blueprint.type !== "SAM Site" && blueprint.type !== "AAA")
.map((blueprint) => {
return (
setBlueprint(blueprint)}
showCost={showCost}
cost={getApp().getUnitsManager().getDatabase().getSpawnPointsByName(blueprint.name)}
/>
);
})}
>
)}
{openAccordion === CategoryGroup.NAVY_UNIT && (
<>
{types.navyunit.sort().map((type) => {
return (
{
selectedType === type ? setSelectedType(null) : setSelectedType(type);
}}
>
{type}
);
})}
{blueprints
?.sort((a, b) => (a.label > b.label ? 1 : -1))
.filter((blueprint) => blueprint.category === "navyunit")
.map((blueprint) => {
return (
setBlueprint(blueprint)}
showCost={showCost}
cost={getApp().getUnitsManager().getDatabase().getSpawnPointsByName(blueprint.name)}
/>
);
})}
>
)}
{openAccordion === CategoryGroup.EFFECT && (
<>
{
setEffect("explosion");
}}
/>
{
setEffect("smoke");
}}
/>
>
)}
{openAccordion === CategoryGroup.SEARCH && (
setFilterString(value)} text={filterString} />
{filteredBlueprints.length > 0 ? (
filteredBlueprints.map((blueprint) => {
return (
setBlueprint(blueprint)}
showCost={showCost}
cost={getApp().getUnitsManager().getDatabase().getSpawnPointsByName(blueprint.name)}
/>
);
})
) : filterString === "" ? (
Type to search
) : (
No results
)}
)}
{openAccordion === CategoryGroup.STARRED && (
{Object.values(starredSpawns).length > 0 ? (
Object.values(starredSpawns).map((spawnRequestTable) => {
return (
{
if (latlng) {
spawnRequestTable.unit.location = latlng;
getApp()
.getUnitsManager()
.spawnUnits(
spawnRequestTable.category,
Array(spawnRequestTable.amount).fill(spawnRequestTable.unit),
spawnRequestTable.coalition,
false
);
getApp().setState(OlympusState.IDLE);
}
}}
>
{getApp().getUnitsManager().getDatabase().getByName(spawnRequestTable.unit.unitType)?.label} (
{spawnRequestTable.quickAccessName})
);
})
) : (
No starred spawns, use the spawn menu to create a quick access spawn
)}
)}
>
>
)}
setBlueprint(null)}
/>
{!(effect === null) && latlng && setEffect(null)} />}
>
);
}