Skip to content

Commit

Permalink
XEEN: Cache mouse clicks as well as keyboard in EventsManager
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dreammaster committed Apr 7, 2018
1 parent 28a3faa commit 6a32516
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 83 deletions.
78 changes: 41 additions & 37 deletions engines/xeen/dialogs/dialogs.cpp
Expand Up @@ -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;

// 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;
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 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;
}
}

if (!_buttonValue && Common::Rect(8, 8, 224, 135).contains(pt)) {
_buttonValue = 1;
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;
}
} 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) {
Expand Down
9 changes: 3 additions & 6 deletions engines/xeen/dialogs/dialogs_input.cpp
Expand Up @@ -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();

Expand All @@ -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("");
Expand All @@ -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() {
Expand Down
52 changes: 29 additions & 23 deletions engines/xeen/events.cpp
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
}
Expand All @@ -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();

Expand All @@ -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;
}

Expand Down
58 changes: 53 additions & 5 deletions engines/xeen/events.h
Expand Up @@ -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;
Expand All @@ -43,19 +64,18 @@ class EventsManager {
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();

/*
Expand All @@ -78,17 +98,45 @@ class EventsManager {
*/
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);

/**
* Adds a mouse button event to the queue
*/
void addEvent(bool leftButton, bool rightButton);

bool isKeyPending() const;
/**
* 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
Expand Down
6 changes: 1 addition & 5 deletions engines/xeen/interface.cpp
Expand Up @@ -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
Expand All @@ -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);

Expand Down
4 changes: 2 additions & 2 deletions engines/xeen/scripts.cpp
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
8 changes: 3 additions & 5 deletions engines/xeen/worldofxeen/worldofxeen_menu.cpp
Expand Up @@ -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();
Expand Down

0 comments on commit 6a32516

Please sign in to comment.