165 lines
6.6 KiB
C++
165 lines
6.6 KiB
C++
#pragma once
|
|
|
|
#include <vector>
|
|
|
|
#include <Qt>
|
|
|
|
#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<ControlBinding> 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<ControlAction> getContextActions(const ControlContext& context);
|
|
std::vector<ControlAction> 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);
|