The controls panel header read the type builder mode was entered with, so tunnel mode always said Tunnel Entry even where the ghost had resolved to an exit. Feed the header the same effective type the ghost and placement already use. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
168 lines
6.8 KiB
C++
168 lines
6.8 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;
|
|
// While mode == Builder: the type a click would place at the current hover position,
|
|
// not the type the mode was entered with — tunnel mode resolves to either end
|
|
// (REQ-BLD-TUNNEL-MODE).
|
|
BuildingType builderType = BuildingType::Belt;
|
|
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);
|