diff options
Diffstat (limited to 'engines/sci/graphics')
-rw-r--r-- | engines/sci/graphics/animate.cpp | 123 | ||||
-rw-r--r-- | engines/sci/graphics/animate.h | 6 | ||||
-rw-r--r-- | engines/sci/graphics/compare.cpp | 26 | ||||
-rw-r--r-- | engines/sci/graphics/controls.cpp | 46 | ||||
-rw-r--r-- | engines/sci/graphics/cursor.cpp | 133 | ||||
-rw-r--r-- | engines/sci/graphics/cursor.h | 23 | ||||
-rw-r--r-- | engines/sci/graphics/frameout.cpp | 131 | ||||
-rw-r--r-- | engines/sci/graphics/frameout.h | 9 | ||||
-rw-r--r-- | engines/sci/graphics/menu.cpp | 2 | ||||
-rw-r--r-- | engines/sci/graphics/paint16.cpp | 8 | ||||
-rw-r--r-- | engines/sci/graphics/palette.cpp | 25 | ||||
-rw-r--r-- | engines/sci/graphics/picture.cpp | 17 | ||||
-rw-r--r-- | engines/sci/graphics/portrait.cpp | 2 | ||||
-rw-r--r-- | engines/sci/graphics/ports.cpp | 29 | ||||
-rw-r--r-- | engines/sci/graphics/robot.cpp | 169 | ||||
-rw-r--r-- | engines/sci/graphics/robot.h | 1 | ||||
-rw-r--r-- | engines/sci/graphics/screen.cpp | 6 | ||||
-rw-r--r-- | engines/sci/graphics/text16.cpp | 79 | ||||
-rw-r--r-- | engines/sci/graphics/text16.h | 10 | ||||
-rw-r--r-- | engines/sci/graphics/transitions.cpp | 144 | ||||
-rw-r--r-- | engines/sci/graphics/transitions.h | 5 | ||||
-rw-r--r-- | engines/sci/graphics/view.cpp | 7 |
22 files changed, 664 insertions, 337 deletions
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp index b962e819a6..62c5f9c19e 100644 --- a/engines/sci/graphics/animate.cpp +++ b/engines/sci/graphics/animate.cpp @@ -96,7 +96,7 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) { invokeSelector(_s, curObject, SELECTOR(doit), argc, argv, 0); // If a game is being loaded, stop processing - if (_s->abortScriptProcessing != kAbortNone || g_engine->shouldQuit()) + if (_s->abortScriptProcessing != kAbortNone) return true; // Stop processing // Lookup node again, since the nodetable it was in may have been reallocated. @@ -141,6 +141,7 @@ void GfxAnimate::makeSortedList(List *list) { AnimateEntry listEntry; const reg_t curObject = curNode->value; listEntry.object = curObject; + listEntry.castHandle = NULL_REG; // Get data from current object listEntry.givenOrderNo = listNr; @@ -190,6 +191,34 @@ void GfxAnimate::makeSortedList(List *list) { Common::sort(_list.begin(), _list.end(), sortHelper); } +void GfxAnimate::applyGlobalScaling(AnimateList::iterator entry, GfxView *view) { + reg_t curObject = entry->object; + + // Global scaling uses global var 2 and some other stuff to calculate scaleX/scaleY + int16 maxScale = readSelectorValue(_s->_segMan, curObject, SELECTOR(maxScale)); + int16 celHeight = view->getHeight(entry->loopNo, entry->celNo); + int16 maxCelHeight = (maxScale * celHeight) >> 7; + reg_t globalVar2 = _s->variables[VAR_GLOBAL][2]; // current room object + int16 vanishingY = readSelectorValue(_s->_segMan, globalVar2, SELECTOR(vanishingY)); + + int16 fixedPortY = _ports->getPort()->rect.bottom - vanishingY; + int16 fixedEntryY = entry->y - vanishingY; + if (!fixedEntryY) + fixedEntryY = 1; + + if ((celHeight == 0) || (fixedPortY == 0)) + error("global scaling panic"); + + entry->scaleY = ( maxCelHeight * fixedEntryY ) / fixedPortY; + entry->scaleY = (entry->scaleY * 128) / celHeight; + + entry->scaleX = entry->scaleY; + + // and set objects scale selectors + writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX), entry->scaleX); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY), entry->scaleY); +} + void GfxAnimate::fill(byte &old_picNotValid, bool maySetNsRect) { reg_t curObject; uint16 signal; @@ -229,44 +258,22 @@ void GfxAnimate::fill(byte &old_picNotValid, bool maySetNsRect) { it->celNo = viewCelCount - 1; } - // Process global scaling, if needed - if (it->scaleSignal & kScaleSignalDoScaling) { - if (it->scaleSignal & kScaleSignalGlobalScaling) { - // Global scaling uses global var 2 and some other stuff to calculate scaleX/scaleY - int16 maxScale = readSelectorValue(_s->_segMan, curObject, SELECTOR(maxScale)); - int16 celHeight = view->getHeight(it->loopNo, it->celNo); - int16 maxCelHeight = (maxScale * celHeight) >> 7; - reg_t globalVar2 = _s->variables[VAR_GLOBAL][2]; // current room object - int16 vanishingY = readSelectorValue(_s->_segMan, globalVar2, SELECTOR(vanishingY)); - - int16 fixedPortY = _ports->getPort()->rect.bottom - vanishingY; - int16 fixedEntryY = it->y - vanishingY; - if (!fixedEntryY) - fixedEntryY = 1; - - if ((celHeight == 0) || (fixedPortY == 0)) - error("global scaling panic"); - - it->scaleY = ( maxCelHeight * fixedEntryY ) / fixedPortY; - it->scaleY = (it->scaleY * 128) / celHeight; - - it->scaleX = it->scaleY; - - // and set objects scale selectors - writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX), it->scaleX); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY), it->scaleY); - } - } - - //warning("%s view %d, loop %d, cel %d", _s->_segMan->getObjectName(curObject), it->viewId, it->loopNo, it->celNo); - if (!view->isScaleable()) { // Laura Bow 2 (especially floppy) depends on this, some views are not supposed to be scaleable // this "feature" was removed in later versions of SCI1.1 it->scaleSignal = 0; it->scaleY = it->scaleX = 128; + } else { + // Process global scaling, if needed + if (it->scaleSignal & kScaleSignalDoScaling) { + if (it->scaleSignal & kScaleSignalGlobalScaling) { + applyGlobalScaling(it, view); + } + } } + //warning("%s view %d, loop %d, cel %d, signal %x", _s->_segMan->getObjectName(curObject), it->viewId, it->loopNo, it->celNo, it->signal); + bool setNsRect = maySetNsRect; // Create rect according to coordinates and given cel @@ -278,6 +285,16 @@ void GfxAnimate::fill(byte &old_picNotValid, bool maySetNsRect) { } else { view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect); } + + // This statement must be here for Hoyle4, otherwise cards are unclickable. + // This is probably one of the experimental features that were occasionally + // added to SCI interpreters; the corresponding check is absent in many SSCI + // versions. m_kiewitz knew about this flag before I (lskovlun) implemented it, + // so it is possible that more test cases are known. Also, some presently open + // SCI1.1 bugs may be fixed by this and should be re-tested with this patch generalized. + if (it->scaleSignal & kScaleSignalDontSetNsrect) + setNsRect = false; + if (setNsRect) { writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), it->celRect.left); writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), it->celRect.top); @@ -533,19 +550,6 @@ void GfxAnimate::reAnimate(Common::Rect rect) { } } -void GfxAnimate::preprocessAddToPicList() { - AnimateList::iterator it; - const AnimateList::iterator end = _list.end(); - - for (it = _list.begin(); it != end; ++it) { - if (it->priority == -1) - it->priority = _ports->kernelCoordinateToPriority(it->y); - - // Do not allow priority to get changed by fill() - it->signal |= kSignalFixedPriority; - } -} - void GfxAnimate::addToPicDrawCels() { reg_t curObject; GfxView *view = NULL; @@ -558,8 +562,31 @@ void GfxAnimate::addToPicDrawCels() { // Get the corresponding view view = _cache->getView(it->viewId); + // kAddToPic does not do loop/cel-number fixups, it also doesn't support global scaling + + if (it->priority == -1) + it->priority = _ports->kernelCoordinateToPriority(it->y); + + if (!view->isScaleable()) { + // Laura Bow 2 specific - ffs. fill() + it->scaleSignal = 0; + it->scaleY = it->scaleX = 128; + } + + // Create rect according to coordinates and given cel + if (it->scaleSignal & kScaleSignalDoScaling) { + applyGlobalScaling(it, view); + view->getCelScaledRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->scaleX, it->scaleY, it->celRect); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), it->celRect.left); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), it->celRect.top); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight), it->celRect.right); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom), it->celRect.bottom); + } else { + view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect); + } + // draw corresponding cel - _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY); + _paint16->drawCel(view, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY); if ((it->signal & kSignalIgnoreActor) == 0) { it->celRect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, it->celRect.top, it->celRect.bottom - 1); _paint16->fillRect(it->celRect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15); @@ -677,6 +704,7 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t int16 onlyWidth = onlyCast->celRect.width(); if (((onlyWidth == 12) && (onlyHeight == 35)) || // regular benchmark view ("fred", "Speedy", "ego") ((onlyWidth == 29) && (onlyHeight == 45)) || // King's Quest 5 french "fred" + ((onlyWidth == 1) && (onlyHeight == 5)) || // Freddy Pharkas "fred" ((onlyWidth == 1) && (onlyHeight == 1))) { // Laura Bow 2 Talkie // check further that there is only one cel in that view GfxView *onlyView = _cache->getView(onlyCast->viewId); @@ -703,7 +731,6 @@ void GfxAnimate::addToPicSetPicNotValid() { void GfxAnimate::kernelAddToPicList(reg_t listReference, int argc, reg_t *argv) { List *list; - byte tempPicNotValid = 0; _ports->setPort((Port *)_ports->_picWind); @@ -712,8 +739,6 @@ void GfxAnimate::kernelAddToPicList(reg_t listReference, int argc, reg_t *argv) error("kAddToPic called with non-list as parameter"); makeSortedList(list); - preprocessAddToPicList(); - fill(tempPicNotValid, getSciVersion() >= SCI_VERSION_1_1 ? true : false); addToPicDrawCels(); addToPicSetPicNotValid(); diff --git a/engines/sci/graphics/animate.h b/engines/sci/graphics/animate.h index f25e54915e..23e7a624d8 100644 --- a/engines/sci/graphics/animate.h +++ b/engines/sci/graphics/animate.h @@ -53,7 +53,8 @@ enum ViewSignals { enum ViewScaleSignals { kScaleSignalDoScaling = 0x0001, // enables scaling when drawing that cel (involves scaleX and scaleY) kScaleSignalGlobalScaling = 0x0002, // means that global scaling shall get applied on that cel (sets scaleX/scaleY) - kScaleSignalUnknown2 = 0x0004 // really unknown + kScaleSignalDontSetNsrect = 0x0004 // do not set nsRect inside kAnimate(); for a test case see bug #3038424 + }; struct AnimateEntry { @@ -83,6 +84,7 @@ class GfxPaint16; class GfxScreen; class GfxPalette; class GfxTransitions; +class GfxView; /** * Animate class, kAnimate and relevant functions for SCI16 (SCI0-SCI1.1) games */ @@ -94,13 +96,13 @@ public: void disposeLastCast(); bool invoke(List *list, int argc, reg_t *argv); void makeSortedList(List *list); + void applyGlobalScaling(AnimateList::iterator entry, GfxView *view); void fill(byte &oldPicNotValid, bool maySetNsRect); void update(); void drawCels(); void updateScreen(byte oldPicNotValid); void restoreAndDelete(int argc, reg_t *argv); void reAnimate(Common::Rect rect); - void preprocessAddToPicList(); void addToPicDrawCels(); void addToPicDrawView(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 leftPos, int16 topPos, int16 priority, int16 control); diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp index 1c961b2ad6..6a99d2384e 100644 --- a/engines/sci/graphics/compare.cpp +++ b/engines/sci/graphics/compare.cpp @@ -185,9 +185,9 @@ reg_t GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) { checkRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight)); checkRect.bottom = readSelectorValue(_segMan, curObject, SELECTOR(brBottom)); - if (!checkRect.isValidRect()) { // can occur in Iceman - HACK? TODO: is this really occuring in sierra sci? check this + if (!checkRect.isValidRect()) { // can occur in Iceman and Mother Goose - HACK? TODO: is this really occuring in sierra sci? check this warning("kCan(t)BeHere - invalid rect %d, %d -> %d, %d", checkRect.left, checkRect.top, checkRect.right, checkRect.bottom); - return NULL_REG; + return NULL_REG; // this means "can be here" } adjustedRect = _coordAdjuster->onControl(checkRect); @@ -237,20 +237,24 @@ void GfxCompare::kernelBaseSetter(reg_t object) { Common::Rect celRect; GfxView *tmpView = _cache->getView(viewId); - if (tmpView->isSci2Hires()) - _screen->adjustToUpscaledCoordinates(y, x); + if (!tmpView->isScaleable()) + scaleSignal = 0; if (scaleSignal & kScaleSignalDoScaling) { - int16 scaleX = readSelectorValue(_segMan, object, SELECTOR(scaleX)); - int16 scaleY = readSelectorValue(_segMan, object, SELECTOR(scaleY)); - tmpView->getCelScaledRect(loopNo, celNo, x, y, z, scaleX, scaleY, celRect); + celRect.left = readSelectorValue(_segMan, object, SELECTOR(nsLeft)); + celRect.right = readSelectorValue(_segMan, object, SELECTOR(nsRight)); + celRect.top = readSelectorValue(_segMan, object, SELECTOR(nsTop)); + celRect.bottom = readSelectorValue(_segMan, object, SELECTOR(nsBottom)); } else { + if (tmpView->isSci2Hires()) + _screen->adjustToUpscaledCoordinates(y, x); + tmpView->getCelRect(loopNo, celNo, x, y, z, celRect); - } - if (tmpView->isSci2Hires()) { - _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left); - _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right); + if (tmpView->isSci2Hires()) { + _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left); + _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right); + } } celRect.bottom = y + 1; diff --git a/engines/sci/graphics/controls.cpp b/engines/sci/graphics/controls.cpp index 5891413be8..66376a793c 100644 --- a/engines/sci/graphics/controls.cpp +++ b/engines/sci/graphics/controls.cpp @@ -150,7 +150,7 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { uint16 maxChars = readSelectorValue(_segMan, controlObject, SELECTOR(max)); reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text)); Common::String text; - uint16 textSize, eventType, eventKey = 0; + uint16 textSize, eventType, eventKey = 0, modifiers = 0; bool textChanged = false; bool textAddChar = false; Common::Rect rect; @@ -159,6 +159,8 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { error("kEditControl called on object that doesnt have a text reference"); text = _segMan->getString(textReference); + uint16 oldCursorPos = cursorPos; + if (!eventObject.isNull()) { textSize = text.size(); eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type)); @@ -169,6 +171,7 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { break; case SCI_EVENT_KEYBOARD: eventKey = readSelectorValue(_segMan, eventObject, SELECTOR(message)); + modifiers = readSelectorValue(_segMan, eventObject, SELECTOR(modifiers)); switch (eventKey) { case SCI_KEY_BACKSPACE: if (cursorPos > 0) { @@ -177,8 +180,10 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { } break; case SCI_KEY_DELETE: - text.deleteChar(cursorPos); - textChanged = true; + if (cursorPos < textSize) { + text.deleteChar(cursorPos); + textChanged = true; + } break; case SCI_KEY_HOME: // HOME cursorPos = 0; textChanged = true; @@ -196,8 +201,20 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { cursorPos++; textChanged = true; } break; + case 3: // returned in SCI1 late and newer when Control - C is pressed + if (modifiers & SCI_KEYMOD_CTRL) { + // Control-C erases the whole line + cursorPos = 0; text.clear(); + textChanged = true; + } + break; default: - if (eventKey > 31 && eventKey < 256 && textSize < maxChars) { + if ((modifiers & SCI_KEYMOD_CTRL) && eventKey == 99) { + // Control-C in earlier SCI games (SCI0 - SCI1 middle) + // Control-C erases the whole line + cursorPos = 0; text.clear(); + textChanged = true; + } else if (eventKey > 31 && eventKey < 256 && textSize < maxChars) { // insert pressed character textAddChar = true; textChanged = true; @@ -208,6 +225,11 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { } } + if (g_sci->getVocabulary() && !textChanged && oldCursorPos != cursorPos) { + assert(!textAddChar); + textChanged = g_sci->getVocabulary()->checkAltInput(text, cursorPos); + } + if (textChanged) { GuiResourceId oldFontId = _text16->GetFontId(); GuiResourceId fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font)); @@ -215,18 +237,28 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { readSelectorValue(_segMan, controlObject, SELECTOR(nsRight)), readSelectorValue(_segMan, controlObject, SELECTOR(nsBottom))); _text16->SetFont(fontId); if (textAddChar) { - // We check, if we are really able to add the new char - uint16 textWidth = 0; + const char *textPtr = text.c_str(); + + // We check if we are really able to add the new char + uint16 textWidth = 0; while (*textPtr) - textWidth += _text16->_font->getCharWidth(*textPtr++); + textWidth += _text16->_font->getCharWidth((byte)*textPtr++); textWidth += _text16->_font->getCharWidth(eventKey); + + // Does it fit? if (textWidth >= rect.width()) { _text16->SetFont(oldFontId); return; } + text.insertChar(eventKey, cursorPos++); + + // Note: the following checkAltInput call might make the text + // too wide to fit, but SSCI fails to check that too. } + if (g_sci->getVocabulary()) + g_sci->getVocabulary()->checkAltInput(text, cursorPos); texteditCursorErase(); _paint16->eraseRect(rect); _text16->Box(text.c_str(), 0, rect, SCI_TEXT16_ALIGNMENT_LEFT, -1); diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp index a906899113..7a37d7e865 100644 --- a/engines/sci/graphics/cursor.cpp +++ b/engines/sci/graphics/cursor.cpp @@ -49,10 +49,21 @@ GfxCursor::GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *sc // center mouse cursor setPosition(Common::Point(_screen->getWidth() / 2, _screen->getHeight() / 2)); _moveZoneActive = false; + + _zoomZoneActive = false; + _zoomZone = Common::Rect(); + _zoomCursorView = 0; + _zoomCursorLoop = 0; + _zoomCursorCel = 0; + _zoomPicView = 0; + _zoomColor = 0; + _zoomMultiplier = 0; + _cursorSurface = 0; } GfxCursor::~GfxCursor() { purgeCache(); + kernelClearZoomZone(); } void GfxCursor::init(GfxCoordAdjuster *coordAdjuster, EventManager *event) { @@ -266,6 +277,16 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu kernelShow(); } +// this list contains all mandatory set cursor changes, that need special handling +// ffs. GfxCursor::setPosition (below) +// Game, newPosition, validRect +static const SciCursorSetPositionWorkarounds setPositionWorkarounds[] = { + { GID_ISLANDBRAIN, 84, 109, 46, 76, 174, 243 }, // island of dr. brain / game menu + { GID_LSL5, 23, 171, 0, 0, 26, 320 }, // larry 5 / skip forward helper + { GID_QFG1VGA, 64, 174, 40, 37, 74, 284 }, // Quest For Glory 1 VGA / run/walk/sleep sub-menu + { (SciGameId)0, -1, -1, -1, -1, -1, -1 } +}; + void GfxCursor::setPosition(Common::Point pos) { // Don't set position, when cursor is not visible. // This fixes eco quest 1 (floppy) right at the start, which is setting @@ -282,6 +303,31 @@ void GfxCursor::setPosition(Common::Point pos) { _screen->adjustToUpscaledCoordinates(pos.y, pos.x); g_system->warpMouse(pos.x, pos.y); } + + // Some games display a new menu, set mouse position somewhere within and + // expect it to be in there. This is fine for a real mouse, but on wii using + // wii-mote or touch interfaces this won't work. In fact on those platforms + // the menus will close immediately because of that behaviour. + // We identify those cases and set a reaction-rect. If the mouse it outside + // of that rect, we won't report the position back to the scripts. + // As soon as the mouse was inside once, we will revert to normal behaviour + // Currently this code is enabled for all platforms, especially because we can't + // differentiate between e.g. Windows used via mouse and Windows used via touchscreen + // The workaround won't hurt real-mouse platforms + const SciGameId gameId = g_sci->getGameId(); + const SciCursorSetPositionWorkarounds *workaround; + workaround = setPositionWorkarounds; + while (workaround->newPositionX != -1) { + if (workaround->gameId == gameId + && ((workaround->newPositionX == pos.x) && (workaround->newPositionY == pos.y))) { + EngineState *s = g_sci->getEngineState(); + s->_cursorWorkaroundActive = true; + s->_cursorWorkaroundPoint = pos; + s->_cursorWorkaroundRect = Common::Rect(workaround->rectLeft, workaround->rectTop, workaround->rectRight, workaround->rectBottom); + return; + } + workaround++; + } } Common::Point GfxCursor::getPosition() { @@ -294,9 +340,10 @@ Common::Point GfxCursor::getPosition() { } void GfxCursor::refreshPosition() { + Common::Point mousePoint = getPosition(); + if (_moveZoneActive) { bool clipped = false; - Common::Point mousePoint = getPosition(); if (mousePoint.x < _moveZone.left) { mousePoint.x = _moveZone.left; @@ -318,6 +365,52 @@ void GfxCursor::refreshPosition() { if (clipped) setPosition(mousePoint); } + + if (_zoomZoneActive) { + // Cursor + const CelInfo *cursorCelInfo = _zoomCursorView->getCelInfo(_zoomCursorLoop, _zoomCursorCel); + const byte *cursorBitmap = _zoomCursorView->getBitmap(_zoomCursorLoop, _zoomCursorCel); + // Pic + const CelInfo *picCelInfo = _zoomPicView->getCelInfo(0, 0); + const byte *rawPicBitmap = _zoomPicView->getBitmap(0, 0); + + // Compute hotspot of cursor + Common::Point cursorHotspot = Common::Point((cursorCelInfo->width >> 1) - cursorCelInfo->displaceX, cursorCelInfo->height - cursorCelInfo->displaceY - 1); + + int16 targetX = ((mousePoint.x - _moveZone.left) * _zoomMultiplier); + int16 targetY = ((mousePoint.y - _moveZone.top) * _zoomMultiplier); + if (targetX < 0) + targetX = 0; + if (targetY < 0) + targetY = 0; + + targetX -= cursorHotspot.x; + targetY -= cursorHotspot.y; + + // Sierra SCI actually drew only within zoom area, thus removing the need to fill any other pixels with upmost/left + // color of the picture cel. This also made the cursor not appear on top of everything. They actually drew the + // cursor manually within kAnimate processing and used a hidden cursor for moving. + // TODO: we should also do this + + // Replace the special magnifier color with the associated magnified pixels + for (int x = 0; x < cursorCelInfo->width; x++) { + for (int y = 0; y < cursorCelInfo->height; y++) { + int curPos = cursorCelInfo->width * y + x; + if (cursorBitmap[curPos] == _zoomColor) { + int16 rawY = targetY + y; + int16 rawX = targetX + x; + if ((rawY >= 0) && (rawY < picCelInfo->height) && (rawX >= 0) && (rawX < picCelInfo->width)) { + int rawPos = picCelInfo->width * rawY + rawX; + _cursorSurface[curPos] = rawPicBitmap[rawPos]; + } else { + _cursorSurface[curPos] = rawPicBitmap[0]; // use left and upmost pixel color + } + } + } + } + + CursorMan.replaceCursor((const byte *)_cursorSurface, cursorCelInfo->width, cursorCelInfo->height, cursorHotspot.x, cursorHotspot.y, cursorCelInfo->clearKey); + } } void GfxCursor::kernelResetMoveZone() { @@ -329,6 +422,44 @@ void GfxCursor::kernelSetMoveZone(Common::Rect zone) { _moveZoneActive = true; } +void GfxCursor::kernelClearZoomZone() { + kernelResetMoveZone(); + _zoomZone = Common::Rect(); + _zoomColor = 0; + _zoomMultiplier = 0; + _zoomZoneActive = false; + delete _zoomCursorView; + _zoomCursorView = 0; + delete _zoomPicView; + _zoomPicView = 0; + delete[] _cursorSurface; + _cursorSurface = 0; +} + +void GfxCursor::kernelSetZoomZone(byte multiplier, Common::Rect zone, GuiResourceId viewNum, int loopNum, int celNum, GuiResourceId picNum, byte zoomColor) { + kernelClearZoomZone(); + + _zoomMultiplier = multiplier; + + if (_zoomMultiplier != 1 && _zoomMultiplier != 2 && _zoomMultiplier != 4) + error("Unexpected zoom multiplier (expected 1, 2 or 4)"); + + _zoomCursorView = new GfxView(_resMan, _screen, _palette, viewNum); + _zoomCursorLoop = (byte)loopNum; + _zoomCursorCel = (byte)celNum; + _zoomPicView = new GfxView(_resMan, _screen, _palette, picNum); + const CelInfo *cursorCelInfo = _zoomCursorView->getCelInfo(_zoomCursorLoop, _zoomCursorCel); + const byte *cursorBitmap = _zoomCursorView->getBitmap(_zoomCursorLoop, _zoomCursorCel); + _cursorSurface = new byte[cursorCelInfo->width * cursorCelInfo->height]; + memcpy(_cursorSurface, cursorBitmap, cursorCelInfo->width * cursorCelInfo->height); + + _zoomZone = zone; + kernelSetMoveZone(_zoomZone); + + _zoomColor = zoomColor; + _zoomZoneActive = true; +} + void GfxCursor::kernelSetPos(Common::Point pos) { _coordAdjuster->setCursorPos(pos); kernelMoveCursor(pos); diff --git a/engines/sci/graphics/cursor.h b/engines/sci/graphics/cursor.h index 787841f5be..ae3b51e26a 100644 --- a/engines/sci/graphics/cursor.h +++ b/engines/sci/graphics/cursor.h @@ -40,6 +40,16 @@ class GfxPalette; typedef Common::HashMap<int, GfxView *> CursorCache; +struct SciCursorSetPositionWorkarounds { + SciGameId gameId; + int16 newPositionY; + int16 newPositionX; + int16 rectTop; + int16 rectLeft; + int16 rectBottom; + int16 rectRight; +}; + class GfxCursor { public: GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *screen); @@ -69,6 +79,9 @@ public: */ void kernelSetMoveZone(Common::Rect zone); + void kernelClearZoomZone(); + void kernelSetZoomZone(byte multiplier, Common::Rect zone, GuiResourceId viewNum, int loopNum, int celNum, GuiResourceId picNum, byte zoomColor); + void kernelSetPos(Common::Point pos); void kernelMoveCursor(Common::Point pos); @@ -86,6 +99,16 @@ private: bool _moveZoneActive; Common::Rect _moveZone; // Rectangle in which the pointer can move + bool _zoomZoneActive; + Common::Rect _zoomZone; + GfxView *_zoomCursorView; + byte _zoomCursorLoop; + byte _zoomCursorCel; + GfxView *_zoomPicView; + byte _zoomColor; + byte _zoomMultiplier; + byte *_cursorSurface; + CursorCache _cachedCursors; bool _isVisible; diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index a433b26ef2..fc374ea143 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -70,6 +70,7 @@ void GfxFrameout::kernelAddPlane(reg_t object) { newPlane.pictureId = 0xFFFF; newPlane.priority = readSelectorValue(_segMan, object, SELECTOR(priority)); newPlane.lastPriority = 0xFFFF; // hidden + newPlane.planeOffsetX = 0; _planes.push_back(newPlane); kernelUpdatePlane(object); @@ -91,6 +92,43 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) { addPlanePicture(object, it->pictureId, 0); } } + it->planeRect.top = readSelectorValue(_segMan, object, SELECTOR(top)); + it->planeRect.left = readSelectorValue(_segMan, object, SELECTOR(left)); + it->planeRect.bottom = readSelectorValue(_segMan, object, SELECTOR(bottom)) + 1; + it->planeRect.right = readSelectorValue(_segMan, object, SELECTOR(right)) + 1; + + Common::Rect screenRect(_screen->getWidth(), _screen->getHeight()); + it->planeRect.top = (it->planeRect.top * screenRect.height()) / scriptsRunningHeight; + it->planeRect.left = (it->planeRect.left * screenRect.width()) / scriptsRunningWidth; + it->planeRect.bottom = (it->planeRect.bottom * screenRect.height()) / scriptsRunningHeight; + it->planeRect.right = (it->planeRect.right * screenRect.width()) / scriptsRunningWidth; + + // We get negative left in kq7 in scrolling rooms + if (it->planeRect.left < 0) { + it->planeOffsetX = -it->planeRect.left; + it->planeRect.left = 0; + } + if (it->planeRect.top < 0) + it->planeRect.top = 0; + // We get bad plane-bottom in sq6 + if (it->planeRect.right > _screen->getWidth()) + it->planeRect.right = _screen->getWidth(); + if (it->planeRect.bottom > _screen->getHeight()) + it->planeRect.bottom = _screen->getHeight(); + + it->planeClipRect = Common::Rect(it->planeRect.width(), it->planeRect.height()); + it->upscaledPlaneRect = it->planeRect; + it->upscaledPlaneClipRect = it->planeClipRect; + if (_screen->getUpscaledHires()) { + _screen->adjustToUpscaledCoordinates(it->upscaledPlaneRect.top, it->upscaledPlaneRect.left); + _screen->adjustToUpscaledCoordinates(it->upscaledPlaneRect.bottom, it->upscaledPlaneRect.right); + _screen->adjustToUpscaledCoordinates(it->upscaledPlaneClipRect.top, it->upscaledPlaneClipRect.left); + _screen->adjustToUpscaledCoordinates(it->upscaledPlaneClipRect.bottom, it->upscaledPlaneClipRect.right); + } + + it->planePictureMirrored = readSelectorValue(_segMan, object, SELECTOR(mirrored)); + it->planeBack = readSelectorValue(_segMan, object, SELECTOR(back)); + sortPlanes(); return; } @@ -98,6 +136,10 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) { error("kUpdatePlane called on plane that wasn't added before"); } +void GfxFrameout::kernelRepaintPlane(reg_t object) { + // TODO +} + void GfxFrameout::kernelDeletePlane(reg_t object) { deletePlanePictures(object); for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) { @@ -147,6 +189,10 @@ void GfxFrameout::kernelAddScreenItem(reg_t object) { _screenItems.push_back(object); } +void GfxFrameout::kernelUpdateScreenItem(reg_t object) { + // TODO +} + void GfxFrameout::kernelDeleteScreenItem(reg_t object) { for (uint32 itemNr = 0; itemNr < _screenItems.size(); itemNr++) { if (_screenItems[itemNr] == object) { @@ -207,72 +253,31 @@ void GfxFrameout::kernelFrameout() { _palette->palVaryUpdate(); // Allocate enough space for all screen items + // TODO: Modify _screenItems to hold FrameoutEntry entries instead. + // Creating and destroying this in kernelFrameout() is overkill! FrameoutEntry *itemData = new FrameoutEntry[_screenItems.size()]; for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) { reg_t planeObject = it->object; uint16 planeLastPriority = it->lastPriority; - Common::Rect planeRect; - planeRect.top = readSelectorValue(_segMan, planeObject, SELECTOR(top)); - planeRect.left = readSelectorValue(_segMan, planeObject, SELECTOR(left)); - planeRect.bottom = readSelectorValue(_segMan, planeObject, SELECTOR(bottom)) + 1; - planeRect.right = readSelectorValue(_segMan, planeObject, SELECTOR(right)) + 1; - // Update priority here, sq6 sets it w/o UpdatePlane uint16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority)); - Common::Rect screenRect(_screen->getWidth(), _screen->getHeight()); - planeRect.top = (planeRect.top * screenRect.height()) / scriptsRunningHeight; - planeRect.left = (planeRect.left * screenRect.width()) / scriptsRunningWidth; - planeRect.bottom = (planeRect.bottom * screenRect.height()) / scriptsRunningHeight; - planeRect.right = (planeRect.right * screenRect.width()) / scriptsRunningWidth; - - int16 planeOffsetX = 0; - - // We get negative left in kq7 in scrolling rooms - if (planeRect.left < 0) { - planeOffsetX = -planeRect.left; - planeRect.left = 0; - } - if (planeRect.top < 0) - planeRect.top = 0; - // We get bad plane-bottom in sq6 - if (planeRect.right > _screen->getWidth()) - planeRect.right = _screen->getWidth(); - if (planeRect.bottom > _screen->getHeight()) - planeRect.bottom = _screen->getHeight(); - it->lastPriority = planePriority; if (planePriority == 0xffff) { // Plane currently not meant to be shown // If plane was shown before, delete plane rect if (planePriority != planeLastPriority) - _paint32->fillRect(planeRect, 0); + _paint32->fillRect(it->planeRect, 0); continue; } - Common::Rect planeClipRect(planeRect.width(), planeRect.height()); - - Common::Rect upscaledPlaneRect = planeRect; - Common::Rect upscaledPlaneClipRect = planeClipRect; - if (_screen->getUpscaledHires()) { - _screen->adjustToUpscaledCoordinates(upscaledPlaneRect.top, upscaledPlaneRect.left); - _screen->adjustToUpscaledCoordinates(upscaledPlaneRect.bottom, upscaledPlaneRect.right); - _screen->adjustToUpscaledCoordinates(upscaledPlaneClipRect.top, upscaledPlaneClipRect.left); - _screen->adjustToUpscaledCoordinates(upscaledPlaneClipRect.bottom, upscaledPlaneClipRect.right); - } - - byte planeBack = readSelectorValue(_segMan, planeObject, SELECTOR(back)); - if (planeBack) - _paint32->fillRect(planeRect, planeBack); + if (it->planeBack) + _paint32->fillRect(it->planeRect, it->planeBack); GuiResourceId planeMainPictureId = it->pictureId; - bool planePictureMirrored = false; - if (readSelectorValue(_segMan, planeObject, SELECTOR(mirrored))) - planePictureMirrored = true; - - _coordAdjuster->pictureSetDisplayArea(planeRect); + _coordAdjuster->pictureSetDisplayArea(it->planeRect); _palette->drewPicture(planeMainPictureId); // Fill our itemlist for this plane @@ -360,25 +365,25 @@ void GfxFrameout::kernelFrameout() { // Out of view int16 pictureCelStartX = itemEntry->picStartX + itemEntry->x; int16 pictureCelEndX = pictureCelStartX + itemEntry->picture->getSci32celWidth(itemEntry->celNo); - int16 planeStartX = planeOffsetX; - int16 planeEndX = planeStartX + planeRect.width(); + int16 planeStartX = it->planeOffsetX; + int16 planeEndX = planeStartX + it->planeRect.width(); if (pictureCelEndX < planeStartX) continue; if (pictureCelStartX > planeEndX) continue; - int16 pictureOffsetX = planeOffsetX; + int16 pictureOffsetX = it->planeOffsetX; int16 pictureX = itemEntry->x; - if ((planeOffsetX) || (itemEntry->picStartX)) { - if (planeOffsetX <= itemEntry->picStartX) { - pictureX += itemEntry->picStartX - planeOffsetX; + if ((it->planeOffsetX) || (itemEntry->picStartX)) { + if (it->planeOffsetX <= itemEntry->picStartX) { + pictureX += itemEntry->picStartX - it->planeOffsetX; pictureOffsetX = 0; } else { - pictureOffsetX = planeOffsetX - itemEntry->picStartX; + pictureOffsetX = it->planeOffsetX - itemEntry->picStartX; } } - itemEntry->picture->drawSci32Vga(itemEntry->celNo, pictureX, itemEntry->y, pictureOffsetX, planePictureMirrored); + itemEntry->picture->drawSci32Vga(itemEntry->celNo, pictureX, itemEntry->y, pictureOffsetX, it->planePictureMirrored); // warning("picture cel %d %d", itemEntry->celNo, itemEntry->priority); } else if (itemEntry->viewId != 0xFFFF) { @@ -403,7 +408,7 @@ void GfxFrameout::kernelFrameout() { break; } // Adjust according to current scroll position - itemEntry->x -= planeOffsetX; + itemEntry->x -= it->planeOffsetX; uint16 useInsetRect = readSelectorValue(_segMan, itemEntry->object, SELECTOR(useInsetRect)); if (useInsetRect) { @@ -426,7 +431,7 @@ void GfxFrameout::kernelFrameout() { Common::Rect nsRect = itemEntry->celRect; // Translate back to actual coordinate within scrollable plane - nsRect.translate(planeOffsetX, 0); + nsRect.translate(it->planeOffsetX, 0); switch (getSciVersion()) { case SCI_VERSION_2: if (view->isSci2Hires()) { @@ -465,13 +470,13 @@ void GfxFrameout::kernelFrameout() { Common::Rect clipRect, translatedClipRect; clipRect = itemEntry->celRect; if (view->isSci2Hires()) { - clipRect.clip(upscaledPlaneClipRect); + clipRect.clip(it->upscaledPlaneClipRect); translatedClipRect = clipRect; - translatedClipRect.translate(upscaledPlaneRect.left, upscaledPlaneRect.top); + translatedClipRect.translate(it->upscaledPlaneRect.left, it->upscaledPlaneRect.top); } else { - clipRect.clip(planeClipRect); + clipRect.clip(it->planeClipRect); translatedClipRect = clipRect; - translatedClipRect.translate(planeRect.left, planeRect.top); + translatedClipRect.translate(it->planeRect.left, it->planeRect.top); } if (!clipRect.isEmpty()) { @@ -501,8 +506,8 @@ void GfxFrameout::kernelFrameout() { itemEntry->y = ((itemEntry->y * _screen->getHeight()) / scriptsRunningHeight); itemEntry->x = ((itemEntry->x * _screen->getWidth()) / scriptsRunningWidth); - uint16 curX = itemEntry->x + planeRect.left; - uint16 curY = itemEntry->y + planeRect.top; + uint16 curX = itemEntry->x + it->planeRect.left; + uint16 curY = itemEntry->y + it->planeRect.top; for (uint32 i = 0; i < text.size(); i++) { unsigned char curChar = text[i]; // TODO: proper text splitting... this is a hack diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index f8f7e54a27..07297a91af 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -32,7 +32,14 @@ struct PlaneEntry { reg_t object; uint16 priority; uint16 lastPriority; + int16 planeOffsetX; GuiResourceId pictureId; + Common::Rect planeRect; + Common::Rect planeClipRect; + Common::Rect upscaledPlaneRect; + Common::Rect upscaledPlaneClipRect; + bool planePictureMirrored; + byte planeBack; }; typedef Common::List<PlaneEntry> PlaneList; @@ -81,8 +88,10 @@ public: void kernelAddPlane(reg_t object); void kernelUpdatePlane(reg_t object); + void kernelRepaintPlane(reg_t object); void kernelDeletePlane(reg_t object); void kernelAddScreenItem(reg_t object); + void kernelUpdateScreenItem(reg_t object); void kernelDeleteScreenItem(reg_t object); int16 kernelGetHighPlanePri(); void kernelAddPicAt(reg_t planeObj, int16 forWidth, GuiResourceId pictureId); diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp index 630626c128..06470bc560 100644 --- a/engines/sci/graphics/menu.cpp +++ b/engines/sci/graphics/menu.cpp @@ -905,7 +905,7 @@ void GfxMenu::kernelDrawStatus(const char *text, int16 colorPen, int16 colorBack _paint16->fillRect(_ports->_menuBarRect, 1, colorBack); _ports->penColor(colorPen); _ports->moveTo(0, 1); - _text16->Draw_String(text); + _text16->Draw_Status(text); _paint16->bitsShow(_ports->_menuBarRect); _ports->setPort(oldPort); } diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp index dbc738e2f7..3c115f0c8e 100644 --- a/engines/sci/graphics/paint16.cpp +++ b/engines/sci/graphics/paint16.cpp @@ -380,6 +380,14 @@ void GfxPaint16::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, b drawPicture(pictureId, animationNr, mirroredFlag, addToFlag, EGApaletteNo); _transitions->setup(animationNr, animationBlackoutFlag); } else { + // We need to set it for SCI1EARLY+ (sierra sci also did so), otherwise we get at least the following issues: + // LSL5 (english) - last wakeup (taj mahal flute dream) + // SQ5 (english v1.03) - during the scene following the scrubbing + // in both situations a window is shown when kDrawPic is called, which would result otherwise in + // no showpic getting called from kAnimate and we would get graphic corruption + // XMAS1990 EGA did not set it in this case, VGA did + if (getSciVersion() >= SCI_VERSION_1_EARLY) + _screen->_picNotValid = 1; _ports->beginUpdate(_ports->_picWind); drawPicture(pictureId, animationNr, mirroredFlag, addToFlag, EGApaletteNo); _ports->endUpdate(_ports->_picWind); diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index 5c17f76558..76b2ed53fc 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -340,7 +340,8 @@ void GfxPalette::drewPicture(GuiResourceId pictureId) { _sysPalette.timestamp++; if (_palVaryResourceId != -1) { - palVaryLoadTargetPalette(pictureId); + if (g_sci->getEngineState()->gameIsRestarting == 0) // only if not restored nor restarted + palVaryLoadTargetPalette(pictureId); } } @@ -613,9 +614,18 @@ bool GfxPalette::kernelPalVaryInit(GuiResourceId resourceId, uint16 ticks, uint1 _palVaryStepStop = stepStop; _palVaryDirection = direction; // if no ticks are given, jump directly to destination - if (!_palVaryTicks) + if (!_palVaryTicks) { _palVaryDirection = stepStop; - palVaryInstallTimer(); + // sierra sci set the timer to 1 tick instead of calling it directly + // we have to change this to prevent a race condition to happen in + // at least freddy pharkas during nighttime. In that case kPalVary is + // called right before a transition and because we load pictures much + // faster, the 1 tick won't pass sometimes resulting in the palette + // being daytime instead of nighttime during the transition. + palVaryProcess(1, true); + } else { + palVaryInstallTimer(); + } return true; } return false; @@ -632,9 +642,14 @@ int16 GfxPalette::kernelPalVaryReverse(int16 ticks, uint16 stepStop, int16 direc _palVaryStepStop = stepStop; _palVaryDirection = direction != -1 ? -direction : -_palVaryDirection; - if (!_palVaryTicks) + if (!_palVaryTicks) { _palVaryDirection = _palVaryStepStop - _palVaryStep; - palVaryInstallTimer(); + // ffs. see palVaryInit right above, we fix the code here as well + // just in case + palVaryProcess(1, true); + } else { + palVaryInstallTimer(); + } return kernelPalVaryGetCurrentStep(); } diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp index e568316919..39666b82cb 100644 --- a/engines/sci/graphics/picture.cpp +++ b/engines/sci/graphics/picture.cpp @@ -120,10 +120,6 @@ void GfxPicture::drawSci11Vga() { // [priorityBandData:WORD] * priorityBandCount // [priority:BYTE] [unknown:BYTE] - // Create palette and set it - _palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette); - _palette->set(&palette, true); - // priority bands are supposed to be 14 for sci1.1 pictures assert(priorityBandsCount == 14); @@ -132,8 +128,13 @@ void GfxPicture::drawSci11Vga() { } // display Cel-data - if (has_cel) + if (has_cel) { + // Create palette and set it + _palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette); + _palette->set(&palette, true); + drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0, 0); + } // process vector data drawVectorData(inbuffer + vector_dataPos, vector_size); @@ -852,11 +853,11 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by // Now remove screens, that already got the right color/priority/control if ((screenMask & GFX_SCREEN_MASK_VISUAL) && (searchColor == color)) - screenMask ^= GFX_SCREEN_MASK_VISUAL; + screenMask &= ~GFX_SCREEN_MASK_VISUAL; if ((screenMask & GFX_SCREEN_MASK_PRIORITY) && (searchPriority == priority)) - screenMask ^= GFX_SCREEN_MASK_PRIORITY; + screenMask &= ~GFX_SCREEN_MASK_PRIORITY; if ((screenMask & GFX_SCREEN_MASK_CONTROL) && (searchControl == control)) - screenMask ^= GFX_SCREEN_MASK_CONTROL; + screenMask &= ~GFX_SCREEN_MASK_CONTROL; // Exit, if no screens left if (!screenMask) diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp index 8f4fe094a8..f21fa39476 100644 --- a/engines/sci/graphics/portrait.cpp +++ b/engines/sci/graphics/portrait.cpp @@ -185,7 +185,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint curEvent = _event->getSciEvent(SCI_EVENT_ANY); if (curEvent.type == SCI_EVENT_MOUSE_PRESS || (curEvent.type == SCI_EVENT_KEYBOARD && curEvent.data == SCI_KEY_ESC) || - g_engine->shouldQuit()) + g_sci->getEngineState()->abortScriptProcessing == kAbortQuitGame) userAbort = true; curPosition = _audio->getAudioPosition(); } while ((curPosition != -1) && (curPosition < timerPosition) && (!userAbort)); diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp index dddd9b1c86..e7f319a25c 100644 --- a/engines/sci/graphics/ports.cpp +++ b/engines/sci/graphics/ports.cpp @@ -105,16 +105,9 @@ void GfxPorts::init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *te case GID_CNICK_KQ: offTop = 0; break; - case GID_MOTHERGOOSE: - // TODO: if mother goose EGA also uses offTop we can simply remove this check altogether - switch (getSciVersion()) { - case SCI_VERSION_1_EARLY: - case SCI_VERSION_1_1: - offTop = 0; - break; - default: - break; - } + case GID_MOTHERGOOSE256: + // only the SCI1 and SCI1.1 (VGA) versions need this + offTop = 0; break; case GID_FAIRYTALES: // Mixed-Up Fairy Tales (& its demo) uses -w 26 0 200 320. If we don't @@ -308,7 +301,7 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor Common::Rect r; if (!pwnd) { - error("Can't open window!"); + error("Can't open window"); return 0; } @@ -376,6 +369,20 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor if (draw) drawWindow(pwnd); setPort((Port *)pwnd); + + // FIXME: changing setOrigin to not clear the rightmost bit fixes the display of windows + // in some fanmade games (e.g. New Year's Mystery (Updated)). Since the fanmade games + // use an unmodified SCI interpreter, this leads me to believe that there either is some + // off-by-one error in the window drawing code, or there is another place where the + // rightmost bit should be cleeared. New Year's Mystery is a good test case for this, as + // it draws dialogs and then draws cels on top of them, for fancier dialog corners (like, + // for example, KQ5). KQ5 has a custom window style, however, whereas New Year's mystery + // has a "classic" style with only SCI_WINDOWMGR_STYLE_NOFRAME set. If + // SCI_WINDOWMGR_STYLE_NOFRAME is removed, the window is cleared correctly, because it + // grows slightly, covering the view pixels on the left. In any case, the views and the + // window have a difference of one pixel when they're drawn via kNewWindow and kDrawCel, + // which causes the glitch to appear when the window is closed. + // All SCI0 games till kq4 .502 (not including) did not adjust against _wmgrPort, we set _wmgrPort->top to 0 in that case setOrigin(pwnd->rect.left, pwnd->rect.top + _wmgrPort->top); pwnd->rect.moveTo(0, 0); diff --git a/engines/sci/graphics/robot.cpp b/engines/sci/graphics/robot.cpp index 1572a0a9ec..0792c6596e 100644 --- a/engines/sci/graphics/robot.cpp +++ b/engines/sci/graphics/robot.cpp @@ -37,7 +37,6 @@ GfxRobot::GfxRobot(ResourceManager *resMan, GfxScreen *screen, GuiResourceId res : _resMan(resMan), _screen(screen), _resourceId(resourceId) { assert(resourceId != -1); initData(resourceId); - _resourceData = 0; } GfxRobot::~GfxRobot() { @@ -57,119 +56,13 @@ void GfxRobot::initData(GuiResourceId resourceId) { warning("Unable to open robot file %s", fileName); return; } - - byte version = _resourceData[6]; - if (version != 4 && version != 5) { - warning("Robot version %d isn't supported yet", version); - return; - } - -// sample data: -// Header - 14 bytes -// DWORD:Sample Size - 2 needs to be subtracted (??!!) -// ??? -// Actual samples following - -// version may be 3, 4 and 5 -// version 3 has a different header (unknown to this point) -// -// main header (56 bytes + 2 bytes resource id) -// followed by sample data if hasSound == 1 -// - -// 90.rbt (640x390, 22050, 1 16, ADPCM) 67 frames -// 00000000: 16 00 53 4f 4c 00 05 00-ad 08 00 00 f0 00 43 00 ..SOL.........C. -// ^ signature ^ version ^ ^ ^ frames -// ^ 2221 -// 00000010: b0 04 00 a0 00 00 00 00-01 01 00 00 0a 00 01 00 -// ^ ^ ^ ^ ^ ^ ^ ^ ^ -// hasSound -// 00000020: 03 00 01 00 00 cf 03 00-00 00 00 00 00 00 00 00 -// ^ ^ ^ pixel count ^ -// ^ -// 00000030: 00 00 00 00 00 00 00 00-00 00 00 00 -// ^ ^ -// Sample-Data (Header): -// compression must be 0 for now -// 0000003c: f2 9f 00 00 00 00 d2 4d 00 00 20 52-00 00 -// ^ ^ ^ -// byte count compression -// 40946 -// Actual Samples following -// a5 11 04 02 85 90 ...M.. R........ -// -// Offset 41020 -// Palette - -// 91.rbt (320x240, 22050, 1 16, ADPCM) 90 frames -// 00000000: 16 00 53 4f 4c 00 05 00-ad 08 00 00 f0 00 5a 00 ..SOL.........Z. -// ^ frames -// 00000010: b0 04 00 a0 00 00 00 00-01 01 00 00 0a 00 01 00 ................ -// 00000020: 03 00 01 00 00 2c 01 00-00 00 00 00 00 00 00 00 .....,.......... -// ^ pixel count -// 00000030: 00 00 00 00 00 00 00 00-00 00 00 00 f2 9f 00 00 ................ offset 60 -// ^ data begin (sample) -// 00000040: 00 00 d2 4d 00 00 20 52-00 00 82 01 00 01 00 01 ...M.. R........ -// ... -// 0000a030: 8d 8d 8f 8e 8f 90 90 91-92 92 92 94 0e 00 00 00 ................ offset 41004 -// ^ palette start -// 0000a040: 00 00 00 00 00 00 01 00-00 09 01 00 00 00 00 00 ................ -// 0000a050: 00 00 00 00 00 37 00 00-00 51 00 01 01 00 00 00 .....7...Q...... -// ^ color start^ color count -// 0000a060: 00 58 6b 2b 4b 69 28 50-5b 24 68 50 20 5b 53 21 .Xk+Ki(P[$hP [S! -// ^ start pal data -// [...] -// 0000a110: 24 05 41 14 04 18 25 10-64 00 00 2d 18 05 58 00 $.A...%.d..-..X. -// 0000a120: 00 16 20 07 50 00 00 20-19 01 2d 0e 00 48 00 00 .. .P.. ..-..H.. -// 0000a130: 40 00 00 10 18 05 38 00-00 30 00 00 28 00 00 0b @.....8..0..(... -// 0000a140: 0e 00 20 00 00 18 00 00-00 08 00 10 00 00 08 00 .. ............. -// 0000a150: 00 00 00 00 70 70 70 70-70 70 70 70 70 70 70 70 ....pppppppppppp -// [...] -// 0000a4e0: 70 70 70 70 70 70 70 70-70 70 70 70 34 0a 75 0a pppppppppppp4.u. -// 0000a4f0: 4a 0b c5 0b f4 0b 54 0c-bd 0c 7a 0d 91 0e 1f 10 J.....T...z..... -// 0000a500: 16 12 72 14 19 17 ef 19-9a 1c b3 1e 79 20 c1 22 ..r.........y ." -// 0000a510: 33 22 33 23 e0 25 84 26-eb 26 1a 2d 43 2d af 2d 3"3#.%.&.&.-C-.- -// [...] -// 0000aff0: 20 20 20 20 20 20 20 20-20 20 20 20 20 20 20 20 -// 0000b000: 01 00 7f 64 40 01 f0 00-00 00 00 00 00 00 00 00 ...d@........... -// ^width^height -// 0000b010: 1c 0a 02 00 7f 7f 7f 7f-04 08 00 00 00 f0 00 00 ................ -// 0000b020: 00 00 43 e0 7f ff ff ff-ff ff ff ff ff ff ff ff ..C............. -// 0000b030: ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff ................ - -// 161.rbt (112x155, 22050, 1 16, ADPCM) 29 frames -// 00000000: 16 00 53 4f 4c 00 05 00-ad 08 00 00 96 00 1d 00 ..SOL........... -// ^ frames -// 00000010: b0 04 00 a0 00 00 00 00-01 01 00 00 0a 00 01 00 ................ -// 00000020: 03 00 01 00 47 3e 00 00-00 00 00 00 00 00 00 00 ....G>.......... -// ^ pixel count -// 00000030: 00 00 00 00 00 00 00 00-00 00 00 00 f2 9f 00 00 ................ -// ^ data begin (sample) -// 00000040: 00 00 d2 4d 00 00 20 52-00 00 00 00 00 00 00 00 ...M.. R........ -// 00000050: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ - -// 213.rbt (125x248, nosound) 30 frames -// 00000000: 16 00 53 4f 4c 00 05 00-ad 08 00 00 96 00 1e 00 ..SOL........... -// ^ frames -// 00000010: b0 04 00 00 00 00 00 00-01 00 00 00 0a 00 01 00 ................ -// ^ ?! ^ no sound?! -// 00000020: 03 00 01 00 82 6e 00 00-00 00 00 00 00 00 00 00 .....n.......... -// ^ pixel count -// 00000030: 00 00 00 00 00 00 00 00-00 00 00 00 0e 00 00 00 ................ -// ^ data begin (palette) -// 00000040: 00 00 00 00 00 00 01 00-00 ca 00 00 00 00 00 00 ................ -// 00000050: 00 00 00 00 00 37 00 00-00 3c 00 01 01 00 00 00 .....7...<...... -// 00000060: 00 d0 d0 c0 d0 c0 a8 c8-b8 c0 d0 b0 a0 c0 a8 88 -// ^ palette data start -// 00000070: c0 a0 a0 c8 98 90 d0 88-60 b0 90 80 b8 88 80 a0 ........`....... -// 00000080: 90 98 b0 88 90 c0 78 60-a0 80 80 a0 80 70 c8 70 ......x`.....p.p -// [...] -// 00000110: 00 00 00 00 08 70 70 70-70 70 70 70 70 70 70 70 .....ppppppppppp -// ^ ?? -// 00000120: 70 70 70 70 70 70 70 70-70 70 70 70 70 70 70 70 pppppppppppppppp + // The RBT video starts with a SOL audio file, followed by + // video data which is appended after it _frameCount = READ_LE_UINT16(_resourceData + 14); + _audioSize = READ_LE_UINT16(_resourceData + 15); + //_frameSize = READ_LE_UINT32(_resourceData + 34); byte hasSound = _resourceData[25]; @@ -179,21 +72,57 @@ void GfxRobot::initData(GuiResourceId resourceId) { // TODO: just trying around in here... void GfxRobot::draw() { - byte *bitmapData = _resourceData + ROBOT_FILE_STARTOFDATA; + byte *bitmapData = _resourceData + _audioSize; int x, y; - //int frame; + int frame; return; - //for (frame = 0; frame < 30; frame++) { - for (y = 0; y < _height; y++) { - for (x = 0; x < _width; x++) { - _screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL, *bitmapData, 0, 0); - bitmapData++; + // Each frame contains these bytes: + // 01 00 7f 64 - always the same, perhaps resource type + extra + // 40 01 - total frame width (320 in this case) + // f0 00 - total frame height (240 in this case) + // The total video size is calculated from the maximum width, height + // of all the frames in the robot file + // 4 zeroes + // 4 bytes, perhaps frame x, y on screen? + // 2 bytes, unknown + // 2 bytes, a small number (e.g. 01 00 or 02 00) + // 7f 7f - 127x127 + // 7f 7f - 127x127 + // 2 bytes, related to frame size? + // 00 00 + // 00 f0 + // 4 zeroes + // 43 e0 + // 7f ff + + // The frames themselves seem to contain a size of the actual drawn data + // on screen. The frame data seems to be uncompressed, placed on screen + // at appropriate x,y coordinates, and each frame can have a different size. + // This is apparent from the fact that a 320x240 frame (e.g. in Phantasmagoria + // demo, 91.rbt) has 4833, 4898, 5111, etc bytes, whereas a full frame would + // be 320x240 = 76800 bytes. Thus, each frame is either somehow compressed + // (but the data seems uncompressed?), or only the part that changes is drawn + // on screen, something like the MPEG I-frames + + for (frame = 0; frame < _frameCount; frame++) { + bitmapData += 4; // skip header bytes + _width = READ_LE_UINT16(bitmapData + 4); bitmapData += 2; + _height = READ_LE_UINT16(bitmapData + 6); bitmapData += 2; + + for (y = 0; y < _width; y++) { + for (x = 0; x < _height; x++) { + _screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL, *bitmapData, 0, 0); + bitmapData++; + } } + + _screen->copyToScreen(); + // Sleep for a second + g_sci->sleep(1000); } - //} - _screen->copyToScreen(); + } #endif diff --git a/engines/sci/graphics/robot.h b/engines/sci/graphics/robot.h index 3ea9a7f735..76dca35a82 100644 --- a/engines/sci/graphics/robot.h +++ b/engines/sci/graphics/robot.h @@ -51,6 +51,7 @@ private: uint16 _height; uint16 _frameCount; uint32 _frameSize; // is width * height (pixelCount) + uint16 _audioSize; }; #endif diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index 839b9975c5..6eabc7c9f0 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -345,11 +345,11 @@ byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byt int offset = y * _width + x; byte match = 0; - if (screenMask & GFX_SCREEN_MASK_VISUAL && *(_visualScreen + offset) == t_color) + if ((screenMask & GFX_SCREEN_MASK_VISUAL) && *(_visualScreen + offset) == t_color) match |= GFX_SCREEN_MASK_VISUAL; - if (screenMask & GFX_SCREEN_MASK_PRIORITY && *(_priorityScreen + offset) == t_pri) + if ((screenMask & GFX_SCREEN_MASK_PRIORITY) && *(_priorityScreen + offset) == t_pri) match |= GFX_SCREEN_MASK_PRIORITY; - if (screenMask & GFX_SCREEN_MASK_CONTROL && *(_controlScreen + offset) == t_con) + if ((screenMask & GFX_SCREEN_MASK_CONTROL) && *(_controlScreen + offset) == t_con) match |= GFX_SCREEN_MASK_CONTROL; return match; } diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp index f5eb268863..3fba3006c7 100644 --- a/engines/sci/graphics/text16.cpp +++ b/engines/sci/graphics/text16.cpp @@ -30,6 +30,7 @@ #include "sci/sci.h" #include "sci/engine/state.h" #include "sci/graphics/cache.h" +#include "sci/graphics/coordadjuster.h" #include "sci/graphics/ports.h" #include "sci/graphics/paint16.h" #include "sci/graphics/font.h" @@ -88,7 +89,7 @@ void GfxText16::ClearChar(int16 chr) { // will process the encountered code and set new font/set color. We only support // one-digit codes currently, don't know if multi-digit codes are possible. // Returns textcode character count. -int16 GfxText16::CodeProcessing(const char *&text, GuiResourceId orgFontId, int16 orgPenColor) { +int16 GfxText16::CodeProcessing(const char *&text, GuiResourceId orgFontId, int16 orgPenColor, bool doingDrawing) { const char *textCode = text; int16 textCodeSize = 0; char curCode; @@ -126,8 +127,20 @@ int16 GfxText16::CodeProcessing(const char *&text, GuiResourceId orgFontId, int1 } } break; - case 'r': // reference?! - // Used in Pepper, no idea how this works out + case 'r': // reference (used in pepper) + if (doingDrawing) { + if (_codeRefTempRect.top == -1) { + // Starting point + _codeRefTempRect.top = _ports->_curPort->curTop; + _codeRefTempRect.left = _ports->_curPort->curLeft; + } else { + // End point reached + _codeRefTempRect.bottom = _ports->_curPort->curTop + _ports->_curPort->fontHeight; + _codeRefTempRect.right = _ports->_curPort->curLeft; + _codeRefRects.push_back(_codeRefTempRect); + _codeRefTempRect.left = _codeRefTempRect.top = -1; + } + } break; } return textCodeSize; @@ -162,7 +175,7 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF case 0x7C: if (getSciVersion() >= SCI_VERSION_1_1) { curCharCount++; - curCharCount += CodeProcessing(text, orgFontId, previousPenColor); + curCharCount += CodeProcessing(text, orgFontId, previousPenColor, false); continue; } break; @@ -258,7 +271,7 @@ void GfxText16::Width(const char *text, int16 from, int16 len, GuiResourceId org break; case 0x7C: if (getSciVersion() >= SCI_VERSION_1_1) { - len -= CodeProcessing(text, orgFontId, 0); + len -= CodeProcessing(text, orgFontId, 0, false); break; } default: @@ -359,7 +372,7 @@ void GfxText16::Draw(const char *text, int16 from, int16 len, GuiResourceId orgF break; case 0x7C: if (getSciVersion() >= SCI_VERSION_1_1) { - len -= CodeProcessing(text, orgFontId, orgPenColor); + len -= CodeProcessing(text, orgFontId, orgPenColor, true); break; } default: @@ -408,6 +421,10 @@ void GfxText16::Box(const char *text, int16 bshow, const Common::Rect &rect, Tex doubleByteMode = true; } + // Reset reference code rects + _codeRefRects.clear(); + _codeRefTempRect.left = _codeRefTempRect.top = -1; + maxTextWidth = 0; while (*text) { charCount = GetLongest(text, rect.width(), fontId); @@ -474,6 +491,32 @@ void GfxText16::Draw_String(const char *text) { _ports->penColor(previousPenColor); } +// we need to have a separate status drawing code +// In KQ4 the IV char is actually 0xA, which would otherwise get considered as linebreak and not printed +void GfxText16::Draw_Status(const char *text) { + uint16 curChar, charWidth; + uint16 textLen = strlen(text); + Common::Rect rect; + + GetFont(); + if (!_font) + return; + + rect.top = _ports->_curPort->curTop; + rect.bottom = rect.top + _ports->_curPort->fontHeight; + while (textLen--) { + curChar = (*(const byte *)text++); + switch (curChar) { + case 0: + break; + default: + charWidth = _font->getCharWidth(curChar); + _font->draw(curChar, _ports->_curPort->top + _ports->_curPort->curTop, _ports->_curPort->left + _ports->_curPort->curLeft, _ports->_curPort->penClr, _ports->_curPort->greyedOutput); + _ports->_curPort->curLeft += charWidth; + } + } +} + // Sierra did this in their PC98 interpreter only, they identify a text as being // sjis and then switch to font 900 bool GfxText16::SwitchToFont900OnSjis(const char *text) { @@ -485,6 +528,30 @@ bool GfxText16::SwitchToFont900OnSjis(const char *text) { return false; } +reg_t GfxText16::allocAndFillReferenceRectArray() { + uint rectCount = _codeRefRects.size(); + if (rectCount) { + reg_t rectArray; + byte *rectArrayPtr = g_sci->getEngineState()->_segMan->allocDynmem(4 * 2 * (rectCount + 1), "text code reference rects", &rectArray); + GfxCoordAdjuster *coordAdjuster = g_sci->_gfxCoordAdjuster; + for (uint curRect = 0; curRect < rectCount; curRect++) { + coordAdjuster->kernelLocalToGlobal(_codeRefRects[curRect].left, _codeRefRects[curRect].top); + coordAdjuster->kernelLocalToGlobal(_codeRefRects[curRect].right, _codeRefRects[curRect].bottom); + WRITE_LE_UINT16(rectArrayPtr + 0, _codeRefRects[curRect].left); + WRITE_LE_UINT16(rectArrayPtr + 2, _codeRefRects[curRect].top); + WRITE_LE_UINT16(rectArrayPtr + 4, _codeRefRects[curRect].right); + WRITE_LE_UINT16(rectArrayPtr + 6, _codeRefRects[curRect].bottom); + rectArrayPtr += 8; + } + WRITE_LE_UINT16(rectArrayPtr + 0, 0x7777); + WRITE_LE_UINT16(rectArrayPtr + 2, 0x7777); + WRITE_LE_UINT16(rectArrayPtr + 4, 0x7777); + WRITE_LE_UINT16(rectArrayPtr + 6, 0x7777); + return rectArray; + } + return NULL_REG; +} + void GfxText16::kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) { Common::Rect rect(0, 0, 0, 0); Size(rect, text, font, maxWidth); diff --git a/engines/sci/graphics/text16.h b/engines/sci/graphics/text16.h index 9b8b6d9f19..dc3ed2f62b 100644 --- a/engines/sci/graphics/text16.h +++ b/engines/sci/graphics/text16.h @@ -32,6 +32,8 @@ namespace Sci { #define SCI_TEXT16_ALIGNMENT_CENTER 1 #define SCI_TEXT16_ALIGNMENT_LEFT 0 +typedef Common::Array<Common::Rect> CodeRefRectArray; + class GfxPorts; class GfxPaint16; class GfxScreen; @@ -48,7 +50,7 @@ public: GfxFont *GetFont(); void SetFont(GuiResourceId fontId); - int16 CodeProcessing(const char *&text, GuiResourceId orgFontId, int16 orgPenColor); + int16 CodeProcessing(const char *&text, GuiResourceId orgFontId, int16 orgPenColor, bool doingDrawing); void ClearChar(int16 chr); @@ -62,9 +64,12 @@ public: void Show(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 orgPenColor); void Box(const char *text, int16 bshow, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId); void Draw_String(const char *text); + void Draw_Status(const char *text); GfxFont *_font; + reg_t allocAndFillReferenceRectArray(); + void kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight); void kernelTextFonts(int argc, reg_t *argv); void kernelTextColors(int argc, reg_t *argv); @@ -83,6 +88,9 @@ private: GuiResourceId *_codeFonts; int _codeColorsCount; uint16 *_codeColors; + + Common::Rect _codeRefTempRect; + CodeRefRectArray _codeRefRects; }; } // End of namespace Sci diff --git a/engines/sci/graphics/transitions.cpp b/engines/sci/graphics/transitions.cpp index abb5e74cbd..3f4ce7bbc8 100644 --- a/engines/sci/graphics/transitions.cpp +++ b/engines/sci/graphics/transitions.cpp @@ -37,6 +37,8 @@ namespace Sci { +//#define DISABLE_TRANSITIONS // uncomment to disable room transitions (for development only! helps in testing games quickly) + GfxTransitions::GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA) : _screen(screen), _palette(palette), _isVGA(isVGA) { init(); @@ -116,16 +118,33 @@ void GfxTransitions::init() { void GfxTransitions::setup(int16 number, bool blackoutFlag) { if (number != -1) { +#ifndef DISABLE_TRANSITIONS _number = number; +#else + _number = SCI_TRANSITIONS_NONE; +#endif _blackoutFlag = blackoutFlag; } } -void GfxTransitions::updateScreenAndWait(int msec) { +bool GfxTransitions::doCreateFrame(uint32 shouldBeAtMsec) { + uint32 msecPos = g_system->getMillis() - _transitionStartTime; + + if (shouldBeAtMsec > msecPos) + return true; + return false; +} + +void GfxTransitions::updateScreenAndWait(uint32 shouldBeAtMsec) { Common::Event ev; - g_system->updateScreen(); - g_system->delayMillis(msec); + while (g_system->getEventManager()->pollEvent(ev)) {} // discard all events + + g_system->updateScreen(); + // if we have still some time left, delay accordingly + uint32 msecPos = g_system->getMillis() - _transitionStartTime; + if (shouldBeAtMsec > msecPos) + g_system->delayMillis(shouldBeAtMsec - msecPos); } // will translate a number and return corresponding translationEntry @@ -191,6 +210,7 @@ void GfxTransitions::doTransition(int16 number, bool blackoutFlag) { setNewPalette(blackoutFlag); } + _transitionStartTime = g_system->getMillis(); switch (number) { case SCI_TRANSITIONS_VERTICALROLL_FROMCENTER: verticalRollFromCenter(blackoutFlag); @@ -285,11 +305,14 @@ void GfxTransitions::copyRectToScreen(const Common::Rect rect, bool blackoutFlag void GfxTransitions::fadeOut() { byte oldPalette[4 * 256], workPalette[4 * 256]; int16 stepNr, colorNr; + // Sierra did not fade in/out color 255 for sci1.1, but they used it in + // several pictures (e.g. qfg3 demo/intro), so the fading looked weird + int16 tillColorNr = getSciVersion() >= SCI_VERSION_1_1 ? 256 : 255; g_system->grabPalette(oldPalette, 0, 256); for (stepNr = 100; stepNr >= 0; stepNr -= 10) { - for (colorNr = 1; colorNr < 255; colorNr++){ + for (colorNr = 1; colorNr < tillColorNr; colorNr++){ workPalette[colorNr * 4 + 0] = oldPalette[colorNr * 4] * stepNr / 100; workPalette[colorNr * 4 + 1] = oldPalette[colorNr * 4 + 1] * stepNr / 100; workPalette[colorNr * 4 + 2] = oldPalette[colorNr * 4 + 2] * stepNr / 100; @@ -303,9 +326,12 @@ void GfxTransitions::fadeOut() { // the load void GfxTransitions::fadeIn() { int16 stepNr; + // Sierra did not fade in/out color 255 for sci1.1, but they used it in + // several pictures (e.g. qfg3 demo/intro), so the fading looked weird + int16 tillColorNr = getSciVersion() >= SCI_VERSION_1_1 ? 256 : 255; for (stepNr = 0; stepNr <= 100; stepNr += 10) { - _palette->kernelSetIntensity(1, 255, stepNr, true); + _palette->kernelSetIntensity(1, tillColorNr, stepNr, true); g_sci->getEngineState()->wait(2); } } @@ -315,6 +341,7 @@ void GfxTransitions::fadeIn() { void GfxTransitions::pixelation(bool blackoutFlag) { uint16 mask = 0x40, stepNr = 0; Common::Rect pixelRect; + uint32 msecCount = 0; do { mask = (mask & 1) ? (mask >> 1) ^ 0xB400 : mask >> 1; @@ -326,7 +353,8 @@ void GfxTransitions::pixelation(bool blackoutFlag) { if (!pixelRect.isEmpty()) copyRectToScreen(pixelRect, blackoutFlag); if ((stepNr & 0x3FF) == 0) { - updateScreenAndWait(5); + msecCount += 9; + updateScreenAndWait(msecCount); } stepNr++; } while (mask != 0x40); @@ -337,6 +365,7 @@ void GfxTransitions::pixelation(bool blackoutFlag) { void GfxTransitions::blocks(bool blackoutFlag) { uint16 mask = 0x40, stepNr = 0; Common::Rect blockRect; + uint32 msecCount = 0; do { mask = (mask & 1) ? (mask >> 1) ^ 0x240 : mask >> 1; @@ -348,7 +377,8 @@ void GfxTransitions::blocks(bool blackoutFlag) { if (!blockRect.isEmpty()) copyRectToScreen(blockRect, blackoutFlag); if ((stepNr & 7) == 0) { - updateScreenAndWait(4); + msecCount += 5; + updateScreenAndWait(msecCount); } stepNr++; } while (mask != 0x40); @@ -359,6 +389,7 @@ void GfxTransitions::blocks(bool blackoutFlag) { void GfxTransitions::straight(int16 number, bool blackoutFlag) { int16 stepNr = 0; Common::Rect newScreenRect = _picRect; + uint32 msecCount = 0; switch (number) { case SCI_TRANSITIONS_STRAIGHT_FROM_RIGHT: @@ -366,7 +397,8 @@ void GfxTransitions::straight(int16 number, bool blackoutFlag) { while (newScreenRect.left >= _picRect.left) { copyRectToScreen(newScreenRect, blackoutFlag); if ((stepNr & 1) == 0) { - updateScreenAndWait(1); + msecCount += 2; + updateScreenAndWait(msecCount); } stepNr++; newScreenRect.translate(-1, 0); @@ -378,7 +410,8 @@ void GfxTransitions::straight(int16 number, bool blackoutFlag) { while (newScreenRect.right <= _picRect.right) { copyRectToScreen(newScreenRect, blackoutFlag); if ((stepNr & 1) == 0) { - updateScreenAndWait(1); + msecCount += 2; + updateScreenAndWait(msecCount); } stepNr++; newScreenRect.translate(1, 0); @@ -389,7 +422,8 @@ void GfxTransitions::straight(int16 number, bool blackoutFlag) { newScreenRect.top = newScreenRect.bottom - 1; while (newScreenRect.top >= _picRect.top) { copyRectToScreen(newScreenRect, blackoutFlag); - updateScreenAndWait(3); + msecCount += 4; + updateScreenAndWait(msecCount); stepNr++; newScreenRect.translate(0, -1); } @@ -399,7 +433,8 @@ void GfxTransitions::straight(int16 number, bool blackoutFlag) { newScreenRect.bottom = newScreenRect.top + 1; while (newScreenRect.bottom <= _picRect.bottom) { copyRectToScreen(newScreenRect, blackoutFlag); - updateScreenAndWait(3); + msecCount += 4; + updateScreenAndWait(msecCount); stepNr++; newScreenRect.translate(0, 1); } @@ -428,6 +463,7 @@ void GfxTransitions::scroll(int16 number) { Common::Rect oldScreenRect = _picRect; Common::Rect newMoveRect = _picRect; Common::Rect newScreenRect = _picRect; + uint32 msecCount = 0; _screen->copyFromScreen(_oldScreen); screenWidth = _screen->getDisplayWidth(); screenHeight = _screen->getDisplayHeight(); @@ -438,42 +474,36 @@ void GfxTransitions::scroll(int16 number) { newMoveRect.left = newMoveRect.right; while (oldMoveRect.left < oldMoveRect.right) { oldMoveRect.right--; oldScreenRect.left++; - if (oldMoveRect.right > oldMoveRect.left) - scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top); newScreenRect.right++; newMoveRect.left--; - _screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top); if ((stepNr & 1) == 0) { - updateScreenAndWait(1); + msecCount += 5; + if (doCreateFrame(msecCount)) { + if (oldMoveRect.right > oldMoveRect.left) + scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top); + _screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top); + updateScreenAndWait(msecCount); + } } stepNr++; } - if ((stepNr & 1) == 0) { - if (g_system->getMillis() - g_sci->getEngineState()->_screenUpdateTime >= 1000 / 60) { - g_system->updateScreen(); - g_sci->getEngineState()->_screenUpdateTime = g_system->getMillis(); - } - } break; case SCI_TRANSITIONS_SCROLL_RIGHT: newScreenRect.left = newScreenRect.right; while (oldMoveRect.left < oldMoveRect.right) { oldMoveRect.left++; oldScreenRect.right--; - if (oldMoveRect.right > oldMoveRect.left) - scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top); newScreenRect.left--; - _screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top); if ((stepNr & 1) == 0) { - updateScreenAndWait(1); + msecCount += 5; + if (doCreateFrame(msecCount)) { + if (oldMoveRect.right > oldMoveRect.left) + scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top); + _screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top); + updateScreenAndWait(msecCount); + } } stepNr++; } - if ((stepNr & 1) == 0) { - if (g_system->getMillis() - g_sci->getEngineState()->_screenUpdateTime >= 1000 / 60) { - g_system->updateScreen(); - g_sci->getEngineState()->_screenUpdateTime = g_system->getMillis(); - } - } break; case SCI_TRANSITIONS_SCROLL_UP: @@ -481,11 +511,15 @@ void GfxTransitions::scroll(int16 number) { newMoveRect.top = newMoveRect.bottom; while (oldMoveRect.top < oldMoveRect.bottom) { oldMoveRect.top++; oldScreenRect.top++; - if (oldMoveRect.top < oldMoveRect.bottom) - scrollCopyOldToScreen(oldScreenRect, _picRect.left, _picRect.top); newScreenRect.bottom++; newMoveRect.top--; - _screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top); - updateScreenAndWait(3); + + msecCount += 5; + if (doCreateFrame(msecCount)) { + if (oldMoveRect.top < oldMoveRect.bottom) + scrollCopyOldToScreen(oldScreenRect, _picRect.left, _picRect.top); + _screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top); + updateScreenAndWait(msecCount); + } } break; @@ -493,14 +527,22 @@ void GfxTransitions::scroll(int16 number) { newScreenRect.top = newScreenRect.bottom; while (oldMoveRect.top < oldMoveRect.bottom) { oldMoveRect.top++; oldScreenRect.bottom--; - if (oldMoveRect.top < oldMoveRect.bottom) - scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top); newScreenRect.top--; - _screen->copyRectToScreen(newScreenRect, _picRect.left, _picRect.top); - updateScreenAndWait(3); + + msecCount += 5; + if (doCreateFrame(msecCount)) { + if (oldMoveRect.top < oldMoveRect.bottom) + scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top); + _screen->copyRectToScreen(newScreenRect, _picRect.left, _picRect.top); + updateScreenAndWait(msecCount); + } } break; } + + // Copy over final position just in case + _screen->copyRectToScreen(newScreenRect); + g_system->updateScreen(); } // Vertically displays new screen starting from center - works on _picRect area @@ -508,6 +550,7 @@ void GfxTransitions::scroll(int16 number) { void GfxTransitions::verticalRollFromCenter(bool blackoutFlag) { Common::Rect leftRect = Common::Rect(_picRect.left + (_picRect.width() / 2) -1, _picRect.top, _picRect.left + (_picRect.width() / 2), _picRect.bottom); Common::Rect rightRect = Common::Rect(leftRect.right, _picRect.top, leftRect.right + 1, _picRect.bottom); + uint32 msecCount = 0; while ((leftRect.left >= _picRect.left) || (rightRect.right <= _picRect.right)) { if (leftRect.left < _picRect.left) @@ -516,7 +559,8 @@ void GfxTransitions::verticalRollFromCenter(bool blackoutFlag) { rightRect.translate(-1, 0); copyRectToScreen(leftRect, blackoutFlag); leftRect.translate(-1, 0); copyRectToScreen(rightRect, blackoutFlag); rightRect.translate(1, 0); - updateScreenAndWait(2); + msecCount += 3; + updateScreenAndWait(msecCount); } } @@ -525,11 +569,13 @@ void GfxTransitions::verticalRollFromCenter(bool blackoutFlag) { void GfxTransitions::verticalRollToCenter(bool blackoutFlag) { Common::Rect leftRect = Common::Rect(_picRect.left, _picRect.top, _picRect.left + 1, _picRect.bottom); Common::Rect rightRect = Common::Rect(_picRect.right - 1, _picRect.top, _picRect.right, _picRect.bottom); + uint32 msecCount = 0; while (leftRect.left < rightRect.right) { copyRectToScreen(leftRect, blackoutFlag); leftRect.translate(1, 0); copyRectToScreen(rightRect, blackoutFlag); rightRect.translate(-1, 0); - updateScreenAndWait(2); + msecCount += 3; + updateScreenAndWait(msecCount); } } @@ -538,6 +584,7 @@ void GfxTransitions::verticalRollToCenter(bool blackoutFlag) { void GfxTransitions::horizontalRollFromCenter(bool blackoutFlag) { Common::Rect upperRect = Common::Rect(_picRect.left, _picRect.top + (_picRect.height() / 2) - 1, _picRect.right, _picRect.top + (_picRect.height() / 2)); Common::Rect lowerRect = Common::Rect(upperRect.left, upperRect.bottom, upperRect.right, upperRect.bottom + 1); + uint32 msecCount = 0; while ((upperRect.top >= _picRect.top) || (lowerRect.bottom <= _picRect.bottom)) { if (upperRect.top < _picRect.top) @@ -546,7 +593,8 @@ void GfxTransitions::horizontalRollFromCenter(bool blackoutFlag) { lowerRect.translate(0, -1); copyRectToScreen(upperRect, blackoutFlag); upperRect.translate(0, -1); copyRectToScreen(lowerRect, blackoutFlag); lowerRect.translate(0, 1); - updateScreenAndWait(3); + msecCount += 4; + updateScreenAndWait(msecCount); } } @@ -555,11 +603,13 @@ void GfxTransitions::horizontalRollFromCenter(bool blackoutFlag) { void GfxTransitions::horizontalRollToCenter(bool blackoutFlag) { Common::Rect upperRect = Common::Rect(_picRect.left, _picRect.top, _picRect.right, _picRect.top + 1); Common::Rect lowerRect = Common::Rect(upperRect.left, _picRect.bottom - 1, upperRect.right, _picRect.bottom); + uint32 msecCount = 0; while (upperRect.top < lowerRect.bottom) { copyRectToScreen(upperRect, blackoutFlag); upperRect.translate(0, 1); copyRectToScreen(lowerRect, blackoutFlag); lowerRect.translate(0, -1); - updateScreenAndWait(3); + msecCount += 4; + updateScreenAndWait(msecCount); } } @@ -571,6 +621,7 @@ void GfxTransitions::diagonalRollFromCenter(bool blackoutFlag) { Common::Rect lowerRect(upperRect.left, upperRect.top, upperRect.right, upperRect.bottom); Common::Rect leftRect(upperRect.left, upperRect.top, upperRect.left + 1, lowerRect.bottom); Common::Rect rightRect(upperRect.right, upperRect.top, upperRect.right + 1, lowerRect.bottom); + uint32 msecCount = 0; while ((upperRect.top >= _picRect.top) || (lowerRect.bottom <= _picRect.bottom)) { if (upperRect.top < _picRect.top) { @@ -589,7 +640,8 @@ void GfxTransitions::diagonalRollFromCenter(bool blackoutFlag) { copyRectToScreen(lowerRect, blackoutFlag); lowerRect.translate(0, 1); lowerRect.left--; lowerRect.right++; copyRectToScreen(leftRect, blackoutFlag); leftRect.translate(-1, 0); leftRect.top--; leftRect.bottom++; copyRectToScreen(rightRect, blackoutFlag); rightRect.translate(1, 0); rightRect.top--; rightRect.bottom++; - updateScreenAndWait(3); + msecCount += 4; + updateScreenAndWait(msecCount); } } @@ -600,13 +652,15 @@ void GfxTransitions::diagonalRollToCenter(bool blackoutFlag) { Common::Rect lowerRect(_picRect.left, _picRect.bottom - 1, _picRect.right, _picRect.bottom); Common::Rect leftRect(_picRect.left, _picRect.top, _picRect.left + 1, _picRect.bottom); Common::Rect rightRect(_picRect.right - 1, _picRect.top, _picRect.right, _picRect.bottom); + uint32 msecCount = 0; while (upperRect.top < lowerRect.bottom) { copyRectToScreen(upperRect, blackoutFlag); upperRect.translate(0, 1); upperRect.left++; upperRect.right--; copyRectToScreen(lowerRect, blackoutFlag); lowerRect.translate(0, -1); lowerRect.left++; lowerRect.right--; copyRectToScreen(leftRect, blackoutFlag); leftRect.translate(1, 0); copyRectToScreen(rightRect, blackoutFlag); rightRect.translate(-1, 0); - updateScreenAndWait(3); + msecCount += 4; + updateScreenAndWait(msecCount); } } diff --git a/engines/sci/graphics/transitions.h b/engines/sci/graphics/transitions.h index 233638ffda..674b7a8173 100644 --- a/engines/sci/graphics/transitions.h +++ b/engines/sci/graphics/transitions.h @@ -91,7 +91,8 @@ private: void horizontalRollToCenter(bool blackoutFlag); void diagonalRollFromCenter(bool blackoutFlag); void diagonalRollToCenter(bool blackoutFlag); - void updateScreenAndWait(int msec); + bool doCreateFrame(uint32 shouldBeAtMsec); + void updateScreenAndWait(uint32 shouldBeAtMsec); GfxScreen *_screen; GfxPalette *_palette; @@ -102,6 +103,8 @@ private: bool _blackoutFlag; Common::Rect _picRect; byte *_oldScreen; // buffer for saving current active screen data to, has dimenions of _screen->_displayScreen + + uint32 _transitionStartTime; // when the current transition started in milliseconds }; } // End of namespace Sci diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp index 5f48574dcb..6b22ed397e 100644 --- a/engines/sci/graphics/view.cpp +++ b/engines/sci/graphics/view.cpp @@ -128,8 +128,11 @@ void GfxView::initData(GuiResourceId resourceId) { _palette->createFromData(&_resourceData[palOffset], _resourceSize - palOffset, &_viewPalette); _embeddedPal = true; } else { - // Only use the EGA-mapping, when being SCI1 - if (getSciVersion() >= SCI_VERSION_1_EGA) { + // Only use the EGA-mapping, when being SCI1 EGA + // SCI1 VGA conversion games (which will get detected as SCI1EARLY/MIDDLE/LATE) have some views + // with broken mapping tables. I guess those games won't use the mapping, so I rather disable it + // for them + if (getSciVersion() == SCI_VERSION_1_EGA) { _EGAmapping = &_resourceData[palOffset]; for (EGAmapNr = 0; EGAmapNr < SCI_VIEW_EGAMAPPING_COUNT; EGAmapNr++) { if (memcmp(_EGAmapping, EGAmappingStraight, SCI_VIEW_EGAMAPPING_SIZE) != 0) |