/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "lastexpress/lastexpress.h" #include "lastexpress/game/events.h" namespace LastExpress { MessageManager::MessageManager(LastExpressEngine *engine) { _engine = engine; for (int i = 0; i < 16; i++) { _eventHandles[i] = nullptr; } for (int i = 0; i < 40; i++) { _messageHandles[i] = nullptr; } _autoMessages = nullptr; } MessageManager::~MessageManager() { free(_autoMessages); _autoMessages = nullptr; } void MessageManager::setMessageHandle(int handleChannel, void (LogicManager::*handle)(Message *)) { _messageHandles[handleChannel] = handle; } void (LogicManager::*MessageManager::getMessageHandle(int handleChannel))(Message *) { return _messageHandles[handleChannel]; } void MessageManager::setEventHandle(int handleChannel, void (LastExpressEngine::*handle)(Event *)) { _eventHandles[handleChannel] = handle; } void (LastExpressEngine::*MessageManager::getEventHandle(int handleChannel))(Event *) { return _eventHandles[handleChannel]; } void MessageManager::addEvent(int channel, int x, int y, int flags) { Common::StackLock lock(*_engine->_soundMutex); if (_numEventsInQueue >= 127) return; if (channel == kEventChannelTimer) { _engine->setEventTickInternal(true); } else if (channel == kEventChannelMouse) { if ((flags & kMouseFlagLeftButton) != 0) { // Originally _engine->mouseSetLeftClicked(true); was called from here, // but it's been moved under the "if" because this lead to fake double // clicks when registering mouse movement events (which are re-paired // with RMOUSE and LMOUSE flags when sent to the engine via this function). if (!_systemEventLeftMouseDown) { _engine->mouseSetLeftClicked(true); flags |= kMouseFlagLeftDown; _systemEventLeftMouseDown = true; if (_doubleClickMaxFrames + _latestTickLeftMousePressed > _engine->getSoundFrameCounter()) flags |= (kMouseFlagDoubleClick | kMouseFlagLeftDown); _latestTickLeftMousePressed = _engine->getSoundFrameCounter(); } } else { if (_systemEventLeftMouseDown) flags |= kMouseFlagLeftUp; _systemEventLeftMouseDown = false; } if ((flags & kMouseFlagRightButton) != 0) { // Originally _engine->mouseSetRightClicked(true); was called from here, // but it's been moved under the "if" because this lead to fake double // clicks when registering mouse movement events (which are re-paired // with RMOUSE and LMOUSE flags when sent to the engine via this function). if (!_systemEventRightMouseDown) { _engine->mouseSetRightClicked(true); flags |= kMouseFlagRightDown; _systemEventRightMouseDown = true; } } else { if (_systemEventRightMouseDown) flags |= kMouseFlagRightUp; _systemEventRightMouseDown = false; } // Originally "if (!flags)"; this tames slowdowns when dragging the mouse // with one of the buttons pressed. Hopefully it doesn't break anything... if (!flags || ((flags & kMouseFlagLeftButton) != 0 || (flags & kMouseFlagRightButton) != 0)) { if (_lastEventIndex != 128) _events[_lastEventIndex].channel = 0; _lastEventIndex = _curEventIndex; } } _events[_curEventIndex].channel = channel; _events[_curEventIndex].x = x; _events[_curEventIndex].y = y; _events[_curEventIndex].flags = flags; _curEventIndex++; _numEventsInQueue++; _curEventIndex &= 0x7F; } Event *MessageManager::getEvent() { Event *result = nullptr; Common::StackLock lock(*_engine->_soundMutex); if (_nextEventIndex == _curEventIndex) { // No events in queue... return nullptr; } // Wrap around the circular buffer... if (_lastEventIndex == _nextEventIndex) { _lastEventIndex = 128; } result = &_events[_nextEventIndex]; // Update event queue indexes... _numEventsInQueue--; _nextEventIndex++; _nextEventIndex &= 0x7F; // Wrap around the circular buffer... return result; } void MessageManager::addMessage(int receiver, int actionId, int sender, ConsCallParam param) { if (_numMsgsInQueue < 127) { Message *newMsg = &_messages[_curMsgIndex]; newMsg->receiver = receiver; newMsg->action = actionId; newMsg->sender = sender; newMsg->param = param; _curMsgIndex++; _numMsgsInQueue++; _curMsgIndex &= 0x7F; } } Message *MessageManager::getMessage() { if (_nextMsgIndex == _curMsgIndex) return nullptr; Message *msg = &_messages[_nextMsgIndex]; _nextMsgIndex = (_nextMsgIndex + 1) & 0x7F; _numMsgsInQueue--; return msg; } bool MessageManager::process() { Event *event; event = getEvent(); if (!event || event->channel >= 16) return false; if (_eventHandles[event->channel]) (_engine->*_eventHandles[event->channel])(event); return true; } void MessageManager::flush() { Message *message; do { message = getMessage(); if (!message) break; if (!_engine->getLogicManager()->doAutoMessage(message)) { if (_messageHandles[message->receiver]) (_engine->getLogicManager()->*_messageHandles[message->receiver])(message); } } while (message && _engine->_navigationEngineIsRunning); } void MessageManager::flushTime() { for (int i = 1; i < ARRAYSIZE(_messageHandles) && _engine->_navigationEngineIsRunning; i++) { if (_messageHandles[i]) (_engine->getLogicManager()->*_messageHandles[i])(&_emptyMessage); } if (_engine->_navigationEngineIsRunning) flush(); } void MessageManager::forceMessage(Message *msg) { if (_messageHandles[msg->receiver]) (_engine->getLogicManager()->*_messageHandles[msg->receiver])(msg); } void MessageManager::clearMessageQueue() { Common::StackLock lock(*_engine->_soundMutex); _numMsgsInQueue = 0; _nextMsgIndex = 0; _curMsgIndex = 0; } void MessageManager::clearEventQueue() { Common::StackLock lock(*_engine->_soundMutex); _numEventsInQueue = 0; _nextEventIndex = 0; _curEventIndex = 0; _engine->setEventTickInternal(false); _engine->mouseSetLeftClicked(false); _engine->mouseSetRightClicked(false); } void MessageManager::clearClickEvents() { Common::StackLock lock(*_engine->_soundMutex); if (_numEventsInQueue) { for (int i = _nextEventIndex; _curEventIndex != i; i = ((i + 1) & 0x7F)) { if (_events[i].channel == 1 && _events[i].flags) _events[i].flags = 0; } } } void MessageManager::saveMessages(CVCRFile *file) { byte *msgData = (byte *)malloc(16 * 128); assert(msgData); for (int i = 0; i < 128; i++) { WRITE_LE_INT32(msgData + (i * 16) + 0, _autoMessages[i].receiver); WRITE_LE_INT32(msgData + (i * 16) + 4, _autoMessages[i].action); WRITE_LE_INT32(msgData + (i * 16) + 8, _autoMessages[i].sender); WRITE_LE_INT32(msgData + (i * 16) + 12, _autoMessages[i].param.intParam); } file->writeRLE(msgData, 16, 128); file->writeRLE(&_numMsgsInQueue, 4, 1); free(msgData); msgData = nullptr; if (_numMsgsInQueue) { int nextIdx = _nextMsgIndex; while (_curMsgIndex != nextIdx) { Message *msgToSave = &_messages[nextIdx]; msgData = (byte *)malloc(16); assert(msgData); WRITE_LE_INT32(msgData + 0, msgToSave->receiver); WRITE_LE_INT32(msgData + 4, msgToSave->action); WRITE_LE_INT32(msgData + 8, msgToSave->sender); WRITE_LE_INT32(msgData + 12, msgToSave->param.intParam); nextIdx = (nextIdx + 1) & 0x7F; file->writeRLE(msgData, 16, 1); free(msgData); msgData = nullptr; } } } void MessageManager::loadMessages(CVCRFile *file) { byte *msgData = (byte *)malloc(16 * 128); assert(msgData); file->readRLE(msgData, 16, 128); for (int i = 0; i < 128; i++) { _autoMessages[i].receiver = READ_LE_INT32(msgData + (i * 16) + 0); _autoMessages[i].action = READ_LE_INT32(msgData + (i * 16) + 4); _autoMessages[i].sender = READ_LE_INT32(msgData + (i * 16) + 8); _autoMessages[i].param.intParam = READ_LE_INT32(msgData + (i * 16) + 12); _autoMessages[i].param.stringParam = nullptr; } free(msgData); msgData = nullptr; int numMsgsInQueue; file->readRLE(&numMsgsInQueue, 4, 1); Message loadedMsg; for (int i = 0; i < numMsgsInQueue; i++) { msgData = (byte *)malloc(16); assert(msgData); file->readRLE(msgData, 16, 1); loadedMsg.receiver = READ_LE_INT32(msgData + 0); loadedMsg.action = READ_LE_INT32(msgData + 4); loadedMsg.sender = READ_LE_INT32(msgData + 8); loadedMsg.param.intParam = READ_LE_INT32(msgData + 12); loadedMsg.param.stringParam = nullptr; addMessage(loadedMsg.receiver, loadedMsg.action, loadedMsg.sender, loadedMsg.param); free(msgData); msgData = nullptr; } } } // End of namespace LastExpress