Skip to main content

Overview

ControllerMode is an abstract base class that extends InputMode to provide controller-specific functionality. It handles the conversion of physical button inputs into analog stick positions and digital button outputs for game controllers.

Class Definition

class ControllerMode : public InputMode {
  public:
    ControllerMode();
    void UpdateOutputs(const InputState &inputs, OutputState &outputs);
    void ResetDirections();
    virtual void UpdateDirections(
        bool lsLeft,
        bool lsRight,
        bool lsDown,
        bool lsUp,
        bool rsLeft,
        bool rsRight,
        bool rsDown,
        bool rsUp,
        uint8_t analogStickMin,
        uint8_t analogStickNeutral,
        uint8_t analogStickMax,
        OutputState &outputs
    );

  protected:
    StickDirections directions;

  private:
    virtual void UpdateDigitalOutputs(const InputState &inputs, OutputState &outputs) = 0;
    virtual void UpdateAnalogOutputs(const InputState &inputs, OutputState &outputs) = 0;
};

Inheritance

Base Class
InputMode
Inherits from InputMode, which provides configuration management, SOCD handling, and button remapping functionality.

Constructor

ControllerMode()

ControllerMode::ControllerMode() : InputMode()
Initializes a new controller mode instance and resets the directional state. Implementation Details:
  • Calls the parent InputMode constructor
  • Calls ResetDirections() to initialize stick direction state

Public Methods

UpdateOutputs

void UpdateOutputs(const InputState &inputs, OutputState &outputs)
Processes input state and updates output state with controller button and analog stick values.
inputs
const InputState&
Reference to the current input state containing all physical button presses
outputs
OutputState&
Reference to the output state to be modified with controller outputs
Processing Order:
  1. Creates a copy of inputs for remapping
  2. Applies button remapping via HandleRemap()
  3. Applies SOCD (Simultaneous Opposite Cardinal Direction) resolution via HandleSocd()
  4. Calls UpdateDigitalOutputs() to set button states
  5. Calls UpdateAnalogOutputs() to set analog stick positions

ResetDirections

void ResetDirections()
Resets the internal directions struct to neutral state. Behavior:
  • Sets all directional flags to false
  • Sets all coordinate values (x, y, cx, cy) to 0

UpdateDirections

virtual void UpdateDirections(
    bool lsLeft,
    bool lsRight,
    bool lsDown,
    bool lsUp,
    bool rsLeft,
    bool rsRight,
    bool rsDown,
    bool rsUp,
    uint8_t analogStickMin,
    uint8_t analogStickNeutral,
    uint8_t analogStickMax,
    OutputState &outputs
)
Calculates stick directions and updates analog stick outputs based on directional inputs.
lsLeft
bool
Left stick left direction active
lsRight
bool
Left stick right direction active
lsDown
bool
Left stick down direction active
lsUp
bool
Left stick up direction active
rsLeft
bool
Right stick (C-stick) left direction active
rsRight
bool
Right stick (C-stick) right direction active
rsDown
bool
Right stick (C-stick) down direction active
rsUp
bool
Right stick (C-stick) up direction active
analogStickMin
uint8_t
Minimum analog value (typically 48 for GameCube)
analogStickNeutral
uint8_t
Neutral/center analog value (typically 128)
analogStickMax
uint8_t
Maximum analog value (typically 208 for GameCube)
outputs
OutputState&
Output state to update with calculated stick positions
Behavior:
  • Resets directions to neutral state
  • Sets stick outputs to neutral position
  • Calculates coordinate flags (horizontal, vertical, diagonal)
  • Sets coordinate values (x, y, cx, cy) to -1, 0, or 1
  • Updates outputs.leftStickX, outputs.leftStickY, outputs.rightStickX, outputs.rightStickY
This method is virtual and can be overridden by derived classes to implement custom directional behavior.

Protected Members

directions

StickDirections directions;
Tracks the current state of analog stick directions.
horizontal
bool
True if left stick has horizontal input
vertical
bool
True if left stick has vertical input
diagonal
bool
True if left stick has both horizontal and vertical input
x
int8_t
Left stick horizontal direction: -1 (left), 0 (neutral), 1 (right)
y
int8_t
Left stick vertical direction: -1 (down), 0 (neutral), 1 (up)
cx
int8_t
Right stick horizontal direction: -1 (left), 0 (neutral), 1 (right)
cy
int8_t
Right stick vertical direction: -1 (down), 0 (neutral), 1 (up)

Pure Virtual Methods

These methods must be implemented by any concrete class derived from ControllerMode.

UpdateDigitalOutputs

virtual void UpdateDigitalOutputs(const InputState &inputs, OutputState &outputs) = 0
Implement this method to map physical button inputs to controller button outputs (A, B, X, Y, triggers, etc.).
inputs
const InputState&
Remapped and SOCD-resolved input state
outputs
OutputState&
Output state to update with button presses

UpdateAnalogOutputs

virtual void UpdateAnalogOutputs(const InputState &inputs, OutputState &outputs) = 0
Implement this method to calculate analog stick positions and trigger values based on inputs.
inputs
const InputState&
Remapped and SOCD-resolved input state
outputs
OutputState&
Output state to update with analog values

Usage Example

Here’s how the Melee20Button mode implements ControllerMode:
class Melee20Button : public ControllerMode {
  public:
    Melee20Button();
    void SetConfig(GameModeConfig &config, const MeleeOptions options);

  protected:
    void UpdateDigitalOutputs(const InputState &inputs, OutputState &outputs);
    void UpdateAnalogOutputs(const InputState &inputs, OutputState &outputs);

  private:
    MeleeOptions _options;
    bool _horizontal_socd;
    void HandleSocd(InputState &inputs);
};

Digital Output Implementation

void Melee20Button::UpdateDigitalOutputs(const InputState &inputs, OutputState &outputs) {
    outputs.a = inputs.rt1;
    outputs.b = inputs.rf1;
    outputs.x = inputs.rf2;
    outputs.y = inputs.rf6;
    outputs.buttonR = inputs.rf3;
    outputs.triggerLDigital = inputs.lf4;
    outputs.triggerRDigital = inputs.rf5;
    outputs.start = inputs.mb1;

    // D-Pad layer activation
    if (inputs.lt1 && inputs.lt2) {
        outputs.dpadUp = inputs.rt4;
        outputs.dpadDown = inputs.rt2;
        outputs.dpadLeft = inputs.rt3;
        outputs.dpadRight = inputs.rt5;
    }
}

Analog Output Implementation

void Melee20Button::UpdateAnalogOutputs(const InputState &inputs, OutputState &outputs) {
    // Calculate base stick directions
    UpdateDirections(
        inputs.lf3, // Left
        inputs.lf1, // Right
        inputs.lf2, // Down
        inputs.rf4, // Up
        inputs.rt3, // C-Left
        inputs.rt5, // C-Right
        inputs.rt2, // C-Down
        inputs.rt4, // C-Up
        ANALOG_STICK_MIN,      // 48
        ANALOG_STICK_NEUTRAL,  // 128
        ANALOG_STICK_MAX,      // 208
        outputs
    );

    // Apply modifiers based on directions struct
    if (directions.diagonal) {
        outputs.leftStickX = 128 + (directions.x * 56);
        outputs.leftStickY = 128 + (directions.y * 56);
    }

    // Modifier X for custom angles
    if (inputs.lt1 && directions.horizontal) {
        outputs.leftStickX = 128 + (directions.x * 53);
    }
}

Best Practices

Always call UpdateDirections() in your UpdateAnalogOutputs() implementation before applying modifiers. This ensures the directions struct is properly initialized.
The directions struct provides a convenient way to check for diagonal inputs and apply different coordinate values based on the direction quadrant.
  • InputState: Defined in include/core/state.hpp - contains all button states
  • OutputState: Defined in include/core/state.hpp - contains controller output structure
  • InputMode: Parent class providing config, SOCD, and remapping functionality

See Also

Build docs developers (and LLMs) love