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)} />}
); }