diff options
-rw-r--r-- | engines/sci/engine/kmisc.cpp | 12 | ||||
-rw-r--r-- | engines/sci/engine/script_patches.cpp | 90 | ||||
-rw-r--r-- | engines/sci/event.cpp | 2 | ||||
-rw-r--r-- | engines/sci/graphics/celobj32.cpp | 67 | ||||
-rw-r--r-- | engines/sci/graphics/celobj32.h | 14 | ||||
-rw-r--r-- | engines/sci/graphics/controls32.cpp | 6 | ||||
-rw-r--r-- | engines/sci/graphics/frameout.cpp | 444 | ||||
-rw-r--r-- | engines/sci/graphics/frameout.h | 29 | ||||
-rw-r--r-- | engines/sci/graphics/plane32.cpp | 563 | ||||
-rw-r--r-- | engines/sci/graphics/plane32.h | 139 | ||||
-rw-r--r-- | engines/sci/graphics/screen_item32.cpp | 125 | ||||
-rw-r--r-- | engines/sci/graphics/screen_item32.h | 44 | ||||
-rw-r--r-- | engines/sci/sound/audio32.cpp | 48 | ||||
-rw-r--r-- | engines/sci/sound/audio32.h | 20 | ||||
-rw-r--r-- | engines/sci/sound/soundcmd.cpp | 2 | ||||
-rw-r--r-- | engines/scumm/debugger.cpp | 22 |
16 files changed, 1057 insertions, 570 deletions
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp index f4bb4ff85b..0a5f26476f 100644 --- a/engines/sci/engine/kmisc.cpp +++ b/engines/sci/engine/kmisc.cpp @@ -243,10 +243,18 @@ reg_t kGetTime(EngineState *s, int argc, reg_t *argv) { debugC(kDebugLevelTime, "GetTime(24h) returns %d", retval); break; case KGETTIME_DATE : - // Year since 1980 (0 = 1980, 1 = 1981, etc.) - retval = loc_time.tm_mday | ((loc_time.tm_mon + 1) << 5) | (((loc_time.tm_year - 80) & 0x7f) << 9); + { + // SCI0 late: Year since 1920 (0 = 1920, 1 = 1921, etc) + // SCI01 and newer: Year since 1980 (0 = 1980, 1 = 1981, etc) + // Atari ST SCI0 late versions use the newer base year. + int baseYear = 80; + if (getSciVersion() == SCI_VERSION_0_LATE && g_sci->getPlatform() == Common::kPlatformDOS) { + baseYear = 20; + } + retval = loc_time.tm_mday | ((loc_time.tm_mon + 1) << 5) | (((loc_time.tm_year - baseYear) & 0x7f) << 9); debugC(kDebugLevelTime, "GetTime(date) returns %d", retval); break; + } default: error("Attempt to use unknown GetTime mode %d", mode); break; diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 5d7fa49ce5..13c3297f90 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -3251,6 +3251,86 @@ static const uint16 qfg3PatchCombatSpeedThrottling2[] = { PATCH_END }; +// In room #750, when the hero enters from the top east path (room #755), it +// could go out of the contained-access polygon bounds, and be able to travel +// freely in the room. +// The reason is that the cutoff y value (42) that determines whether the hero +// enters from the top or bottom path is inaccurate: it's possible to enter the +// top path from as low as y=45. +// This patch changes the cutoff to be 50 which should be low enough. +// It also changes the position in which the hero enters from the top east path +// as the current location is hidden behind the tree. +// +// Applies to: English, French, German, Italian, Spanish and the GOG release. +// Responsible method: enterEast::changeState (script 750) +// Fixes bug #6693 +static const uint16 qfg3SignatureRoom750Bounds1[] = { + // (if (< (ego y?) 42) + 0x76, // push0 ("y") + 0x76, // push0 + 0x81, 0x00, // lag global[0] (ego) + 0x4a, 0x04, // send 4 + SIG_MAGICDWORD, + 0x36, // push + 0x35, 42, // ldi 42 <-- comparing ego.y with 42 + 0x22, // lt? + SIG_END +}; + +static const uint16 qfg3PatchRoom750Bounds1[] = { + // (if (< (ego y?) 50) + PATCH_ADDTOOFFSET(+8), + 50, // 42 --> 50 + PATCH_END +}; + +static const uint16 qfg3SignatureRoom750Bounds2[] = { + // (ego x: 294 y: 39) + 0x78, // push1 ("x") + 0x78, // push1 + 0x38, SIG_UINT16(294), // pushi 294 + 0x76, // push0 ("y") + 0x78, // push1 + SIG_MAGICDWORD, + 0x39, 29, // pushi 29 + 0x81, 0x00, // lag global[0] (ego) + 0x4a, 0x0c, // send 12 + SIG_END +}; + +static const uint16 qfg3PatchRoom750Bounds2[] = { + // (ego x: 320 y: 39) + PATCH_ADDTOOFFSET(+3), + PATCH_UINT16(320), // 294 --> 320 + PATCH_ADDTOOFFSET(+3), + 39, // 29 --> 39 + PATCH_END +}; + +static const uint16 qfg3SignatureRoom750Bounds3[] = { + // (ego setMotion: MoveTo 282 29 self) + 0x38, SIG_SELECTOR16(setMotion), // pushi "setMotion" 0x133 in QfG3 + 0x39, 0x04, // pushi 4 + 0x51, SIG_ADDTOOFFSET(+1), // class MoveTo + 0x36, // push + 0x38, SIG_UINT16(282), // pushi 282 + SIG_MAGICDWORD, + 0x39, 29, // pushi 29 + 0x7c, // pushSelf + 0x81, 0x00, // lag global[0] (ego) + 0x4a, 0x0c, // send 12 + SIG_END +}; + +static const uint16 qfg3PatchRoom750Bounds3[] = { + // (ego setMotion: MoveTo 309 35 self) + PATCH_ADDTOOFFSET(+9), + PATCH_UINT16(309), // 282 --> 309 + PATCH_ADDTOOFFSET(+1), + 35, // 29 --> 35 + PATCH_END +}; + // script, description, signature patch static const SciScriptPatcherEntry qfg3Signatures[] = { { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog }, @@ -3264,6 +3344,9 @@ static const SciScriptPatcherEntry qfg3Signatures[] = { { true, 285, "missing points for telling about initiation script", 1, qfg3SignatureMissingPoints2b, qfg3PatchMissingPoints2 }, { true, 550, "combat speed throttling script", 1, qfg3SignatureCombatSpeedThrottling1, qfg3PatchCombatSpeedThrottling1 }, { true, 550, "combat speed throttling heap", 1, qfg3SignatureCombatSpeedThrottling2, qfg3PatchCombatSpeedThrottling2 }, + { true, 750, "hero goes out of bounds in room 750", 2, qfg3SignatureRoom750Bounds1, qfg3PatchRoom750Bounds1 }, + { true, 750, "hero goes out of bounds in room 750", 2, qfg3SignatureRoom750Bounds2, qfg3PatchRoom750Bounds2 }, + { true, 750, "hero goes out of bounds in room 750", 2, qfg3SignatureRoom750Bounds3, qfg3PatchRoom750Bounds3 }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -4055,7 +4138,7 @@ int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, cons return findSignature(runtimeEntry->magicDWord, runtimeEntry->magicOffset, patchEntry->signatureData, patchEntry->description, scriptData, scriptSize); } -// Attention: Magic DWord is returns using platform specific byte order. This is done on purpose for performance. +// Attention: Magic DWord is returned using platform specific byte order. This is done on purpose for performance. void ScriptPatcher::calculateMagicDWordAndVerify(const char *signatureDescription, const uint16 *signatureData, bool magicDWordIncluded, uint32 &calculatedMagicDWord, int &calculatedMagicDWordOffset) { Selector curSelector = -1; int magicOffset; @@ -4136,7 +4219,7 @@ void ScriptPatcher::calculateMagicDWordAndVerify(const char *signatureDescriptio } if (!magicDWordLeft) { // Magic DWORD is now known, convert to platform specific byte order - calculatedMagicDWord = READ_LE_UINT32(magicDWord); + calculatedMagicDWord = READ_UINT32(magicDWord); } } break; @@ -4161,7 +4244,8 @@ void ScriptPatcher::calculateMagicDWordAndVerify(const char *signatureDescriptio magicDWord[4 - magicDWordLeft] = (byte)curValue; magicDWordLeft--; if (!magicDWordLeft) { - calculatedMagicDWord = READ_LE_UINT32(magicDWord); + // Magic DWORD is now known, convert to platform specific byte order + calculatedMagicDWord = READ_UINT32(magicDWord); } } break; diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index 34f1618514..4ad2a0cfa3 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -166,7 +166,7 @@ SciEvent EventManager::getScummVMEvent() { #if ENABLE_SCI32 if (getSciVersion() >= SCI_VERSION_2) { - Buffer &screen = g_sci->_gfxFrameout->getCurrentBuffer(); + const Buffer &screen = g_sci->_gfxFrameout->getCurrentBuffer(); Common::Point mousePosSci = mousePos; mulru(mousePosSci, Ratio(screen.scriptWidth, screen.screenWidth), Ratio(screen.scriptHeight, screen.screenHeight)); diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp index af5ee3abbc..f8cd5fd171 100644 --- a/engines/sci/graphics/celobj32.cpp +++ b/engines/sci/graphics/celobj32.cpp @@ -176,34 +176,61 @@ struct SCALER_Scale { // line of source data when scaling _reader(celObj, celObj._width) { // In order for scaling ratios to apply equally across objects that - // start at different positions on the screen, the pixels that are - // read from the source bitmap must all use the same pattern of - // division. In other words, cels must follow the same scaling pattern - // as if they were drawn starting at an even multiple of the scaling - // ratio, even if they were not. + // start at different positions on the screen (like the cels of a + // picture), the pixels that are read from the source bitmap must all + // use the same pattern of division. In other words, cels must follow + // a global scaling pattern as if they were always drawn starting at an + // even multiple of the scaling ratio, even if they are not. // // To get the correct source pixel when reading out through the scaler, // the engine creates a lookup table for each axis that translates // directly from target positions to the indexes of source pixels using // the global cadence for the given scaling ratio. + // + // Note, however, that not all games use the global scaling mode. + // + // SQ6 definitely uses the global scaling mode (an easy visual + // comparison is to leave Implants N' Stuff and then look at Roger); + // Torin definitely does not (scaling subtitle backgrounds will cause it + // to attempt a read out of bounds and crash). They are both SCI + // "2.1mid" games, so currently the common denominator looks to be that + // games which use global scaling are the ones that use low-resolution + // script coordinates too. const CelScalerTable *table = CelObj::_scaler->getScalerTable(scaleX, scaleY); - const int16 unscaledX = (scaledPosition.x / scaleX).toInt(); - if (FLIP) { - int lastIndex = celObj._width - 1; - for (int16 x = targetRect.left; x < targetRect.right; ++x) { - _valuesX[x] = lastIndex - (table->valuesX[x] - unscaledX); + if (g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth == kLowResX) { + const int16 unscaledX = (scaledPosition.x / scaleX).toInt(); + if (FLIP) { + int lastIndex = celObj._width - 1; + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = lastIndex - (table->valuesX[x] - unscaledX); + } + } else { + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = table->valuesX[x] - unscaledX; + } + } + + const int16 unscaledY = (scaledPosition.y / scaleY).toInt(); + for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { + _valuesY[y] = table->valuesY[y] - unscaledY; } } else { - for (int16 x = targetRect.left; x < targetRect.right; ++x) { - _valuesX[x] = table->valuesX[x] - unscaledX; + if (FLIP) { + int lastIndex = celObj._width - 1; + for (int16 x = 0; x < targetRect.width(); ++x) { + _valuesX[targetRect.left + x] = lastIndex - table->valuesX[x]; + } + } else { + for (int16 x = 0; x < targetRect.width(); ++x) { + _valuesX[targetRect.left + x] = table->valuesX[x]; + } } - } - const int16 unscaledY = (scaledPosition.y / scaleY).toInt(); - for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { - _valuesY[y] = table->valuesY[y] - unscaledY; + for (int16 y = 0; y < targetRect.height(); ++y) { + _valuesY[targetRect.top + y] = table->valuesY[y]; + } } } @@ -799,8 +826,8 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int if (_scaledWidth == 0 || _scaledHeight == 0) { byte sizeFlag = data[5]; if (sizeFlag == 0) { - _scaledWidth = 320; - _scaledHeight = 200; + _scaledWidth = kLowResX; + _scaledHeight = kLowResY; } else if (sizeFlag == 1) { _scaledWidth = 640; _scaledHeight = 480; @@ -985,8 +1012,8 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { _scaledWidth = sizeFlag1; _scaledHeight = sizeFlag2; } else if (sizeFlag1 == 0) { - _scaledWidth = 320; - _scaledHeight = 200; + _scaledWidth = kLowResX; + _scaledHeight = kLowResY; } else if (sizeFlag1 == 1) { _scaledWidth = 640; _scaledHeight = 480; diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h index 6e401b3df4..e405592b5f 100644 --- a/engines/sci/graphics/celobj32.h +++ b/engines/sci/graphics/celobj32.h @@ -31,6 +31,20 @@ namespace Sci { typedef Common::Rational Ratio; +// SCI32 has four different coordinate systems: +// 1. low resolution, 2. game/script resolution, +// 3. text/bitmap resolution, 4. screen resolution +// +// In CelObj, these values are used when there is +// no baked in resolution of cels. +// +// In ScreenItem, it is used when deciding which +// path to take to calculate dimensions. +enum { + kLowResX = 320, + kLowResY = 200 +}; + enum CelType { kCelTypeView = 0, kCelTypePic = 1, diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp index 61dfbedfc5..4cbb4541df 100644 --- a/engines/sci/graphics/controls32.cpp +++ b/engines/sci/graphics/controls32.cpp @@ -191,7 +191,8 @@ reg_t GfxControls32::kernelEditText(const reg_t controlObject) { // Consume the event now that we know it is not one of the // defocusing events above - eventManager->getSciEvent(SCI_EVENT_ANY); + if (event.type != SCI_EVENT_NONE) + eventManager->getSciEvent(SCI_EVENT_ANY); // NOTE: In the original engine, the font and bitmap were // reset here on each iteration through the loop, but it @@ -309,8 +310,7 @@ reg_t GfxControls32::kernelEditText(const reg_t controlObject) { g_sci->_gfxFrameout->frameOut(true); g_sci->getSciDebugger()->onFrame(); - g_sci->getEngineState()->speedThrottler(16); - g_sci->getEngineState()->_throttleTrigger = true; + g_sci->_gfxFrameout->throttle(); } g_sci->_gfxFrameout->deletePlane(*plane); diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index cb777301b0..c0feea8999 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -76,6 +76,7 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd _benchmarkingFinished(false), _throttleFrameOut(true), _showStyles(nullptr), + _throttleState(0), // TODO: Stop using _gfxScreen _currentBuffer(screen->getDisplayWidth(), screen->getDisplayHeight(), nullptr), _remapOccurred(false), @@ -492,7 +493,7 @@ void GfxFrameout::kernelAddPicAt(const reg_t planeObject, const GuiResourceId pi #pragma mark - #pragma mark Rendering -void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &rect) { +void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseRect) { // TODO: Robot // if (_robot != nullptr) { // _robot.doRobot(); @@ -510,7 +511,7 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &rect) remapMarkRedraw(); } - calcLists(screenItemLists, eraseLists, rect); + calcLists(screenItemLists, eraseLists, eraseRect); for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { list->sort(); @@ -553,130 +554,221 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &rect) // } } -// Determine the parts of 'r' that aren't overlapped by 'other'. -// Returns -1 if r and other have no intersection. -// Returns number of returned parts (in outRects) otherwise. -// (In particular, this returns 0 if r is contained in other.) +/** + * Determines the parts of `r` that aren't overlapped by `other`. + * Returns -1 if `r` and `other` have no intersection. + * Returns number of returned parts (in `outRects`) otherwise. + * (In particular, this returns 0 if `r` is contained in `other`.) + */ int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]) { if (!r.intersects(other)) { return -1; } - int count = 0; + int splitCount = 0; if (r.top < other.top) { - Common::Rect &t = outRects[count++]; + Common::Rect &t = outRects[splitCount++]; t = r; t.bottom = other.top; r.top = other.top; } if (r.bottom > other.bottom) { - Common::Rect &t = outRects[count++]; + Common::Rect &t = outRects[splitCount++]; t = r; t.top = other.bottom; r.bottom = other.bottom; } if (r.left < other.left) { - Common::Rect &t = outRects[count++]; + Common::Rect &t = outRects[splitCount++]; t = r; t.right = other.left; r.left = other.left; } if (r.right > other.right) { - Common::Rect &t = outRects[count++]; + Common::Rect &t = outRects[splitCount++]; t = r; t.left = other.right; } - return count; + return splitCount; } -void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &calcRect) { - RectList rectlist; - Common::Rect outRects[4]; +/** + * Determines the parts of `middleRect` that aren't overlapped + * by `showRect`, optimised for contiguous memory writes. + * Returns -1 if `middleRect` and `showRect` have no intersection. + * Returns number of returned parts (in `outRects`) otherwise. + * (In particular, this returns 0 if `middleRect` is contained + * in `other`.) + * + * `middleRect` is modified directly to extend into the upper + * and lower rects. + */ +int splitRectsForRender(Common::Rect &middleRect, const Common::Rect &showRect, Common::Rect(&outRects)[2]) { + if (!middleRect.intersects(showRect)) { + return -1; + } + + const int16 minLeft = MIN(middleRect.left, showRect.left); + const int16 maxRight = MAX(middleRect.right, showRect.right); + + int16 upperLeft, upperTop, upperRight, upperMaxTop; + if (middleRect.top < showRect.top) { + upperLeft = middleRect.left; + upperTop = middleRect.top; + upperRight = middleRect.right; + upperMaxTop = showRect.top; + } + else { + upperLeft = showRect.left; + upperTop = showRect.top; + upperRight = showRect.right; + upperMaxTop = middleRect.top; + } + + int16 lowerLeft, lowerRight, lowerBottom, lowerMinBottom; + if (middleRect.bottom > showRect.bottom) { + lowerLeft = middleRect.left; + lowerRight = middleRect.right; + lowerBottom = middleRect.bottom; + lowerMinBottom = showRect.bottom; + } else { + lowerLeft = showRect.left; + lowerRight = showRect.right; + lowerBottom = showRect.bottom; + lowerMinBottom = middleRect.bottom; + } + + int splitCount = 0; + middleRect.left = minLeft; + middleRect.top = upperMaxTop; + middleRect.right = maxRight; + middleRect.bottom = lowerMinBottom; + + if (upperTop != upperMaxTop) { + Common::Rect &upperRect = outRects[0]; + upperRect.left = upperLeft; + upperRect.top = upperTop; + upperRect.right = upperRight; + upperRect.bottom = upperMaxTop; + + // Merge upper rect into middle rect if possible + if (upperRect.left == middleRect.left && upperRect.right == middleRect.right) { + middleRect.top = upperRect.top; + } else { + ++splitCount; + } + } + + if (lowerBottom != lowerMinBottom) { + Common::Rect &lowerRect = outRects[splitCount]; + lowerRect.left = lowerLeft; + lowerRect.top = lowerMinBottom; + lowerRect.right = lowerRight; + lowerRect.bottom = lowerBottom; + // Merge lower rect into middle rect if possible + if (lowerRect.left == middleRect.left && lowerRect.right == middleRect.right) { + middleRect.bottom = lowerRect.bottom; + } else { + ++splitCount; + } + } + + assert(splitCount <= 2); + return splitCount; +} + +// NOTE: The third rectangle parameter is only ever given a non-empty rect +// by VMD code, via `frameOut` +void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &eraseRect) { + RectList eraseList; + Common::Rect outRects[4]; int deletedPlaneCount = 0; - bool addedToRectList = false; - int planeCount = _planes.size(); + bool addedToEraseList = false; bool foundTransparentPlane = false; - if (!calcRect.isEmpty()) { - addedToRectList = true; - rectlist.add(calcRect); + if (!eraseRect.isEmpty()) { + addedToEraseList = true; + eraseList.add(eraseRect); } - for (int outerPlaneIndex = 0; outerPlaneIndex < planeCount; ++outerPlaneIndex) { - Plane *outerPlane = _planes[outerPlaneIndex]; + PlaneList::size_type planeCount = _planes.size(); + for (PlaneList::size_type outerPlaneIndex = 0; outerPlaneIndex < planeCount; ++outerPlaneIndex) { + const Plane *outerPlane = _planes[outerPlaneIndex]; + const Plane *visiblePlane = _visiblePlanes.findByObject(outerPlane->_object); + // NOTE: SSCI only ever checks for kPlaneTypeTransparent here, even + // though kPlaneTypeTransparentPicture is also a transparent plane if (outerPlane->_type == kPlaneTypeTransparent) { foundTransparentPlane = true; } - Plane *visiblePlane = _visiblePlanes.findByObject(outerPlane->_object); - if (outerPlane->_deleted) { - if (visiblePlane != nullptr) { - if (!visiblePlane->_screenRect.isEmpty()) { - addedToRectList = true; - rectlist.add(visiblePlane->_screenRect); - } + if (visiblePlane != nullptr && !visiblePlane->_screenRect.isEmpty()) { + eraseList.add(visiblePlane->_screenRect); + addedToEraseList = true; } ++deletedPlaneCount; - } else if (visiblePlane != nullptr) { - if (outerPlane->_updated) { - --outerPlane->_updated; - - int splitcount = splitRects(visiblePlane->_screenRect, outerPlane->_screenRect, outRects); - if (splitcount) { - if (splitcount == -1) { - if (!visiblePlane->_screenRect.isEmpty()) { - rectlist.add(visiblePlane->_screenRect); - } + } else if (visiblePlane != nullptr && outerPlane->_moved) { + // _moved will be decremented in the final loop through the planes, + // at the end of this function + + { + const int splitCount = splitRects(visiblePlane->_screenRect, outerPlane->_screenRect, outRects); + if (splitCount) { + if (splitCount == -1 && !visiblePlane->_screenRect.isEmpty()) { + eraseList.add(visiblePlane->_screenRect); } else { - for (int i = 0; i < splitcount; ++i) { - rectlist.add(outRects[i]); + for (int i = 0; i < splitCount; ++i) { + eraseList.add(outRects[i]); } } - - addedToRectList = true; + addedToEraseList = true; } + } - if (!outerPlane->_redrawAllCount) { - int splitCount = splitRects(outerPlane->_screenRect, visiblePlane->_screenRect, outRects); - if (splitCount) { - for (int i = 0; i < splitCount; ++i) { - rectlist.add(outRects[i]); - } - addedToRectList = true; + if (!outerPlane->_redrawAllCount) { + const int splitCount = splitRects(outerPlane->_screenRect, visiblePlane->_screenRect, outRects); + if (splitCount) { + for (int i = 0; i < splitCount; ++i) { + eraseList.add(outRects[i]); } + addedToEraseList = true; } } } - if (addedToRectList) { - for (RectList::iterator rect = rectlist.begin(); rect != rectlist.end(); ++rect) { - for (int innerPlaneIndex = _planes.size() - 1; innerPlaneIndex >= 0; --innerPlaneIndex) { - Plane *innerPlane = _planes[innerPlaneIndex]; - - if (!innerPlane->_deleted && innerPlane->_type != kPlaneTypeTransparent && innerPlane->_screenRect.intersects(**rect)) { - if (innerPlane->_redrawAllCount == 0) { - eraseLists[innerPlaneIndex].add(innerPlane->_screenRect.findIntersectingRect(**rect)); + if (addedToEraseList) { + for (RectList::size_type rectIndex = 0; rectIndex < eraseList.size(); ++rectIndex) { + const Common::Rect &rect = *eraseList[rectIndex]; + for (int innerPlaneIndex = planeCount - 1; innerPlaneIndex >= 0; --innerPlaneIndex) { + const Plane &innerPlane = *_planes[innerPlaneIndex]; + + if ( + !innerPlane._deleted && + innerPlane._type != kPlaneTypeTransparent && + innerPlane._screenRect.intersects(rect) + ) { + if (!innerPlane._redrawAllCount) { + eraseLists[innerPlaneIndex].add(innerPlane._screenRect.findIntersectingRect(rect)); } - int splitCount = splitRects(**rect, innerPlane->_screenRect, outRects); + const int splitCount = splitRects(rect, innerPlane._screenRect, outRects); for (int i = 0; i < splitCount; ++i) { - rectlist.add(outRects[i]); + eraseList.add(outRects[i]); } - rectlist.erase(rect); + eraseList.erase_at(rectIndex); break; } } } - rectlist.pack(); + eraseList.pack(); } } @@ -688,9 +780,9 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL if (plane->_deleted) { --plane->_deleted; if (plane->_deleted <= 0) { - PlaneList::iterator visiblePlaneIt = Common::find_if(_visiblePlanes.begin(), _visiblePlanes.end(), FindByObject<Plane *>(plane->_object)); - if (visiblePlaneIt != _visiblePlanes.end()) { - _visiblePlanes.erase(visiblePlaneIt); + const int visiblePlaneIndex = _visiblePlanes.findIndexByObject(plane->_object); + if (visiblePlaneIndex != -1) { + _visiblePlanes.remove_at(visiblePlaneIndex); } _planes.remove_at(planeIndex); @@ -705,107 +797,114 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL } } + // Some planes may have been deleted, so re-retrieve count planeCount = _planes.size(); - for (int outerIndex = 0; outerIndex < planeCount; ++outerIndex) { + + for (PlaneList::size_type outerIndex = 0; outerIndex < planeCount; ++outerIndex) { // "outer" just refers to the outer loop - Plane *outerPlane = _planes[outerIndex]; - if (outerPlane->_priorityChanged) { - --outerPlane->_priorityChanged; + Plane &outerPlane = *_planes[outerIndex]; + if (outerPlane._priorityChanged) { + --outerPlane._priorityChanged; - Plane *visibleOuterPlane = _visiblePlanes.findByObject(outerPlane->_object); + const Plane *visibleOuterPlane = _visiblePlanes.findByObject(outerPlane._object); if (visibleOuterPlane == nullptr) { - warning("calcLists could not find visible plane for %04x:%04x", PRINT_REG(outerPlane->_object)); + warning("calcLists could not find visible plane for %04x:%04x", PRINT_REG(outerPlane._object)); continue; } - rectlist.add(outerPlane->_screenRect.findIntersectingRect(visibleOuterPlane->_screenRect)); + eraseList.add(outerPlane._screenRect.findIntersectingRect(visibleOuterPlane->_screenRect)); - for (int innerIndex = planeCount - 1; innerIndex >= 0; --innerIndex) { + for (int innerIndex = (int)planeCount - 1; innerIndex >= 0; --innerIndex) { // "inner" just refers to the inner loop - Plane *innerPlane = _planes[innerIndex]; - Plane *visibleInnerPlane = _visiblePlanes.findByObject(innerPlane->_object); - - int rectCount = rectlist.size(); - for (int rectIndex = 0; rectIndex < rectCount; ++rectIndex) { - int splitCount = splitRects(*rectlist[rectIndex], _planes[innerIndex]->_screenRect, outRects); + const Plane &innerPlane = *_planes[innerIndex]; + const Plane *visibleInnerPlane = _visiblePlanes.findByObject(innerPlane._object); + const RectList::size_type rectCount = eraseList.size(); + for (RectList::size_type rectIndex = 0; rectIndex < rectCount; ++rectIndex) { + const int splitCount = splitRects(*eraseList[rectIndex], innerPlane._screenRect, outRects); if (splitCount == 0) { if (visibleInnerPlane != nullptr) { // same priority, or relative priority between inner/outer changed - if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane->_priority - innerPlane->_priority) <= 0) { - if (outerPlane->_priority <= innerPlane->_priority) { - eraseLists[innerIndex].add(*rectlist[rectIndex]); + if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane._priority - innerPlane._priority) <= 0) { + if (outerPlane._priority <= innerPlane._priority) { + eraseLists[innerIndex].add(*eraseList[rectIndex]); } else { - eraseLists[outerIndex].add(*rectlist[rectIndex]); + eraseLists[outerIndex].add(*eraseList[rectIndex]); } } } - rectlist.erase_at(rectIndex); + eraseList.erase_at(rectIndex); } else if (splitCount != -1) { for (int i = 0; i < splitCount; ++i) { - rectlist.add(outRects[i]); + eraseList.add(outRects[i]); } if (visibleInnerPlane != nullptr) { // same priority, or relative priority between inner/outer changed - if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane->_priority - innerPlane->_priority) <= 0) { - *rectlist[rectIndex] = outerPlane->_screenRect.findIntersectingRect(innerPlane->_screenRect); - if (outerPlane->_priority <= innerPlane->_priority) { - eraseLists[innerIndex].add(*rectlist[rectIndex]); - } - else { - eraseLists[outerIndex].add(*rectlist[rectIndex]); + if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane._priority - innerPlane._priority) <= 0) { + *eraseList[rectIndex] = outerPlane._screenRect.findIntersectingRect(innerPlane._screenRect); + + if (outerPlane._priority <= innerPlane._priority) { + eraseLists[innerIndex].add(*eraseList[rectIndex]); + } else { + eraseLists[outerIndex].add(*eraseList[rectIndex]); } } } - rectlist.erase_at(rectIndex); + eraseList.erase_at(rectIndex); } } - rectlist.pack(); + eraseList.pack(); } } } - for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) { - Plane *plane = _planes[planeIndex]; - Plane *visiblePlane = nullptr; + for (PlaneList::size_type planeIndex = 0; planeIndex < planeCount; ++planeIndex) { + Plane &plane = *_planes[planeIndex]; + Plane *visiblePlane = _visiblePlanes.findByObject(plane._object); - PlaneList::iterator visiblePlaneIt = Common::find_if(_visiblePlanes.begin(), _visiblePlanes.end(), FindByObject<Plane *>(plane->_object)); - if (visiblePlaneIt != _visiblePlanes.end()) { - visiblePlane = *visiblePlaneIt; - } + if (!plane._screenRect.isEmpty()) { + if (plane._redrawAllCount) { + plane.redrawAll(visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]); + } else { + if (visiblePlane == nullptr) { + error("Missing visible plane for source plane %04x:%04x", PRINT_REG(plane._object)); + } - if (plane->_redrawAllCount) { - plane->redrawAll(visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]); - } else { - if (visiblePlane == nullptr) { - error("Missing visible plane for source plane %04x:%04x", PRINT_REG(plane->_object)); + plane.calcLists(*visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]); } + } else { + plane.decrementScreenItemArrayCounts(visiblePlane, false); + } - plane->calcLists(*visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]); + if (plane._moved) { + // the work for handling moved/resized planes was already done + // earlier in the function, we are just cleaning up now + --plane._moved; } - if (plane->_created) { - _visiblePlanes.add(new Plane(*plane)); - --plane->_created; - } else if (plane->_moved) { - assert(visiblePlaneIt != _visiblePlanes.end()); - **visiblePlaneIt = *plane; - --plane->_moved; + if (plane._created) { + _visiblePlanes.add(new Plane(plane)); + --plane._created; + } else if (plane._updated) { + *visiblePlane = plane; + --plane._updated; } } + // NOTE: SSCI only looks for kPlaneTypeTransparent, not + // kPlaneTypeTransparentPicture if (foundTransparentPlane) { - for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) { - for (int i = planeIndex + 1; i < planeCount; ++i) { + for (PlaneList::size_type planeIndex = 0; planeIndex < planeCount; ++planeIndex) { + for (PlaneList::size_type i = planeIndex + 1; i < planeCount; ++i) { if (_planes[i]->_type == kPlaneTypeTransparent) { _planes[i]->filterUpEraseRects(drawLists[i], eraseLists[planeIndex]); } } if (_planes[planeIndex]->_type == kPlaneTypeTransparent) { - for (int i = planeIndex - 1; i >= 0; --i) { + for (int i = (int)planeIndex - 1; i >= 0; --i) { _planes[i]->filterDownEraseRects(drawLists[i], eraseLists[i], eraseLists[planeIndex]); } @@ -814,7 +913,7 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL } } - for (int i = planeIndex + 1; i < planeCount; ++i) { + for (PlaneList::size_type i = planeIndex + 1; i < planeCount; ++i) { if (_planes[i]->_type == kPlaneTypeTransparent) { _planes[i]->filterUpDrawRects(drawLists[i], drawLists[planeIndex]); } @@ -828,17 +927,19 @@ void GfxFrameout::drawEraseList(const RectList &eraseList, const Plane &plane) { return; } - for (RectList::const_iterator it = eraseList.begin(); it != eraseList.end(); ++it) { - mergeToShowList(**it, _showList, _overdrawThreshold); - _currentBuffer.fillRect(**it, plane._back); + const RectList::size_type eraseListSize = eraseList.size(); + for (RectList::size_type i = 0; i < eraseListSize; ++i) { + mergeToShowList(*eraseList[i], _showList, _overdrawThreshold); + _currentBuffer.fillRect(*eraseList[i], plane._back); } } void GfxFrameout::drawScreenItemList(const DrawList &screenItemList) { - for (DrawList::const_iterator it = screenItemList.begin(); it != screenItemList.end(); ++it) { - DrawItem &drawItem = **it; + const DrawList::size_type drawListSize = screenItemList.size(); + for (DrawList::size_type i = 0; i < drawListSize; ++i) { + const DrawItem &drawItem = *screenItemList[i]; mergeToShowList(drawItem.rect, _showList, _overdrawThreshold); - ScreenItem &screenItem = *drawItem.screenItem; + const ScreenItem &screenItem = *drawItem.screenItem; // TODO: Remove // debug("Drawing item %04x:%04x to %d %d %d %d", PRINT_REG(screenItem._object), PRINT_RECT(drawItem.rect)); CelObj &celObj = *screenItem._celObj; @@ -847,32 +948,61 @@ void GfxFrameout::drawScreenItemList(const DrawList &screenItemList) { } void GfxFrameout::mergeToShowList(const Common::Rect &drawRect, RectList &showList, const int overdrawThreshold) { - Common::Rect merged(drawRect); - - bool didDelete = true; - RectList::size_type count = showList.size(); - while (didDelete && count) { - didDelete = false; - - for (RectList::size_type i = 0; i < count; ++i) { - Common::Rect existing = *showList[i]; - Common::Rect candidate; - candidate.left = MIN(merged.left, existing.left); - candidate.top = MIN(merged.top, existing.top); - candidate.right = MAX(merged.right, existing.right); - candidate.bottom = MAX(merged.bottom, existing.bottom); - - if (candidate.height() * candidate.width() - merged.width() * merged.height() - existing.width() * existing.height() <= overdrawThreshold) { - merged = candidate; - showList.erase_at(i); - didDelete = true; + RectList mergeList; + Common::Rect merged; + mergeList.add(drawRect); + + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + bool didMerge = false; + const Common::Rect &r1 = *mergeList[i]; + if (!r1.isEmpty()) { + for (RectList::size_type j = 0; j < showList.size(); ++j) { + const Common::Rect &r2 = *showList[j]; + if (!r2.isEmpty()) { + merged = r1; + merged.extend(r2); + + int difference = merged.width() * merged.height(); + difference -= r1.width() * r1.height(); + difference -= r2.width() * r2.height(); + if (r1.intersects(r2)) { + const Common::Rect overlap = r1.findIntersectingRect(r2); + difference += overlap.width() * overlap.height(); + } + + if (difference <= overdrawThreshold) { + mergeList.erase_at(i); + showList.erase_at(j); + mergeList.add(merged); + didMerge = true; + break; + } else { + Common::Rect outRects[2]; + int splitCount = splitRectsForRender(*mergeList[i], *showList[j], outRects); + if (splitCount != -1) { + mergeList.add(*mergeList[i]); + mergeList.erase_at(i); + showList.erase_at(j); + didMerge = true; + while (splitCount--) { + mergeList.add(outRects[splitCount]); + } + break; + } + } + } } - } - count = showList.pack(); + if (didMerge) { + showList.pack(); + } + } } - showList.add(merged); + mergeList.pack(); + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + showList.add(*mergeList[i]); + } } void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry *showStyle) { @@ -885,8 +1015,6 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry _showList.add(rect); showBits(); - Common::Rect calcRect(0, 0); - // NOTE: The original engine allocated these as static arrays of 100 // pointers to ScreenItemList / RectList ScreenItemListList screenItemLists; @@ -899,7 +1027,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry remapMarkRedraw(); } - calcLists(screenItemLists, eraseLists, calcRect); + calcLists(screenItemLists, eraseLists); for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { list->sort(); } @@ -929,6 +1057,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry } } 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; @@ -959,7 +1088,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry remapMarkRedraw(); } - calcLists(screenItemLists, eraseLists, calcRect); + calcLists(screenItemLists, eraseLists); for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { list->sort(); } @@ -1438,8 +1567,21 @@ void GfxFrameout::kernelFrameOut(const bool shouldShowBits) { frameOut(shouldShowBits); } + throttle(); +} + +void GfxFrameout::throttle() { if (_throttleFrameOut) { - g_sci->getEngineState()->speedThrottler(16); + uint8 throttleTime; + if (_throttleState == 2) { + throttleTime = 17; + _throttleState = 0; + } else { + throttleTime = 16; + ++_throttleState; + } + + g_sci->getEngineState()->speedThrottler(throttleTime); g_sci->getEngineState()->_throttleTrigger = true; } } diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index 0be5aece1e..cc62c61d22 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -185,7 +185,7 @@ private: /** * Whether or not calls to kFrameOut should be framerate - * limited to ~60fps. + * limited to 60fps. */ bool _throttleFrameOut; @@ -304,6 +304,12 @@ public: #pragma mark Rendering private: /** + * State tracker to provide more accurate 60fps + * video throttling. + */ + uint8 _throttleState; + + /** * TODO: Documentation */ int8 _styleRanges[256]; @@ -318,7 +324,9 @@ private: Buffer _currentBuffer; /** - * TODO: Documentation + * When true, a change to the remap zone in the palette + * has occurred and screen items with remap data need to + * be redrawn. */ bool _remapOccurred; @@ -376,8 +384,10 @@ private: * over the entire screen for rendering the next frame. * The draw and erase lists in `drawLists` and * `eraseLists` each represent one plane on the screen. + * The optional `eraseRect` argument allows a specific + * area of the screen to be erased. */ - void calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &calcRect); + void calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &eraseRect = Common::Rect()); /** * Erases the areas in the given erase list from the @@ -421,18 +431,25 @@ public: */ bool _palMorphIsOn; - inline Buffer &getCurrentBuffer() { + inline const Buffer &getCurrentBuffer() const { return _currentBuffer; } void kernelFrameOut(const bool showBits); /** + * Throttles the engine as necessary to maintain + * 60fps output. + */ + void throttle(); + + /** * Updates the internal screen buffer for the next * frame. If `shouldShowBits` is true, also sends the - * buffer to hardware. + * buffer to hardware. If `eraseRect` is non-empty, + * it is added to the erase list for this frame. */ - void frameOut(const bool shouldShowBits, const Common::Rect &rect = Common::Rect()); + void frameOut(const bool shouldShowBits, const Common::Rect &eraseRect = Common::Rect()); /** * Modifies the raw pixel data for the next frame with diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp index 3f0b035738..175875c414 100644 --- a/engines/sci/graphics/plane32.cpp +++ b/engines/sci/graphics/plane32.cpp @@ -163,11 +163,15 @@ void Plane::printDebugInfo(Console *con) const { void Plane::addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX) { uint16 celCount = 1000; + bool transparent = true; for (uint16 celNo = 0; celNo < celCount; ++celNo) { CelObjPic *celObj = new CelObjPic(pictureId, celNo); if (celCount == 1000) { celCount = celObj->_celCount; } + if (!celObj->_transparent) { + transparent = false; + } ScreenItem *screenItem = new ScreenItem(_object, celObj->_info); screenItem->_pictureId = pictureId; @@ -184,6 +188,7 @@ void Plane::addPicInternal(const GuiResourceId pictureId, const Common::Point *p delete screenItem->_celObj; screenItem->_celObj = celObj; } + _type = transparent ? kPlaneTypeTransparentPicture : kPlaneTypePicture; } void Plane::addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX) { @@ -196,7 +201,7 @@ void Plane::addPic(const GuiResourceId pictureId, const Common::Point &position, void Plane::changePic() { _pictureChanged = false; - if (_type != kPlaneTypePicture) { + if (_type != kPlaneTypePicture && _type != kPlaneTypeTransparentPicture) { return; } @@ -240,16 +245,20 @@ void Plane::deleteAllPics() { #pragma mark Plane - Rendering void Plane::breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const { - int index = planeList.findIndexByObject(_object); + const int nextPlaneIndex = planeList.findIndexByObject(_object) + 1; + const PlaneList::size_type planeCount = planeList.size(); for (DrawList::size_type i = 0; i < drawList.size(); ++i) { - for (PlaneList::size_type j = index + 1; j < planeList.size(); ++j) { - if (planeList[j]->_type != kPlaneTypeTransparent) { - Common::Rect ptr[4]; - int count = splitRects(drawList[i]->rect, planeList[j]->_screenRect, ptr); - if (count != -1) { - for (int k = count - 1; k >= 0; --k) { - drawList.add(drawList[i]->screenItem, ptr[k]); + for (PlaneList::size_type j = nextPlaneIndex; j < planeCount; ++j) { + if ( + planeList[j]->_type != kPlaneTypeTransparent && + planeList[j]->_type != kPlaneTypeTransparentPicture + ) { + Common::Rect outRects[4]; + int splitCount = splitRects(drawList[i]->rect, planeList[j]->_screenRect, outRects); + if (splitCount != -1) { + while (splitCount--) { + drawList.add(drawList[i]->screenItem, outRects[splitCount]); } drawList.erase_at(i); @@ -262,17 +271,20 @@ void Plane::breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList } void Plane::breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeList) const { - int index = planeList.findIndexByObject(_object); + const int nextPlaneIndex = planeList.findIndexByObject(_object) + 1; + const PlaneList::size_type planeCount = planeList.size(); for (RectList::size_type i = 0; i < eraseList.size(); ++i) { - for (PlaneList::size_type j = index + 1; j < planeList.size(); ++j) { - if (planeList[j]->_type != kPlaneTypeTransparent) { - Common::Rect ptr[4]; - - int count = splitRects(*eraseList[i], planeList[j]->_screenRect, ptr); - if (count != -1) { - for (int k = count - 1; k >= 0; --k) { - eraseList.add(ptr[k]); + for (PlaneList::size_type j = nextPlaneIndex; j < planeCount; ++j) { + if ( + planeList[j]->_type != kPlaneTypeTransparent && + planeList[j]->_type != kPlaneTypeTransparentPicture + ) { + Common::Rect outRects[4]; + int splitCount = splitRects(*eraseList[i], planeList[j]->_screenRect, outRects); + if (splitCount != -1) { + while (splitCount--) { + eraseList.add(outRects[splitCount]); } eraseList.erase_at(i); @@ -285,94 +297,109 @@ void Plane::breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeLi } void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList) { - ScreenItemList::size_type planeItemCount = _screenItemList.size(); - ScreenItemList::size_type visiblePlaneItemCount = visiblePlane._screenItemList.size(); + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + const ScreenItemList::size_type visiblePlaneItemCount = visiblePlane._screenItemList.size(); + + for (ScreenItemList::size_type i = 0; i < screenItemCount; ++i) { + // Items can be added to ScreenItemList and we don't want to process + // those new items, but the list also can grow smaller, so we need + // to check that we are still within the upper bound of the list and + // quit if we aren't any more + if (i >= _screenItemList.size()) { + break; + } + + ScreenItem *item = _screenItemList[i]; + if (item == nullptr) { + continue; + } - for (ScreenItemList::size_type i = 0; i < planeItemCount; ++i) { - ScreenItem *vitem = nullptr; // NOTE: The original engine used an array without bounds checking // so could just get the visible screen item directly; we need to // verify that the index is actually within the valid range for // the visible plane before accessing the item to avoid a range // error. + const ScreenItem *visibleItem = nullptr; if (i < visiblePlaneItemCount) { - vitem = visiblePlane._screenItemList[i]; + visibleItem = visiblePlane._screenItemList[i]; } - ScreenItem *item = _screenItemList[i]; - if (i < _screenItemList.size() && item != nullptr) { - if (item->_deleted) { - // add item's rect to erase list - if ( - i < visiblePlane._screenItemList.size() && - vitem != nullptr && - !vitem->_screenRect.isEmpty() - ) { - if (g_sci->_gfxRemap32->getRemapCount()) { - mergeToRectList(vitem->_screenRect, eraseList); - } else { - eraseList.add(vitem->_screenRect); - } - } - } else if (item->_created) { - // add item to draw list - item->calcRects(*this); - - if(!item->_screenRect.isEmpty()) { - if (g_sci->_gfxRemap32->getRemapCount()) { - drawList.add(item, item->_screenRect); - mergeToRectList(item->_screenRect, eraseList); - } else { - drawList.add(item, item->_screenRect); - } - } - } else if (item->_updated) { - // add old rect to erase list, new item to draw list - item->calcRects(*this); + // Keep erase rects for this screen item from drawing outside + // of its owner plane + Common::Rect visibleItemScreenRect; + if (visibleItem != nullptr) { + visibleItemScreenRect = visibleItem->_screenRect; + visibleItemScreenRect.clip(_screenRect); + } + + if (item->_deleted) { + // Add item's rect to erase list + if ( + visibleItem != nullptr && + !visibleItemScreenRect.isEmpty() + ) { if (g_sci->_gfxRemap32->getRemapCount()) { - // if item and vitem don't overlap, ... - if (item->_screenRect.isEmpty() || - i >= visiblePlaneItemCount || - vitem == nullptr || - vitem->_screenRect.isEmpty() || - !vitem->_screenRect.intersects(item->_screenRect) - ) { - // add item to draw list, and old rect to erase list - if (!item->_screenRect.isEmpty()) { - drawList.add(item, item->_screenRect); - mergeToRectList(item->_screenRect, eraseList); - } - if ( - i < visiblePlaneItemCount && - vitem != nullptr && - !vitem->_screenRect.isEmpty() - ) { - mergeToRectList(vitem->_screenRect, eraseList); - } - } else { - // otherwise, add bounding box of old+new to erase list, - // and item to draw list + mergeToRectList(visibleItemScreenRect, eraseList); + } else { + eraseList.add(visibleItemScreenRect); + } + } + } - // TODO: This was changed from disasm, verify please! - Common::Rect extendedScreenRect = vitem->_screenRect; - extendedScreenRect.extend(item->_screenRect); + if (!item->_created && !item->_updated) { + continue; + } - drawList.add(item, item->_screenRect); - mergeToRectList(extendedScreenRect, eraseList); - } + item->calcRects(*this); + const Common::Rect itemScreenRect(item->_screenRect); + + if (item->_created) { + // Add item to draw list + if(!itemScreenRect.isEmpty()) { + if (g_sci->_gfxRemap32->getRemapCount()) { + drawList.add(item, itemScreenRect); + mergeToRectList(itemScreenRect, eraseList); } else { - // if no active remaps, just add item to draw list and old rect - // to erase list - if (!item->_screenRect.isEmpty()) { - drawList.add(item, item->_screenRect); + drawList.add(item, itemScreenRect); + } + } + } else { + // Add old rect to erase list, new item to draw list + + if (g_sci->_gfxRemap32->getRemapCount()) { + // If item and visibleItem don't overlap... + if (itemScreenRect.isEmpty() || + visibleItem == nullptr || + visibleItemScreenRect.isEmpty() || + !visibleItemScreenRect.intersects(itemScreenRect) + ) { + // ...add item to draw list, and old rect to erase list... + if (!itemScreenRect.isEmpty()) { + drawList.add(item, itemScreenRect); + mergeToRectList(itemScreenRect, eraseList); } - if ( - i < visiblePlaneItemCount && - vitem != nullptr && - !vitem->_screenRect.isEmpty() - ) { - eraseList.add(vitem->_screenRect); + if (visibleItem != nullptr && !visibleItemScreenRect.isEmpty()) { + mergeToRectList(visibleItemScreenRect, eraseList); } + } else { + // ...otherwise, add bounding box of old+new to erase list, + // and item to draw list + Common::Rect extendedScreenRect = visibleItemScreenRect; + extendedScreenRect.extend(itemScreenRect); + + drawList.add(item, itemScreenRect); + mergeToRectList(extendedScreenRect, eraseList); + } + } else { + // If no active remaps, just add item to draw list and old rect + // to erase list + + // TODO: SCI3 update rects for VMD? + if (!itemScreenRect.isEmpty()) { + drawList.add(item, itemScreenRect); + } + if (visibleItem != nullptr && !visibleItemScreenRect.isEmpty()) { + eraseList.add(visibleItemScreenRect); } } } @@ -385,40 +412,44 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList // We store the current size of the drawlist, as we want to loop // over the currently inserted entries later. DrawList::size_type drawListSizePrimary = drawList.size(); + const RectList::size_type eraseListCount = eraseList.size(); - if (/* TODO: dword_C6288 */ false) { // "high resolution pictures"???? + // TODO: Figure out which games need which rendering method + if (/* TODO: dword_C6288 */ false) { // "high resolution pictures" _screenItemList.sort(); - bool encounteredPic = false; - bool v81 = false; + bool pictureDrawn = false; + bool screenItemDrawn = false; - for (RectList::size_type i = 0; i < eraseList.size(); ++i) { - const Common::Rect *rect = eraseList[i]; + for (RectList::size_type i = 0; i < eraseListCount; ++i) { + const Common::Rect &rect = *eraseList[i]; - for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { ScreenItem *item = _screenItemList[j]; - if (j < _screenItemList.size() && item != nullptr) { - if (rect->intersects(item->_screenRect)) { - const Common::Rect intersection = rect->findIntersectingRect(item->_screenRect); - if (!item->_deleted) { - if (encounteredPic) { - if (item->_celInfo.type == kCelTypePic) { - if (v81 || item->_celInfo.celNo == 0) { - drawList.add(item, intersection); - } - } else { - if (!item->_updated && !item->_created) { - drawList.add(item, intersection); - } - v81 = true; + if (item == nullptr) { + continue; + } + + if (rect.intersects(item->_screenRect)) { + const Common::Rect intersection = rect.findIntersectingRect(item->_screenRect); + if (!item->_deleted) { + if (pictureDrawn) { + if (item->_celInfo.type == kCelTypePic) { + if (screenItemDrawn || item->_celInfo.celNo == 0) { + mergeToDrawList(j, intersection, drawList); } } else { if (!item->_updated && !item->_created) { - drawList.add(item, intersection); - } - if (item->_celInfo.type == kCelTypePic) { - encounteredPic = true; + mergeToDrawList(j, intersection, drawList); } + screenItemDrawn = true; + } + } else { + if (!item->_updated && !item->_created) { + mergeToDrawList(j, intersection, drawList); + } + if (item->_celInfo.type == kCelTypePic) { + pictureDrawn = true; } } } @@ -428,22 +459,23 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList _screenItemList.unsort(); } else { - // add all items overlapping the erase list to the draw list - for (RectList::size_type i = 0; i < eraseList.size(); ++i) { - for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { + // Add all items overlapping the erase list to the draw list + for (RectList::size_type i = 0; i < eraseListCount; ++i) { + const Common::Rect &rect = *eraseList[i]; + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { ScreenItem *item = _screenItemList[j]; if ( item != nullptr && !item->_created && !item->_updated && !item->_deleted && - eraseList[i]->intersects(item->_screenRect) + rect.intersects(item->_screenRect) ) { - drawList.add(item, eraseList[i]->findIntersectingRect(item->_screenRect)); + drawList.add(item, rect.findIntersectingRect(item->_screenRect)); } } } } - if (g_sci->_gfxRemap32->getRemapCount() == 0) { // no remaps active? + if (g_sci->_gfxRemap32->getRemapCount() == 0) { // Add all items that overlap with items in the drawlist and have higher // priority. @@ -451,23 +483,28 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList // those that were added because of the erase list in the previous loop, // or those to be added in this loop. for (DrawList::size_type i = 0; i < drawListSizePrimary; ++i) { - DrawItem *dli = drawList[i]; + const DrawItem *drawListEntry = nullptr; + if (i < drawList.size()) { + drawListEntry = drawList[i]; + } - for (ScreenItemList::size_type j = 0; j < planeItemCount; ++j) { - ScreenItem *sli = _screenItemList[j]; + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { + ScreenItem *newItem = nullptr; + if (j < _screenItemList.size()) { + newItem = _screenItemList[j]; + } if ( - i < drawList.size() && dli != nullptr && - j < _screenItemList.size() && sli != nullptr && - !sli->_created && !sli->_updated && !sli->_deleted + drawListEntry != nullptr && newItem != nullptr && + !newItem->_created && !newItem->_updated && !newItem->_deleted ) { - ScreenItem *item = dli->screenItem; + const ScreenItem *drawnItem = drawListEntry->screenItem; if ( - (sli->_priority > item->_priority || (sli->_priority == item->_priority && sli->_object > item->_object)) && - dli->rect.intersects(sli->_screenRect) + (newItem->_priority > drawnItem->_priority || (newItem->_priority == drawnItem->_priority && newItem->_object > drawnItem->_object)) && + drawListEntry->rect.intersects(newItem->_screenRect) ) { - drawList.add(sli, dli->rect.findIntersectingRect(sli->_screenRect)); + mergeToDrawList(j, drawListEntry->rect.findIntersectingRect(newItem->_screenRect), drawList); } } } @@ -475,14 +512,11 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList } decrementScreenItemArrayCounts(&visiblePlane, false); - _screenItemList.pack(); - visiblePlane._screenItemList.pack(); } void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate) { - // The size of the screenItemList may change, so it is - // critical to re-check the size on each iteration - for (ScreenItemList::size_type i = 0; i < _screenItemList.size(); ++i) { + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type i = 0; i < screenItemCount; ++i) { ScreenItem *item = _screenItemList[i]; if (item != nullptr) { @@ -495,7 +529,7 @@ void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool force visiblePlane->_screenItemList.findByObject(item->_object) != nullptr ) ) { - *visiblePlane->_screenItemList[i] = *_screenItemList[i]; + *visiblePlane->_screenItemList[i] = *item; } if (item->_updated) { @@ -514,175 +548,180 @@ void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool force if (item->_deleted) { item->_deleted--; if (!item->_deleted) { - visiblePlane->_screenItemList.erase_at(i); + if (visiblePlane != nullptr && visiblePlane->_screenItemList.findByObject(item->_object) != nullptr) { + visiblePlane->_screenItemList.erase_at(i); + } _screenItemList.erase_at(i); } } } } + + _screenItemList.pack(); + if (visiblePlane != nullptr) { + visiblePlane->_screenItemList.pack(); + } } -void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &transparentEraseList) const { - if (_type == kPlaneTypeTransparent) { - for (RectList::size_type i = 0; i < transparentEraseList.size(); ++i) { - const Common::Rect *r = transparentEraseList[i]; - for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { - ScreenItem *item = _screenItemList[j]; - if (item != nullptr) { - if (r->intersects(item->_screenRect)) { - mergeToDrawList(j, *r, drawList); - } +void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &higherEraseList) const { + const RectList::size_type higherEraseCount = higherEraseList.size(); + + if (_type == kPlaneTypeTransparent || _type == kPlaneTypeTransparentPicture) { + for (RectList::size_type i = 0; i < higherEraseCount; ++i) { + const Common::Rect &r = *higherEraseList[i]; + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { + const ScreenItem *item = _screenItemList[j]; + if (item != nullptr && r.intersects(item->_screenRect)) { + mergeToDrawList(j, r, drawList); } } } } else { - for (RectList::size_type i = 0; i < transparentEraseList.size(); ++i) { - Common::Rect *r = transparentEraseList[i]; - if (r->intersects(_screenRect)) { - r->clip(_screenRect); - mergeToRectList(*r, eraseList); - - for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { - ScreenItem *item = _screenItemList[j]; - - if (item != nullptr) { - if (r->intersects(item->_screenRect)) { - mergeToDrawList(j, *r, drawList); - } + for (RectList::size_type i = 0; i < higherEraseCount; ++i) { + Common::Rect r = *higherEraseList[i]; + if (r.intersects(_screenRect)) { + r.clip(_screenRect); + mergeToRectList(r, eraseList); + + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { + const ScreenItem *item = _screenItemList[j]; + if (item != nullptr && r.intersects(item->_screenRect)) { + mergeToDrawList(j, r, drawList); } } - Common::Rect ptr[4]; - const Common::Rect *r2 = transparentEraseList[i]; - int count = splitRects(*r2, *r, ptr); - for (int k = count - 1; k >= 0; --k) { - transparentEraseList.add(ptr[k]); + Common::Rect outRects[4]; + const Common::Rect &r2 = *higherEraseList[i]; + int splitCount = splitRects(r2, r, outRects); + while (splitCount--) { + higherEraseList.add(outRects[splitCount]); } - transparentEraseList.erase_at(i); + higherEraseList.erase_at(i); } } - transparentEraseList.pack(); + higherEraseList.pack(); } } -void Plane::filterUpDrawRects(DrawList &transparentDrawList, const DrawList &drawList) const { - for (DrawList::size_type i = 0; i < drawList.size(); ++i) { - const Common::Rect &r = drawList[i]->rect; - - for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { - ScreenItem *item = _screenItemList[j]; - if (item != nullptr) { - if (r.intersects(item->_screenRect)) { - mergeToDrawList(j, r, transparentDrawList); - } +void Plane::filterUpDrawRects(DrawList &drawList, const DrawList &lowerDrawList) const { + const DrawList::size_type lowerDrawCount = lowerDrawList.size(); + for (DrawList::size_type i = 0; i < lowerDrawCount; ++i) { + const Common::Rect &r = lowerDrawList[i]->rect; + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { + const ScreenItem *item = _screenItemList[j]; + if (item != nullptr && r.intersects(item->_screenRect)) { + mergeToDrawList(j, r, drawList); } } } } -void Plane::filterUpEraseRects(DrawList &drawList, RectList &eraseList) const { - for (RectList::size_type i = 0; i < eraseList.size(); ++i) { - const Common::Rect &r = *eraseList[i]; - for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { - ScreenItem *item = _screenItemList[j]; - - if (item != nullptr) { - if (r.intersects(item->_screenRect)) { - mergeToDrawList(j, r, drawList); - } +void Plane::filterUpEraseRects(DrawList &drawList, const RectList &lowerEraseList) const { + const RectList::size_type lowerEraseCount = lowerEraseList.size(); + for (RectList::size_type i = 0; i < lowerEraseCount; ++i) { + const Common::Rect &r = *lowerEraseList[i]; + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type j = 0; j < screenItemCount; ++j) { + const ScreenItem *item = _screenItemList[j]; + if (item != nullptr && r.intersects(item->_screenRect)) { + mergeToDrawList(j, r, drawList); } } } } void Plane::mergeToDrawList(const ScreenItemList::size_type index, const Common::Rect &rect, DrawList &drawList) const { - RectList rects; - - ScreenItem *item = _screenItemList[index]; - Common::Rect r = item->_screenRect; + RectList mergeList; + ScreenItem &item = *_screenItemList[index]; + Common::Rect r = item._screenRect; r.clip(rect); - rects.add(r); + mergeList.add(r); - for (RectList::size_type i = 0; i < rects.size(); ++i) { - r = *rects[i]; + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + r = *mergeList[i]; - for (DrawList::size_type j = 0; j < drawList.size(); ++j) { - const DrawItem *drawitem = drawList[j]; - if (item->_object == drawitem->screenItem->_object) { - if (drawitem->rect.contains(r)) { - rects.erase_at(i); + const DrawList::size_type drawCount = drawList.size(); + for (DrawList::size_type j = 0; j < drawCount; ++j) { + const DrawItem &drawItem = *drawList[j]; + if (item._object == drawItem.screenItem->_object) { + if (drawItem.rect.contains(r)) { + mergeList.erase_at(i); break; } Common::Rect outRects[4]; - const int count = splitRects(r, drawitem->rect, outRects); - if (count != -1) { - for (int k = count - 1; k >= 0; --k) { - rects.add(outRects[k]); + int splitCount = splitRects(r, drawItem.rect, outRects); + if (splitCount != -1) { + while (splitCount--) { + mergeList.add(outRects[splitCount]); } - rects.erase_at(i); + mergeList.erase_at(i); // proceed to the next rect - r = *rects[++i]; + r = *mergeList[++i]; } } } } - rects.pack(); + mergeList.pack(); - for (RectList::size_type i = 0; i < rects.size(); ++i) { - drawList.add(item, *rects[i]); + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + drawList.add(&item, *mergeList[i]); } } -void Plane::mergeToRectList(const Common::Rect &rect, RectList &rectList) const { - RectList temp; - temp.add(rect); +void Plane::mergeToRectList(const Common::Rect &rect, RectList &eraseList) const { + RectList mergeList; + Common::Rect r; + mergeList.add(rect); - for (RectList::size_type i = 0; i < temp.size(); ++i) { - Common::Rect r = *temp[i]; + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + r = *mergeList[i]; - for (RectList::size_type j = 0; j < rectList.size(); ++j) { - const Common::Rect *innerRect = rectList[j]; - if (innerRect->contains(r)) { - temp.erase_at(i); + const RectList::size_type eraseCount = eraseList.size(); + for (RectList::size_type j = 0; j < eraseCount; ++j) { + const Common::Rect &eraseRect = *eraseList[j]; + if (eraseRect.contains(r)) { + mergeList.erase_at(i); break; } - Common::Rect out[4]; - const int count = splitRects(r, *innerRect, out); - if (count != -1) { - for (int k = count - 1; k >= 0; --k) { - temp.add(out[k]); + Common::Rect outRects[4]; + int splitCount = splitRects(r, eraseRect, outRects); + if (splitCount != -1) { + while (splitCount--) { + mergeList.add(outRects[splitCount]); } - temp.erase_at(i); + mergeList.erase_at(i); // proceed to the next rect - r = *temp[++i]; + r = *mergeList[++i]; } } } - temp.pack(); + mergeList.pack(); - for (RectList::size_type i = 0; i < temp.size(); ++i) { - rectList.add(*temp[i]); + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + eraseList.add(*mergeList[i]); } } void Plane::redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList) { - for (ScreenItemList::const_iterator screenItemPtr = _screenItemList.begin(); screenItemPtr != _screenItemList.end(); ++screenItemPtr) { - if (*screenItemPtr != nullptr) { - ScreenItem &screenItem = **screenItemPtr; - if (!screenItem._deleted) { - screenItem.calcRects(*this); - if (!screenItem._screenRect.isEmpty()) { - drawList.add(&screenItem, screenItem._screenRect); - } + const ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type i = 0; i < screenItemCount; ++i) { + ScreenItem *screenItem = _screenItemList[i]; + if (screenItem != nullptr && !screenItem->_deleted) { + screenItem->calcRects(*this); + if (!screenItem->_screenRect.isEmpty()) { + mergeToDrawList(i, screenItem->_screenRect, drawList); } } } @@ -696,21 +735,27 @@ void Plane::redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList breakDrawListByPlanes(drawList, planeList); --_redrawAllCount; decrementScreenItemArrayCounts(visiblePlane, true); - _screenItemList.pack(); - if (visiblePlane != nullptr) { - visiblePlane->_screenItemList.pack(); - } } void Plane::setType() { - if (_pictureId == kPlanePicOpaque) { - _type = kPlaneTypeOpaque; - } else if (_pictureId == kPlanePicTransparent) { - _type = kPlaneTypeTransparent; - } else if (_pictureId == kPlanePicColored) { + switch (_pictureId) { + case kPlanePicColored: _type = kPlaneTypeColored; - } else { - _type = kPlaneTypePicture; + break; + case kPlanePicTransparent: + _type = kPlaneTypeTransparent; + break; + case kPlanePicOpaque: + _type = kPlaneTypeOpaque; + break; + case kPlanePicTransparentPicture: + _type = kPlaneTypeTransparentPicture; + break; + default: + if (_type != kPlaneTypeTransparentPicture) { + _type = kPlaneTypePicture; + } + break; } } @@ -731,10 +776,12 @@ void Plane::sync(const Plane *other, const Common::Rect &screenRect) { _planeRect.right > other->_planeRect.right || _planeRect.bottom > other->_planeRect.bottom ) { + // the plane moved or got larger _redrawAllCount = g_sci->_gfxFrameout->getScreenCount(); - _updated = g_sci->_gfxFrameout->getScreenCount(); + _moved = g_sci->_gfxFrameout->getScreenCount(); } else if (_planeRect != other->_planeRect) { - _updated = g_sci->_gfxFrameout->getScreenCount(); + // the plane got smaller + _moved = g_sci->_gfxFrameout->getScreenCount(); } if (_priority != other->_priority) { @@ -755,7 +802,7 @@ void Plane::sync(const Plane *other, const Common::Rect &screenRect) { _deleted = 0; if (_created == 0) { - _moved = g_sci->_gfxFrameout->getScreenCount(); + _updated = g_sci->_gfxFrameout->getScreenCount(); } convertGameRectToPlaneRect(); @@ -801,18 +848,22 @@ void Plane::scrollScreenItems(const int16 deltaX, const int16 deltaY, const bool } void Plane::remapMarkRedraw() { - for (ScreenItemList::const_iterator screenItemPtr = _screenItemList.begin(); screenItemPtr != _screenItemList.end(); ++screenItemPtr) { - if (*screenItemPtr != nullptr) { - ScreenItem &screenItem = **screenItemPtr; - if (screenItem.getCelObj()._remap && !screenItem._deleted && !screenItem._created) { - screenItem._updated = g_sci->_gfxFrameout->getScreenCount(); - } + ScreenItemList::size_type screenItemCount = _screenItemList.size(); + for (ScreenItemList::size_type i = 0; i < screenItemCount; ++i) { + ScreenItem *screenItem = _screenItemList[i]; + if ( + screenItem != nullptr && + !screenItem->_deleted && !screenItem->_created && + screenItem->getCelObj()._remap + ) { + screenItem->_updated = g_sci->_gfxFrameout->getScreenCount(); } } } #pragma mark - #pragma mark PlaneList + void PlaneList::add(Plane *plane) { for (iterator it = begin(); it != end(); ++it) { if ((*it)->_priority > plane->_priority) { diff --git a/engines/sci/graphics/plane32.h b/engines/sci/graphics/plane32.h index c93fb5b64e..a1739264e8 100644 --- a/engines/sci/graphics/plane32.h +++ b/engines/sci/graphics/plane32.h @@ -32,19 +32,21 @@ namespace Sci { enum PlaneType { - kPlaneTypeColored = 0, - kPlaneTypePicture = 1, - kPlaneTypeTransparent = 2, - kPlaneTypeOpaque = 3 + kPlaneTypeColored = 0, + kPlaneTypePicture = 1, + kPlaneTypeTransparent = 2, + kPlaneTypeOpaque = 3, + kPlaneTypeTransparentPicture = 4 }; enum PlanePictureCodes { - // NOTE: Any value at or below 65532 means the plane + // NOTE: Any value at or below 65531 means the plane // is a kPlaneTypePicture. - kPlanePic = 65532, - kPlanePicOpaque = 65533, - kPlanePicTransparent = 65534, - kPlanePicColored = 65535 + kPlanePic = 65531, + kPlanePicTransparentPicture = 65532, + kPlanePicOpaque = 65533, + kPlanePicTransparent = 65534, + kPlanePicColored = 65535 }; #pragma mark - @@ -62,7 +64,14 @@ public: #pragma mark DrawList struct DrawItem { + /** + * The screen item to draw. + */ ScreenItem *screenItem; + + /** + * The target rectangle of the draw operation. + */ Common::Rect rect; inline bool operator<(const DrawItem &other) const { @@ -189,16 +198,15 @@ public: * not match * - `deleted` is set when the plane is deleted by a * kernel call - * - `moved` is set when the plane is synchronised from - * another plane and is not already in the "created" - * state + * - `moved` is set when the plane has been moved or + * resized */ int _created, _updated, _deleted, _moved; /** * The vanishing point for the plane. Used when - * calculating the correct scaling of the plane's screen - * items according to their position. + * automatically calculating the correct scaling of the + * plane's screen items according to their position. */ Common::Point _vanishingPoint; @@ -358,42 +366,33 @@ public: private: /** * Splits all rects in the given draw list at the edges - * of all non-transparent planes above the current - * plane. + * of all higher-priority, non-transparent, intersecting + * planes. */ void breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const; /** - * Splits all rects in the given erase list rects at the - * edges of all non-transparent planes above the current - * plane. + * Splits all rects in the given erase list at the + * edges of higher-priority, non-transparent, + * intersecting planes. */ void breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeList) const; /** - * Synchronises changes to screen items from the current - * plane to the visible plane and deletes screen items - * from the current plane that have been marked as - * deleted. If `forceUpdate` is true, all screen items - * on the visible plane will be updated, even if they - * are not marked as having changed. - */ - void decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate); - - /** - * Merges the screen item from this plane at the given - * index into the given draw list, clipped to the given - * rect. TODO: Finish documenting + * Adds the screen item at `index` into `drawList`, + * ensuring it is only drawn within the bounds of + * `rect`. If an existing draw list entry exists + * for this screen item, it will be modified. + * Otherwise, a new entry will be added. */ void mergeToDrawList(const DrawList::size_type index, const Common::Rect &rect, DrawList &drawList) const; /** - * Adds the given rect into the given rect list, - * merging it with other rects already inside the list, - * if possible, to avoid overdraw. TODO: Finish - * documenting + * Merges `rect` with an existing rect in `eraseList`, + * if possible. Otherwise, adds the rect as a new entry + * to `eraseList`. */ - void mergeToRectList(const Common::Rect &rect, RectList &rectList) const; + void mergeToRectList(const Common::Rect &rect, RectList &eraseList) const; public: /** @@ -406,19 +405,73 @@ public: void calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList); /** - * TODO: Documentation + * Synchronises changes to screen items from the current + * plane to the visible plane and deletes screen items + * from the current plane that have been marked as + * deleted. If `forceUpdate` is true, all screen items + * on the visible plane will be updated, even if they + * are not marked as having changed. + */ + void decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate); + + /** + * This method is called from the highest priority plane + * to the lowest priority plane. + * + * Adds screen items from this plane to the draw list + * that must be redrawn because they intersect entries + * in the `higherEraseList`. + * + * If this plane is opaque, all intersecting erase rects + * in `lowerEraseList` are removed, as they would be + * completely overwritten by the contents of this plane. + * + * If this plane is transparent, erase rects from the + * `lowerEraseList` are added to the erase list for this + * plane, so that lower planes. + * + * @param drawList The draw list for this plane. + * @param eraseList The erase list for this plane. + * @param higherEraseList The erase list for a plane + * above this plane. */ - void filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &transparentEraseList) const; + void filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &higherEraseList) const; /** - * TODO: Documentation + * This method is called from the lowest priority plane + * to the highest priority plane. + * + * Adds screen items from this plane to the draw list + * that must be drawn because the lower plane is being + * redrawn and potentially transparent screen items + * from this plane would draw over the lower priority + * plane's screen items. + * + * This method applies only to transparent planes. + * + * @param drawList The draw list for this plane. + * @param eraseList The erase list for a plane below + * this plane. */ - void filterUpEraseRects(DrawList &drawList, RectList &eraseList) const; + void filterUpEraseRects(DrawList &drawList, const RectList &lowerEraseList) const; /** - * TODO: Documentation + * This method is called from the lowest priority plane + * to the highest priority plane. + * + * Adds screen items from this plane to the draw list + * that must be drawn because the lower plane is being + * redrawn and potentially transparent screen items + * from this plane would draw over the lower priority + * plane's screen items. + * + * This method applies only to transparent planes. + * + * @param drawList The draw list for this plane. + * @param lowerDrawList The draw list for a plane below + * this plane. */ - void filterUpDrawRects(DrawList &transparentDrawList, const DrawList &drawList) const; + void filterUpDrawRects(DrawList &drawList, const DrawList &lowerDrawList) const; /** * Updates all of the plane's non-deleted screen items diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp index c0b3240c7e..ebaf132890 100644 --- a/engines/sci/graphics/screen_item32.cpp +++ b/engines/sci/graphics/screen_item32.cpp @@ -273,7 +273,9 @@ void ScreenItem::calcRects(const Plane &plane) { // Cel may use a coordinate system that is not the same size as the // script coordinate system (usually this means high-resolution // pictures with low-resolution scripts) - if (celObj._scaledWidth != scriptWidth || celObj._scaledHeight != scriptHeight) { + if (celObj._scaledWidth != kLowResX || celObj._scaledHeight != kLowResY) { + // high resolution coordinates + if (_useInsetRect) { const Ratio scriptToCelX(celObj._scaledWidth, scriptWidth); const Ratio scriptToCelY(celObj._scaledHeight, scriptHeight); @@ -294,7 +296,30 @@ void ScreenItem::calcRects(const Plane &plane) { } if (!scaleX.isOne() || !scaleY.isOne()) { - mulinc(_screenItemRect, scaleX, scaleY); + // Different games use a different cel scaling mode, but the + // difference isn't consistent across SCI versions; instead, + // it seems to be related to an update that happened during + // SCI2.1mid where games started using hi-resolution game + // scripts + if (scriptWidth == kLowResX) { + mulinc(_screenItemRect, scaleX, scaleY); + } else { + _screenItemRect.left = (_screenItemRect.left * scaleX).toInt(); + _screenItemRect.top = (_screenItemRect.top * scaleY).toInt(); + + if (scaleX.getNumerator() > scaleX.getDenominator()) { + _screenItemRect.right = (_screenItemRect.right * scaleX).toInt(); + } else { + _screenItemRect.right = ((_screenItemRect.right - 1) * scaleX).toInt() + 1; + } + + if (scaleY.getNumerator() > scaleY.getDenominator()) { + _screenItemRect.bottom = (_screenItemRect.bottom * scaleY).toInt(); + } else { + _screenItemRect.bottom = ((_screenItemRect.bottom - 1) * scaleY).toInt() + 1; + } + } + displaceX = (displaceX * scaleX).toInt(); displaceY = (displaceY * scaleY).toInt(); } @@ -345,6 +370,8 @@ void ScreenItem::calcRects(const Plane &plane) { _ratioX = scaleX * celToScreenX; _ratioY = scaleY * celToScreenY; } else { + // low resolution coordinates + int displaceX = celObj._displace.x; if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) { displaceX = celObj._width - celObj._displace.x - 1; @@ -534,8 +561,6 @@ void ScreenItem::update() { _celObj = nullptr; } -// TODO: This code is quite similar to calcRects, so try to deduplicate -// if possible Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { CelObj &celObj = getCelObj(); @@ -543,10 +568,7 @@ Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { Common::Rect nsRect; if (_useInsetRect) { - // TODO: This is weird. Checking to see if the inset rect is - // fully inside the bounds of the celObjRect, and then - // clipping to the celObjRect, is pretty useless. - if (_insetRect.right > 0 && _insetRect.bottom > 0 && _insetRect.left < celObj._width && _insetRect.top < celObj._height) { + if (_insetRect.intersects(celObjRect)) { nsRect = _insetRect; nsRect.clip(celObjRect); } else { @@ -582,16 +604,15 @@ Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { displaceX = celObj._width - displaceX - 1; } - if (celObj._scaledWidth != scriptWidth || celObj._scaledHeight != scriptHeight) { + if (celObj._scaledWidth != kLowResX || celObj._scaledHeight != kLowResY) { + // high resolution coordinates + if (_useInsetRect) { Ratio scriptToCelX(celObj._scaledWidth, scriptWidth); Ratio scriptToCelY(celObj._scaledHeight, scriptHeight); mulru(nsRect, scriptToCelX, scriptToCelY, 0); - // TODO: This is weird. Checking to see if the inset rect is - // fully inside the bounds of the celObjRect, and then - // clipping to the celObjRect, is pretty useless. - if (nsRect.right > 0 && nsRect.bottom > 0 && nsRect.left < celObj._width && nsRect.top < celObj._height) { + if (nsRect.intersects(celObjRect)) { nsRect.clip(celObjRect); } else { nsRect = Common::Rect(); @@ -599,12 +620,34 @@ Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { } if (!scaleX.isOne() || !scaleY.isOne()) { - mulinc(nsRect, scaleX, scaleY); - // TODO: This was in the original code, baked into the - // multiplication though it is not immediately clear - // why this is the only one that reduces the BR corner - nsRect.right -= 1; - nsRect.bottom -= 1; + // Different games use a different cel scaling mode, but the + // difference isn't consistent across SCI versions; instead, + // it seems to be related to an update that happened during + // SCI2.1mid where games started using hi-resolution game + // scripts + if (scriptWidth == kLowResX) { + mulinc(nsRect, scaleX, scaleY); + // TODO: This was in the original code, baked into the + // multiplication though it is not immediately clear + // why this is the only one that reduces the BR corner + nsRect.right -= 1; + nsRect.bottom -= 1; + } else { + nsRect.left = (nsRect.left * scaleX).toInt(); + nsRect.top = (nsRect.top * scaleY).toInt(); + + if (scaleX.getNumerator() > scaleX.getDenominator()) { + nsRect.right = (nsRect.right * scaleX).toInt(); + } else { + nsRect.right = ((nsRect.right - 1) * scaleX).toInt() + 1; + } + + if (scaleY.getNumerator() > scaleY.getDenominator()) { + nsRect.bottom = (nsRect.bottom * scaleY).toInt(); + } else { + nsRect.bottom = ((nsRect.bottom - 1) * scaleY).toInt() + 1; + } + } } Ratio celToScriptX(scriptWidth, celObj._scaledWidth); @@ -616,6 +659,8 @@ Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { mulinc(nsRect, celToScriptX, celToScriptY); nsRect.translate(_position.x - displaceX, _position.y - displaceY); } else { + // low resolution coordinates + if (!scaleX.isOne() || !scaleY.isOne()) { mulinc(nsRect, scaleX, scaleY); // TODO: This was in the original code, baked into the @@ -649,23 +694,43 @@ ScreenItem *ScreenItemList::findByObject(const reg_t object) const { return *screenItemIt; } void ScreenItemList::sort() { - // TODO: SCI engine used _unsorted as an array of indexes into the - // list itself and then performed the same swap operations on the - // _unsorted array as the _storage array during sorting, but the - // only reason to do this would be if some of the pointers in the - // list were replaced so the pointer values themselves couldn’t - // simply be recorded and then restored later. It is not yet - // verified whether this simplification of the sort/unsort is - // safe. + if (size() < 2) { + return; + } + for (size_type i = 0; i < size(); ++i) { - _unsorted[i] = (*this)[i]; + _unsorted[i] = i; } - Common::sort(begin(), end(), sortHelper); + for (size_type i = size() - 1; i > 0; --i) { + bool swap = false; + + for (size_type j = 0; j < i; ++j) { + value_type &a = operator[](j); + value_type &b = operator[](j + 1); + + if (a == nullptr || *a > *b) { + SWAP(a, b); + SWAP(_unsorted[j], _unsorted[j + 1]); + swap = true; + } + } + + if (!swap) { + break; + } + } } void ScreenItemList::unsort() { + if (size() < 2) { + return; + } + for (size_type i = 0; i < size(); ++i) { - (*this)[i] = _unsorted[i]; + while (_unsorted[i] != i) { + SWAP(operator[](_unsorted[i]), operator[](i)); + SWAP(_unsorted[_unsorted[i]], _unsorted[i]); + } } } diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h index eef2f488a5..caa7a9d725 100644 --- a/engines/sci/graphics/screen_item32.h +++ b/engines/sci/graphics/screen_item32.h @@ -88,15 +88,23 @@ private: Common::Rect _screenItemRect; /** - * TODO: Document + * If true, the `_insetRect` rectangle will be used + * when calculating the dimensions of the screen item + * instead of the cel's intrinsic width and height. + * + * In other words, using an inset rect means that + * the cel is cropped to the dimensions given in + * `_insetRect`. */ bool _useInsetRect; /** - * TODO: Documentation - * The insetRect is also used to describe the fill - * rectangle of a screen item that is drawn using - * CelObjColor. + * The cropping rectangle used when `_useInsetRect` + * is true. + * + * `_insetRect` is also used to describe the fill + * rectangle of a screen item with a CelObjColor + * cel. */ Common::Rect _insetRect; @@ -236,6 +244,24 @@ public: return false; } + inline bool operator>(const ScreenItem &other) const { + if (_priority > other._priority) { + return true; + } + + if (_priority == other._priority) { + if (_position.y + _z > other._position.y + other._z) { + return true; + } + + if (_position.y + _z == other._position.y + other._z) { + return _object > other._object; + } + } + + return false; + } + /** * Calculates the dimensions and scaling parameters for * the screen item, using the given plane as the parent @@ -279,12 +305,10 @@ public: typedef StablePointerArray<ScreenItem, 250> ScreenItemListBase; class ScreenItemList : public ScreenItemListBase { - inline static bool sortHelper(const ScreenItem *a, const ScreenItem *b) { - return *a < *b; - } -public: - ScreenItem *_unsorted[250]; +private: + size_type _unsorted[250]; +public: ScreenItem *findByObject(const reg_t object) const; void sort(); void unsort(); diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp index 20595b073b..4689d13d7d 100644 --- a/engines/sci/sound/audio32.cpp +++ b/engines/sci/sound/audio32.cpp @@ -109,8 +109,11 @@ Audio32::Audio32(ResourceManager *resMan) : _numActiveChannels(0), _inAudioThread(false), + _globalSampleRate(44100), _maxAllowedSampleRate(44100), + _globalBitDepth(16), _maxAllowedBitDepth(16), + _globalNumOutputChannels(2), _maxAllowedOutputChannels(2), _preload(0), @@ -302,7 +305,7 @@ int Audio32::readBuffer(Audio::st_sample_t *buffer, const int numSamples) { // Channel finished fading and had the // stopChannelOnFade flag set, so no longer exists - if (channel.fadeStepsRemaining && processFade(channelIndex)) { + if (channel.fadeStartTick && processFade(channelIndex)) { --channelIndex; continue; } @@ -599,8 +602,7 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool channel.loop = loop; channel.robot = false; channel.vmd = false; - channel.lastFadeTick = 0; - channel.fadeStepsRemaining = 0; + channel.fadeStartTick = 0; channel.soundNode = soundNode; channel.volume = volume < 0 || volume > kMaxVolume ? (int)kMaxVolume : volume; // TODO: SCI3 introduces stereo audio @@ -924,12 +926,12 @@ bool Audio32::fadeChannel(const int16 channelIndex, const int16 targetVolume, co return false; } - if (steps) { - channel.fadeVolume = targetVolume; - channel.fadeSpeed = speed; - channel.fadeStepsRemaining = steps; + if (steps && speed) { + channel.fadeStartTick = g_sci->getTickCount(); + channel.fadeStartVolume = channel.volume; + channel.fadeTargetVolume = targetVolume; + channel.fadeDuration = speed * steps; channel.stopChannelOnFade = stopAfterFade; - channel.lastFadeTick = g_sci->getTickCount(); } else { setVolume(channelIndex, targetVolume); } @@ -941,28 +943,28 @@ bool Audio32::processFade(const int16 channelIndex) { Common::StackLock lock(_mutex); AudioChannel &channel = getChannel(channelIndex); - uint32 now = g_sci->getTickCount(); - - if (channel.lastFadeTick + channel.fadeSpeed <= now) { - --channel.fadeStepsRemaining; - - if (!channel.fadeStepsRemaining) { + if (channel.fadeStartTick) { + const uint32 fadeElapsed = g_sci->getTickCount() - channel.fadeStartTick; + if (fadeElapsed > channel.fadeDuration) { + channel.fadeStartTick = 0; if (channel.stopChannelOnFade) { stop(channelIndex); return true; } else { - setVolume(channelIndex, channel.fadeVolume); - } - } else { - int volume = channel.volume - (channel.volume - channel.fadeVolume) / (channel.fadeStepsRemaining + 1); - - if (volume == channel.fadeVolume) { - channel.fadeStepsRemaining = 1; + setVolume(channelIndex, channel.fadeTargetVolume); } + return false; + } - setVolume(channelIndex, volume); - channel.lastFadeTick = now; + int volume; + if (channel.fadeStartVolume > channel.fadeTargetVolume) { + volume = channel.fadeStartVolume - fadeElapsed * (channel.fadeStartVolume - channel.fadeTargetVolume) / channel.fadeDuration; + } else { + volume = channel.fadeStartVolume + fadeElapsed * (channel.fadeTargetVolume - channel.fadeStartVolume) / channel.fadeDuration; } + + setVolume(channelIndex, volume); + return false; } return false; diff --git a/engines/sci/sound/audio32.h b/engines/sci/sound/audio32.h index 416b81d865..42211eb890 100644 --- a/engines/sci/sound/audio32.h +++ b/engines/sci/sound/audio32.h @@ -90,27 +90,25 @@ struct AudioChannel { bool loop; /** - * The time the last fade iteration occurred. + * The time, in ticks, that the channel fade began. + * If 0, the channel is not being faded. */ - uint32 lastFadeTick; + uint32 fadeStartTick; /** - * The target volume of the fade. + * The start volume of a fade. */ - int fadeVolume; + int fadeStartVolume; /** - * The number of ticks that should elapse between - * each change of volume. + * The total length of the fade, in ticks. */ - int fadeSpeed; + uint32 fadeDuration; /** - * The number of iterations the fade should take to - * complete. If this value is 0, it indicates that the - * channel is not fading. + * The end volume of a fade. */ - int fadeStepsRemaining; + uint32 fadeTargetVolume; /** * Whether or not the channel should be stopped and diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index c5d8dda56b..c3c159de55 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -411,7 +411,7 @@ reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) { #ifdef ENABLE_SCI32 if (_soundVersion >= SCI_VERSION_2_1_EARLY && musicSlot->isSample) { - g_sci->_audio32->fadeChannel(ResourceId(kResourceTypeAudio, musicSlot->resourceId), musicSlot->soundObj, argv[2].toSint16(), argv[3].toSint16(), argv[4].toSint16(), (bool)argv[5].toSint16()); + g_sci->_audio32->fadeChannel(ResourceId(kResourceTypeAudio, musicSlot->resourceId), musicSlot->soundObj, argv[1].toSint16(), argv[2].toSint16(), argv[3].toSint16(), argc > 4 ? (bool)argv[4].toSint16() : false); return acc; } #endif diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp index 0ebea94608..617c2ab85f 100644 --- a/engines/scumm/debugger.cpp +++ b/engines/scumm/debugger.cpp @@ -411,14 +411,15 @@ bool ScummDebugger::Cmd_PrintActor(int argc, const char **argv) { int i; Actor *a; - debugPrintf("+---------------------------------------------------------------+\n"); - debugPrintf("|# | x | y | w | h |elev|cos|box|mov| zp|frm|scl|dir| cls |\n"); - debugPrintf("+--+----+----+---+---+----+---+---+---+---+---+---+---+---------+\n"); + debugPrintf("+----------------------------------------------------------------------------+\n"); + debugPrintf("|# | name | x | y | w | h |elev|cos|box|mov| zp|frm|scl|dir| cls |\n"); + debugPrintf("+--+------------+----+----+---+---+----+---+---+---+---+---+---+---+---------+\n"); for (i = 1; i < _vm->_numActors; i++) { a = _vm->_actors[i]; + const byte *name = _vm->getObjOrActorName(_vm->actorToObj(a->_number)); if (a->_visible) - debugPrintf("|%2d|%4d|%4d|%3d|%3d|%4d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|$%08x|\n", - a->_number, a->getRealPos().x, a->getRealPos().y, a->_width, a->_bottom - a->_top, + debugPrintf("|%2d|%-12.12s|%4d|%4d|%3d|%3d|%4d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|$%08x|\n", + a->_number, name, a->getRealPos().x, a->getRealPos().y, a->_width, a->_bottom - a->_top, a->getElevation(), a->_costume, a->_walkbox, a->_moving, a->_forceClip, a->_frame, a->_scalex, a->getFacing(), _vm->_classData[a->_number]); @@ -431,17 +432,18 @@ bool ScummDebugger::Cmd_PrintObjects(int argc, const char **argv) { int i; ObjectData *o; debugPrintf("Objects in current room\n"); - debugPrintf("+---------------------------------+------------+\n"); - debugPrintf("|num | x | y |width|height|state|fl| cls |\n"); - debugPrintf("+----+----+----+-----+------+-----+--+---------+\n"); + debugPrintf("+-----------------------------------------------------------+\n"); + debugPrintf("|num | name | x | y |width|height|state|fl| cls |\n"); + debugPrintf("+----+------------+----+----+-----+------+-----+--+---------+\n"); for (i = 1; i < _vm->_numLocalObjects; i++) { o = &(_vm->_objs[i]); if (o->obj_nr == 0) continue; int classData = (_vm->_game.version != 0 ? _vm->_classData[o->obj_nr] : 0); - debugPrintf("|%4d|%4d|%4d|%5d|%6d|%5d|%2d|$%08x|\n", - o->obj_nr, o->x_pos, o->y_pos, o->width, o->height, o->state, + const byte *name = _vm->getObjOrActorName(o->obj_nr); + debugPrintf("|%4d|%-12.12s|%4d|%4d|%5d|%6d|%5d|%2d|$%08x|\n", + o->obj_nr, name, o->x_pos, o->y_pos, o->width, o->height, o->state, o->fl_object_index, classData); } debugPrintf("\n"); |