diff options
-rw-r--r-- | engines/sci/engine/kernel_tables.h | 2 | ||||
-rw-r--r-- | engines/sci/engine/kgraphics32.cpp | 38 | ||||
-rw-r--r-- | engines/sci/graphics/frameout.cpp | 610 | ||||
-rw-r--r-- | engines/sci/graphics/frameout.h | 199 | ||||
-rw-r--r-- | engines/sci/graphics/plane32.cpp | 6 | ||||
-rw-r--r-- | engines/sci/graphics/plane32.h | 18 | ||||
-rw-r--r-- | engines/sci/graphics/transitions32.cpp | 981 | ||||
-rw-r--r-- | engines/sci/graphics/transitions32.h | 476 | ||||
-rw-r--r-- | engines/sci/module.mk | 1 | ||||
-rw-r--r-- | engines/sci/sci.cpp | 6 | ||||
-rw-r--r-- | engines/sci/sci.h | 2 |
11 files changed, 1659 insertions, 680 deletions
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 8a1176eed8..7d622d16d1 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -772,7 +772,7 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(EditText), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(MakeSaveCatName), SIG_EVERYWHERE, "rr", NULL, NULL }, { MAP_CALL(MakeSaveFileName), SIG_EVERYWHERE, "rri", NULL, NULL }, - { MAP_CALL(SetScroll), SIG_EVERYWHERE, "oiiiii(i)", NULL, NULL }, + { MAP_CALL(SetScroll), SIG_EVERYWHERE, "oiiii(i)(i)", NULL, NULL }, { MAP_CALL(PalCycle), SIG_EVERYWHERE, "(.*)", kPalCycle_subops, NULL }, // SCI2 Empty functions diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index b8682b3f47..40c048bdeb 100644 --- a/engines/sci/engine/kgraphics32.cpp +++ b/engines/sci/engine/kgraphics32.cpp @@ -56,6 +56,7 @@ #include "sci/graphics/palette32.h" #include "sci/graphics/remap32.h" #include "sci/graphics/text32.h" +#include "sci/graphics/transitions32.h" #endif namespace Sci { @@ -139,7 +140,7 @@ reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) { } reg_t kSetPalStyleRange(EngineState *s, int argc, reg_t *argv) { - g_sci->_gfxFrameout->kernelSetPalStyleRange(argv[0].toUint16(), argv[1].toUint16()); + g_sci->_gfxTransitions32->kernelSetPalStyleRange(argv[0].toUint16(), argv[1].toUint16()); return s->r_acc; } @@ -310,7 +311,7 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { // NOTE: The order of planeObj and showStyle are reversed // because this is how SCI3 called the corresponding method // on the KernelMgr - g_sci->_gfxFrameout->kernelSetShowStyle(argc, planeObj, type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen); + g_sci->_gfxTransitions32->kernelSetShowStyle(argc, planeObj, type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen); return s->r_acc; } @@ -760,28 +761,19 @@ reg_t kDeleteLine(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } +// Used by LSL6hires intro (room 110) reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) { - // Called in the intro of LSL6 hires (room 110) - // The end effect of this is the same as the old screen scroll transition - - // 7 parameters - reg_t planeObject = argv[0]; - //int16 x = argv[1].toSint16(); - //int16 y = argv[2].toSint16(); - uint16 pictureId = argv[3].toUint16(); - // param 4: int (0 in LSL6, probably scroll direction? The picture in LSL6 scrolls down) - // param 5: int (first call is 1, then the subsequent one is 0 in LSL6) - // param 6: optional int (0 in LSL6) - - // Set the new picture directly for now - //writeSelectorValue(s->_segMan, planeObject, SELECTOR(left), x); - //writeSelectorValue(s->_segMan, planeObject, SELECTOR(top), y); - writeSelectorValue(s->_segMan, planeObject, SELECTOR(picture), pictureId); - // and update our draw list - g_sci->_gfxFrameout->kernelUpdatePlane(planeObject); - - // TODO - return kStub(s, argc, argv); + const reg_t plane = argv[0]; + const int16 deltaX = argv[1].toSint16(); + const int16 deltaY = argv[2].toSint16(); + const GuiResourceId pictureId = argv[3].toUint16(); + const bool animate = argv[4].toUint16(); + // NOTE: speed was accepted as an argument, but then never actually used + // const int16 speed = argc > 5 ? (bool)argv[5].toSint16() : -1; + const bool mirrorX = argc > 6 ? (bool)argv[6].toUint16() : false; + + g_sci->_gfxTransitions32->kernelSetScroll(plane, deltaX, deltaY, pictureId, animate, mirrorX); + return s->r_acc; } // Used by SQ6, script 900, the datacorder reprogramming puzzle (from room 270) diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index b5b320637f..a264516bf7 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -51,31 +51,19 @@ #include "sci/graphics/text32.h" #include "sci/graphics/frameout.h" #include "sci/video/robot_decoder.h" +#include "sci/graphics/transitions32.h" namespace Sci { -static int dissolveSequences[2][20] = { - /* SCI2.1early- */ { 3, 6, 12, 20, 48, 96, 184, 272, 576, 1280, 3232, 6912, 13568, 24576, 46080 }, - /* SCI2.1mid+ */ { 0, 0, 3, 6, 12, 20, 48, 96, 184, 272, 576, 1280, 3232, 6912, 13568, 24576, 46080, 73728, 132096, 466944 } -}; -static int16 divisionsDefaults[2][16] = { - /* SCI2.1early- */ { 1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 40, 40, 101, 101 }, - /* SCI2.1mid+ */ { 1, 20, 20, 20, 20, 10, 10, 10, 10, 20, 20, 6, 10, 101, 101, 2 } -}; -static int16 unknownCDefaults[2][16] = { - /* SCI2.1early- */ { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 0, 0, 0, 0 }, - /* SCI2.1mid+ */ { 0, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 0, 0, 7, 7, 0 } -}; - -GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette32 *palette) : +GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette32 *palette, GfxTransitions32 *transitions) : _isHiRes(false), _palette(palette), _resMan(resMan), _screen(screen), _segMan(segMan), + _transitions(transitions), _benchmarkingFinished(false), _throttleFrameOut(true), - _showStyles(nullptr), _throttleState(0), // TODO: Stop using _gfxScreen _currentBuffer(screen->getDisplayWidth(), screen->getDisplayHeight(), nullptr), @@ -87,14 +75,6 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd _currentBuffer.setPixels(calloc(1, screen->getDisplayWidth() * screen->getDisplayHeight())); - for (int i = 0; i < 236; i += 2) { - _styleRanges[i] = 0; - _styleRanges[i + 1] = -1; - } - for (int i = 236; i < ARRAYSIZE(_styleRanges); ++i) { - _styleRanges[i] = 0; - } - // TODO: Make hires detection work uniformly across all SCI engine // versions (this flag is normally passed by SCI::MakeGraphicsMgr // to the GraphicsMgr constructor depending upon video configuration, @@ -104,16 +84,6 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd _isHiRes = true; } - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - _dissolveSequenceSeeds = dissolveSequences[0]; - _defaultDivisions = divisionsDefaults[0]; - _defaultUnknownC = unknownCDefaults[0]; - } else { - _dissolveSequenceSeeds = dissolveSequences[1]; - _defaultDivisions = divisionsDefaults[1]; - _defaultUnknownC = unknownCDefaults[1]; - } - switch (g_sci->getGameId()) { case GID_HOYLE5: case GID_GK2: @@ -587,6 +557,115 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseR // } } +void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *showStyle) { + Palette sourcePalette(_palette->getNextPalette()); + alterVmap(sourcePalette, sourcePalette, -1, styleRanges); + + int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16(); + + Common::Rect rect(_screen->getDisplayWidth(), _screen->getDisplayHeight()); + _showList.add(rect); + showBits(); + + // NOTE: The original engine allocated these as static arrays of 100 + // pointers to ScreenItemList / RectList + ScreenItemListList screenItemLists; + EraseListList eraseLists; + + screenItemLists.resize(_planes.size()); + eraseLists.resize(_planes.size()); + + if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) { + remapMarkRedraw(); + } + + calcLists(screenItemLists, eraseLists); + for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { + list->sort(); + } + + for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { + for (DrawList::iterator drawItem = list->begin(); drawItem != list->end(); ++drawItem) { + (*drawItem)->screenItem->getCelObj().submitPalette(); + } + } + + _remapOccurred = _palette->updateForFrame(); + _frameNowVisible = false; + + for (PlaneList::size_type i = 0; i < _planes.size(); ++i) { + drawEraseList(eraseLists[i], *_planes[i]); + drawScreenItemList(screenItemLists[i]); + } + + Palette nextPalette(_palette->getNextPalette()); + + if (prevRoom < 1000) { + for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) { + if (styleRanges[i] == -1 || styleRanges[i] == 0) { + sourcePalette.colors[i] = nextPalette.colors[i]; + sourcePalette.colors[i].used = true; + } + } + } else { + for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) { + if (styleRanges[i] == -1 || validZeroStyle(styleRanges[i], i)) { + sourcePalette.colors[i] = nextPalette.colors[i]; + sourcePalette.colors[i].used = true; + } + } + } + + _palette->submit(sourcePalette); + _palette->updateFFrame(); + _palette->updateHardware(); + alterVmap(nextPalette, sourcePalette, 1, _transitions->_styleRanges); + + if (showStyle && showStyle->type != kShowStyleMorph) { + _transitions->processEffects(*showStyle); + } else { + showBits(); + } + + _frameNowVisible = true; + + for (PlaneList::iterator plane = _planes.begin(); plane != _planes.end(); ++plane) { + (*plane)->_redrawAllCount = getScreenCount(); + } + + if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) { + remapMarkRedraw(); + } + + calcLists(screenItemLists, eraseLists); + for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { + list->sort(); + } + + for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { + for (DrawList::iterator drawItem = list->begin(); drawItem != list->end(); ++drawItem) { + (*drawItem)->screenItem->getCelObj().submitPalette(); + } + } + + _remapOccurred = _palette->updateForFrame(); + // NOTE: During this second loop, `_frameNowVisible = false` is + // inside the next loop in SCI2.1mid + _frameNowVisible = false; + + for (PlaneList::size_type i = 0; i < _planes.size(); ++i) { + drawEraseList(eraseLists[i], *_planes[i]); + drawScreenItemList(screenItemLists[i]); + } + + _palette->submit(nextPalette); + _palette->updateFFrame(); + _palette->updateHardware(false); + showBits(); + + _frameNowVisible = true; +} + /** * Determines the parts of `r` that aren't overlapped by `other`. * Returns -1 if `r` and `other` have no intersection. @@ -1038,118 +1117,6 @@ void GfxFrameout::mergeToShowList(const Common::Rect &drawRect, RectList &showLi } } -void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry *showStyle) { - Palette sourcePalette(_palette->getNextPalette()); - alterVmap(sourcePalette, sourcePalette, -1, styleRanges); - - int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16(); - - Common::Rect rect(_screen->getDisplayWidth(), _screen->getDisplayHeight()); - _showList.add(rect); - showBits(); - - // NOTE: The original engine allocated these as static arrays of 100 - // pointers to ScreenItemList / RectList - ScreenItemListList screenItemLists; - EraseListList eraseLists; - - screenItemLists.resize(_planes.size()); - eraseLists.resize(_planes.size()); - - if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) { - remapMarkRedraw(); - } - - calcLists(screenItemLists, eraseLists); - for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { - list->sort(); - } - - for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { - for (DrawList::iterator drawItem = list->begin(); drawItem != list->end(); ++drawItem) { - (*drawItem)->screenItem->getCelObj().submitPalette(); - } - } - - _remapOccurred = _palette->updateForFrame(); - _frameNowVisible = false; - - for (PlaneList::size_type i = 0; i < _planes.size(); ++i) { - drawEraseList(eraseLists[i], *_planes[i]); - drawScreenItemList(screenItemLists[i]); - } - - Palette nextPalette(_palette->getNextPalette()); - - if (prevRoom < 1000) { - for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) { - if (styleRanges[i] == -1 || styleRanges[i] == 0) { - sourcePalette.colors[i] = nextPalette.colors[i]; - sourcePalette.colors[i].used = true; - } - } - } else { - for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) { - // TODO: Limiting range 72 to 103 is NOT present in every game - if (styleRanges[i] == -1 || (styleRanges[i] == 0 && i > 71 && i < 104)) { - sourcePalette.colors[i] = nextPalette.colors[i]; - sourcePalette.colors[i].used = true; - } - } - } - - _palette->submit(sourcePalette); - _palette->updateFFrame(); - _palette->updateHardware(); - alterVmap(nextPalette, sourcePalette, 1, _styleRanges); - - if (showStyle && showStyle->type != kShowStyleUnknown) { -// TODO: SCI2.1mid transition effects -// processEffects(); - warning("Transition %d not implemented!", showStyle->type); - } else { - showBits(); - } - - _frameNowVisible = true; - - for (PlaneList::iterator plane = _planes.begin(); plane != _planes.end(); ++plane) { - (*plane)->_redrawAllCount = getScreenCount(); - } - - if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) { - remapMarkRedraw(); - } - - calcLists(screenItemLists, eraseLists); - for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { - list->sort(); - } - - for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { - for (DrawList::iterator drawItem = list->begin(); drawItem != list->end(); ++drawItem) { - (*drawItem)->screenItem->getCelObj().submitPalette(); - } - } - - _remapOccurred = _palette->updateForFrame(); - // NOTE: During this second loop, `_frameNowVisible = false` is - // inside the next loop in SCI2.1mid - _frameNowVisible = false; - - for (PlaneList::size_type i = 0; i < _planes.size(); ++i) { - drawEraseList(eraseLists[i], *_planes[i]); - drawScreenItemList(screenItemLists[i]); - } - - _palette->submit(nextPalette); - _palette->updateFFrame(); - _palette->updateHardware(false); - showBits(); - - _frameNowVisible = true; -} - void GfxFrameout::showBits() { for (RectList::const_iterator rect = _showList.begin(); rect != _showList.end(); ++rect) { Common::Rect rounded(**rect); @@ -1233,7 +1200,6 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co } } - // NOTE: This is currBuffer->ptr in SCI engine byte *pixels = (byte *)_currentBuffer.getPixels(); for (int pixelIndex = 0, numPixels = _currentBuffer.screenWidth * _currentBuffer.screenHeight; pixelIndex < numPixels; ++pixelIndex) { @@ -1256,348 +1222,16 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co } } -void GfxFrameout::kernelSetPalStyleRange(const uint8 fromColor, const uint8 toColor) { - if (toColor > fromColor) { - return; - } - - for (int i = fromColor; i < toColor; ++i) { - _styleRanges[i] = 0; - } -} - -inline ShowStyleEntry * GfxFrameout::findShowStyleForPlane(const reg_t planeObj) const { - ShowStyleEntry *entry = _showStyles; - while (entry != nullptr) { - if (entry->plane == planeObj) { - break; - } - entry = entry->next; - } - - return entry; -} - -inline ShowStyleEntry *GfxFrameout::deleteShowStyleInternal(ShowStyleEntry *const showStyle) { - ShowStyleEntry *lastEntry = nullptr; - - for (ShowStyleEntry *testEntry = _showStyles; testEntry != nullptr; testEntry = testEntry->next) { - if (testEntry == showStyle) { - break; - } - lastEntry = testEntry; - } - - if (lastEntry == nullptr) { - _showStyles = showStyle->next; - lastEntry = _showStyles; - } else { - lastEntry->next = showStyle->next; - } - - delete[] showStyle->fadeColorRanges; - delete showStyle; - - // TODO: Verify that this is the correct entry to return - // for the loop in processShowStyles to work correctly - return lastEntry; -} - -// TODO: 10-argument version is only in SCI3; argc checks are currently wrong for this version -// and need to be fixed in future -// TODO: SQ6 does not use 'priority' (exists since SCI2) or 'blackScreen' (exists since SCI3); -// check to see if other versions use or if they are just always ignored -void GfxFrameout::kernelSetShowStyle(const uint16 argc, const reg_t planeObj, const ShowStyleType type, const int16 seconds, const int16 back, const int16 priority, const int16 animate, const int16 frameOutNow, reg_t pFadeArray, int16 divisions, const int16 blackScreen) { - - bool hasDivisions = false; - bool hasFadeArray = false; - - // KQ7 2.0b uses a mismatched version of the Styler script (SCI2.1early script - // for SCI2.1mid engine), so the calls it makes to kSetShowStyle are wrong and - // put `divisions` where `pFadeArray` is supposed to be - if (getSciVersion() == SCI_VERSION_2_1_MIDDLE && g_sci->getGameId() == GID_KQ7) { - hasDivisions = argc > 7; - hasFadeArray = false; - divisions = argc > 7 ? pFadeArray.toSint16() : -1; - pFadeArray = NULL_REG; - } else if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - hasDivisions = argc > 7; - hasFadeArray = false; - } else if (getSciVersion() < SCI_VERSION_3) { - hasDivisions = argc > 8; - hasFadeArray = argc > 7; - } else { - hasDivisions = argc > 9; - hasFadeArray = argc > 8; - } - - bool isFadeUp; - int16 color; - if (back != -1) { - isFadeUp = false; - color = back; - } else { - isFadeUp = true; - color = 0; - } - - if ((getSciVersion() < SCI_VERSION_2_1_MIDDLE && type == 15) || type > 15) { - error("Illegal show style %d for plane %04x:%04x", type, PRINT_REG(planeObj)); - } - - Plane *plane = _planes.findByObject(planeObj); - if (plane == nullptr) { - error("Plane %04x:%04x is not present in active planes list", PRINT_REG(planeObj)); - } - - bool createNewEntry = true; - ShowStyleEntry *entry = findShowStyleForPlane(planeObj); - if (entry != nullptr) { - // TODO: SCI2.1early has different criteria for show style reuse - bool useExisting = true; - - if (useExisting) { - useExisting = entry->divisions == (hasDivisions ? divisions : _defaultDivisions[type]) && entry->unknownC == _defaultUnknownC[type]; - } - - if (useExisting) { - createNewEntry = false; - isFadeUp = true; - entry->currentStep = 0; - } else { - isFadeUp = true; - color = entry->color; - deleteShowStyleInternal(entry/*, true*/); - entry = nullptr; - } - } - - if (type > 0) { - if (createNewEntry) { - entry = new ShowStyleEntry; - // NOTE: SCI2.1 engine tests if allocation returned a null pointer - // but then only avoids setting currentStep if this is so. Since - // this is a nonsensical approach, we do not do that here - entry->currentStep = 0; - entry->unknownC = _defaultUnknownC[type]; - entry->processed = false; - entry->divisions = hasDivisions ? divisions : _defaultDivisions[type]; - entry->plane = planeObj; - - entry->fadeColorRanges = nullptr; - if (hasFadeArray) { - // NOTE: SCI2.1mid engine does no check to verify that an array is - // successfully retrieved, and SegMan will cause a fatal error - // if we try to use a memory segment that is not an array - SciArray<reg_t> *table = _segMan->lookupArray(pFadeArray); - - uint32 rangeCount = table->getSize(); - entry->fadeColorRangesCount = rangeCount; - - // NOTE: SCI engine code always allocates memory even if the range - // table has no entries, but this does not really make sense, so - // we avoid the allocation call in this case - if (rangeCount > 0) { - entry->fadeColorRanges = new uint16[rangeCount]; - for (size_t i = 0; i < rangeCount; ++i) { - entry->fadeColorRanges[i] = table->getValue(i).toUint16(); - } - } - } else { - entry->fadeColorRangesCount = 0; - } - } - - // NOTE: The original engine had no nullptr check and would just crash - // if it got to here - if (entry == nullptr) { - error("Cannot edit non-existing ShowStyle entry"); - } - - entry->fadeUp = isFadeUp; - entry->color = color; - entry->nextTick = g_sci->getTickCount(); - entry->type = type; - entry->animate = animate; - entry->delay = (seconds * 60 + entry->divisions - 1) / entry->divisions; - - if (entry->delay == 0) { - if (entry->fadeColorRanges != nullptr) { - delete[] entry->fadeColorRanges; - } - delete entry; - error("ShowStyle has no duration"); - } - - if (frameOutNow) { - Common::Rect frameOutRect(0, 0); - frameOut(false, frameOutRect); - } - - if (createNewEntry) { - // TODO: Implement SCI2.1early and SCI3 - entry->next = _showStyles; - _showStyles = entry; - } - } -} - -// NOTE: Different version of SCI engine support different show styles -// SCI2 implements 0, 1/3/5/7/9, 2/4/6/8/10, 11, 12, 13, 14 -// SCI2.1 implements 0, 1/2/3/4/5/6/7/8/9/10/11/12/15, 13, 14 -// SCI3 implements 0, 1/3/5/7/9, 2/4/6/8/10, 11, 12/15, 13, 14 -// TODO: Sierra code needs to be replaced with code that uses the -// computed entry->delay property instead of just counting divisors, -// as the latter is machine-speed-dependent and leads to wrong -// transition speeds -void GfxFrameout::processShowStyles() { - uint32 now = g_sci->getTickCount(); - - bool continueProcessing; - - // TODO: Change to bool? Engine uses inc to set the value to true, - // but there does not seem to be any reason to actually count how - // many times it was set - int doFrameOut; - do { - continueProcessing = false; - doFrameOut = 0; - ShowStyleEntry *showStyle = _showStyles; - while (showStyle != nullptr) { - bool retval = false; - - if (!showStyle->animate) { - ++doFrameOut; - } - - if (showStyle->nextTick < now || !showStyle->animate) { - // TODO: Different versions of SCI use different processors! - // This is the SQ6/KQ7/SCI2.1mid table. - switch (showStyle->type) { - case kShowStyleNone: { - retval = processShowStyleNone(showStyle); - break; - } - case kShowStyleHShutterOut: - case kShowStyleVShutterOut: - case kShowStyleWipeLeft: - case kShowStyleWipeUp: - case kShowStyleIrisOut: - case kShowStyleHShutterIn: - case kShowStyleVShutterIn: - case kShowStyleWipeRight: - case kShowStyleWipeDown: - case kShowStyleIrisIn: - case kShowStyle11: - case kShowStyle12: - case kShowStyleUnknown: { - retval = processShowStyleMorph(showStyle); - break; - } - case kShowStyleFadeOut: { - retval = processShowStyleFade(-1, showStyle); - break; - } - case kShowStyleFadeIn: { - retval = processShowStyleFade(1, showStyle); - break; - } - } - } - - if (!retval) { - continueProcessing = true; - } - - if (retval && showStyle->processed) { - showStyle = deleteShowStyleInternal(showStyle); - } else { - showStyle = showStyle->next; - } - } - - if (g_engine->shouldQuit()) { - return; - } - - if (doFrameOut) { - frameOut(true); - - // TODO: Transitions without the “animate” flag are too - // fast, but the throttle value is arbitrary. Someone on - // real hardware probably needs to test what the actual - // speed of these transitions should be - throttle(); - } - } while(continueProcessing && doFrameOut); -} - -bool GfxFrameout::processShowStyleNone(ShowStyleEntry *const showStyle) { - if (showStyle->fadeUp) { - _palette->setFade(100, 0, 255); - } else { - _palette->setFade(0, 0, 255); - } - - showStyle->processed = true; - return true; -} - -bool GfxFrameout::processShowStyleMorph(ShowStyleEntry *const showStyle) { - palMorphFrameOut(_styleRanges, showStyle); - showStyle->processed = true; - return true; -} - -// TODO: Normalise use of 'entry' vs 'showStyle' -bool GfxFrameout::processShowStyleFade(const int direction, ShowStyleEntry *const showStyle) { - bool unchanged = true; - if (showStyle->currentStep < showStyle->divisions) { - int percent; - if (direction <= 0) { - percent = showStyle->divisions - showStyle->currentStep - 1; - } else { - percent = showStyle->currentStep; - } - - percent *= 100; - percent /= showStyle->divisions - 1; - - if (showStyle->fadeColorRangesCount > 0) { - for (int i = 0, len = showStyle->fadeColorRangesCount; i < len; i += 2) { - _palette->setFade(percent, showStyle->fadeColorRanges[i], showStyle->fadeColorRanges[i + 1]); - } - } else { - _palette->setFade(percent, 0, 255); - } - - ++showStyle->currentStep; - showStyle->nextTick += showStyle->delay; - unchanged = false; - } - - if (showStyle->currentStep >= showStyle->divisions && unchanged) { - if (direction > 0) { - showStyle->processed = true; - } - - return true; - } - - return false; -} - void GfxFrameout::kernelFrameOut(const bool shouldShowBits) { - if (_showStyles != nullptr) { - processShowStyles(); + if (_transitions->hasShowStyles()) { + _transitions->processShowStyles(); } else if (_palMorphIsOn) { - palMorphFrameOut(_styleRanges, nullptr); + palMorphFrameOut(_transitions->_styleRanges, nullptr); _palMorphIsOn = false; } else { -// TODO: Window scroll -// if (g_PlaneScroll) { -// processScrolls(); -// } + if (_transitions->hasScrolls()) { + _transitions->processScrolls(); + } frameOut(shouldShowBits); } @@ -1621,6 +1255,14 @@ void GfxFrameout::throttle() { } } +void GfxFrameout::showRect(const Common::Rect &rect) { + if (!rect.isEmpty()) { + _showList.clear(); + _showList.add(rect); + showBits(); + } +} + #pragma mark - #pragma mark Mouse cursor diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index 99658ede6a..035555af6f 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -27,130 +27,13 @@ #include "sci/graphics/screen_item32.h" namespace Sci { -// TODO: Don't do this this way -int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]); - -// TODO: Verify display styles and adjust names appropriately for -// types 1 through 12 & 15 (others are correct) -// Names should be: -// * VShutterIn, VShutterOut -// * HShutterIn, HShutterOut -// * WipeLeft, WipeRight, WipeDown, WipeUp -// * PixelDissolve -// * ShutDown and Kill? (and Plain and Fade?) -enum ShowStyleType /* : uint8 */ { - kShowStyleNone = 0, - kShowStyleHShutterOut = 1, - kShowStyleHShutterIn = 2, - kShowStyleVShutterOut = 3, - kShowStyleVShutterIn = 4, - kShowStyleWipeLeft = 5, - kShowStyleWipeRight = 6, - kShowStyleWipeUp = 7, - kShowStyleWipeDown = 8, - kShowStyleIrisOut = 9, - kShowStyleIrisIn = 10, - kShowStyle11 = 11, - kShowStyle12 = 12, - kShowStyleFadeOut = 13, - kShowStyleFadeIn = 14, - // TODO: Only in SCI3 - kShowStyleUnknown = 15 -}; - -/** - * Show styles represent transitions applied to draw planes. - * One show style per plane can be active at a time. - */ -struct ShowStyleEntry { - /** - * The ID of the plane this show style belongs to. - * In SCI2.1mid (at least SQ6), per-plane transitions - * were removed and a single plane ID is used. - */ - reg_t plane; - - /** - * The type of the transition. - */ - ShowStyleType type; - - // TODO: This name is probably incorrect - bool fadeUp; - - /** - * The number of steps for the show style. - */ - int16 divisions; - - // NOTE: This property exists from SCI2 through at least - // SCI2.1mid but is never used in the actual processing - // of the styles? - int unknownC; - - /** - * The color used by transitions that draw CelObjColor - * screen items. -1 for transitions that do not draw - * screen items. - */ - int16 color; - - // TODO: Probably uint32 - // TODO: This field probably should be used in order to - // provide time-accurate processing of show styles. In the - // actual SCI engine (at least 2–2.1mid) it appears that - // style transitions are drawn “as fast as possible”, one - // step per loop, even though this delay field exists - int delay; - - // TODO: Probably bool, but never seems to be true? - int animate; - - /** - * The wall time at which the next step of the animation - * should execute. - */ - uint32 nextTick; - - /** - * During playback of the show style, the current step - * (out of divisions). - */ - int currentStep; - - /** - * The next show style. - */ - ShowStyleEntry *next; - - /** - * Whether or not this style has finished running and - * is ready for disposal. - */ - bool processed; - - // - // Engine specific properties for SCI2.1mid through SCI3 - // - - /** - * The number of entries in the fadeColorRanges array. - */ - uint8 fadeColorRangesCount; - - /** - * A pointer to an dynamically sized array of palette - * indexes, in the order [ fromColor, toColor, ... ]. - * Only colors within this range are transitioned. - */ - uint16 *fadeColorRanges; -}; - typedef Common::Array<DrawList> ScreenItemListList; typedef Common::Array<RectList> EraseListList; class GfxCoordAdjuster32; class GfxScreen; +class GfxTransitions32; +class PlaneShowStyle; /** * Frameout class, kFrameout and relevant functions for SCI32 games. @@ -166,7 +49,7 @@ private: SegManager *_segMan; public: - GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette32 *palette); + GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette32 *palette, GfxTransitions32 *transitions); ~GfxFrameout(); void clear(); @@ -287,37 +170,10 @@ public: void kernelAddPicAt(const reg_t planeObject, const GuiResourceId pictureId, const int16 pictureX, const int16 pictureY, const bool mirrorX); #pragma mark - - - // TODO: Remap-related? - void kernelSetPalStyleRange(const uint8 fromColor, const uint8 toColor); - -#pragma mark - -#pragma mark Transitions -private: - int *_dissolveSequenceSeeds; - int16 *_defaultDivisions; - int16 *_defaultUnknownC; - - /** - * TODO: Documentation - */ - ShowStyleEntry *_showStyles; - - inline ShowStyleEntry *findShowStyleForPlane(const reg_t planeObj) const; - inline ShowStyleEntry *deleteShowStyleInternal(ShowStyleEntry *const showStyle); - void processShowStyles(); - bool processShowStyleNone(ShowStyleEntry *showStyle); - bool processShowStyleMorph(ShowStyleEntry *showStyle); - bool processShowStyleFade(const int direction, ShowStyleEntry *showStyle); - -public: - // NOTE: This signature is taken from SCI3 Phantasmagoria 2 - // and is valid for all implementations of SCI32 - void kernelSetShowStyle(const uint16 argc, const reg_t planeObj, const ShowStyleType type, const int16 seconds, const int16 direction, const int16 priority, const int16 animate, const int16 frameOutNow, reg_t pFadeArray, int16 divisions, const int16 blackScreen); - -#pragma mark - #pragma mark Rendering private: + GfxTransitions32 *_transitions; + /** * State tracker to provide more accurate 60fps * video throttling. @@ -325,11 +181,6 @@ private: uint8 _throttleState; /** - * TODO: Documentation - */ - int8 _styleRanges[256]; - - /** * The internal display pixel buffer. During frameOut, * this buffer is drawn into according to the draw and * erase rects calculated by `calcLists`, then drawn out @@ -428,16 +279,35 @@ private: void mergeToShowList(const Common::Rect &drawRect, RectList &showList, const int overdrawThreshold); /** - * TODO: Documentation - */ - void palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry *showStyle); - - /** * Writes the internal frame buffer out to hardware and * clears the show list. */ void showBits(); + /** + * Validates whether the given palette index in the + * style range should copy a color from the next + * palette to the source palette during a palette + * morph operation. + */ + inline bool validZeroStyle(const uint8 style, const int i) const { + if (style != 0) { + return false; + } + + // TODO: Cannot check Shivers or MGDX until those executables can be + // unwrapped + switch (g_sci->getGameId()) { + case GID_KQ7: + case GID_PHANTASMAGORIA: + case GID_SQ6: + return (i > 71 && i < 104); + break; + default: + return true; + } + } + public: /** * Whether palMorphFrameOut should be used instead of @@ -467,6 +337,11 @@ public: void frameOut(const bool shouldShowBits, const Common::Rect &eraseRect = Common::Rect()); /** + * TODO: Documentation + */ + void palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *showStyle); + + /** * Modifies the raw pixel data for the next frame with * new palette indexes based on matched style ranges. */ @@ -484,6 +359,12 @@ public: return 1; }; + /** + * Draws a portion of the current screen buffer to + * hardware. Used to display show styles in SCI2.1mid+. + */ + void showRect(const Common::Rect &rect); + #pragma mark - #pragma mark Mouse cursor private: diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp index 6b05b2d98a..cb6ec74e8f 100644 --- a/engines/sci/graphics/plane32.cpp +++ b/engines/sci/graphics/plane32.cpp @@ -194,11 +194,11 @@ void Plane::addPicInternal(const GuiResourceId pictureId, const Common::Point *p _type = transparent ? kPlaneTypeTransparentPicture : kPlaneTypePicture; } -void Plane::addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX) { +GuiResourceId Plane::addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX) { + GuiResourceId oldPictureId = _pictureId; deletePic(pictureId); addPicInternal(pictureId, &position, mirrorX); - // NOTE: In SCI engine this method returned the pictureId of the - // plane, but this return value was never used + return oldPictureId; } void Plane::changePic() { diff --git a/engines/sci/graphics/plane32.h b/engines/sci/graphics/plane32.h index 3981a2b319..f55d97b6ed 100644 --- a/engines/sci/graphics/plane32.h +++ b/engines/sci/graphics/plane32.h @@ -336,14 +336,6 @@ private: /** * Marks all screen items to be deleted that are within - * this plane and match the given picture ID, then sets - * the picture ID of the plane to the new picture ID - * without adding any screen items. - */ - void deletePic(const GuiResourceId oldPictureId, const GuiResourceId newPictureId); - - /** - * Marks all screen items to be deleted that are within * this plane and are picture cels. */ void deleteAllPics(); @@ -355,7 +347,7 @@ public: * new picture resource to the plane at the given * position. */ - void addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX); + GuiResourceId addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX); /** * If the plane is a picture plane, re-adds all cels @@ -364,6 +356,14 @@ public: */ void changePic(); + /** + * Marks all screen items to be deleted that are within + * this plane and match the given picture ID, then sets + * the picture ID of the plane to the new picture ID + * without adding any screen items. + */ + void deletePic(const GuiResourceId oldPictureId, const GuiResourceId newPictureId); + #pragma mark - #pragma mark Plane - Rendering private: diff --git a/engines/sci/graphics/transitions32.cpp b/engines/sci/graphics/transitions32.cpp new file mode 100644 index 0000000000..01956e6a2f --- /dev/null +++ b/engines/sci/graphics/transitions32.cpp @@ -0,0 +1,981 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sci/engine/segment.h" +#include "sci/engine/seg_manager.h" +#include "sci/engine/state.h" +#include "sci/graphics/frameout.h" +#include "sci/graphics/palette32.h" +#include "sci/graphics/text32.h" +#include "sci/graphics/transitions32.h" +#include "sci/sci.h" + +namespace Sci { +static int dissolveSequences[2][20] = { + /* SCI2.1early- */ { 3, 6, 12, 20, 48, 96, 184, 272, 576, 1280, 3232, 6912, 13568, 24576, 46080 }, + /* SCI2.1mid+ */ { 0, 0, 3, 6, 12, 20, 48, 96, 184, 272, 576, 1280, 3232, 6912, 13568, 24576, 46080, 73728, 132096, 466944 } +}; +static int16 divisionsDefaults[2][16] = { + /* SCI2.1early- */ { 1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 40, 40, 101, 101 }, + /* SCI2.1mid+ */ { 1, 20, 20, 20, 20, 10, 10, 10, 10, 20, 20, 6, 10, 101, 101, 2 } +}; + +GfxTransitions32::GfxTransitions32(SegManager *segMan) : + _segMan(segMan), + _throttleState(0) { + for (int i = 0; i < 236; i += 2) { + _styleRanges[i] = 0; + _styleRanges[i + 1] = -1; + } + for (int i = 236; i < ARRAYSIZE(_styleRanges); ++i) { + _styleRanges[i] = 0; + } + + if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { + _dissolveSequenceSeeds = dissolveSequences[0]; + _defaultDivisions = divisionsDefaults[0]; + } else { + _dissolveSequenceSeeds = dissolveSequences[1]; + _defaultDivisions = divisionsDefaults[1]; + } +} + +GfxTransitions32::~GfxTransitions32() { + for (ShowStyleList::iterator it = _showStyles.begin(); it != _showStyles.end(); ++it) { + deleteShowStyle(it); + } + _scrolls.clear(); +} + +void GfxTransitions32::throttle() { + uint8 throttleTime; + if (_throttleState == 2) { + throttleTime = 34; + _throttleState = 0; + } else { + throttleTime = 33; + ++_throttleState; + } + + g_sci->getEngineState()->speedThrottler(throttleTime); + g_sci->getEngineState()->_throttleTrigger = true; +} + +#pragma mark - +#pragma mark Show styles + +void GfxTransitions32::processShowStyles() { + uint32 now = g_sci->getTickCount(); + + bool continueProcessing; + bool doFrameOut; + do { + continueProcessing = false; + doFrameOut = false; + ShowStyleList::iterator showStyle = _showStyles.begin(); + while (showStyle != _showStyles.end()) { + bool finished = false; + + if (!showStyle->animate) { + doFrameOut = true; + } + + finished = processShowStyle(*showStyle, now); + + if (!finished) { + continueProcessing = true; + } + + if (finished && showStyle->processed) { + showStyle = deleteShowStyle(showStyle); + } else { + showStyle = ++showStyle; + } + } + + if (g_engine->shouldQuit()) { + return; + } + + if (doFrameOut) { + g_sci->_gfxFrameout->frameOut(true); + throttle(); + } + } while(continueProcessing && doFrameOut); +} + +void GfxTransitions32::processEffects(PlaneShowStyle &showStyle) { + switch(showStyle.type) { + case kShowStyleHShutterOut: + processHShutterOut(showStyle); + break; + case kShowStyleHShutterIn: + processHShutterIn(showStyle); + break; + case kShowStyleVShutterOut: + processVShutterOut(showStyle); + break; + case kShowStyleVShutterIn: + processVShutterIn(showStyle); + break; + case kShowStyleWipeLeft: + processWipeLeft(showStyle); + break; + case kShowStyleWipeRight: + processWipeRight(showStyle); + break; + case kShowStyleWipeUp: + processWipeUp(showStyle); + break; + case kShowStyleWipeDown: + processWipeDown(showStyle); + break; + case kShowStyleIrisOut: + processIrisOut(showStyle); + break; + case kShowStyleIrisIn: + processIrisIn(showStyle); + break; + case kShowStyleDissolveNoMorph: + case kShowStyleDissolve: + processPixelDissolve(showStyle); + break; + case kShowStyleNone: + case kShowStyleFadeOut: + case kShowStyleFadeIn: + case kShowStyleMorph: + break; + } +} + +// TODO: 10-argument version is only in SCI3; argc checks are currently wrong for this version +// and need to be fixed in future +void GfxTransitions32::kernelSetShowStyle(const uint16 argc, const reg_t planeObj, const ShowStyleType type, const int16 seconds, const int16 back, const int16 priority, const int16 animate, const int16 frameOutNow, reg_t pFadeArray, int16 divisions, const int16 blackScreen) { + + bool hasDivisions = false; + bool hasFadeArray = false; + + // KQ7 2.0b uses a mismatched version of the Styler script (SCI2.1early script + // for SCI2.1mid engine), so the calls it makes to kSetShowStyle are wrong and + // put `divisions` where `pFadeArray` is supposed to be + if (getSciVersion() == SCI_VERSION_2_1_MIDDLE && g_sci->getGameId() == GID_KQ7) { + hasDivisions = argc > 7; + hasFadeArray = false; + divisions = argc > 7 ? pFadeArray.toSint16() : -1; + pFadeArray = NULL_REG; + } else if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { + hasDivisions = argc > 7; + hasFadeArray = false; + } else if (getSciVersion() < SCI_VERSION_3) { + hasDivisions = argc > 8; + hasFadeArray = argc > 7; + } else { + hasDivisions = argc > 9; + hasFadeArray = argc > 8; + } + + bool isFadeUp; + int16 color; + if (back != -1) { + isFadeUp = false; + color = back; + } else { + isFadeUp = true; + color = 0; + } + + if ((getSciVersion() < SCI_VERSION_2_1_MIDDLE && g_sci->getGameId() != GID_KQ7 && type == 15) || type > 15) { + error("Illegal show style %d for plane %04x:%04x", type, PRINT_REG(planeObj)); + } + + Plane *plane = g_sci->_gfxFrameout->getPlanes().findByObject(planeObj); + if (plane == nullptr) { + error("Plane %04x:%04x is not present in active planes list", PRINT_REG(planeObj)); + } + + bool createNewEntry = true; + PlaneShowStyle *entry = findShowStyleForPlane(planeObj); + if (entry != nullptr) { + bool useExisting = true; + + if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { + useExisting = plane->_gameRect.width() == entry->width && plane->_gameRect.height() == entry->height; + } + + if (useExisting) { + useExisting = entry->divisions == (hasDivisions ? divisions : _defaultDivisions[type]); + } + + if (useExisting) { + createNewEntry = false; + isFadeUp = true; + entry->currentStep = 0; + } else { + isFadeUp = true; + color = entry->color; + deleteShowStyle(findIteratorForPlane(planeObj)); + entry = nullptr; + } + } + + if (type > 0) { + if (createNewEntry) { + entry = new PlaneShowStyle; + // NOTE: SCI2.1 engine tests if allocation returned a null pointer + // but then only avoids setting currentStep if this is so. Since + // this is a nonsensical approach, we do not do that here + entry->currentStep = 0; + entry->processed = false; + entry->divisions = hasDivisions ? divisions : _defaultDivisions[type]; + entry->plane = planeObj; + entry->fadeColorRangesCount = 0; + + if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { + // for pixel dissolve + entry->bitmap = NULL_REG; + entry->bitmapScreenItem = nullptr; + + // for wipe + entry->screenItems.clear(); + entry->width = plane->_gameRect.width(); + entry->height = plane->_gameRect.height(); + } else { + entry->fadeColorRanges = nullptr; + if (hasFadeArray) { + // NOTE: SCI2.1mid engine does no check to verify that an array is + // successfully retrieved, and SegMan will cause a fatal error + // if we try to use a memory segment that is not an array + SciArray<reg_t> *table = _segMan->lookupArray(pFadeArray); + + uint32 rangeCount = table->getSize(); + entry->fadeColorRangesCount = rangeCount; + + // NOTE: SCI engine code always allocates memory even if the range + // table has no entries, but this does not really make sense, so + // we avoid the allocation call in this case + if (rangeCount > 0) { + entry->fadeColorRanges = new uint16[rangeCount]; + for (size_t i = 0; i < rangeCount; ++i) { + entry->fadeColorRanges[i] = table->getValue(i).toUint16(); + } + } + } + } + } + + // NOTE: The original engine had no nullptr check and would just crash + // if it got to here + if (entry == nullptr) { + error("Cannot edit non-existing ShowStyle entry"); + } + + entry->fadeUp = isFadeUp; + entry->color = color; + entry->nextTick = g_sci->getTickCount(); + entry->type = type; + entry->animate = animate; + entry->delay = (seconds * 60 + entry->divisions - 1) / entry->divisions; + + if (entry->delay == 0) { + error("ShowStyle has no duration"); + } + + if (frameOutNow) { + // Creates a reference frame for the pixel dissolves to use + g_sci->_gfxFrameout->frameOut(false); + } + + if (createNewEntry) { + if (getSciVersion() <= SCI_VERSION_2_1_EARLY) { + switch (entry->type) { + case kShowStyleIrisOut: + case kShowStyleIrisIn: + configure21EarlyIris(*entry, priority); + break; + case kShowStyleDissolve: + configure21EarlyDissolve(*entry, priority, plane->_gameRect); + break; + default: + // do nothing + break; + } + } + + _showStyles.push_back(*entry); + delete entry; + } + } +} + +void GfxTransitions32::kernelSetPalStyleRange(const uint8 fromColor, const uint8 toColor) { + if (toColor > fromColor) { + return; + } + + for (int i = fromColor; i <= toColor; ++i) { + _styleRanges[i] = 0; + } +} + +PlaneShowStyle *GfxTransitions32::findShowStyleForPlane(const reg_t planeObj) { + for (ShowStyleList::iterator it = _showStyles.begin(); it != _showStyles.end(); ++it) { + if (it->plane == planeObj) { + return &*it; + } + } + + return nullptr; +} + +ShowStyleList::iterator GfxTransitions32::findIteratorForPlane(const reg_t planeObj) { + ShowStyleList::iterator it; + for (it = _showStyles.begin(); it != _showStyles.end(); ++it) { + if (it->plane == planeObj) { + break; + } + } + + return it; +} + +ShowStyleList::iterator GfxTransitions32::deleteShowStyle(const ShowStyleList::iterator &showStyle) { + switch (showStyle->type) { + case kShowStyleDissolveNoMorph: + case kShowStyleDissolve: + if (getSciVersion() <= SCI_VERSION_2_1_EARLY) { + _segMan->freeHunkEntry(showStyle->bitmap); + g_sci->_gfxFrameout->deleteScreenItem(*showStyle->bitmapScreenItem); + } + break; + case kShowStyleIrisOut: + case kShowStyleIrisIn: + if (getSciVersion() <= SCI_VERSION_2_1_EARLY) { + for (uint i = 0; i < showStyle->screenItems.size(); ++i) { + ScreenItem *screenItem = showStyle->screenItems[i]; + if (screenItem != nullptr) { + g_sci->_gfxFrameout->deleteScreenItem(*screenItem); + } + } + } + break; + case kShowStyleFadeIn: + case kShowStyleFadeOut: + if (getSciVersion() > SCI_VERSION_2_1_EARLY && showStyle->fadeColorRangesCount > 0) { + delete[] showStyle->fadeColorRanges; + } + break; + case kShowStyleNone: + case kShowStyleMorph: + // do nothing + break; + default: + error("Unknown delete transition type %d", showStyle->type); + } + + return _showStyles.erase(showStyle); +} + +void GfxTransitions32::configure21EarlyIris(PlaneShowStyle &showStyle, const int16 priority) { + showStyle.numEdges = 4; + const int numScreenItems = showStyle.numEdges * showStyle.divisions; + showStyle.screenItems.reserve(numScreenItems); + + CelInfo32 celInfo; + celInfo.type = kCelTypeColor; + celInfo.color = showStyle.color; + + const int width = showStyle.width; + const int height = showStyle.height; + const int divisions = showStyle.divisions; + + for (int i = 0; i < divisions; ++i) { + Common::Rect rect; + + // Top + rect.left = (width * i) / (2 * divisions); + rect.top = (height * i) / (2 * divisions); + rect.right = width - rect.left; + rect.bottom = (height + 1) * (i + 1) / (2 * divisions); + const int16 topTop = rect.top; + const int16 topBottom = rect.bottom; + + showStyle.screenItems.push_back(new ScreenItem(showStyle.plane, celInfo, rect)); + showStyle.screenItems.back()->_priority = priority; + showStyle.screenItems.back()->_fixedPriority = true; + + // Bottom + rect.top = height - rect.bottom; + rect.bottom = height - topTop; + const int16 bottomTop = rect.top; + + showStyle.screenItems.push_back(new ScreenItem(showStyle.plane, celInfo, rect)); + showStyle.screenItems.back()->_priority = priority; + showStyle.screenItems.back()->_fixedPriority = true; + + // Left + rect.top = topBottom; + rect.right = (width + 1) * (i + 1) / (2 * divisions); + rect.bottom = bottomTop; + const int16 leftLeft = rect.left; + + showStyle.screenItems.push_back(new ScreenItem(showStyle.plane, celInfo, rect)); + showStyle.screenItems.back()->_priority = priority; + showStyle.screenItems.back()->_fixedPriority = true; + + // Right + rect.left = width - rect.right; + rect.right = width - leftLeft; + + showStyle.screenItems.push_back(new ScreenItem(showStyle.plane, celInfo, rect)); + showStyle.screenItems.back()->_priority = priority; + showStyle.screenItems.back()->_fixedPriority = true; + } + + if (showStyle.fadeUp) { + for (int i = 0; i < numScreenItems; ++i) { + g_sci->_gfxFrameout->addScreenItem(*showStyle.screenItems[i]); + } + } +} + +void GfxTransitions32::configure21EarlyDissolve(PlaneShowStyle &showStyle, const int16 priority, const Common::Rect &gameRect) { + + BitmapResource bitmap(_segMan, showStyle.width, showStyle.height, kDefaultSkipColor, 0, 0, kLowResX, kLowResY, 0, false, false); + + showStyle.bitmap = bitmap.getObject(); + + const Buffer &source = g_sci->_gfxFrameout->getCurrentBuffer(); + Buffer target(showStyle.width, showStyle.height, bitmap.getPixels()); + + target.fillRect(Common::Rect(bitmap.getWidth(), bitmap.getHeight()), kDefaultSkipColor); + target.copyRectToSurface(source, 0, 0, gameRect); + + CelInfo32 celInfo; + celInfo.type = kCelTypeMem; + celInfo.bitmap = bitmap.getObject(); + + showStyle.bitmapScreenItem = new ScreenItem(showStyle.plane, celInfo, Common::Point(0, 0), ScaleInfo()); + showStyle.bitmapScreenItem->_priority = priority; + showStyle.bitmapScreenItem->_fixedPriority = true; + + g_sci->_gfxFrameout->addScreenItem(*showStyle.bitmapScreenItem); +} + +bool GfxTransitions32::processShowStyle(PlaneShowStyle &showStyle, uint32 now) { + if (showStyle.nextTick >= now && showStyle.animate) { + return false; + } + + switch (showStyle.type) { + default: + case kShowStyleNone: + return processNone(showStyle); + case kShowStyleHShutterOut: + case kShowStyleHShutterIn: + case kShowStyleVShutterOut: + case kShowStyleVShutterIn: + case kShowStyleWipeLeft: + case kShowStyleWipeRight: + case kShowStyleWipeUp: + case kShowStyleWipeDown: + case kShowStyleDissolveNoMorph: + case kShowStyleMorph: + return processMorph(showStyle); + case kShowStyleDissolve: + if (getSciVersion() > SCI_VERSION_2_1_EARLY) { + return processMorph(showStyle); + } else { + return processPixelDissolve(showStyle); + } + case kShowStyleIrisOut: + if (getSciVersion() > SCI_VERSION_2_1_EARLY) { + return processMorph(showStyle); + } else { + return processIrisOut(showStyle); + } + case kShowStyleIrisIn: + if (getSciVersion() > SCI_VERSION_2_1_EARLY) { + return processMorph(showStyle); + } else { + return processIrisIn(showStyle); + } + case kShowStyleFadeOut: + return processFade(-1, showStyle); + case kShowStyleFadeIn: + return processFade(1, showStyle); + } +} + +bool GfxTransitions32::processNone(PlaneShowStyle &showStyle) { + if (showStyle.fadeUp) { + g_sci->_gfxPalette32->setFade(100, 0, 255); + } else { + g_sci->_gfxPalette32->setFade(0, 0, 255); + } + + showStyle.processed = true; + return true; +} + +void GfxTransitions32::processHShutterOut(PlaneShowStyle &showStyle) { + error("HShutterOut is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); +} + +void GfxTransitions32::processHShutterIn(PlaneShowStyle &showStyle) { + error("HShutterIn is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); +} + +void GfxTransitions32::processVShutterOut(PlaneShowStyle &showStyle) { + error("VShutterOut is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); +} + +void GfxTransitions32::processVShutterIn(PlaneShowStyle &showStyle) { + error("VShutterIn is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); +} + +void GfxTransitions32::processWipeLeft(PlaneShowStyle &showStyle) { + error("WipeLeft is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); +} + +void GfxTransitions32::processWipeRight(PlaneShowStyle &showStyle) { + error("WipeRight is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); +} + +void GfxTransitions32::processWipeUp(PlaneShowStyle &showStyle) { + error("WipeUp is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); +} + +void GfxTransitions32::processWipeDown(PlaneShowStyle &showStyle) { + error("WipeDown is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); +} + +bool GfxTransitions32::processIrisOut(PlaneShowStyle &showStyle) { + if (getSciVersion() > SCI_VERSION_2_1_EARLY) { + error("IrisOut is not known to be used by any SCI2.1mid+ game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); + } + + return processWipe(-1, showStyle); +} + +bool GfxTransitions32::processIrisIn(PlaneShowStyle &showStyle) { + if (getSciVersion() > SCI_VERSION_2_1_EARLY) { + error("IrisIn is not known to be used by any SCI2.1mid+ game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); + } + + return processWipe(1, showStyle); +} + +void GfxTransitions32::processDissolveNoMorph(PlaneShowStyle &showStyle) { + error("DissolveNoMorph is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); +} + +inline int bitWidth(int number) { + int width = 0; + while (number != 0) { + number >>= 1; + width += 1; + } + return width; +} + +bool GfxTransitions32::processPixelDissolve(PlaneShowStyle &showStyle) { + if (getSciVersion() > SCI_VERSION_2_1_EARLY) { + return processPixelDissolve21Mid(showStyle); + } else { + return processPixelDissolve21Early(showStyle); + } +} + +bool GfxTransitions32::processPixelDissolve21Early(PlaneShowStyle &showStyle) { + bool unchanged = true; + + BitmapResource bitmap(showStyle.bitmap); + Buffer buffer(showStyle.width, showStyle.height, bitmap.getPixels()); + + uint32 numPixels = showStyle.width * showStyle.height; + uint32 numPixelsPerDivision = (numPixels + showStyle.divisions) / showStyle.divisions; + + uint32 index; + if (showStyle.currentStep == 0) { + int i = 0; + index = numPixels; + if (index != 1) { + for (;;) { + index >>= 1; + if (index == 1) { + break; + } + i++; + } + } + + showStyle.dissolveMask = _dissolveSequenceSeeds[i]; + index = 53427; + + showStyle.firstPixel = index; + showStyle.pixel = index; + } else { + index = showStyle.pixel; + for (;;) { + if (index & 1) { + index >>= 1; + index ^= showStyle.dissolveMask; + } else { + index >>= 1; + } + + if (index < numPixels) { + break; + } + } + + if (index == showStyle.firstPixel) { + index = 0; + } + } + + if (showStyle.currentStep < showStyle.divisions) { + for (uint32 i = 0; i < numPixelsPerDivision; ++i) { + *(byte *)buffer.getBasePtr(index % showStyle.width, index / showStyle.width) = showStyle.color; + + for (;;) { + if (index & 1) { + index >>= 1; + index ^= showStyle.dissolveMask; + } else { + index >>= 1; + } + + if (index < numPixels) { + break; + } + } + + if (index == showStyle.firstPixel) { + buffer.fillRect(Common::Rect(0, 0, showStyle.width, showStyle.height), showStyle.color); + break; + } + } + + showStyle.pixel = index; + showStyle.nextTick += showStyle.delay; + ++showStyle.currentStep; + unchanged = false; + if (showStyle.bitmapScreenItem->_created == 0) { + showStyle.bitmapScreenItem->_updated = g_sci->_gfxFrameout->getScreenCount(); + } + } + + if ((showStyle.currentStep >= showStyle.divisions) && unchanged) { + if (showStyle.fadeUp) { + showStyle.processed = true; + } + + return true; + } + + return false; +} + +bool GfxTransitions32::processPixelDissolve21Mid(PlaneShowStyle &showStyle) { + // SQ6 room 530 + + Plane* plane = g_sci->_gfxFrameout->getVisiblePlanes().findByObject(showStyle.plane); + const Common::Rect &screenRect = plane->_screenRect; + Common::Rect rect; + + const int planeWidth = screenRect.width(); + const int planeHeight = screenRect.height(); + const int divisions = showStyle.divisions; + const int width = planeWidth / divisions + ((planeWidth % divisions) ? 1 : 0); + const int height = planeHeight / divisions + ((planeHeight % divisions) ? 1 : 0); + + const uint32 mask = _dissolveSequenceSeeds[bitWidth(width * height - 1)]; + int seq = 1; + + uint iteration = 0; + const uint numIterationsPerTick = (width * height + divisions) / divisions; + + do { + int row = seq / width; + int col = seq % width; + + if (row < height) { + if (row == height && (planeHeight % divisions)) { + if (col == width && (planeWidth % divisions)) { + rect.left = col * divisions; + rect.top = row * divisions; + rect.right = col * divisions + (planeWidth % divisions); + rect.bottom = row * divisions + (planeHeight % divisions); + rect.clip(screenRect); + g_sci->_gfxFrameout->showRect(rect); + } else { + rect.left = col * divisions; + rect.top = row * divisions; + rect.right = col * divisions * 2; + rect.bottom = row * divisions + (planeHeight % divisions); + rect.clip(screenRect); + g_sci->_gfxFrameout->showRect(rect); + } + } else { + if (col == width && (planeWidth % divisions)) { + rect.left = col * divisions; + rect.top = row * divisions; + rect.right = col * divisions + (planeWidth % divisions) + 1; + rect.bottom = row * divisions * 2 + 1; + rect.clip(screenRect); + g_sci->_gfxFrameout->showRect(rect); + } else { + rect.left = col * divisions; + rect.top = row * divisions; + rect.right = col * divisions * 2 + 1; + rect.bottom = row * divisions * 2 + 1; + rect.clip(screenRect); + g_sci->_gfxFrameout->showRect(rect); + } + } + } + + if (seq & 1) { + seq = (seq >> 1) ^ mask; + } else { + seq >>= 1; + } + + if (++iteration == numIterationsPerTick) { + throttle(); + iteration = 0; + } + } while(seq != 1 && !g_engine->shouldQuit()); + + rect.left = screenRect.left; + rect.top = screenRect.top; + rect.right = divisions + screenRect.left; + rect.bottom = divisions + screenRect.bottom; + rect.clip(screenRect); + g_sci->_gfxFrameout->showRect(rect); + throttle(); + + g_sci->_gfxFrameout->showRect(screenRect); + return true; +} + +bool GfxTransitions32::processFade(const int8 direction, PlaneShowStyle &showStyle) { + bool unchanged = true; + if (showStyle.currentStep < showStyle.divisions) { + int percent; + if (direction <= 0) { + percent = showStyle.divisions - showStyle.currentStep - 1; + } else { + percent = showStyle.currentStep; + } + + percent *= 100; + percent /= showStyle.divisions - 1; + + if (showStyle.fadeColorRangesCount > 0) { + for (int i = 0, len = showStyle.fadeColorRangesCount; i < len; i += 2) { + g_sci->_gfxPalette32->setFade(percent, showStyle.fadeColorRanges[i], showStyle.fadeColorRanges[i + 1]); + } + } else { + g_sci->_gfxPalette32->setFade(percent, 0, 255); + } + + ++showStyle.currentStep; + showStyle.nextTick += showStyle.delay; + unchanged = false; + } + + if (showStyle.currentStep >= showStyle.divisions && unchanged) { + if (direction > 0) { + showStyle.processed = true; + } + + return true; + } + + return false; +} + +bool GfxTransitions32::processMorph(PlaneShowStyle &showStyle) { + g_sci->_gfxFrameout->palMorphFrameOut(_styleRanges, &showStyle); + showStyle.processed = true; + return true; +} + +bool GfxTransitions32::processWipe(const int8 direction, PlaneShowStyle &showStyle) { + bool unchanged = true; + if (showStyle.currentStep < showStyle.divisions) { + int index; + if (direction > 0) { + index = showStyle.currentStep; + } else { + index = showStyle.divisions - showStyle.currentStep - 1; + } + + index *= showStyle.numEdges; + for (int i = 0; i < showStyle.numEdges; ++i) { + ScreenItem *screenItem = showStyle.screenItems[index + i]; + if (showStyle.fadeUp) { + g_sci->_gfxFrameout->deleteScreenItem(*screenItem); + showStyle.screenItems[index + i] = nullptr; + } else { + g_sci->_gfxFrameout->addScreenItem(*screenItem); + } + } + + ++showStyle.currentStep; + showStyle.nextTick += showStyle.delay; + unchanged = false; + } + + if (showStyle.currentStep >= showStyle.divisions && unchanged) { + if (showStyle.fadeUp) { + showStyle.processed = true; + } + + return true; + } + + return false; +} + +#pragma mark - +#pragma mark Scrolls + +void GfxTransitions32::processScrolls() { + for (ScrollList::iterator it = _scrolls.begin(); it != _scrolls.end(); ) { + bool finished = processScroll(*it); + if (finished) { + it = _scrolls.erase(it); + } else { + ++it; + } + } + + throttle(); +} + +void GfxTransitions32::kernelSetScroll(const reg_t planeId, const int16 deltaX, const int16 deltaY, const GuiResourceId pictureId, const bool animate, const bool mirrorX) { + + for (ScrollList::const_iterator it = _scrolls.begin(); it != _scrolls.end(); ++it) { + if (it->plane == planeId) { + error("Scroll already exists on plane %04x:%04x", PRINT_REG(planeId)); + } + } + + if (!deltaX && !deltaY) { + error("kSetScroll: Scroll has no movement"); + } + + if (deltaX && deltaY) { + error("kSetScroll: Cannot scroll in two dimensions"); + } + + PlaneScroll *scroll = new PlaneScroll; + scroll->plane = planeId; + scroll->x = 0; + scroll->y = 0; + scroll->deltaX = deltaX; + scroll->deltaY = deltaY; + scroll->newPictureId = pictureId; + scroll->animate = animate; + scroll->startTick = g_sci->getTickCount(); + + Plane *plane = g_sci->_gfxFrameout->getPlanes().findByObject(planeId); + if (plane == nullptr) { + error("kSetScroll: Plane %04x:%04x not found", PRINT_REG(planeId)); + } + + Plane *visiblePlane = g_sci->_gfxFrameout->getPlanes().findByObject(planeId); + if (visiblePlane == nullptr) { + error("kSetScroll: Visible plane %04x:%04x not found", PRINT_REG(planeId)); + } + + const Common::Rect &gameRect = visiblePlane->_gameRect; + Common::Point picOrigin; + + if (deltaX) { + picOrigin.y = 0; + + if (deltaX > 0) { + scroll->x = picOrigin.x = -gameRect.width(); + } else { + scroll->x = picOrigin.x = gameRect.width(); + } + } else { + picOrigin.x = 0; + + if (deltaY > 0) { + scroll->y = picOrigin.y = -gameRect.height(); + } else { + scroll->y = picOrigin.y = gameRect.height(); + } + } + + scroll->oldPictureId = plane->addPic(pictureId, picOrigin, mirrorX); + + if (animate) { + _scrolls.push_front(*scroll); + } else { + bool finished = false; + while (!finished && !g_engine->shouldQuit()) { + finished = processScroll(*scroll); + g_sci->_gfxFrameout->frameOut(true); + throttle(); + } + delete scroll; + } +} + +bool GfxTransitions32::processScroll(PlaneScroll &scroll) { + bool finished = false; + uint32 now = g_sci->getTickCount(); + if (scroll.startTick >= now) { + return false; + } + + int deltaX = scroll.deltaX; + int deltaY = scroll.deltaY; + if (((scroll.x + deltaX) * scroll.y) <= 0) { + deltaX = -scroll.x; + } + if (((scroll.y + deltaY) * scroll.y) <= 0) { + deltaY = -scroll.y; + } + + scroll.x += deltaX; + scroll.y += deltaY; + + Plane *plane = g_sci->_gfxFrameout->getPlanes().findByObject(scroll.plane); + + if ((scroll.x == 0) && (scroll.y == 0)) { + plane->deletePic(scroll.oldPictureId, scroll.newPictureId); + finished = true; + } + + plane->scrollScreenItems(deltaX, deltaY, true); + + return finished; +} + +} // End of namespace Sci diff --git a/engines/sci/graphics/transitions32.h b/engines/sci/graphics/transitions32.h new file mode 100644 index 0000000000..3968378a3c --- /dev/null +++ b/engines/sci/graphics/transitions32.h @@ -0,0 +1,476 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCI_GRAPHICS_TRANSITIONS32_H +#define SCI_GRAPHICS_TRANSITIONS32_H + +#include "common/list.h" +#include "common/scummsys.h" +#include "sci/engine/vm_types.h" + +namespace Sci { +enum ShowStyleType /* : uint8 */ { + kShowStyleNone = 0, + kShowStyleHShutterOut = 1, + kShowStyleHShutterIn = 2, + kShowStyleVShutterOut = 3, + kShowStyleVShutterIn = 4, + kShowStyleWipeLeft = 5, + kShowStyleWipeRight = 6, + kShowStyleWipeUp = 7, + kShowStyleWipeDown = 8, + kShowStyleIrisOut = 9, + kShowStyleIrisIn = 10, + kShowStyleDissolveNoMorph = 11, + kShowStyleDissolve = 12, + kShowStyleFadeOut = 13, + kShowStyleFadeIn = 14, + kShowStyleMorph = 15 +}; + +/** + * Show styles represent transitions applied to draw planes. + * One show style per plane can be active at a time. + */ +struct PlaneShowStyle { + /** + * The ID of the plane this show style belongs to. + * In SCI2.1mid (at least SQ6), per-plane transitions + * were removed and a single plane ID is used. + */ + reg_t plane; + + /** + * The type of the transition. + */ + ShowStyleType type; + + /** + * When true, the show style is an entry transition + * to a new room. When false, it is an exit + * transition away from an old room. + */ + bool fadeUp; + + /** + * The number of steps for the show style. + */ + int16 divisions; + + /** + * The color used by transitions that draw CelObjColor + * screen items. -1 for transitions that do not draw + * screen items. + */ + int16 color; + + // TODO: Probably uint32 + // TODO: This field probably should be used in order to + // provide time-accurate processing of show styles. In the + // actual SCI engine (at least 2–2.1mid) it appears that + // style transitions are drawn “as fast as possible”, one + // step per loop, even though this delay field exists + int delay; + + // TODO: Probably bool, but never seems to be true? + bool animate; + + /** + * The wall time at which the next step of the animation + * should execute. + */ + uint32 nextTick; + + /** + * During playback of the show style, the current step + * (out of divisions). + */ + int currentStep; + + /** + * Whether or not this style has finished running and + * is ready for disposal. + */ + bool processed; + + // + // Engine specific properties for SCI2.1early + // + + /** + * A list of screen items, each representing one + * block of a wipe transition. + */ + Common::Array<ScreenItem *> screenItems; + + /** + * For wipe transitions, the number of edges with a + * moving wipe (1, 2, or 4). + */ + uint8 numEdges; + + /** + * The dimensions of the plane, in game script + * coordinates. + */ + int16 width, height; + + /** + * For pixel dissolve transitions, the screen item + * used to render the transition. + */ + ScreenItem *bitmapScreenItem; + + /** + * For pixel dissolve transitions, the bitmap used + * to render the transition. + */ + reg_t bitmap; + + /** + * The bit mask used by pixel dissolve transitions. + */ + uint32 dissolveMask; + + /** + * The first pixel that was dissolved in a pixel + * dissolve transition. + */ + uint32 firstPixel; + + /** + * The last pixel that was dissolved. Once all + * pixels have been dissolved, `pixel` will once + * again equal `firstPixel`. + */ + uint32 pixel; + + // + // Engine specific properties for SCI2.1mid through SCI3 + // + + /** + * The number of entries in the fadeColorRanges array. + */ + uint8 fadeColorRangesCount; + + /** + * A pointer to an dynamically sized array of palette + * indexes, in the order [ fromColor, toColor, ... ]. + * Only colors within this range are transitioned. + */ + uint16 *fadeColorRanges; +}; + +/** + * PlaneScroll describes a transition between two different + * pictures within a single plane. + */ +struct PlaneScroll { + /** + * The ID of the plane to be scrolled. + */ + reg_t plane; + + /** + * The current position of the scroll. + */ + int16 x, y; + + /** + * The distance that should be scrolled. Only one of + * `deltaX` or `deltaY` may be set. + */ + int16 deltaX, deltaY; + + /** + * The pic that should be created and scrolled into + * view inside the plane. + */ + GuiResourceId newPictureId; + + /** + * The picture that should be scrolled out of view + * and deleted from the plane. + */ + GuiResourceId oldPictureId; + + /** + * If true, the scroll animation is interleaved + * with other updates to the graphics. If false, + * the scroll will be exclusively animated until + * it is finished. + */ + bool animate; + + /** + * The tick after which the animation will start. + */ + uint32 startTick; +}; + +typedef Common::List<PlaneShowStyle> ShowStyleList; +typedef Common::List<PlaneScroll> ScrollList; + +class GfxTransitions32 { +public: + GfxTransitions32(SegManager *_segMan); + ~GfxTransitions32(); +private: + SegManager *_segMan; + + /** + * Throttles transition playback to prevent + * transitions from being instant on fast + * computers. + */ + void throttle(); + int8 _throttleState; + +#pragma mark - +#pragma mark Show styles +public: + inline bool hasShowStyles() const { return !_showStyles.empty(); } + + /** + * Processes all active show styles in a loop + * until they are finished. + */ + void processShowStyles(); + + /** + * Processes show styles that are applied + * through `GfxFrameout::palMorphFrameOut`. + */ + void processEffects(PlaneShowStyle &showStyle); + + // NOTE: This signature is taken from SCI3 Phantasmagoria 2 + // and is valid for all implementations of SCI32 + void kernelSetShowStyle(const uint16 argc, const reg_t planeObj, const ShowStyleType type, const int16 seconds, const int16 direction, const int16 priority, const int16 animate, const int16 frameOutNow, reg_t pFadeArray, int16 divisions, const int16 blackScreen); + + /** + * Sets the range that will be used by + * `GfxFrameout::palMorphFrameOut` to alter + * palette entries. + */ + void kernelSetPalStyleRange(const uint8 fromColor, const uint8 toColor); + + /** + * A map of palette entries that can be morphed + * by the Morph show style. + */ + int8 _styleRanges[256]; + +private: + /** + * Default sequence values for pixel dissolve + * transition bit masks. + */ + int *_dissolveSequenceSeeds; + + /** + * Default values for `PlaneShowStyle::divisions` + * for the current SCI version. + */ + int16 *_defaultDivisions; + + /** + * The list of PlaneShowStyles that are + * currently active. + */ + ShowStyleList _showStyles; + + /** + * Finds a show style that applies to the given + * plane. + */ + PlaneShowStyle *findShowStyleForPlane(const reg_t planeObj); + + /** + * Finds the iterator for a show style that + * applies to the given plane. + */ + ShowStyleList::iterator findIteratorForPlane(const reg_t planeObj); + + /** + * Deletes the given PlaneShowStyle and returns + * the next PlaneShowStyle from the list of + * styles. + */ + ShowStyleList::iterator deleteShowStyle(const ShowStyleList::iterator &showStyle); + + /** + * Initializes the given PlaneShowStyle for an + * iris effect for SCI2 to 2.1early. + */ + void configure21EarlyIris(PlaneShowStyle &showStyle, const int16 priority); + + /** + * Initializes the given PlaneShowStyle for a + * pixel dissolve effect for SCI2 to 2.1early. + */ + void configure21EarlyDissolve(PlaneShowStyle &showStyle, const int16 priority, const Common::Rect &gameRect); + + /** + * Processes one tick of the given + * PlaneShowStyle. + */ + bool processShowStyle(PlaneShowStyle &showStyle, uint32 now); + + /** + * Performs an instant transition between two + * rooms. + */ + bool processNone(PlaneShowStyle &showStyle); + + /** + * Performs a transition that renders into a room + * with a horizontal shutter effect. + */ + void processHShutterOut(PlaneShowStyle &showStyle); + + /** + * Performs a transition that renders to black + * with a horizontal shutter effect. + */ + void processHShutterIn(PlaneShowStyle &showStyle); + + /** + * Performs a transition that renders into a room + * with a vertical shutter effect. + */ + void processVShutterOut(PlaneShowStyle &showStyle); + + /** + * Performs a transition that renders to black + * with a vertical shutter effect. + */ + void processVShutterIn(PlaneShowStyle &showStyle); + + /** + * Performs a transition that renders into a room + * with a wipe to the left. + */ + void processWipeLeft(PlaneShowStyle &showStyle); + + /** + * Performs a transition that renders to black + * with a wipe to the right. + */ + void processWipeRight(PlaneShowStyle &showStyle); + + /** + * Performs a transition that renders into a room + * with a wipe upwards. + */ + void processWipeUp(PlaneShowStyle &showStyle); + + /** + * Performs a transition that renders to black + * with a wipe downwards. + */ + void processWipeDown(PlaneShowStyle &showStyle); + + /** + * Performs a transition that renders into a room + * with an iris effect. + */ + bool processIrisOut(PlaneShowStyle &showStyle); + + /** + * Performs a transition that renders to black + * with an iris effect. + */ + bool processIrisIn(PlaneShowStyle &showStyle); + + /** + * Performs a transition that renders between + * rooms using a block dissolve effect. + */ + void processDissolveNoMorph(PlaneShowStyle &showStyle); + + /** + * Performs a transition that renders between + * rooms with a pixel dissolve effect. + */ + bool processPixelDissolve(PlaneShowStyle &showStyle); + + /** + * SCI2 to 2.1early implementation of pixel + * dissolve. + */ + bool processPixelDissolve21Early(PlaneShowStyle &showStyle); + + /** + * SCI2.1mid and later implementation of + * pixel dissolve. + */ + bool processPixelDissolve21Mid(PlaneShowStyle &showStyle); + + /** + * Performs a transition that fades to black + * between rooms. + */ + bool processFade(const int8 direction, PlaneShowStyle &showStyle); + + /** + * Morph transition calls back into the + * transition system's `processEffects` + * method, which then applies transitions + * other than None, Fade, or Morph. + */ + bool processMorph(PlaneShowStyle &showStyle); + + /** + * Performs a generic transition for any of + * the wipe/shutter/iris effects. + */ + bool processWipe(const int8 direction, PlaneShowStyle &showStyle); + +#pragma mark - +#pragma mark Scrolls +public: + inline bool hasScrolls() const { return !_scrolls.empty(); } + + /** + * Processes all active plane scrolls + * in a loop until they are finished. + */ + void processScrolls(); + + void kernelSetScroll(const reg_t plane, const int16 deltaX, const int16 deltaY, const GuiResourceId pictureId, const bool animate, const bool mirrorX); + +private: + /** + * A list of active plane scrolls. + */ + ScrollList _scrolls; + + /** + * Performs a scroll of the content of + * a plane. + */ + bool processScroll(PlaneScroll &scroll); +}; + +} // End of namespace Sci +#endif diff --git a/engines/sci/module.mk b/engines/sci/module.mk index 1511356747..18d97ea57e 100644 --- a/engines/sci/module.mk +++ b/engines/sci/module.mk @@ -91,6 +91,7 @@ MODULE_OBJS += \ graphics/remap32.o \ graphics/screen_item32.o \ graphics/text32.o \ + graphics/transitions32.o \ graphics/video32.o \ sound/audio32.o \ sound/decoders/sol.o \ diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index 3ea2756f56..4c178b6ed7 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -69,6 +69,7 @@ #include "sci/graphics/palette32.h" #include "sci/graphics/remap32.h" #include "sci/graphics/text32.h" +#include "sci/graphics/transitions32.h" #include "sci/graphics/video32.h" #include "sci/sound/audio32.h" // TODO: Move this to video32 @@ -176,6 +177,7 @@ SciEngine::~SciEngine() { // components delete _video32; delete _gfxPalette32; + delete _gfxTransitions32; delete _gfxFrameout; delete _gfxRemap32; delete _audio32; @@ -712,6 +714,7 @@ void SciEngine::initGraphics() { _gfxPaint32 = 0; _gfxPalette32 = 0; _gfxRemap32 = 0; + _gfxTransitions32 = 0; #endif if (hasMacIconBar()) @@ -741,7 +744,8 @@ void SciEngine::initGraphics() { _gfxCompare = new GfxCompare(_gamestate->_segMan, _gfxCache, _gfxScreen, _gfxCoordAdjuster); _gfxPaint32 = new GfxPaint32(_gamestate->_segMan); _robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh); - _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxScreen, _gfxPalette32); + _gfxTransitions32 = new GfxTransitions32(_gamestate->_segMan); + _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxScreen, _gfxPalette32, _gfxTransitions32); _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache); _gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxText32); _gfxFrameout->run(); diff --git a/engines/sci/sci.h b/engines/sci/sci.h index 0020d25b91..a42095259b 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -85,6 +85,7 @@ class RobotDecoder; class GfxFrameout; class Audio32; class Video32; +class GfxTransitions32; #endif // our engine debug levels @@ -377,6 +378,7 @@ public: Video32 *_video32; RobotDecoder *_robotDecoder; GfxFrameout *_gfxFrameout; // kFrameout and the like for 32-bit gfx + GfxTransitions32 *_gfxTransitions32; #endif AudioPlayer *_audio; |