XEEN: Cache mouse clicks as well as keyboard in EventsManager

This allows the well open door/gate, shoot at enemies, then close
to work with the mouse as well as the keyboard. The pending event
queue has also been limited to 5 pending events. Trust me, you
don't want to spent time spamming Shoot at a high level monster
that can't reach you, only for when it's killed to have to wait
several minutes whilst your party keeps shooting.
This commit is contained in:
Paul Gilbert 2018-04-07 11:02:09 -04:00
parent 28a3faa178
commit 6a32516490
7 changed files with 132 additions and 83 deletions

View File

@ -66,51 +66,55 @@ bool ButtonContainer::checkEvents(XeenEngine *vm) {
EventsManager &events = *vm->_events;
Party &party = *vm->_party;
Windows &windows = *_vm->_windows;
PendingEvent event;
const Common::Rect WAIT_BOUNDS(8, 8, 224, 140);
_buttonValue = 0;
if (events._leftButton) {
Common::Point pt = events._mousePos;
if (events.getEvent(event)) {
if (event._leftButton) {
Common::Point pt = events._mousePos;
// Check for party member glyphs being clicked
Common::Rect r(0, 0, 32, 32);
for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
r.moveTo(Res.CHAR_FACES_X[idx], 150);
if (r.contains(pt)) {
_buttonValue = Common::KEYCODE_F1 + idx;
break;
// Check for party member glyphs being clicked
Common::Rect r(0, 0, 32, 32);
for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
r.moveTo(Res.CHAR_FACES_X[idx], 150);
if (r.contains(pt)) {
_buttonValue = Common::KEYCODE_F1 + idx;
break;
}
}
}
// Check whether any button is selected
for (uint i = 0; i < _buttons.size(); ++i) {
if (_buttons[i]._bounds.contains(pt)) {
events.debounceMouse();
// Check whether any button is selected
for (uint i = 0; i < _buttons.size(); ++i) {
if (_buttons[i]._bounds.contains(pt)) {
events.debounceMouse();
_buttonValue = _buttons[i]._value;
break;
_buttonValue = _buttons[i]._value;
break;
}
}
if (!_buttonValue && WAIT_BOUNDS.contains(pt)) {
_buttonValue = Common::KEYCODE_SPACE;
return true;
}
} else if (event.isKeyboard()) {
const Common::KeyCode &keycode = event._keyState.keycode;
if (keycode == Common::KEYCODE_KP8)
_buttonValue = Common::KEYCODE_UP;
else if (keycode == Common::KEYCODE_KP2)
_buttonValue = Common::KEYCODE_DOWN;
else if (keycode == Common::KEYCODE_KP_ENTER)
_buttonValue = Common::KEYCODE_RETURN;
else if (keycode != Common::KEYCODE_LCTRL && keycode != Common::KEYCODE_RCTRL
&& keycode != Common::KEYCODE_LALT && keycode != Common::KEYCODE_RALT)
_buttonValue = keycode;
if (_buttonValue)
_buttonValue |= (event._keyState.flags & ~Common::KBD_STICKY) << 16;
}
if (!_buttonValue && Common::Rect(8, 8, 224, 135).contains(pt)) {
_buttonValue = 1;
return true;
}
} else if (events.isKeyPending()) {
Common::KeyState keyState;
events.getKey(keyState);
if (keyState.keycode == Common::KEYCODE_KP8)
_buttonValue = Common::KEYCODE_UP;
else if (keyState.keycode == Common::KEYCODE_KP2)
_buttonValue = Common::KEYCODE_DOWN;
else if (keyState.keycode == Common::KEYCODE_KP_ENTER)
_buttonValue = Common::KEYCODE_RETURN;
else if (keyState.keycode != Common::KEYCODE_LCTRL && keyState.keycode != Common::KEYCODE_RCTRL
&& keyState.keycode != Common::KEYCODE_LALT && keyState.keycode != Common::KEYCODE_RALT)
_buttonValue = keyState.keycode;
if (_buttonValue)
_buttonValue |= (keyState.flags & ~Common::KBD_STICKY) << 16;
}
if (_buttonValue) {

View File

@ -86,7 +86,7 @@ Common::KeyState Input::waitForKey(const Common::String &msg) {
bool flag = !_vm->_startupWindowActive && !windows[25]._enabled
&& _vm->_mode != MODE_FF && _vm->_mode != MODE_17;
Common::KeyState ks;
PendingEvent pe;
while (!_vm->shouldExit()) {
events.updateGameCounter();
@ -100,11 +100,8 @@ Common::KeyState Input::waitForKey(const Common::String &msg) {
windows[3].update();
events.wait(1);
if (events.isKeyPending()) {
events.getKey(ks);
if (events.getEvent(pe) && pe.isKeyboard())
break;
}
}
_window->writeString("");
@ -113,7 +110,7 @@ Common::KeyState Input::waitForKey(const Common::String &msg) {
intf._tillMove = oldTillMove;
intf._upDoorText = oldUpDoorText;
return ks;
return pe._keyState;
}
void Input::animateCursor() {

View File

@ -33,7 +33,7 @@ namespace Xeen {
EventsManager::EventsManager(XeenEngine *vm) : _vm(vm), _playTime(0),
_frameCounter(0), _priorFrameCounterTime(0), _gameCounter(0),
_leftButton(false), _rightButton(false), _sprites("mouse.icn") {
_mousePressed(false), _sprites("mouse.icn") {
Common::fill(&_gameCounters[0], &_gameCounters[6], 0);
}
@ -80,24 +80,23 @@ void EventsManager::pollEvents() {
_vm->_debugger->attach();
_vm->_debugger->onFrame();
} else {
_keys.push(event.kbd);
addEvent(event.kbd);
}
break;
case Common::EVENT_MOUSEMOVE:
_mousePos = event.mouse;
break;
case Common::EVENT_LBUTTONDOWN:
_leftButton = true;
return;
case Common::EVENT_LBUTTONUP:
_leftButton = false;
_mousePressed = true;
addEvent(true, false);
return;
case Common::EVENT_RBUTTONDOWN:
_rightButton = true;
return;
_mousePressed = true;
addEvent(false, true);
case Common::EVENT_LBUTTONUP:
case Common::EVENT_RBUTTONUP:
_rightButton = false;
break;
_mousePressed = false;
return;
default:
break;
}
@ -110,31 +109,38 @@ void EventsManager::pollEventsAndWait() {
}
void EventsManager::clearEvents() {
_keys.clear();
_leftButton = _rightButton = false;
_pendingEvents.clear();
_mousePressed = false;
}
void EventsManager::debounceMouse() {
while (_leftButton && !_vm->shouldExit()) {
while (_mousePressed && !_vm->shouldExit()) {
pollEventsAndWait();
}
}
bool EventsManager::getKey(Common::KeyState &key) {
if (_keys.empty()) {
void EventsManager::addEvent(const Common::KeyState &keyState) {
if (_pendingEvents.size() < MAX_PENDING_EVENTS)
_pendingEvents.push(PendingEvent(keyState));
}
void EventsManager::addEvent(bool leftButton, bool rightButton) {
if (_pendingEvents.size() < MAX_PENDING_EVENTS)
_pendingEvents.push(PendingEvent(leftButton, rightButton));
}
bool EventsManager::getEvent(PendingEvent &pe) {
if (_pendingEvents.empty()) {
return false;
} else {
key = _keys.pop();
pe = _pendingEvents.pop();
return true;
}
}
bool EventsManager::isKeyPending() const {
return !_keys.empty();
}
bool EventsManager::isKeyMousePressed() {
bool result = _leftButton || _rightButton || isKeyPending();
bool result = isEventPending();
debounceMouse();
clearEvents();
@ -144,7 +150,7 @@ bool EventsManager::isKeyMousePressed() {
bool EventsManager::wait(uint numFrames, bool interruptable) {
while (!_vm->shouldExit() && timeElapsed() < numFrames) {
pollEventsAndWait();
if (interruptable && (_leftButton || _rightButton || isKeyPending()))
if (interruptable && isEventPending())
return true;
}

View File

@ -32,9 +32,30 @@ namespace Xeen {
#define GAME_FRAME_RATE (1000 / 50)
#define GAME_FRAME_TIME 50
#define MAX_PENDING_EVENTS 5
class XeenEngine;
struct PendingEvent {
Common::KeyState _keyState;
bool _leftButton;
bool _rightButton;
PendingEvent() : _leftButton(false), _rightButton(false) {}
PendingEvent(const Common::KeyState &keyState) : _keyState(keyState), _leftButton(false), _rightButton(false) {}
PendingEvent(bool leftButton, bool rightButton) : _leftButton(leftButton), _rightButton(rightButton) {}
/**
* Returns true if a keyboard event is pending
*/
bool isKeyboard() const { return _keyState.keycode != Common::KEYCODE_INVALID; }
/**
* Returns ture if a mouse button event is pending
*/
bool isMouse() const { return _leftButton || _rightButton; }
};
class EventsManager {
private:
XeenEngine *_vm;
@ -43,19 +64,18 @@ private:
uint32 _gameCounter;
uint32 _gameCounters[6];
uint32 _playTime;
Common::Queue<Common::KeyState> _keys;
Common::Queue<PendingEvent> _pendingEvents;
SpriteResource _sprites;
bool _mousePressed;
/**
* Handles moving to the next game frame
*/
void nextFrame();
public:
bool _leftButton, _rightButton;
Common::Point _mousePos;
public:
EventsManager(XeenEngine *vm);
~EventsManager();
/*
@ -78,17 +98,45 @@ public:
*/
bool isCursorVisible();
/**
* Polls the ScummVM backend for any pending events
*/
void pollEvents();
/**
* Polls for events, and wait a slight delay. This ensures the game doesn't use up 100% of the CPU
*/
void pollEventsAndWait();
/**
* Clears all pending events
*/
void clearEvents();
/**
* Waits for a mouse press to be released
*/
void debounceMouse();
bool getKey(Common::KeyState &key);
/**
* Adds a keyboard event to the queue
*/
void addEvent(const Common::KeyState &keyState);
bool isKeyPending() const;
/**
* Adds a mouse button event to the queue
*/
void addEvent(bool leftButton, bool rightButton);
/**
* Returns the next pending key/mouse press, if any
*/
bool getEvent(PendingEvent &pe);
/**
* Returns true if a key or mouse event is pending
*/
bool isEventPending() const { return !_pendingEvents.empty(); }
/**
* Returns true if a key or mouse press is pending

View File

@ -262,7 +262,6 @@ void Interface::perform() {
Party &party = *_vm->_party;
Scripts &scripts = *_vm->_scripts;
Sound &sound = *_vm->_sound;
const Common::Rect WAIT_BOUNDS(8, 8, 224, 140);
do {
// Draw the next frame
@ -276,10 +275,7 @@ void Interface::perform() {
if (g_vm->shouldExit() || g_vm->isLoadPending() || party._dead)
return;
if (events._leftButton && WAIT_BOUNDS.contains(events._mousePos))
_buttonValue = Common::KEYCODE_SPACE;
else
checkEvents(g_vm);
checkEvents(g_vm);
} while (!_buttonValue && events.timeElapsed() < 1);
} while (!_buttonValue);

View File

@ -214,7 +214,7 @@ int Scripts::checkEvents() {
MazeObject &selectedObj = map._mobData._objects[intf._objNumber];
if (selectedObj._spriteId == (ccNum ? 15 : 16)) {
for (uint idx = 0; idx < MIN((int)map._mobData._objects.size(), 16); ++idx) {
for (int idx = 0; idx < MIN((int)map._mobData._objects.size(), 16); ++idx) {
MazeObject &obj = map._mobData._objects[idx];
if (obj._spriteId == (ccNum ? 62 : 57)) {
selectedObj._id = idx;
@ -223,7 +223,7 @@ int Scripts::checkEvents() {
}
}
} else if (selectedObj._spriteId == 73) {
for (uint idx = 0; idx < MIN((int)map._mobData._objects.size(), 16); ++idx) {
for (int idx = 0; idx < MIN((int)map._mobData._objects.size(), 16); ++idx) {
MazeObject &obj = map._mobData._objects[idx];
if (obj._spriteId == 119) {
selectedObj._id = idx;

View File

@ -110,11 +110,9 @@ void MainMenuContainer::execute() {
} else {
// No active dialog. If Escape pressed, exit game entirely. Otherwise,
// open up the main menu dialog
if (events.isKeyPending()) {
Common::KeyState key;
if (events.getKey(key) && key.keycode == Common::KEYCODE_ESCAPE)
g_vm->_gameMode = GMODE_QUIT;
}
PendingEvent pe;
if (events.getEvent(pe) && pe._keyState.keycode == Common::KEYCODE_ESCAPE)
g_vm->_gameMode = GMODE_QUIT;
events.clearEvents();
showMenuDialog();