diff options
Diffstat (limited to 'engines')
-rw-r--r-- | engines/mohawk/graphics.cpp | 75 | ||||
-rw-r--r-- | engines/mohawk/graphics.h | 11 | ||||
-rw-r--r-- | engines/mohawk/riven_external.cpp | 55 | ||||
-rw-r--r-- | engines/mohawk/riven_external.h | 4 | ||||
-rw-r--r-- | engines/sci/engine/kevent.cpp | 26 | ||||
-rw-r--r-- | engines/sci/event.cpp | 335 | ||||
-rw-r--r-- | engines/sci/event.h | 12 | ||||
-rw-r--r-- | engines/sci/graphics/menu.cpp | 22 | ||||
-rw-r--r-- | engines/sci/graphics/view.cpp | 212 |
9 files changed, 433 insertions, 319 deletions
diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp index 897ca79d0e..19882c4f1c 100644 --- a/engines/mohawk/graphics.cpp +++ b/engines/mohawk/graphics.cpp @@ -246,6 +246,13 @@ void GraphicsManager::copyAnimImageSectionToScreen(MohawkSurface *image, Common: getVM()->_system->unlockScreen(); } +void GraphicsManager::addImageToCache(uint16 id, MohawkSurface *surface) { + if (_cache.contains(id)) + error("Image %d already in cache", id); + + _cache[id] = surface; +} + MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) { _bmpDecoder = new MystBitmap(); @@ -630,6 +637,9 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm _scheduledTransition = -1; // no transition _dirtyScreen = false; _inventoryDrawn = false; + + _creditsImage = 302; + _creditsPos = 0; } RivenGraphics::~RivenGraphics() { @@ -840,6 +850,17 @@ void RivenGraphics::runScheduledTransition() { _scheduledTransition = -1; // Clear scheduled transition } +void RivenGraphics::clearMainScreen() { + _mainScreen->fillRect(Common::Rect(0, 0, 608, 392), _pixelFormat.RGBToColor(0, 0, 0)); +} + +void RivenGraphics::fadeToBlack() { + // Self-explanatory + scheduleTransition(16); + clearMainScreen(); + runScheduledTransition(); +} + void RivenGraphics::showInventory() { // Don't redraw the inventory if (_inventoryDrawn) @@ -955,6 +976,60 @@ void RivenGraphics::drawExtrasImage(uint16 id, Common::Rect dstRect) { _dirtyScreen = true; } +void RivenGraphics::beginCredits() { + // Clear the old cache + clearCache(); + + // Now cache all the credits images + for (uint16 i = 302; i <= 320; i++) { + MohawkSurface *surface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, i)); + surface->convertToTrueColor(); + addImageToCache(i, surface); + } + + // And clear our screen too + clearMainScreen(); +} + +void RivenGraphics::updateCredits() { + if ((_creditsImage == 303 || _creditsImage == 304) && _creditsPos == 0) + fadeToBlack(); + + if (_creditsImage < 304) { + // For the first two credit images, they are faded from black to the image and then out again + scheduleTransition(16); + + Graphics::Surface *frame = findImage(_creditsImage++)->getSurface(); + + for (int y = 0; y < frame->h; y++) + memcpy(_mainScreen->getBasePtr(124, y), frame->getBasePtr(0, y), frame->pitch); + + runScheduledTransition(); + } else { + // Otheriwse, we're scrolling + // Move the screen up one row + memmove(_mainScreen->pixels, _mainScreen->getBasePtr(0, 1), _mainScreen->pitch * (_mainScreen->h - 1)); + + // Only update as long as we're not before the last frame + // Otherwise, we're just moving up a row (which we already did) + if (_creditsImage <= 320) { + // Copy the next row to the bottom of the screen + Graphics::Surface *frame = findImage(_creditsImage)->getSurface(); + memcpy(_mainScreen->getBasePtr(124, _mainScreen->h - 1), frame->getBasePtr(0, _creditsPos), frame->pitch); + _creditsPos++; + + if (_creditsPos == _mainScreen->h) { + _creditsImage++; + _creditsPos = 0; + } + } + + // Now flush the new screen + _vm->_system->copyRectToScreen((byte *)_mainScreen->pixels, _mainScreen->pitch, 0, 0, _mainScreen->w, _mainScreen->h); + _vm->_system->updateScreen(); + } +} + LBGraphics::LBGraphics(MohawkEngine_LivingBooks *vm, uint16 width, uint16 height) : GraphicsManager(), _vm(vm) { _bmpDecoder = _vm->isPreMohawk() ? new OldMohawkBitmap() : new MohawkBitmap(); diff --git a/engines/mohawk/graphics.h b/engines/mohawk/graphics.h index 89189d442a..fbac2f2ea1 100644 --- a/engines/mohawk/graphics.h +++ b/engines/mohawk/graphics.h @@ -110,6 +110,7 @@ protected: virtual Common::Array<MohawkSurface *> decodeImages(uint16 id); virtual MohawkEngine *getVM() = 0; + void addImageToCache(uint16 id, MohawkSurface *surface); private: // An image cache that stores images until clearCache() is called @@ -195,11 +196,17 @@ public: // Transitions void scheduleTransition(uint16 id, Common::Rect rect = Common::Rect(0, 0, 608, 392)); void runScheduledTransition(); + void fadeToBlack(); // Inventory void showInventory(); void hideInventory(); + // Credits + void beginCredits(); + void updateCredits(); + uint getCurCreditsImage() { return _creditsImage; } + protected: MohawkSurface *decodeImage(uint16 id); MohawkEngine *getVM() { return (MohawkEngine *)_vm; } @@ -224,6 +231,10 @@ private: Graphics::Surface *_mainScreen; bool _dirtyScreen; Graphics::PixelFormat _pixelFormat; + void clearMainScreen(); + + // Credits + uint _creditsImage, _creditsPos; }; class LBGraphics : public GraphicsManager { diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp index e6ea25c9a6..5424a07a3c 100644 --- a/engines/mohawk/riven_external.cpp +++ b/engines/mohawk/riven_external.cpp @@ -216,20 +216,37 @@ void RivenExternal::runDemoBoundaryDialog() { dialog.runModal(); } -void RivenExternal::runEndGame(uint16 video) { +void RivenExternal::runEndGame(uint16 video, uint32 delay) { _vm->_sound->stopAllSLST(); _vm->_video->playMovieRiven(video); - runCredits(video); + runCredits(video, delay); } -void RivenExternal::runCredits(uint16 video) { - // TODO: Play until the last frame and then run the credits +void RivenExternal::runCredits(uint16 video, uint32 delay) { + // Initialize our credits state + _vm->_cursor->hideCursor(); + _vm->_gfx->beginCredits(); + uint nextCreditsFrameStart = 0; VideoHandle videoHandle = _vm->_video->findVideoHandleRiven(video); - while (!_vm->_video->endOfVideo(videoHandle) && !_vm->shouldQuit()) { - if (_vm->_video->updateMovies()) - _vm->_system->updateScreen(); + while (!_vm->shouldQuit() && _vm->_gfx->getCurCreditsImage() <= 320) { + if (_vm->_video->getCurFrame(videoHandle) >= (int32)_vm->_video->getFrameCount(videoHandle) - 1) { + if (nextCreditsFrameStart == 0) { + // Set us up to start after delay ms + nextCreditsFrameStart = _vm->_system->getMillis() + delay; + } else if (_vm->_system->getMillis() >= nextCreditsFrameStart) { + // the first two frames stay on for 5 seconds + // the rest of the scroll updates happen at 30Hz + if (_vm->_gfx->getCurCreditsImage() < 304) + nextCreditsFrameStart = _vm->_system->getMillis() + 5000; + else + nextCreditsFrameStart = _vm->_system->getMillis() + 1000 / 30; + + _vm->_gfx->updateCredits(); + } + } else if (_vm->_video->updateMovies()) + _vm->_system->updateScreen(); Common::Event event; while (_vm->_system->getEventManager()->pollEvent(event)) @@ -1838,11 +1855,11 @@ void RivenExternal::xorollcredittime(uint16 argc, uint16 *argv) { uint32 *gehnState = _vm->getVar("agehn"); if (*gehnState == 0) // Gehn who? - runEndGame(1); + runEndGame(1, 9500); else if (*gehnState == 4) // You freed him? Are you kidding me? - runEndGame(2); + runEndGame(2, 12000); else // You already spoke with Gehn. What were you thinking? - runEndGame(3); + runEndGame(3, 8000); } void RivenExternal::xbookclick(uint16 argc, uint16 *argv) { @@ -1923,7 +1940,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) { *_vm->getVar("agehn") = 4; // Set Gehn to the trapped state *_vm->getVar("atrapbook") = 1; // We've got the trap book again _vm->_sound->playSound(0); // Play the link sound again - _vm->changeToCard(_vm->matchRMAPToCard(0x2885)); // Link out! (TODO: Shouldn't this card change?) + _vm->changeToCard(_vm->matchRMAPToCard(0x2885)); // Link out! return; } break; @@ -1951,7 +1968,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) { // Run the credits from here. if (*_vm->getVar("agehn") == 3) { _vm->_scriptMan->stopAllScripts(); - runCredits(argv[0]); + runCredits(argv[0], 5000); return; } @@ -2126,7 +2143,7 @@ void RivenExternal::xrcredittime(uint16 argc, uint16 *argv) { // For the record, when agehn == 4, Gehn will thank you for // showing him the rebel age and then leave you to die. // Otherwise, the rebels burn the book. Epic fail either way. - runEndGame(1); + runEndGame(1, 1500); } void RivenExternal::xrshowinventory(uint16 argc, uint16 *argv) { @@ -2166,30 +2183,26 @@ void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) { if (*_vm->getVar("pcage") == 2) { // The best ending: Catherine is free, Gehn is trapped, Atrus comes to rescue you. // And now we fall back to Earth... all the way... - warning("xtexterior300_telescopedown: Good ending"); _vm->_video->activateMLST(8, _vm->getCurCard()); - runEndGame(8); + runEndGame(8, 5000); } else if (*_vm->getVar("agehn") == 4) { // The ok ending: Catherine is still trapped, Gehn is trapped, Atrus comes to rescue you. // Nice going! Catherine and the islanders are all dead now! Just go back to your home... - warning("xtexterior300_telescopedown: OK ending"); _vm->_video->activateMLST(9, _vm->getCurCard()); - runEndGame(9); + runEndGame(9, 5000); } else if (*_vm->getVar("atrapbook") == 1) { // The bad ending: Catherine is trapped, Gehn is free, Atrus gets shot by Gehn, // And then you get shot by Cho. Nice going! Catherine and the islanders are dead // and you have just set Gehn free from Riven, not to mention you're dead. - warning("xtexterior300_telescopedown: Bad ending"); _vm->_video->activateMLST(10, _vm->getCurCard()); - runEndGame(10); + runEndGame(10, 5000); } else { // The impossible ending: You don't have Catherine's journal and yet you were somehow // able to open the hatch on the telescope. The game provides an ending for those who // cheat, load a saved game with the combo, or just guess the telescope combo. Atrus // doesn't come and you just fall into the fissure. - warning("xtexterior300_telescopedown: Wtf ending"); _vm->_video->activateMLST(11, _vm->getCurCard()); - runEndGame(11); + runEndGame(11, 5000); } } else { // ...the telescope can't move down anymore. diff --git a/engines/mohawk/riven_external.h b/engines/mohawk/riven_external.h index 818bc06c54..90fdc664c1 100644 --- a/engines/mohawk/riven_external.h +++ b/engines/mohawk/riven_external.h @@ -61,8 +61,8 @@ private: // Supplementary Functions int jspitElevatorLoop(); void runDemoBoundaryDialog(); - void runEndGame(uint16 video); - void runCredits(uint16 video); + void runEndGame(uint16 video, uint32 delay); + void runCredits(uint16 video, uint32 delay); void runDomeCheck(); void runDomeButtonMovie(); void resetDomeSliders(uint16 soundId, uint16 startHotspot); diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp index e5a9931605..2540861a93 100644 --- a/engines/sci/engine/kevent.cpp +++ b/engines/sci/engine/kevent.cpp @@ -46,17 +46,18 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; Common::Point mousePos; - // Limit the mouse cursor position, if necessary - g_sci->_gfxCursor->refreshPosition(); - mousePos = g_sci->_gfxCursor->getPosition(); -#ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2_1) - g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x); -#endif - // If there's a simkey pending, and the game wants a keyboard event, use the // simkey instead of a normal event if (g_debug_simulated_key && (mask & SCI_EVENT_KEYBOARD)) { + // In case we use a simulated event we query the current mouse position + mousePos = g_sci->_gfxCursor->getPosition(); +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2_1) + g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x); +#endif + // Limit the mouse cursor position, if necessary + g_sci->_gfxCursor->refreshPosition(); + writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event writeSelectorValue(segMan, obj, SELECTOR(message), g_debug_simulated_key); writeSelectorValue(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on @@ -68,6 +69,15 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { curEvent = g_sci->getEventManager()->getSciEvent(mask); + // For a real event we use its associated mouse position + mousePos = curEvent.mousePos; +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2_1) + g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x); +#endif + // Limit the mouse cursor position, if necessary + g_sci->_gfxCursor->refreshPosition(); + if (g_sci->getVocabulary()) g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index d607a5314f..cb0e6b3c03 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -35,12 +35,6 @@ namespace Sci { -EventManager::EventManager(bool fontIsExtended) : _fontIsExtended(fontIsExtended), _modifierStates(0) { -} - -EventManager::~EventManager() { -} - struct ScancodeRow { int offset; const char *keys; @@ -52,27 +46,6 @@ const ScancodeRow s_scancodeRows[] = { { 0x2c, "ZXCVBNM,./" } }; -static int altify(int ch) { - // Calculates a PC keyboard scancode from a character */ - int row; - int c = toupper((char)ch); - - for (row = 0; row < ARRAYSIZE(s_scancodeRows); row++) { - const char *keys = s_scancodeRows[row].keys; - int offset = s_scancodeRows[row].offset; - - while (*keys) { - if (*keys == c) - return offset << 8; - - offset++; - keys++; - } - } - - return ch; -} - const byte codepagemap_88591toDOS[0x80] = { '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x9x @@ -120,147 +93,178 @@ const SciKeyConversion keyMappings[] = { { Common::KEYCODE_KP_DIVIDE , '/' , '/' }, }; +struct MouseEventConversion { + Common::EventType commonType; + short sciType; + short data; +}; + +const MouseEventConversion mouseEventMappings[] = { + { Common::EVENT_LBUTTONDOWN, SCI_EVENT_MOUSE_PRESS, 1 }, + { Common::EVENT_RBUTTONDOWN, SCI_EVENT_MOUSE_PRESS, 2 }, + { Common::EVENT_MBUTTONDOWN, SCI_EVENT_MOUSE_PRESS, 3 }, + { Common::EVENT_LBUTTONUP, SCI_EVENT_MOUSE_RELEASE, 1 }, + { Common::EVENT_LBUTTONUP, SCI_EVENT_MOUSE_RELEASE, 2 }, + { Common::EVENT_LBUTTONUP, SCI_EVENT_MOUSE_RELEASE, 3 } +}; + +EventManager::EventManager(bool fontIsExtended) : _fontIsExtended(fontIsExtended) { +} + +EventManager::~EventManager() { +} + +static int altify(int ch) { + // Calculates a PC keyboard scancode from a character */ + int row; + int c = toupper((char)ch); + + for (row = 0; row < ARRAYSIZE(s_scancodeRows); row++) { + const char *keys = s_scancodeRows[row].keys; + int offset = s_scancodeRows[row].offset; + + while (*keys) { + if (*keys == c) + return offset << 8; + + offset++; + keys++; + } + } + + return ch; +} + SciEvent EventManager::getScummVMEvent() { - SciEvent input = { SCI_EVENT_NONE, 0, 0, 0 }; + SciEvent input = { SCI_EVENT_NONE, 0, 0, 0, Common::Point(0, 0) }; + SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, 0, Common::Point(0, 0) }; Common::EventManager *em = g_system->getEventManager(); Common::Event ev; bool found = em->pollEvent(ev); - Common::Point p = ev.mouse; // Don't generate events for mouse movement - while (found && ev.type == Common::EVENT_MOUSEMOVE) { + while (found && ev.type == Common::EVENT_MOUSEMOVE) found = em->pollEvent(ev); + + // Save the mouse position + // + // We call getMousePos of the event manager here, since we also want to + // store the mouse position in case of keyboard events, which do not feature + // any mouse position information itself. + // This should be safe, since the mouse position in the event manager should + // only be updated when a mouse related event has been taken from the queue + // via pollEvent. + noEvent.mousePos = input.mousePos = em->getMousePos(); + + if (!found || ev.type == Common::EVENT_MOUSEMOVE) + return noEvent; + + if (ev.type == Common::EVENT_QUIT) { + input.type = SCI_EVENT_QUIT; + return input; } - if (found && ev.type != Common::EVENT_MOUSEMOVE) { - int modifiers = em->getModifierState(); - bool numlockOn = (ev.kbd.flags & Common::KBD_NUM); - - // We add the modifier key status to buckybits - //TODO: SCI_EVM_INSERT - - input.modifiers = - ((modifiers & Common::KBD_ALT) ? SCI_KEYMOD_ALT : 0) | - ((modifiers & Common::KBD_CTRL) ? SCI_KEYMOD_CTRL : 0) | - ((modifiers & Common::KBD_SHIFT) ? SCI_KEYMOD_LSHIFT | SCI_KEYMOD_RSHIFT : 0) | - ((ev.kbd.flags & Common::KBD_CAPS) ? SCI_KEYMOD_CAPSLOCK : 0) | - ((ev.kbd.flags & Common::KBD_SCRL) ? SCI_KEYMOD_SCRLOCK : 0) | - _modifierStates; - - switch (ev.type) { - // Keyboard events - case Common::EVENT_KEYDOWN: - input.data = ev.kbd.keycode; - input.character = ev.kbd.ascii; - - // Debug console - if (ev.kbd.hasFlags(Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d) { - // Open debug console - Console *con = g_sci->getSciDebugger(); - con->attach(); - - // Clear keyboard event - input.type = SCI_EVENT_NONE; - input.character = 0; - input.data = 0; - input.modifiers = 0; - - return input; - } + // Handle mouse events + for (int i = 0; i < ARRAYSIZE(mouseEventMappings); i++) { + if (mouseEventMappings[i].commonType == ev.type) { + input.type = mouseEventMappings[i].sciType; + input.data = mouseEventMappings[i].data; + return input; + } + } - if (!(input.data & 0xFF00)) { - // Directly accept most common keys without conversion - input.type = SCI_EVENT_KEYBOARD; - if ((input.character >= 0x80) && (input.character <= 0xFF)) { - // If there is no extended font, we will just clear the current event - // Sierra SCI actually accepted those characters, but didn't display them inside textedit-controls - // because the characters were missing inside the font(s) - // We filter them out for non-multilingual games because of that - if (!_fontIsExtended) { - input.type = SCI_EVENT_NONE; - input.character = 0; - input.data = 0; - input.modifiers = 0; - return input; - } - // We get a 8859-1 character, we need dos (cp850/437) character for multilingual sci01 games - input.character = codepagemap_88591toDOS[input.character & 0x7f]; - } - if (input.data == Common::KEYCODE_TAB) { - // Tab - input.type = SCI_EVENT_KEYBOARD; - input.data = SCI_KEY_TAB; - if (input.modifiers & (SCI_KEYMOD_LSHIFT | SCI_KEYMOD_RSHIFT)) - input.character = SCI_KEY_SHIFT_TAB; - else - input.character = SCI_KEY_TAB; - } - if (input.data == Common::KEYCODE_DELETE) { - // Delete key - input.type = SCI_EVENT_KEYBOARD; - input.data = input.character = SCI_KEY_DELETE; - } - } else if ((input.data >= Common::KEYCODE_F1) && input.data <= Common::KEYCODE_F10) { - // F1-F10 - input.type = SCI_EVENT_KEYBOARD; - // SCI_K_F1 == 59 << 8 - // SCI_K_SHIFT_F1 == 84 << 8 - input.data = SCI_KEY_F1 + ((input.data - Common::KEYCODE_F1)<<8); - if (input.modifiers & (SCI_KEYMOD_LSHIFT | SCI_KEYMOD_RSHIFT)) - input.character = input.data + 0x1900; - else - input.character = input.data; - } else { - // Special keys that need conversion - input.type = SCI_EVENT_KEYBOARD; - for (int i = 0; i < ARRAYSIZE(keyMappings); i++) { - if (keyMappings[i].scummVMKey == ev.kbd.keycode) { - input.data = numlockOn ? keyMappings[i].sciKeyNumlockOn : keyMappings[i].sciKeyNumlockOff; - break; - } - } - input.character = input.data; - } - break; + // If we reached here, make sure that it's a keydown event + if (ev.type != Common::EVENT_KEYDOWN) + return noEvent; - // Mouse events - case Common::EVENT_LBUTTONDOWN: - input.type = SCI_EVENT_MOUSE_PRESS; - input.data = 1; - break; - case Common::EVENT_RBUTTONDOWN: - input.type = SCI_EVENT_MOUSE_PRESS; - input.data = 2; - break; - case Common::EVENT_MBUTTONDOWN: - input.type = SCI_EVENT_MOUSE_PRESS; - input.data = 3; - break; - case Common::EVENT_LBUTTONUP: - input.type = SCI_EVENT_MOUSE_RELEASE; - input.data = 1; - break; - case Common::EVENT_RBUTTONUP: - input.type = SCI_EVENT_MOUSE_RELEASE; - input.data = 2; - break; - case Common::EVENT_MBUTTONUP: - input.type = SCI_EVENT_MOUSE_RELEASE; - input.data = 3; - break; - - // Misc events - case Common::EVENT_QUIT: - input.type = SCI_EVENT_QUIT; - break; + // Check for Control-D (debug console) + if (ev.kbd.hasFlags(Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d) { + // Open debug console + Console *con = g_sci->getSciDebugger(); + con->attach(); + return noEvent; + } - default: - break; + // Process keyboard events + + int modifiers = em->getModifierState(); + bool numlockOn = (ev.kbd.flags & Common::KBD_NUM); + + input.data = ev.kbd.keycode; + input.character = ev.kbd.ascii; + input.type = SCI_EVENT_KEYBOARD; + + input.modifiers = + ((modifiers & Common::KBD_ALT) ? SCI_KEYMOD_ALT : 0) | + ((modifiers & Common::KBD_CTRL) ? SCI_KEYMOD_CTRL : 0) | + ((modifiers & Common::KBD_SHIFT) ? SCI_KEYMOD_LSHIFT | SCI_KEYMOD_RSHIFT : 0); + + // Caps lock and Scroll lock have been removed, cause we already handle upper + // case keys ad Scroll lock doesn't seem to be used anywhere + //((ev.kbd.flags & Common::KBD_CAPS) ? SCI_KEYMOD_CAPSLOCK : 0) | + //((ev.kbd.flags & Common::KBD_SCRL) ? SCI_KEYMOD_SCRLOCK : 0) | + + if (!(input.data & 0xFF00)) { + // Directly accept most common keys without conversion + if ((input.character >= 0x80) && (input.character <= 0xFF)) { + // If there is no extended font, we will just clear the + // current event. + // Sierra SCI actually accepted those characters, but + // didn't display them inside text edit controls because + // the characters were missing inside the font(s). + // We filter them out for non-multilingual games because + // of that. + if (!_fontIsExtended) + return noEvent; + // Convert 8859-1 characters to DOS (cp850/437) for + // multilingual SCI01 games + input.character = codepagemap_88591toDOS[input.character & 0x7f]; + } + if (input.data == Common::KEYCODE_TAB) { + input.character = input.data = SCI_KEY_TAB; + if (modifiers & Common::KBD_SHIFT) + input.character = SCI_KEY_SHIFT_TAB; + } + if (input.data == Common::KEYCODE_DELETE) + input.data = input.character = SCI_KEY_DELETE; + } else if ((input.data >= Common::KEYCODE_F1) && input.data <= Common::KEYCODE_F10) { + // SCI_K_F1 == 59 << 8 + // SCI_K_SHIFT_F1 == 84 << 8 + input.character = input.data = SCI_KEY_F1 + ((input.data - Common::KEYCODE_F1)<<8); + if (modifiers & Common::KBD_SHIFT) + input.character = input.data + 0x1900; + } else { + // Special keys that need conversion + for (int i = 0; i < ARRAYSIZE(keyMappings); i++) { + if (keyMappings[i].scummVMKey == ev.kbd.keycode) { + input.character = input.data = numlockOn ? keyMappings[i].sciKeyNumlockOn : keyMappings[i].sciKeyNumlockOff; + break; + } } } + + // When Ctrl AND Alt are pressed together with a regular key, Linux will give us control-key, Windows will give + // us the actual key. My opinion is that windows is right, because under DOS the keys worked the same, anyway + // we support the other case as well + if ((modifiers & Common::KBD_SHIFT) && input.character < 27) + input.character += 96; // 0x01 -> 'a' + + if (getSciVersion() <= SCI_VERSION_1_MIDDLE) { + // TODO: find out if altify is also not needed for sci1late+, couldnt find any game that uses those keys + // Scancodify if appropriate + if (modifiers & Common::KBD_ALT) + input.character = altify(input.character); + else if ((modifiers & Common::KBD_CTRL) && input.character > 0 && input.character < 27) + input.character += 96; // 0x01 -> 'a' + } + // If no actual key was pressed (e.g. if only a modifier key was pressed), + // ignore the event + if (!input.character) + return noEvent; + return input; } @@ -282,8 +286,7 @@ void EventManager::updateScreen() { } SciEvent EventManager::getSciEvent(unsigned int mask) { - //sci_event_t error_event = { SCI_EVT_ERROR, 0, 0, 0 }; - SciEvent event = { 0, 0, 0, 0 }; + SciEvent event = { 0, 0, 0, 0, Common::Point(0, 0) }; EventManager::updateScreen(); @@ -304,9 +307,8 @@ SciEvent EventManager::getSciEvent(unsigned int mask) { event = *iter; // If not peeking at the queue, remove the event - if (!(mask & SCI_EVENT_PEEK)) { + if (!(mask & SCI_EVENT_PEEK)) _events.erase(iter); - } } else { // No event found: we must return a SCI_EVT_NONE event. @@ -314,29 +316,6 @@ SciEvent EventManager::getSciEvent(unsigned int mask) { // there is no need to change it. } - if (event.type == SCI_EVENT_KEYBOARD) { - // Do we still have to translate the key? - - // When Ctrl AND Alt are pressed together with a regular key, Linux will give us control-key, Windows will give - // us the actual key. My opinion is that windows is right, because under DOS the keys worked the same, anyway - // we support the other case as well - if (event.modifiers & SCI_KEYMOD_ALT) { - if (event.character < 27) - event.character += 96; // 0x01 -> 'a' - } - - if (getSciVersion() <= SCI_VERSION_1_MIDDLE) { - // TODO: find out if altify is also not needed for sci1late+, couldnt find any game that uses those keys - // Scancodify if appropriate - if (event.modifiers & SCI_KEYMOD_ALT) { - event.character = altify(event.character); - } else if (event.modifiers & SCI_KEYMOD_CTRL) { - if (event.character < 27) - event.character += 96; // 0x01 -> 'a' - } - } - } - return event; } diff --git a/engines/sci/event.h b/engines/sci/event.h index 7c83476294..be0322f2a4 100644 --- a/engines/sci/event.h +++ b/engines/sci/event.h @@ -27,6 +27,7 @@ #define SCI_EVENT_H #include "common/list.h" +#include "common/rect.h" namespace Sci { @@ -46,6 +47,13 @@ struct SciEvent { * PC keyboard scancodes. */ short character; + + /** + * The mouse position at the time the event was created. + * + * These are display coordinates! + */ + Common::Point mousePos; }; /*Values for type*/ @@ -56,11 +64,8 @@ struct SciEvent { #define SCI_EVENT_DIRECTION (1<<6) #define SCI_EVENT_SAID (1<<7) /*Fake values for other events*/ -#define SCI_EVENT_ERROR (1<<10) #define SCI_EVENT_QUIT (1<<11) #define SCI_EVENT_PEEK (1<<15) -/* The QUIT event may be used to signal an external 'quit' command being -** issued to the gfx driver. */ #define SCI_EVENT_ANY 0x7fff /* Keycodes of special keys: */ @@ -121,7 +126,6 @@ private: SciEvent getScummVMEvent(); const bool _fontIsExtended; - int _modifierStates; Common::List<SciEvent> _events; }; diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp index b2e564c288..b0d12038e8 100644 --- a/engines/sci/graphics/menu.cpp +++ b/engines/sci/graphics/menu.cpp @@ -399,7 +399,6 @@ void GfxMenu::calculateMenuAndItemWidth() { reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { int16 eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type)); int16 keyPress, keyModifier; - Common::Point mousePosition; GuiMenuItemList::iterator itemIterator = _itemList.begin(); GuiMenuItemList::iterator itemEnd = _itemList.end(); GuiMenuItemEntry *itemEntry = NULL; @@ -457,15 +456,17 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { itemEntry = NULL; break; - case SCI_EVENT_MOUSE_PRESS: - mousePosition = _cursor->getPosition(); + case SCI_EVENT_MOUSE_PRESS: { + Common::Point mousePosition; + mousePosition.x = readSelectorValue(_segMan, eventObject, SELECTOR(x)); + mousePosition.y = readSelectorValue(_segMan, eventObject, SELECTOR(y)); if (mousePosition.y < 10) { interactiveStart(pauseSound); itemEntry = interactiveWithMouse(); interactiveEnd(pauseSound); forceClaimed = true; } - break; + } break; } if (!_menuSaveHandle.isNull()) { @@ -715,7 +716,6 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() { uint16 newItemId = _curItemId; GuiMenuItemEntry *curItemEntry = findItem(_curMenuId, _curItemId); GuiMenuItemEntry *newItemEntry = curItemEntry; - Common::Point mousePosition; // We don't 100% follow Sierra here: we select last item instead of // selecting first item of first menu every time. Also sierra sci didn't @@ -793,9 +793,9 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() { } break; - case SCI_EVENT_MOUSE_PRESS: - mousePosition = _cursor->getPosition(); - if (_cursor->getPosition().y < 10) { + case SCI_EVENT_MOUSE_PRESS: { + Common::Point mousePosition = curEvent.mousePos; + if (mousePosition.y < 10) { // Somewhere on the menubar newMenuId = mouseFindMenuSelection(mousePosition); if (newMenuId) { @@ -824,7 +824,8 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() { } newItemId = curItemEntry->id; } - break; + } break; + case SCI_EVENT_NONE: g_sci->sleep(2500 / 1000); break; @@ -840,7 +841,6 @@ GuiMenuItemEntry *GfxMenu::interactiveWithMouse() { SciEvent curEvent; uint16 newMenuId = 0, newItemId = 0; uint16 curMenuId = 0, curItemId = 0; - Common::Point mousePosition = _cursor->getPosition(); bool firstMenuChange = true; GuiMenuItemEntry *curItemEntry = NULL; @@ -871,7 +871,7 @@ GuiMenuItemEntry *GfxMenu::interactiveWithMouse() { } // Find out where mouse is currently pointing to - mousePosition = _cursor->getPosition(); + Common::Point mousePosition = curEvent.mousePos; if (mousePosition.y < 10) { // Somewhere on the menubar newMenuId = mouseFindMenuSelection(mousePosition); diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp index fd74714495..b99861319a 100644 --- a/engines/sci/graphics/view.cpp +++ b/engines/sci/graphics/view.cpp @@ -375,116 +375,138 @@ void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCo byte *outPtr = celBitmap; byte curByte, runLength; byte *rlePtr = inBuffer + rlePos; + // The existence of a literal position pointer signifies data with two + // separate streams, most likely a SCI1.1 view byte *literalPtr = inBuffer + literalPos; int pixelNr = 0; memset(celBitmap, clearColor, pixelCount); - if (!literalPos) { - // decompression for data that has only one combined stream - switch (viewType) { - case kViewEga: - while (pixelNr < pixelCount) { - curByte = *rlePtr++; - runLength = curByte >> 4; - memset(outPtr + pixelNr, curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr)); - pixelNr += runLength; + // View unpacking: + // + // EGA: + // Each byte is like XXXXYYYY (XXXX: 0 - 15, YYYY: 0 - 15) + // Set the next XXXX pixels to YYYY + // + // Amiga: + // Each byte is like XXXXXYYY (XXXXX: 0 - 31, YYY: 0 - 7) + // - Case A: YYY != 0 + // Set the next YYY pixels to XXXXX + // - Case B: YYY == 0 + // Skip the next XXXXX pixels (i.e. transparency) + // + // Amiga 64: + // Each byte is like XXYYYYYY (XX: 0 - 3, YYYYYY: 0 - 63) + // - Case A: XX != 0 + // Set the next XX pixels to YYYYYY + // - Case B: XX == 0 + // Skip the next YYYYYY pixels (i.e. transparency) + // + // VGA: + // Each byte is like XXYYYYYY (YYYYY: 0 - 63) + // - Case A: XX == 00 (binary) + // Copy next YYYYYY bytes as-is + // - Case B: XX == 01 (binary) + // Same as above, copy YYYYYY + 64 bytes as-is + // - Case C: XX == 10 (binary) + // Set the next YYYYY pixels to the next byte value + // - Case D: XX == 11 (binary) + // Skip the next YYYYY pixels (i.e. transparency) + + if (literalPos && isMacSci11ViewData) { + // KQ6/Freddy Pharkas use byte lengths, all others use uint16 + // The SCI devs must have realized that a max of 255 pixels wide + // was not very good for 320 or 640 width games. + bool hasByteLengths = (g_sci->getGameId() == GID_KQ6 || g_sci->getGameId() == GID_FREDDYPHARKAS); + + // compression for SCI1.1+ Mac + while (pixelNr < pixelCount) { + uint32 pixelLine = pixelNr; + + if (hasByteLengths) { + pixelNr += *rlePtr++; + runLength = *rlePtr++; + } else { + pixelNr += READ_BE_UINT16(rlePtr); + runLength = READ_BE_UINT16(rlePtr + 2); + rlePtr += 4; } - break; - case kViewAmiga: - while (pixelNr < pixelCount) { - curByte = *rlePtr++; - if (curByte & 0x07) { // fill with color - runLength = curByte & 0x07; - curByte = curByte >> 3; - while (runLength-- && pixelNr < pixelCount) - outPtr[pixelNr++] = curByte; - } else { // fill with transparent - runLength = curByte >> 3; - pixelNr += runLength; - } + + while (runLength-- && pixelNr < pixelCount) + outPtr[pixelNr++] = *literalPtr++; + + pixelNr = pixelLine + width; + } + return; + } + + switch (viewType) { + case kViewEga: + while (pixelNr < pixelCount) { + curByte = *rlePtr++; + runLength = curByte >> 4; + memset(outPtr + pixelNr, curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr)); + pixelNr += runLength; + } + break; + case kViewAmiga: + while (pixelNr < pixelCount) { + curByte = *rlePtr++; + if (curByte & 0x07) { // fill with color + runLength = curByte & 0x07; + curByte = curByte >> 3; + memset(outPtr + pixelNr, curByte, MIN<uint16>(runLength, pixelCount - pixelNr)); + } else { // skip the next pixels (transparency) + runLength = curByte >> 3; } - break; - case kViewAmiga64: - // TODO: This isn't 100% right. Implement it fully. - while (pixelNr < pixelCount) { - curByte = *rlePtr++; + pixelNr += runLength; + } + break; + case kViewAmiga64: + while (pixelNr < pixelCount) { + curByte = *rlePtr++; + if (curByte & 0xC0) { // fill with color runLength = curByte >> 6; - memset(outPtr + pixelNr, curByte & 0x3F, MIN<uint16>(runLength, pixelCount - pixelNr)); - pixelNr += runLength; - } - break; - case kViewVga: - case kViewVga11: - while (pixelNr < pixelCount) { - curByte = *rlePtr++; + memset(outPtr + pixelNr, curByte & 0x3F, MIN<uint16>(runLength, pixelCount - pixelNr)); + } else { // skip the next pixels (transparency) runLength = curByte & 0x3F; - switch (curByte & 0xC0) { - case 0: // copy bytes as-is - while (runLength-- && pixelNr < pixelCount) - outPtr[pixelNr++] = *rlePtr++; - break; - case 0x40: // copy bytes as is (In copy case, runLength can go upto 127 i.e. pixel & 0x40). Fixes bug #3135872. - runLength += 64; - break; - case 0x80: // fill with color - memset(outPtr + pixelNr, *rlePtr++, MIN<uint16>(runLength, pixelCount - pixelNr)); - pixelNr += runLength; - break; - case 0xC0: // fill with transparent - pixelNr += runLength; - break; - } } - break; - default: - error("Unsupported picture viewtype"); + pixelNr += runLength; } - } else { - // decompression for data that has two separate streams (probably a SCI 1.1 view) - if (isMacSci11ViewData) { - // KQ6/Freddy Pharkas use byte lengths, all others use uint16 - // The SCI devs must have realized that a max of 255 pixels wide - // was not very good for 320 or 640 width games. - bool hasByteLengths = (g_sci->getGameId() == GID_KQ6 || g_sci->getGameId() == GID_FREDDYPHARKAS); - - // compression for SCI1.1+ Mac - while (pixelNr < pixelCount) { - uint32 pixelLine = pixelNr; - - if (hasByteLengths) { - pixelNr += *rlePtr++; - runLength = *rlePtr++; + break; + case kViewVga: + case kViewVga11: + while (pixelNr < pixelCount) { + curByte = *rlePtr++; + runLength = curByte & 0x3F; + + switch (curByte & 0xC0) { + case 0x40: // copy bytes as is (In copy case, runLength can go up to 127 i.e. pixel & 0x40). Fixes bug #3135872. + runLength += 64; + case 0x00: // copy bytes as-is + if (!literalPos) { + memcpy(outPtr + pixelNr, rlePtr, MIN<uint16>(runLength, pixelCount - pixelNr)); + rlePtr += runLength; } else { - pixelNr += READ_BE_UINT16(rlePtr); - runLength = READ_BE_UINT16(rlePtr + 2); - rlePtr += 4; - } - - while (runLength-- && pixelNr < pixelCount) - outPtr[pixelNr++] = *literalPtr++; - - pixelNr = pixelLine + width; - } - } else { - while (pixelNr < pixelCount) { - curByte = *rlePtr++; - runLength = curByte & 0x3F; - switch (curByte & 0xC0) { - case 0: // copy bytes as-is - while (runLength-- && pixelNr < pixelCount) - outPtr[pixelNr++] = *literalPtr++; - break; - case 0x80: // fill with color + memcpy(outPtr + pixelNr, literalPtr, MIN<uint16>(runLength, pixelCount - pixelNr)); + literalPtr += runLength; + } + break; + case 0x80: // fill with color + if (!literalPos) + memset(outPtr + pixelNr, *rlePtr++, MIN<uint16>(runLength, pixelCount - pixelNr)); + else memset(outPtr + pixelNr, *literalPtr++, MIN<uint16>(runLength, pixelCount - pixelNr)); - pixelNr += runLength; - break; - case 0xC0: // fill with transparent - pixelNr += runLength; - break; - } + break; + case 0xC0: // skip the next pixels (transparency) + break; } + + pixelNr += runLength; } + break; + default: + error("Unsupported picture viewtype"); } } |