Paddle mode enhancements (#861)

* Separate control precedence for paddles A and B

Previously, the paddle pair was treated as a single unit as far as
event overrides were concerned, so an analog input on paddle B would
prevent a mouse or even another digital controller from controlling
paddle A.

This commit separates out paddle A and B as far as event handling is
concerned. The same steps are applied, but now events affecting paddle
A will not preempt events of other types for paddle B from being
tested, and vice versa.

* Paddle mode enhancements

Map paddles to distinct controllers. For two player games, having
both players share a single controller isn't ideal. Instead, put
- Left A -> joystick 0
- Left B -> joystick 1
- Right A -> joystick 2
- Right B -> joystick 3
This commit is contained in:
eds-collabora 2022-03-06 10:23:31 +00:00 committed by GitHub
parent 74b9eb2f73
commit 6ab680cf07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 232 additions and 59 deletions

View File

@ -378,8 +378,6 @@ void PhysicalJoystickHandler::setStickDefaultMapping(int stick, Event::Type even
// put all controller events into their own mode's mappings
for (const auto& item : DefaultLeftJoystickMapping)
setDefaultAction(stick, item, event, EventMode::kJoystickMode, updateDefaults);
for (const auto& item : DefaultLeftPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
for (const auto& item : DefaultLeftKeyboardMapping)
setDefaultAction(stick, item, event, EventMode::kKeyboardMode, updateDefaults);
for (const auto& item : DefaultLeftDrivingMapping)
@ -390,13 +388,79 @@ void PhysicalJoystickHandler::setStickDefaultMapping(int stick, Event::Type even
// put all controller events into their own mode's mappings
for (const auto& item : DefaultRightJoystickMapping)
setDefaultAction(stick, item, event, EventMode::kJoystickMode, updateDefaults);
for (const auto& item : DefaultRightPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
for (const auto& item : DefaultRightKeyboardMapping)
setDefaultAction(stick, item, event, EventMode::kKeyboardMode, updateDefaults);
for (const auto& item : DefaultRightDrivingMapping)
setDefaultAction(stick, item, event, EventMode::kDrivingMode, updateDefaults);
}
#if defined(RETRON77)
const bool retron77 = true;
#else
const bool retron77 = false;
#endif
// Regular joysticks can only be used by one player at a time,
// so we need to separate the paddles onto different
// devices. The R77 controllers support two players each when
// used as paddles, so are different. Similarly, stelladaptors
// and 2600-daptors support two players natively.
const int paddlesPerJoystick = (j->type == PhysicalJoystick::Type::REGULAR && !retron77) ? 1 : 2;
if( paddlesPerJoystick == 2 )
{
if( useLeftMappings )
{
for (const auto& item : DefaultLeftPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
}
else
{
for (const auto& item : DefaultRightPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
}
}
else
{
// One paddle per joystick means we need different defaults,
// such that each player gets the same mapping.
// This mapping is:
// - stick 0: left paddle A
// - stick 1: left paddle B
// - stick 2: right paddle A
// - stick 3: right paddle B
const bool useLeftPaddleMappings = (stick % 4) < 2;
const bool useAPaddleMappings = (stick % 2) == 0;
if( useLeftPaddleMappings )
{
if( useAPaddleMappings )
{
for (const auto& item : DefaultLeftAPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
}
else
{
for (const auto& item : DefaultLeftBPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
}
}
else
{
if( useAPaddleMappings )
{
for (const auto& item : DefaultRightAPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
}
else
{
for (const auto& item : DefaultRightBPaddlesMapping)
setDefaultAction(stick, item, event, EventMode::kPaddlesMode, updateDefaults);
}
}
}
for(const auto& item : DefaultCommonMapping)
setDefaultAction(stick, item, event, EventMode::kCommonMode, updateDefaults);
// update running emulation mapping too
@ -1231,6 +1295,40 @@ PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRight
{Event::RightPaddleBFire, 1},
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftAPaddlesMapping = {
{Event::LeftPaddleAAnalog, JOY_CTRL_NONE, JoyAxis::X, JoyDir::ANALOG},
// Current code does NOT allow digital and anlog events on the same axis at the same time
//{Event::LeftPaddleADecrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
//{Event::LeftPaddleAIncrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
{Event::LeftPaddleAFire, 0},
};
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftBPaddlesMapping = {
{Event::LeftPaddleBAnalog, JOY_CTRL_NONE, JoyAxis::X, JoyDir::ANALOG},
// Current code does NOT allow digital and anlog events on the same axis at the same
//{Event::LeftPaddleBDecrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
//{Event::LeftPaddleBIncrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
{Event::LeftPaddleBFire, 0},
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRightAPaddlesMapping = {
{Event::RightPaddleAAnalog, JOY_CTRL_NONE, JoyAxis::X, JoyDir::ANALOG},
// Current code does NOT allow digital and anlog events on the same axis at the same
//{Event::RightPaddleADecrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
//{Event::RightPaddleAIncrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
{Event::RightPaddleAFire, 0},
};
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRightBPaddlesMapping = {
{Event::RightPaddleBAnalog, JOY_CTRL_NONE, JoyAxis::X, JoyDir::ANALOG},
// Current code does NOT allow digital and anlog events on the same axis at the same
//{Event::RightPaddleBDecrease,JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
//{Event::RightPaddleBIncrease,JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
{Event::RightPaddleBFire, 0},
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftKeyboardMapping = {
{Event::LeftKeyboard1, 0},

View File

@ -201,6 +201,10 @@ class PhysicalJoystickHandler
static EventMappingArray DefaultRightJoystickMapping;
static EventMappingArray DefaultLeftPaddlesMapping;
static EventMappingArray DefaultRightPaddlesMapping;
static EventMappingArray DefaultLeftAPaddlesMapping;
static EventMappingArray DefaultLeftBPaddlesMapping;
static EventMappingArray DefaultRightAPaddlesMapping;
static EventMappingArray DefaultRightBPaddlesMapping;
static EventMappingArray DefaultLeftKeyboardMapping;
static EventMappingArray DefaultRightKeyboardMapping;
static EventMappingArray DefaultLeftDrivingMapping;

View File

@ -155,12 +155,16 @@ void Paddles::swapEvents(Event::Type& event1, Event::Type& event2)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Paddles::update()
{
setPin(DigitalPin::Three, true);
updateA();
updateB();
}
void Paddles::updateA()
{
setPin(DigitalPin::Four, true);
// Digital events (from keyboard or joystick hats & buttons)
bool firePressedA = myEvent.get(myLeftAFireEvent) != 0;
bool firePressedB = myEvent.get(myLeftBFireEvent) != 0;
// Paddle movement is a very difficult thing to accurately emulate,
// since it originally came from an analog device that had very
@ -173,17 +177,12 @@ void Paddles::update()
// And to top it all off, we don't want one devices input to conflict
// with the others ...
if(!updateAnalogAxes())
if(!updateAnalogAxesA())
{
updateMouse(firePressedA, firePressedB);
updateDigitalAxes();
updateMouseA(firePressedA);
updateDigitalAxesA();
// Only change state if the charge has actually changed
if(myCharge[1] != myLastCharge[1])
{
setPin(AnalogPin::Five, AnalogReadout::connectToVcc(MAX_RESISTANCE * (myCharge[1] / double(TRIGMAX))));
myLastCharge[1] = myCharge[1];
}
if(myCharge[0] != myLastCharge[0])
{
setPin(AnalogPin::Nine, AnalogReadout::connectToVcc(MAX_RESISTANCE * (myCharge[0] / double(TRIGMAX))));
@ -192,7 +191,6 @@ void Paddles::update()
}
setPin(DigitalPin::Four, !getAutoFireState(firePressedA));
setPin(DigitalPin::Three, !getAutoFireStateP1(firePressedB));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -251,7 +249,7 @@ AnalogReadout::Connection Paddles::getReadOut(int lastAxis, int& newAxis, int ce
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Paddles::updateAnalogAxes()
bool Paddles::updateAnalogAxesA()
{
// Analog axis events from Stelladaptor-like devices,
// (which includes analog USB controllers)
@ -264,7 +262,6 @@ bool Paddles::updateAnalogAxes()
int sa_xaxis = myEvent.get(myAAxisValue);
int sa_yaxis = myEvent.get(myBAxisValue);
bool sa_changed = false;
if(abs(myLastAxisX - sa_xaxis) > 10)
@ -273,71 +270,52 @@ bool Paddles::updateAnalogAxes()
sa_changed = true;
}
if(abs(myLastAxisY - sa_yaxis) > 10)
{
setPin(AnalogPin::Five, getReadOut(myLastAxisY, sa_yaxis, YCENTER));
sa_changed = true;
}
myLastAxisX = sa_xaxis;
myLastAxisY = sa_yaxis;
return sa_changed;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Paddles::updateMouse(bool& firePressedA, bool& firePressedB)
void Paddles::updateMouseA(bool& firePressedA)
{
// Mouse motion events give relative movement
// That is, they're only relevant if they're non-zero
if(myMPaddleID > -1)
if(myMPaddleID == 0)
{
// We're in auto mode, where a single axis is used for one paddle only
myCharge[myMPaddleID] = BSPF::clamp(myCharge[myMPaddleID] -
(myEvent.get(myAxisMouseMotion) * MOUSE_SENSITIVITY),
TRIGMIN, TRIGRANGE);
if(myMPaddleID == 0)
firePressedA = firePressedA
|| myEvent.get(Event::MouseButtonLeftValue)
|| myEvent.get(Event::MouseButtonRightValue);
else
firePressedB = firePressedB
|| myEvent.get(Event::MouseButtonLeftValue)
|| myEvent.get(Event::MouseButtonRightValue);
firePressedA = firePressedA
|| myEvent.get(Event::MouseButtonLeftValue)
|| myEvent.get(Event::MouseButtonRightValue);
}
else
{
// Test for 'untied' mouse axis mode, where each axis is potentially
// mapped to a separate paddle
if(myMPaddleIDX > -1)
if(myMPaddleIDX == 0)
{
myCharge[myMPaddleIDX] = BSPF::clamp(myCharge[myMPaddleIDX] -
(myEvent.get(Event::MouseAxisXMove) * MOUSE_SENSITIVITY),
TRIGMIN, TRIGRANGE);
if(myMPaddleIDX == 0)
firePressedA = firePressedA
|| myEvent.get(Event::MouseButtonLeftValue);
else
firePressedB = firePressedB
|| myEvent.get(Event::MouseButtonLeftValue);
firePressedA = firePressedA
|| myEvent.get(Event::MouseButtonLeftValue);
}
if(myMPaddleIDY > -1)
if(myMPaddleIDY == 0)
{
myCharge[myMPaddleIDY] = BSPF::clamp(myCharge[myMPaddleIDY] -
(myEvent.get(Event::MouseAxisYMove) * MOUSE_SENSITIVITY),
TRIGMIN, TRIGRANGE);
if(myMPaddleIDY == 0)
firePressedA = firePressedA
|| myEvent.get(Event::MouseButtonRightValue);
else
firePressedB = firePressedB
|| myEvent.get(Event::MouseButtonRightValue);
firePressedA = firePressedA
|| myEvent.get(Event::MouseButtonRightValue);
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Paddles::updateDigitalAxes()
void Paddles::updateDigitalAxesA()
{
// Finally, consider digital input, where movement happens
// until a digital event is released
@ -347,15 +325,8 @@ void Paddles::updateDigitalAxes()
if(myPaddleRepeatA > DIGITAL_SENSITIVITY)
myPaddleRepeatA = DIGITAL_DISTANCE;
}
if(myKeyRepeatB)
{
myPaddleRepeatB++;
if(myPaddleRepeatB > DIGITAL_SENSITIVITY)
myPaddleRepeatB = DIGITAL_DISTANCE;
}
myKeyRepeatA = false;
myKeyRepeatB = false;
if(myEvent.get(myLeftADecEvent))
{
@ -369,6 +340,100 @@ void Paddles::updateDigitalAxes()
if((myCharge[myAxisDigitalZero] + myPaddleRepeatA) < TRIGRANGE)
myCharge[myAxisDigitalZero] += myPaddleRepeatA;
}
}
void Paddles::updateB()
{
setPin(DigitalPin::Three, true);
// Digital events (from keyboard or joystick hats & buttons)
bool firePressedB = myEvent.get(myLeftBFireEvent) != 0;
if(!updateAnalogAxesB())
{
updateMouseB(firePressedB);
updateDigitalAxesB();
// Only change state if the charge has actually changed
if(myCharge[1] != myLastCharge[1])
{
setPin(AnalogPin::Five, AnalogReadout::connectToVcc(MAX_RESISTANCE * (myCharge[1] / double(TRIGMAX))));
myLastCharge[1] = myCharge[1];
}
}
setPin(DigitalPin::Three, !getAutoFireStateP1(firePressedB));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Paddles::updateAnalogAxesB()
{
int sa_yaxis = myEvent.get(myBAxisValue);
bool sa_changed = false;
if(abs(myLastAxisY - sa_yaxis) > 10)
{
setPin(AnalogPin::Five, getReadOut(myLastAxisY, sa_yaxis, YCENTER));
sa_changed = true;
}
myLastAxisY = sa_yaxis;
return sa_changed;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Paddles::updateMouseB(bool& firePressedB)
{
// Mouse motion events give relative movement
// That is, they're only relevant if they're non-zero
if(myMPaddleID == 1)
{
// We're in auto mode, where a single axis is used for one paddle only
myCharge[myMPaddleID] = BSPF::clamp(myCharge[myMPaddleID] -
(myEvent.get(myAxisMouseMotion) * MOUSE_SENSITIVITY),
TRIGMIN, TRIGRANGE);
firePressedB = firePressedB
|| myEvent.get(Event::MouseButtonLeftValue)
|| myEvent.get(Event::MouseButtonRightValue);
}
else
{
// Test for 'untied' mouse axis mode, where each axis is potentially
// mapped to a separate paddle
if(myMPaddleIDX == 1)
{
myCharge[myMPaddleIDX] = BSPF::clamp(myCharge[myMPaddleIDX] -
(myEvent.get(Event::MouseAxisXMove) * MOUSE_SENSITIVITY),
TRIGMIN, TRIGRANGE);
firePressedB = firePressedB
|| myEvent.get(Event::MouseButtonLeftValue);
}
if(myMPaddleIDY == 1)
{
myCharge[myMPaddleIDY] = BSPF::clamp(myCharge[myMPaddleIDY] -
(myEvent.get(Event::MouseAxisYMove) * MOUSE_SENSITIVITY),
TRIGMIN, TRIGRANGE);
firePressedB = firePressedB
|| myEvent.get(Event::MouseButtonRightValue);
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Paddles::updateDigitalAxesB()
{
// Finally, consider digital input, where movement happens
// until a digital event is released
if(myKeyRepeatB)
{
myPaddleRepeatB++;
if(myPaddleRepeatB > DIGITAL_SENSITIVITY)
myPaddleRepeatB = DIGITAL_DISTANCE;
}
myKeyRepeatB = false;
if(myEvent.get(myLeftBDecEvent))
{
myKeyRepeatB = true;

View File

@ -206,20 +206,26 @@ class Paddles : public Controller
AnalogReadout::Connection getReadOut(int lastAxis, int& newAxis, int center);
void updateA();
void updateB();
/**
Update the axes pin state according to the events currently set.
*/
bool updateAnalogAxes();
bool updateAnalogAxesA();
bool updateAnalogAxesB();
/**
Update the entire state according to mouse events currently set.
*/
void updateMouse(bool& firePressedA, bool& firePressedB);
void updateMouseA(bool& firePressedA);
void updateMouseB(bool& firePressedB);
/**
Update the axes pin state according to the keyboard events currently set.
*/
void updateDigitalAxes();
void updateDigitalAxesA();
void updateDigitalAxesB();
private:
// Following constructors and assignment operators not supported