mirror of https://github.com/stella-emu/stella.git
Merge remote-tracking branch 'remotes/origin/feature-lightgun'
This commit is contained in:
commit
9a7dc74aea
|
@ -275,7 +275,7 @@
|
||||||
<li>Emulates <a href="http://en.wikipedia.org/wiki/CompuMate">Spectravideo CompuMate</a> system using your computer's keyboard,
|
<li>Emulates <a href="http://en.wikipedia.org/wiki/CompuMate">Spectravideo CompuMate</a> system using your computer's keyboard,
|
||||||
including mapping of CompuMate 'Backspace', 'Space' and 'Enter' functionality to
|
including mapping of CompuMate 'Backspace', 'Space' and 'Enter' functionality to
|
||||||
to the actual keys on your keyboard</li>
|
to the actual keys on your keyboard</li>
|
||||||
<li>Emulates the Mindlink Controller using your computer's mouse</li>
|
<li>Emulates the Mindlink Controller and the Light Gun using your computer's mouse</li>
|
||||||
<li>Supports autodetection for most common controller types</li>
|
<li>Supports autodetection for most common controller types</li>
|
||||||
<li>Support for real Atari 2600 controllers using the
|
<li>Support for real Atari 2600 controllers using the
|
||||||
<a href="http://www.grandideastudio.com/portfolio/stelladaptor-2600">Stelladaptor</a> and
|
<a href="http://www.grandideastudio.com/portfolio/stelladaptor-2600">Stelladaptor</a> and
|
||||||
|
@ -1784,6 +1784,14 @@
|
||||||
<td> ✕</td>
|
<td> ✕</td>
|
||||||
<td> ✕</td>
|
<td> ✕</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th> Light Gun</th>
|
||||||
|
<td> ✕</td>
|
||||||
|
<td> ✕</td>
|
||||||
|
<td> ✓</td>
|
||||||
|
<td> ✕</td>
|
||||||
|
<td> ✕</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th> Mindlink</th>
|
<th> Mindlink</th>
|
||||||
<td> ✕</td>
|
<td> ✕</td>
|
||||||
|
@ -3817,6 +3825,7 @@ Ms Pac-Man (Stella extended codes):
|
||||||
<tr><td>SaveKey</td><td>A 32K EEPROM for saving high scores, etc. (the EEPROM portion of an AtariVox).</td></tr>
|
<tr><td>SaveKey</td><td>A 32K EEPROM for saving high scores, etc. (the EEPROM portion of an AtariVox).</td></tr>
|
||||||
<tr><td>Genesis </td><td>Sega Genesis controller, which can be used similar to a BoosterGrip, giving an extra button.</td></tr>
|
<tr><td>Genesis </td><td>Sega Genesis controller, which can be used similar to a BoosterGrip, giving an extra button.</td></tr>
|
||||||
<tr><td>CompuMate ¹</td><td>Spectravideo CompuMate (if either left or right is set, CompuMate is used for both).</td></tr>
|
<tr><td>CompuMate ¹</td><td>Spectravideo CompuMate (if either left or right is set, CompuMate is used for both).</td></tr>
|
||||||
|
<tr><td>Lightgun</td><td>Atari XG-1 compatible Light Gun</td></tr>
|
||||||
<tr><td>Mindlink ¹</td><td>Mindlink controller.</td></tr>
|
<tr><td>Mindlink ¹</td><td>Mindlink controller.</td></tr>
|
||||||
</table></td>
|
</table></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -99,8 +99,8 @@ void BoosterGrip::update()
|
||||||
{
|
{
|
||||||
// The following code was taken from z26
|
// The following code was taken from z26
|
||||||
#define MJ_Threshold 2
|
#define MJ_Threshold 2
|
||||||
int mousex = myEvent.get(Event::MouseAxisXValue),
|
int mousex = myEvent.get(Event::MouseAxisXMove),
|
||||||
mousey = myEvent.get(Event::MouseAxisYValue);
|
mousey = myEvent.get(Event::MouseAxisYMove);
|
||||||
if(mousex || mousey)
|
if(mousex || mousey)
|
||||||
{
|
{
|
||||||
if((!(abs(mousey) > abs(mousex) << 1)) && (abs(mousex) >= MJ_Threshold))
|
if((!(abs(mousey) > abs(mousex) << 1)) && (abs(mousex) >= MJ_Threshold))
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include "AmigaMouse.hxx"
|
#include "AmigaMouse.hxx"
|
||||||
#include "AtariMouse.hxx"
|
#include "AtariMouse.hxx"
|
||||||
#include "TrakBall.hxx"
|
#include "TrakBall.hxx"
|
||||||
|
#include "Lightgun.hxx"
|
||||||
#include "FrameBuffer.hxx"
|
#include "FrameBuffer.hxx"
|
||||||
#include "TIASurface.hxx"
|
#include "TIASurface.hxx"
|
||||||
#include "OSystem.hxx"
|
#include "OSystem.hxx"
|
||||||
|
@ -871,6 +872,10 @@ unique_ptr<Controller> Console::getControllerPort(const Controller::Type type,
|
||||||
controller = make_unique<MindLink>(port, myEvent, *mySystem);
|
controller = make_unique<MindLink>(port, myEvent, *mySystem);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Controller::Type::Lightgun:
|
||||||
|
controller = make_unique<Lightgun>(port, myEvent, *mySystem, myOSystem.frameBuffer());
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// What else can we do?
|
// What else can we do?
|
||||||
// always create because it may have been changed by user dialog
|
// always create because it may have been changed by user dialog
|
||||||
|
|
|
@ -109,7 +109,8 @@ string Controller::getName(const Type type)
|
||||||
"Unknown",
|
"Unknown",
|
||||||
"AmigaMouse", "AtariMouse", "AtariVox", "BoosterGrip", "CompuMate",
|
"AmigaMouse", "AtariMouse", "AtariVox", "BoosterGrip", "CompuMate",
|
||||||
"Driving", "Sega Genesis", "Joystick", "Keyboard", "KidVid", "MindLink",
|
"Driving", "Sega Genesis", "Joystick", "Keyboard", "KidVid", "MindLink",
|
||||||
"Paddles", "Paddles_IAxis", "Paddles_IAxDr", "SaveKey", "TrakBall"
|
"Paddles", "Paddles_IAxis", "Paddles_IAxDr", "SaveKey", "TrakBall",
|
||||||
|
"Lightgun"
|
||||||
};
|
};
|
||||||
|
|
||||||
return NAMES[int(type)];
|
return NAMES[int(type)];
|
||||||
|
@ -123,7 +124,8 @@ string Controller::getPropName(const Type type)
|
||||||
"AUTO",
|
"AUTO",
|
||||||
"AMIGAMOUSE", "ATARIMOUSE", "ATARIVOX", "BOOSTERGRIP", "COMPUMATE",
|
"AMIGAMOUSE", "ATARIMOUSE", "ATARIVOX", "BOOSTERGRIP", "COMPUMATE",
|
||||||
"DRIVING", "GENESIS", "JOYSTICK", "KEYBOARD", "KIDVID", "MINDLINK",
|
"DRIVING", "GENESIS", "JOYSTICK", "KEYBOARD", "KIDVID", "MINDLINK",
|
||||||
"PADDLES", "PADDLES_IAXIS", "PADDLES_IAXDR", "SAVEKEY", "TRAKBALL"
|
"PADDLES", "PADDLES_IAXIS", "PADDLES_IAXDR", "SAVEKEY", "TRAKBALL",
|
||||||
|
"LIGHTGUN"
|
||||||
};
|
};
|
||||||
|
|
||||||
return PROP_NAMES[int(type)];
|
return PROP_NAMES[int(type)];
|
||||||
|
|
|
@ -94,6 +94,7 @@ class Controller : public Serializable
|
||||||
AmigaMouse, AtariMouse, AtariVox, BoosterGrip, CompuMate,
|
AmigaMouse, AtariMouse, AtariVox, BoosterGrip, CompuMate,
|
||||||
Driving, Genesis, Joystick, Keyboard, KidVid, MindLink,
|
Driving, Genesis, Joystick, Keyboard, KidVid, MindLink,
|
||||||
Paddles, PaddlesIAxis, PaddlesIAxDr, SaveKey, TrakBall,
|
Paddles, PaddlesIAxis, PaddlesIAxDr, SaveKey, TrakBall,
|
||||||
|
Lightgun,
|
||||||
LastType
|
LastType
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -70,8 +70,9 @@ Controller::Type ControllerDetector::autodetectPort(const uInt8* image, size_t s
|
||||||
else if(usesKeyboard(image, size, port))
|
else if(usesKeyboard(image, size, port))
|
||||||
type = Controller::Type::Keyboard;
|
type = Controller::Type::Keyboard;
|
||||||
else if(usesGenesisButton(image, size, port))
|
else if(usesGenesisButton(image, size, port))
|
||||||
|
|
||||||
type = Controller::Type::Genesis;
|
type = Controller::Type::Genesis;
|
||||||
|
else if(isProbablyLightGun(image, size, port))
|
||||||
|
type = Controller::Type::Lightgun;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -643,3 +644,41 @@ bool ControllerDetector::isProbablySaveKey(const uInt8* image, size_t size,
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool ControllerDetector::isProbablyLightGun(const uInt8* image, size_t size,
|
||||||
|
Controller::Jack port)
|
||||||
|
{
|
||||||
|
if (port == Controller::Jack::Left)
|
||||||
|
{
|
||||||
|
// check for INPT4 after NOPs access
|
||||||
|
const int NUM_SIGS = 2;
|
||||||
|
const int SIG_SIZE = 6;
|
||||||
|
uInt8 signature[NUM_SIGS][SIG_SIZE] = {
|
||||||
|
{ 0xea, 0xea, 0xea, 0x24, 0x0c, 0x10 },
|
||||||
|
{ 0xea, 0xea, 0xea, 0x24, 0x3c, 0x10 }
|
||||||
|
}; // all pattern checked, only 'Sentinel' and 'Shooting Arcade' match
|
||||||
|
|
||||||
|
for (uInt32 i = 0; i < NUM_SIGS; ++i)
|
||||||
|
if (searchForBytes(image, size, signature[i], SIG_SIZE))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if(port == Controller::Jack::Right)
|
||||||
|
{
|
||||||
|
// check for INPT5 after NOPs access
|
||||||
|
const int NUM_SIGS = 2;
|
||||||
|
const int SIG_SIZE = 6;
|
||||||
|
uInt8 signature[NUM_SIGS][SIG_SIZE] = {
|
||||||
|
{ 0xea, 0xea, 0xea, 0x24, 0x0d, 0x10 },
|
||||||
|
{ 0xea, 0xea, 0xea, 0x24, 0x3d, 0x10 }
|
||||||
|
}; // all pattern checked, only 'Bobby is Hungry' matches
|
||||||
|
|
||||||
|
for (uInt32 i = 0; i < NUM_SIGS; ++i)
|
||||||
|
if (searchForBytes(image, size, signature[i], SIG_SIZE))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -111,6 +111,10 @@ class ControllerDetector
|
||||||
// Returns true if a SaveKey code pattern is found.
|
// Returns true if a SaveKey code pattern is found.
|
||||||
static bool isProbablySaveKey(const uInt8* image, size_t size, Controller::Jack port);
|
static bool isProbablySaveKey(const uInt8* image, size_t size, Controller::Jack port);
|
||||||
|
|
||||||
|
// Returns true if a Lightgun code pattern is found
|
||||||
|
static bool isProbablyLightGun(const uInt8* image, size_t size, Controller::Jack port);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Following constructors and assignment operators not supported
|
// Following constructors and assignment operators not supported
|
||||||
ControllerDetector() = delete;
|
ControllerDetector() = delete;
|
||||||
|
|
|
@ -62,7 +62,7 @@ void Driving::update()
|
||||||
// Mouse motion and button events
|
// Mouse motion and button events
|
||||||
if(myControlID > -1)
|
if(myControlID > -1)
|
||||||
{
|
{
|
||||||
int m_axis = myEvent.get(Event::MouseAxisXValue);
|
int m_axis = myEvent.get(Event::MouseAxisXMove);
|
||||||
if(m_axis < -2) --myCounter;
|
if(m_axis < -2) --myCounter;
|
||||||
else if(m_axis > 2) ++myCounter;
|
else if(m_axis > 2) ++myCounter;
|
||||||
if(myEvent.get(Event::MouseButtonLeftValue) ||
|
if(myEvent.get(Event::MouseButtonLeftValue) ||
|
||||||
|
@ -75,7 +75,7 @@ void Driving::update()
|
||||||
// mapped to a separate driving controller
|
// mapped to a separate driving controller
|
||||||
if(myControlIDX > -1)
|
if(myControlIDX > -1)
|
||||||
{
|
{
|
||||||
int m_axis = myEvent.get(Event::MouseAxisXValue);
|
int m_axis = myEvent.get(Event::MouseAxisXMove);
|
||||||
if(m_axis < -2) --myCounter;
|
if(m_axis < -2) --myCounter;
|
||||||
else if(m_axis > 2) ++myCounter;
|
else if(m_axis > 2) ++myCounter;
|
||||||
if(myEvent.get(Event::MouseButtonLeftValue))
|
if(myEvent.get(Event::MouseButtonLeftValue))
|
||||||
|
@ -83,7 +83,7 @@ void Driving::update()
|
||||||
}
|
}
|
||||||
if(myControlIDY > -1)
|
if(myControlIDY > -1)
|
||||||
{
|
{
|
||||||
int m_axis = myEvent.get(Event::MouseAxisYValue);
|
int m_axis = myEvent.get(Event::MouseAxisYMove);
|
||||||
if(m_axis < -2) --myCounter;
|
if(m_axis < -2) --myCounter;
|
||||||
else if(m_axis > 2) ++myCounter;
|
else if(m_axis > 2) ++myCounter;
|
||||||
if(myEvent.get(Event::MouseButtonRightValue))
|
if(myEvent.get(Event::MouseButtonRightValue))
|
||||||
|
|
|
@ -68,7 +68,7 @@ class Event
|
||||||
Combo1, Combo2, Combo3, Combo4, Combo5, Combo6, Combo7, Combo8,
|
Combo1, Combo2, Combo3, Combo4, Combo5, Combo6, Combo7, Combo8,
|
||||||
Combo9, Combo10, Combo11, Combo12, Combo13, Combo14, Combo15, Combo16,
|
Combo9, Combo10, Combo11, Combo12, Combo13, Combo14, Combo15, Combo16,
|
||||||
|
|
||||||
MouseAxisXValue, MouseAxisYValue,
|
MouseAxisXMove, MouseAxisYMove,
|
||||||
MouseButtonLeftValue, MouseButtonRightValue,
|
MouseButtonLeftValue, MouseButtonRightValue,
|
||||||
|
|
||||||
ChangeState, LoadState, SaveState, TakeSnapshot, Quit,
|
ChangeState, LoadState, SaveState, TakeSnapshot, Quit,
|
||||||
|
@ -121,6 +121,7 @@ class Event
|
||||||
CompuMateSlash,
|
CompuMateSlash,
|
||||||
|
|
||||||
ToggleInter,
|
ToggleInter,
|
||||||
|
MouseAxisXValue, MouseAxisYValue,
|
||||||
|
|
||||||
LastType
|
LastType
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "OSystem.hxx"
|
#include "OSystem.hxx"
|
||||||
#include "Joystick.hxx"
|
#include "Joystick.hxx"
|
||||||
#include "Paddles.hxx"
|
#include "Paddles.hxx"
|
||||||
|
#include "Lightgun.hxx"
|
||||||
#include "PointingDevice.hxx"
|
#include "PointingDevice.hxx"
|
||||||
#include "PropsSet.hxx"
|
#include "PropsSet.hxx"
|
||||||
#include "Settings.hxx"
|
#include "Settings.hxx"
|
||||||
|
@ -247,8 +248,8 @@ void EventHandler::poll(uInt64 time)
|
||||||
|
|
||||||
// Turn off all mouse-related items; if they haven't been taken care of
|
// Turn off all mouse-related items; if they haven't been taken care of
|
||||||
// in the previous ::update() methods, they're now invalid
|
// in the previous ::update() methods, they're now invalid
|
||||||
myEvent.set(Event::MouseAxisXValue, 0);
|
myEvent.set(Event::MouseAxisXMove, 0);
|
||||||
myEvent.set(Event::MouseAxisYValue, 0);
|
myEvent.set(Event::MouseAxisYMove, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -269,8 +270,10 @@ void EventHandler::handleMouseMotionEvent(int x, int y, int xrel, int yrel)
|
||||||
{
|
{
|
||||||
if(!mySkipMouseMotion)
|
if(!mySkipMouseMotion)
|
||||||
{
|
{
|
||||||
myEvent.set(Event::MouseAxisXValue, xrel);
|
myEvent.set(Event::MouseAxisXValue, x); // required for Lightgun controller
|
||||||
myEvent.set(Event::MouseAxisYValue, yrel);
|
myEvent.set(Event::MouseAxisYValue, y); // required for Lightgun controller
|
||||||
|
myEvent.set(Event::MouseAxisXMove, xrel);
|
||||||
|
myEvent.set(Event::MouseAxisYMove, yrel);
|
||||||
}
|
}
|
||||||
mySkipMouseMotion = false;
|
mySkipMouseMotion = false;
|
||||||
}
|
}
|
||||||
|
@ -1952,7 +1955,7 @@ const Event::EventSet EventHandler::MiscEvents = {
|
||||||
Event::Quit, Event::ReloadConsole, Event::Fry, Event::StartPauseMode,
|
Event::Quit, Event::ReloadConsole, Event::Fry, Event::StartPauseMode,
|
||||||
Event::TogglePauseMode, Event::OptionsMenuMode, Event::CmdMenuMode, Event::ExitMode,
|
Event::TogglePauseMode, Event::OptionsMenuMode, Event::CmdMenuMode, Event::ExitMode,
|
||||||
Event::TakeSnapshot, Event::ToggleContSnapshots, Event::ToggleContSnapshotsFrame,
|
Event::TakeSnapshot, Event::ToggleContSnapshots, Event::ToggleContSnapshotsFrame,
|
||||||
// Event::MouseAxisXValue, Event::MouseAxisYValue,
|
// Event::MouseAxisXMove, Event::MouseAxisYMove,
|
||||||
// Event::MouseButtonLeftValue, Event::MouseButtonRightValue,
|
// Event::MouseButtonLeftValue, Event::MouseButtonRightValue,
|
||||||
Event::HandleMouseControl, Event::ToggleGrabMouse,
|
Event::HandleMouseControl, Event::ToggleGrabMouse,
|
||||||
Event::ToggleSAPortOrder,
|
Event::ToggleSAPortOrder,
|
||||||
|
|
|
@ -841,10 +841,17 @@ void FrameBuffer::setCursorState()
|
||||||
bool analog = myOSystem.hasConsole() ?
|
bool analog = myOSystem.hasConsole() ?
|
||||||
(myOSystem.console().leftController().isAnalog() ||
|
(myOSystem.console().leftController().isAnalog() ||
|
||||||
myOSystem.console().rightController().isAnalog()) : false;
|
myOSystem.console().rightController().isAnalog()) : false;
|
||||||
|
bool usesLightgun = emulation && myOSystem.hasConsole() ?
|
||||||
|
myOSystem.console().leftController().type() == Controller::Type::Lightgun ||
|
||||||
|
myOSystem.console().rightController().type() == Controller::Type::Lightgun : false;
|
||||||
bool alwaysUseMouse = BSPF::equalsIgnoreCase("always", myOSystem.settings().getString("usemouse"));
|
bool alwaysUseMouse = BSPF::equalsIgnoreCase("always", myOSystem.settings().getString("usemouse"));
|
||||||
|
|
||||||
// Show/hide cursor in UI/emulation mode based on 'cursor' setting
|
// Show/hide cursor in UI/emulation mode based on 'cursor' setting
|
||||||
int cursor = myOSystem.settings().getInt("cursor");
|
int cursor = myOSystem.settings().getInt("cursor");
|
||||||
|
// always enable cursor in lightgun games
|
||||||
|
if (usesLightgun)
|
||||||
|
cursor |= 1;
|
||||||
|
|
||||||
switch(cursor)
|
switch(cursor)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
|
|
|
@ -67,8 +67,8 @@ void Genesis::update()
|
||||||
{
|
{
|
||||||
// The following code was taken from z26
|
// The following code was taken from z26
|
||||||
#define MJ_Threshold 2
|
#define MJ_Threshold 2
|
||||||
int mousex = myEvent.get(Event::MouseAxisXValue),
|
int mousex = myEvent.get(Event::MouseAxisXMove),
|
||||||
mousey = myEvent.get(Event::MouseAxisYValue);
|
mousey = myEvent.get(Event::MouseAxisYMove);
|
||||||
if(mousex || mousey)
|
if(mousex || mousey)
|
||||||
{
|
{
|
||||||
if((!(abs(mousey) > abs(mousex) << 1)) && (abs(mousex) >= MJ_Threshold))
|
if((!(abs(mousey) > abs(mousex) << 1)) && (abs(mousex) >= MJ_Threshold))
|
||||||
|
|
|
@ -81,8 +81,8 @@ void Joystick::update()
|
||||||
{
|
{
|
||||||
// The following code was taken from z26
|
// The following code was taken from z26
|
||||||
#define MJ_Threshold 2
|
#define MJ_Threshold 2
|
||||||
int mousex = myEvent.get(Event::MouseAxisXValue),
|
int mousex = myEvent.get(Event::MouseAxisXMove),
|
||||||
mousey = myEvent.get(Event::MouseAxisYValue);
|
mousey = myEvent.get(Event::MouseAxisYMove);
|
||||||
if(mousex || mousey)
|
if(mousex || mousey)
|
||||||
{
|
{
|
||||||
if((!(abs(mousey) > abs(mousex) << 1)) && (abs(mousex) >= MJ_Threshold))
|
if((!(abs(mousey) > abs(mousex) << 1)) && (abs(mousex) >= MJ_Threshold))
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SSSS tt lll lll
|
||||||
|
// SS SS tt ll ll
|
||||||
|
// SS tttttt eeee ll ll aaaa
|
||||||
|
// SSSS tt ee ee ll ll aa
|
||||||
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||||
|
// SS SS tt ee ll ll aa aa
|
||||||
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
|
//
|
||||||
|
// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
|
||||||
|
// and the Stella Team
|
||||||
|
//
|
||||||
|
// See the file "License.txt" for information on usage and redistribution of
|
||||||
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
#include "Event.hxx"
|
||||||
|
#include "TIA.hxx"
|
||||||
|
#include "FrameBuffer.hxx"
|
||||||
|
|
||||||
|
#include "Lightgun.hxx"
|
||||||
|
|
||||||
|
// | | Left port | Right port |
|
||||||
|
// | Fire button | SWCHA bit 4 | SWCHA bit 0 | DP:1
|
||||||
|
// | Detect light | INPT4 bit 7 | INPT5 bit 7 | DP:6
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Lightgun::Lightgun(Jack jack, const Event& event, const System& system, const FrameBuffer& frameBuffer)
|
||||||
|
: Controller(jack, event, system, Controller::Type::Lightgun),
|
||||||
|
myFrameBuffer(frameBuffer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool Lightgun::read(DigitalPin pin)
|
||||||
|
{
|
||||||
|
// We need to override the Controller::read() method, since the lightgun
|
||||||
|
// checks this multiple times per frame
|
||||||
|
// (we can't just read 60 times per second in the ::update() method)
|
||||||
|
switch (pin)
|
||||||
|
{
|
||||||
|
case DigitalPin::Six: // INPT4/5
|
||||||
|
{
|
||||||
|
const Common::Rect& rect = myFrameBuffer.imageRect();
|
||||||
|
// scale mouse coordinates into TIA coordinates
|
||||||
|
Int32 xMouse = (myEvent.get(Event::MouseAxisXValue) - rect.x())
|
||||||
|
* TIAConstants::H_PIXEL / rect.w();
|
||||||
|
Int32 yMouse = (myEvent.get(Event::MouseAxisYValue) - rect.y())
|
||||||
|
* 210 / rect.h(); // TODO: replace "magic number"
|
||||||
|
// get adjusted TIA coordinates
|
||||||
|
Int32 xTia = mySystem.tia().clocksThisLine() - TIAConstants::H_BLANK_CLOCKS + X_OFS;
|
||||||
|
Int32 yTia = mySystem.tia().scanlines() - mySystem.tia().startLine() + Y_OFS;
|
||||||
|
|
||||||
|
if (xTia < 0)
|
||||||
|
xTia += TIAConstants::H_CLOCKS;
|
||||||
|
|
||||||
|
bool enable = !((xTia - xMouse) >= 0 && (xTia - xMouse) < 15 && (yTia - yMouse) >= 0);
|
||||||
|
|
||||||
|
return enable;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return Controller::read(pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void Lightgun::update()
|
||||||
|
{
|
||||||
|
// we allow left and right mouse buttons for fire button
|
||||||
|
setPin(DigitalPin::One, myEvent.get(Event::MouseButtonLeftValue)
|
||||||
|
|| myEvent.get(Event::MouseButtonRightValue));
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SSSS tt lll lll
|
||||||
|
// SS SS tt ll ll
|
||||||
|
// SS tttttt eeee ll ll aaaa
|
||||||
|
// SSSS tt ee ee ll ll aa
|
||||||
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||||
|
// SS SS tt ee ll ll aa aa
|
||||||
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
|
//
|
||||||
|
// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
|
||||||
|
// and the Stella Team
|
||||||
|
//
|
||||||
|
// See the file "License.txt" for information on usage and redistribution of
|
||||||
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#ifndef LIGHTGUN_HXX
|
||||||
|
#define LIGHTGUN_HXX
|
||||||
|
|
||||||
|
/**
|
||||||
|
This class handles the lightgun controller
|
||||||
|
|
||||||
|
@author Thomas Jentzsch
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Lightgun : public Controller
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
Create a new pair of paddle controllers plugged into the specified jack
|
||||||
|
|
||||||
|
@param jack The jack the controller is plugged into
|
||||||
|
@param event The event object to use for events
|
||||||
|
@param system The system using this controller
|
||||||
|
*/
|
||||||
|
Lightgun(Jack jack, const Event& event, const System& system, const FrameBuffer& frameBuffer);
|
||||||
|
virtual ~Lightgun() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Controller::read;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Read the value of the specified digital pin for this controller.
|
||||||
|
|
||||||
|
@param pin The pin of the controller jack to read
|
||||||
|
@return The state of the pin
|
||||||
|
*/
|
||||||
|
bool read(DigitalPin pin) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Update the entire digital and analog pin state according to the
|
||||||
|
events currently set.
|
||||||
|
*/
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the name of this controller.
|
||||||
|
*/
|
||||||
|
string name() const override { return "Lightgun"; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const FrameBuffer& myFrameBuffer;
|
||||||
|
|
||||||
|
static constexpr Int32 X_OFS = -21;
|
||||||
|
static constexpr Int32 Y_OFS = 5;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Following constructors and assignment operators not supported
|
||||||
|
Lightgun() = delete;
|
||||||
|
Lightgun(const Lightgun&) = delete;
|
||||||
|
Lightgun(Lightgun&&) = delete;
|
||||||
|
Lightgun& operator=(const Lightgun&) = delete;
|
||||||
|
Lightgun& operator=(Lightgun&&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -40,7 +40,7 @@ void MindLink::update()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
myMindlinkPos = (myMindlinkPos & 0x3fffffff) +
|
myMindlinkPos = (myMindlinkPos & 0x3fffffff) +
|
||||||
(myEvent.get(Event::MouseAxisXValue) << 3);
|
(myEvent.get(Event::MouseAxisXMove) << 3);
|
||||||
if(myMindlinkPos < 0x2800)
|
if(myMindlinkPos < 0x2800)
|
||||||
myMindlinkPos = 0x2800;
|
myMindlinkPos = 0x2800;
|
||||||
if(myMindlinkPos >= 0x3800)
|
if(myMindlinkPos >= 0x3800)
|
||||||
|
|
|
@ -162,13 +162,13 @@ Paddles::Paddles(Jack jack, const Event& event, const System& system,
|
||||||
abs(MOUSE_SENSITIVITY);
|
abs(MOUSE_SENSITIVITY);
|
||||||
if(!swapaxis)
|
if(!swapaxis)
|
||||||
{
|
{
|
||||||
myAxisMouseMotion = Event::MouseAxisXValue;
|
myAxisMouseMotion = Event::MouseAxisXMove;
|
||||||
myAxisDigitalZero = 0;
|
myAxisDigitalZero = 0;
|
||||||
myAxisDigitalOne = 1;
|
myAxisDigitalOne = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
myAxisMouseMotion = Event::MouseAxisYValue;
|
myAxisMouseMotion = Event::MouseAxisYMove;
|
||||||
myAxisDigitalZero = 1;
|
myAxisDigitalZero = 1;
|
||||||
myAxisDigitalOne = 0;
|
myAxisDigitalOne = 0;
|
||||||
}
|
}
|
||||||
|
@ -277,7 +277,7 @@ void Paddles::update()
|
||||||
if(myMPaddleIDX > -1)
|
if(myMPaddleIDX > -1)
|
||||||
{
|
{
|
||||||
myCharge[myMPaddleIDX] = BSPF::clamp(myCharge[myMPaddleIDX] -
|
myCharge[myMPaddleIDX] = BSPF::clamp(myCharge[myMPaddleIDX] -
|
||||||
(myEvent.get(Event::MouseAxisXValue) * MOUSE_SENSITIVITY),
|
(myEvent.get(Event::MouseAxisXMove) * MOUSE_SENSITIVITY),
|
||||||
TRIGMIN, TRIGRANGE);
|
TRIGMIN, TRIGRANGE);
|
||||||
if(myEvent.get(Event::MouseButtonLeftValue))
|
if(myEvent.get(Event::MouseButtonLeftValue))
|
||||||
setPin(ourButtonPin[myMPaddleIDX], false);
|
setPin(ourButtonPin[myMPaddleIDX], false);
|
||||||
|
@ -285,7 +285,7 @@ void Paddles::update()
|
||||||
if(myMPaddleIDY > -1)
|
if(myMPaddleIDY > -1)
|
||||||
{
|
{
|
||||||
myCharge[myMPaddleIDY] = BSPF::clamp(myCharge[myMPaddleIDY] -
|
myCharge[myMPaddleIDY] = BSPF::clamp(myCharge[myMPaddleIDY] -
|
||||||
(myEvent.get(Event::MouseAxisYValue) * MOUSE_SENSITIVITY),
|
(myEvent.get(Event::MouseAxisYMove) * MOUSE_SENSITIVITY),
|
||||||
TRIGMIN, TRIGRANGE);
|
TRIGMIN, TRIGRANGE);
|
||||||
if(myEvent.get(Event::MouseButtonRightValue))
|
if(myEvent.get(Event::MouseButtonRightValue))
|
||||||
setPin(ourButtonPin[myMPaddleIDY], false);
|
setPin(ourButtonPin[myMPaddleIDY], false);
|
||||||
|
|
|
@ -79,11 +79,11 @@ void PointingDevice::update()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Update horizontal direction
|
// Update horizontal direction
|
||||||
updateDirection( myEvent.get(Event::MouseAxisXValue), myHCounterRemainder,
|
updateDirection( myEvent.get(Event::MouseAxisXMove), myHCounterRemainder,
|
||||||
myTrackBallLeft, myTrackBallLinesH, myScanCountH, myFirstScanOffsetH);
|
myTrackBallLeft, myTrackBallLinesH, myScanCountH, myFirstScanOffsetH);
|
||||||
|
|
||||||
// Update vertical direction
|
// Update vertical direction
|
||||||
updateDirection(-myEvent.get(Event::MouseAxisYValue), myVCounterRemainder,
|
updateDirection(-myEvent.get(Event::MouseAxisYMove), myVCounterRemainder,
|
||||||
myTrackBallDown, myTrackBallLinesV, myScanCountV, myFirstScanOffsetV);
|
myTrackBallDown, myTrackBallLinesV, myScanCountV, myFirstScanOffsetV);
|
||||||
|
|
||||||
// Get mouse button state
|
// Get mouse button state
|
||||||
|
|
|
@ -66,6 +66,7 @@ MODULE_OBJS := \
|
||||||
src/emucore/Joystick.o \
|
src/emucore/Joystick.o \
|
||||||
src/emucore/Keyboard.o \
|
src/emucore/Keyboard.o \
|
||||||
src/emucore/KidVid.o \
|
src/emucore/KidVid.o \
|
||||||
|
src/emucore/Lightgun.o \
|
||||||
src/emucore/MindLink.o \
|
src/emucore/MindLink.o \
|
||||||
src/emucore/M6502.o \
|
src/emucore/M6502.o \
|
||||||
src/emucore/M6532.o \
|
src/emucore/M6532.o \
|
||||||
|
|
|
@ -220,8 +220,9 @@ GameInfoDialog::GameInfoDialog(
|
||||||
VarList::push_back(ctrls, "SaveKey", "SAVEKEY");
|
VarList::push_back(ctrls, "SaveKey", "SAVEKEY");
|
||||||
VarList::push_back(ctrls, "Sega Genesis", "GENESIS");
|
VarList::push_back(ctrls, "Sega Genesis", "GENESIS");
|
||||||
VarList::push_back(ctrls, "KidVid", "KIDVID");
|
VarList::push_back(ctrls, "KidVid", "KIDVID");
|
||||||
|
VarList::push_back(ctrls, "Lightgun", "LIGHTGUN");
|
||||||
VarList::push_back(ctrls, "MindLink", "MINDLINK");
|
VarList::push_back(ctrls, "MindLink", "MINDLINK");
|
||||||
|
|
||||||
ypos = VBORDER;
|
ypos = VBORDER;
|
||||||
pwidth = font.getStringWidth("Paddles_IAxis");
|
pwidth = font.getStringWidth("Paddles_IAxis");
|
||||||
myLeftPortLabel = new StaticTextWidget(myTab, font, HBORDER, ypos+1, "Left port ");
|
myLeftPortLabel = new StaticTextWidget(myTab, font, HBORDER, ypos+1, "Left port ");
|
||||||
|
|
|
@ -487,6 +487,7 @@
|
||||||
<ClCompile Include="..\emucore\EmulationTiming.cxx" />
|
<ClCompile Include="..\emucore\EmulationTiming.cxx" />
|
||||||
<ClCompile Include="..\emucore\EmulationWorker.cxx" />
|
<ClCompile Include="..\emucore\EmulationWorker.cxx" />
|
||||||
<ClCompile Include="..\emucore\FBSurface.cxx" />
|
<ClCompile Include="..\emucore\FBSurface.cxx" />
|
||||||
|
<ClCompile Include="..\emucore\Lightgun.cxx" />
|
||||||
<ClCompile Include="..\emucore\MindLink.cxx" />
|
<ClCompile Include="..\emucore\MindLink.cxx" />
|
||||||
<ClCompile Include="..\emucore\PointingDevice.cxx" />
|
<ClCompile Include="..\emucore\PointingDevice.cxx" />
|
||||||
<ClCompile Include="..\emucore\ProfilingRunner.cxx" />
|
<ClCompile Include="..\emucore\ProfilingRunner.cxx" />
|
||||||
|
@ -1210,6 +1211,7 @@
|
||||||
<ClInclude Include="..\emucore\exception\FatalEmulationError.hxx" />
|
<ClInclude Include="..\emucore\exception\FatalEmulationError.hxx" />
|
||||||
<ClInclude Include="..\emucore\FBSurface.hxx" />
|
<ClInclude Include="..\emucore\FBSurface.hxx" />
|
||||||
<ClInclude Include="..\emucore\FrameBufferConstants.hxx" />
|
<ClInclude Include="..\emucore\FrameBufferConstants.hxx" />
|
||||||
|
<ClInclude Include="..\emucore\Lightgun.hxx" />
|
||||||
<ClInclude Include="..\emucore\MindLink.hxx" />
|
<ClInclude Include="..\emucore\MindLink.hxx" />
|
||||||
<ClInclude Include="..\emucore\PointingDevice.hxx" />
|
<ClInclude Include="..\emucore\PointingDevice.hxx" />
|
||||||
<ClInclude Include="..\emucore\ProfilingRunner.hxx" />
|
<ClInclude Include="..\emucore\ProfilingRunner.hxx" />
|
||||||
|
|
|
@ -996,6 +996,9 @@
|
||||||
<ClCompile Include="..\common\PhosphorHandler.cxx">
|
<ClCompile Include="..\common\PhosphorHandler.cxx">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\emucore\Lightgun.cxx">
|
||||||
|
<Filter>Source Files\emucore</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\common\bspf.hxx">
|
<ClInclude Include="..\common\bspf.hxx">
|
||||||
|
@ -2036,6 +2039,9 @@
|
||||||
<ClInclude Include="..\common\PhosphorHandler.hxx">
|
<ClInclude Include="..\common\PhosphorHandler.hxx">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\emucore\Lightgun.hxx">
|
||||||
|
<Filter>Header Files\emucore</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="stella.ico">
|
<None Include="stella.ico">
|
||||||
|
|
Loading…
Reference in New Issue