mirror of https://github.com/stella-emu/stella.git
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:
parent
74b9eb2f73
commit
6ab680cf07
|
@ -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},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue