#pragma once #include #include #include "BuildModeController.h" #include "BuildingType.h" // The single statement of what the player can do right now, and which input does it // (REQ-UI-CONTROLS-CONTENT, REQ-UI-CONTROLS-ACCURACY). // // This file declares actions; it never performs them and it never names them. It knows // an action's bindings and the situations in which it does something -- nothing about // the simulation, the widgets, the events an action ends up firing, or the words shown // to the player. Display text lives in ui/ControlActionText.h, which formats the // bindings this hands it, so a badge is derived from the real binding rather than // typed beside it. // // Three readers, all of them consuming this and none of them extending it: // // * ControlsPanel calls getContextActions()/getAlwaysAvailableActions() and draws them. // * InputMapper calls resolveKeyAction() and fires the event the action stands for. // * GameWorldView calls resolveMouseAction() and runs the branch it already ran. // // Two bindings are deliberately absent. Build hotkeys (REQ-UI-HOTKEYS) are advertised // on the build buttons instead of in the panel, and InputMapper::getBuildHotkeyLabel // already derives their badges from the same table the handler switches on, so they // have no drift to fix. F3/F4 are development controls and appear nowhere // (REQ-UI-CONTROLS-ACCURACY). // // When bindings become player-configurable, only the binding tables in the .cpp turn // from hard-coded data into loaded data. The actions, the availability rules, the // panel, and every handler are unaffected. enum class ControlAction { None, // no action is bound to the queried input in the queried context // Always available (REQ-UI-CONTROLS-CONTENT). Move, GameSpeed, TogglePause, PasteTemporary, OpenBlueprints, OpenMenu, // No build mode active. Select, SelectArea, AddToSelection, AddAreaToSelection, EnterDeconstruct, // No build mode active, with something selected. CopyTemporary, CreateBlueprint, // Builder and blueprint placement mode. Place, ApplySettings, PlaceBeltLine, Rotate, CancelBeltLine, ExitMode, // Deconstruct mode. ToggleDeconstruct, DeconstructArea }; // The mouse gestures that carry a binding. Each is a whole gesture rather than a raw // event: a drag is one binding, not a press plus a release, because that is the unit // the player and the panel both think in. Which events make up the gesture, and the // state it runs on, stay with the widget that owns them. enum class MouseBinding { LeftClick, LeftDrag, CtrlLeftClick, CtrlLeftDrag, RightClick }; // Which selection category is held, mirroring REQ-UI-SELECTION-CATEGORIES without // depending on SelectionController. enum class ControlSelection { None, Buildings, FieldObjects }; // Which card the panel is showing (REQ-UI-CONTROLS-CONTENT). Named rather than // spelled, so the heading text stays a presentation concern. enum class ControlContextKind { General, Selection, Build, Blueprint, Deconstruct }; // Everything the availability rules are allowed to depend on, as a plain snapshot. // // Taking a snapshot rather than references to the live controllers is what keeps this // testable without a world, and it is what stops an action from reaching into the // simulation: if a rule needs a fact, the fact is named here and the caller supplies it. struct ControlContext { BuildMode mode = BuildMode::None; BuildingType builderType = BuildingType::Belt; // while mode == Builder bool draggingBelt = false; // A single-building blueprint whose ghost is over a configuration-transfer target, // so clicking hands over settings rather than placing (REQ-UI-BLUEPRINT-TRANSFER). bool hoveredGhostIsTransfer = false; ControlSelection selection = ControlSelection::None; int selectionCount = 0; // At least one selected building is player-placeable, the condition under which // C and Ctrl+C do anything (REQ-UI-HOTKEYS). bool placeableBuildingSelected = false; bool temporaryBlueprintExists = false; }; // One input an action answers to, in structured form so the badge can be rendered from // it. `modifiers` is what the badge shows; matching is looser than equality, see // resolveKeyAction. struct ControlBinding { bool isMouse = false; MouseBinding mouse = MouseBinding::LeftClick; int key = 0; // Qt::Key_*, when !isMouse Qt::KeyboardModifiers modifiers = Qt::NoModifier; }; // True when triggering the action in this context would do what its label says. The // panel shows exactly the available actions, and the resolvers return only available // ones, which is REQ-UI-CONTROLS-ACCURACY expressed as one function. bool isControlActionAvailable(ControlAction action, const ControlContext& context); // The inputs an action answers to, in the order the panel should badge them. // Context-dependent because a binding can be taken over: while a belt drag is in // progress the right mouse button cancels the drag, so ExitMode is left with its key // binding alone (REQ-BLD-BELT-DRAG). std::vector getControlActionBindings(ControlAction action, const ControlContext& context); // Which card is showing, and the rows it holds -- the context's own, then the block // available everywhere (REQ-UI-CONTROLS-CARD). Both lists are already filtered to the // available actions and ordered as REQ-UI-CONTROLS-CONTENT lists them. ControlContextKind getControlContextKind(const ControlContext& context); std::vector getContextActions(const ControlContext& context); std::vector getAlwaysAvailableActions(const ControlContext& context); // The action a key press or a mouse gesture triggers here, or None when the input is // unbound in this context. Both return only actions that are available, so a caller can // act on the result without re-checking the situation. // // Rotation direction is not part of the action: R and Shift+R are one Rotate, and the // caller reads the modifier for the direction, exactly as the digit of a build hotkey // is read from the key. An action with a parameter keeps the parameter at the handler. ControlAction resolveKeyAction(int key, Qt::KeyboardModifiers modifiers, const ControlContext& context); ControlAction resolveMouseAction(MouseBinding binding, const ControlContext& context);