void GfxFrameout::createPlaneItemList(reg_t planeObject, FrameoutList &itemList) { // Copy screen items of the current frame to the list of items to be drawn for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) { reg_t itemPlane = readSelector(_segMan, (*listIterator)->object, SELECTOR(plane)); if (planeObject == itemPlane) { kernelUpdateScreenItem((*listIterator)->object); // TODO: Why is this necessary? itemList.push_back(*listIterator); } } for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) { if (pictureIt->object == planeObject) { GfxPicture *planePicture = pictureIt->picture; // Allocate memory for picture cels pictureIt->pictureCels = new FrameoutEntry[planePicture->getSci32celCount()]; // Add following cels to the itemlist FrameoutEntry *picEntry = pictureIt->pictureCels; int planePictureCels = planePicture->getSci32celCount(); for (int pictureCelNr = 0; pictureCelNr < planePictureCels; pictureCelNr++) { picEntry->celNo = pictureCelNr; picEntry->object = NULL_REG; picEntry->picture = planePicture; picEntry->y = planePicture->getSci32celY(pictureCelNr); picEntry->x = planePicture->getSci32celX(pictureCelNr); picEntry->picStartX = pictureIt->startX; picEntry->picStartY = pictureIt->startY; picEntry->visible = true; picEntry->priority = planePicture->getSci32celPriority(pictureCelNr); itemList.push_back(picEntry); picEntry++; } } } // Now sort our itemlist Common::sort(itemList.begin(), itemList.end(), sortHelper); }
void GfxFrameout::kernelFrameout() { if (g_sci->_robotDecoder->isVideoLoaded()) { showVideo(); return; } _palette->palVaryUpdate(); for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) { reg_t planeObject = it->object; // Draw any plane lines, if they exist // These are drawn on invisible planes as well. (e.g. "invisiblePlane" in LSL6 hires) // FIXME: Lines aren't always drawn (e.g. when the narrator speaks in LSL6 hires). // Perhaps something is painted over them? for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) { Common::Point startPoint = it2->startPoint; Common::Point endPoint = it2->endPoint; _coordAdjuster->kernelLocalToGlobal(startPoint.x, startPoint.y, it->object); _coordAdjuster->kernelLocalToGlobal(endPoint.x, endPoint.y, it->object); _screen->drawLine(startPoint, endPoint, it2->color, it2->priority, it2->control); } int16 planeLastPriority = it->lastPriority; // Update priority here, sq6 sets it w/o UpdatePlane int16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority)); it->lastPriority = planePriority; if (planePriority < 0) { // Plane currently not meant to be shown // If plane was shown before, delete plane rect if (planePriority != planeLastPriority) _paint32->fillRect(it->planeRect, 0); continue; } // There is a race condition lurking in SQ6, which causes the game to hang in the intro, when teleporting to Polysorbate LX. // Since I first wrote the patch, the race has stopped occurring for me though. // I'll leave this for investigation later, when someone can reproduce. //if (it->pictureId == kPlanePlainColored) // FIXME: This is what SSCI does, and fixes the intro of LSL7, but breaks the dialogs in GK1 (adds black boxes) if (it->pictureId == kPlanePlainColored && (it->planeBack || g_sci->getGameId() != GID_GK1)) _paint32->fillRect(it->planeRect, it->planeBack); _coordAdjuster->pictureSetDisplayArea(it->planeRect); // Invoking drewPicture() with an invalid picture ID in SCI32 results in // invalidating the palVary palette when a palVary effect is active. This // is quite obvious in QFG4, where the day time palette is incorrectly // shown when exiting the caves, and the correct night time palette // flashes briefly each time that kPalVaryInit is called. if (it->pictureId != 0xFFFF) _palette->drewPicture(it->pictureId); FrameoutList itemList; createPlaneItemList(planeObject, itemList); for (FrameoutList::iterator listIterator = itemList.begin(); listIterator != itemList.end(); listIterator++) { FrameoutEntry *itemEntry = *listIterator; if (!itemEntry->visible) continue; if (itemEntry->object.isNull()) { // Picture cel data _coordAdjuster->fromScriptToDisplay(itemEntry->y, itemEntry->x); _coordAdjuster->fromScriptToDisplay(itemEntry->picStartY, itemEntry->picStartX); if (!isPictureOutOfView(itemEntry, it->planeRect, it->planeOffsetX, it->planeOffsetY)) drawPicture(itemEntry, it->planeOffsetX, it->planeOffsetY, it->planePictureMirrored); } else { GfxView *view = (itemEntry->viewId != 0xFFFF) ? _cache->getView(itemEntry->viewId) : NULL; int16 dummyX = 0; if (view && view->isSci2Hires()) { view->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x); view->adjustToUpscaledCoordinates(itemEntry->z, dummyX); } else if (getSciVersion() >= SCI_VERSION_2_1) { _coordAdjuster->fromScriptToDisplay(itemEntry->y, itemEntry->x); _coordAdjuster->fromScriptToDisplay(itemEntry->z, dummyX); } // Adjust according to current scroll position itemEntry->x -= it->planeOffsetX; itemEntry->y -= it->planeOffsetY; uint16 useInsetRect = readSelectorValue(_segMan, itemEntry->object, SELECTOR(useInsetRect)); if (useInsetRect) { itemEntry->celRect.top = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inTop)); itemEntry->celRect.left = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inLeft)); itemEntry->celRect.bottom = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inBottom)); itemEntry->celRect.right = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inRight)); if (view && view->isSci2Hires()) { view->adjustToUpscaledCoordinates(itemEntry->celRect.top, itemEntry->celRect.left); view->adjustToUpscaledCoordinates(itemEntry->celRect.bottom, itemEntry->celRect.right); } itemEntry->celRect.translate(itemEntry->x, itemEntry->y); // TODO: maybe we should clip the cels rect with this, i'm not sure // the only currently known usage is game menu of gk1 } else if (view) { // Process global scaling, if needed. // TODO: Seems like SCI32 always processes global scaling for scaled objects // TODO: We can only process symmetrical scaling for now (i.e. same value for scaleX/scaleY) if ((itemEntry->scaleSignal & kScaleSignalDoScaling32) && !(itemEntry->scaleSignal & kScaleSignalDisableGlobalScaling32) && (itemEntry->scaleX == itemEntry->scaleY) && itemEntry->scaleX != 128) applyGlobalScaling(itemEntry, it->planeRect, view->getHeight(itemEntry->loopNo, itemEntry->celNo)); if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) view->getCelRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect); else view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, itemEntry->scaleY, itemEntry->celRect); Common::Rect nsRect = itemEntry->celRect; // Translate back to actual coordinate within scrollable plane nsRect.translate(it->planeOffsetX, it->planeOffsetY); if (g_sci->getGameId() == GID_PHANTASMAGORIA2) { // HACK: Some (?) objects in Phantasmagoria 2 have no NS rect. Skip them for now. // TODO: Remove once we figure out how Phantasmagoria 2 draws objects on screen. if (lookupSelector(_segMan, itemEntry->object, SELECTOR(nsLeft), NULL, NULL) != kSelectorVariable) continue; } if (view && view->isSci2Hires()) { view->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left); view->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right); g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect); } else if (getSciVersion() >= SCI_VERSION_2_1 && _resMan->detectHires()) { _coordAdjuster->fromDisplayToScript(nsRect.top, nsRect.left); _coordAdjuster->fromDisplayToScript(nsRect.bottom, nsRect.right); g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect); } } // Don't attempt to draw sprites that are outside the visible // screen area. An example is the random people walking in // Jackson Square in GK1. if (itemEntry->celRect.bottom < 0 || itemEntry->celRect.top >= _screen->getDisplayHeight() || itemEntry->celRect.right < 0 || itemEntry->celRect.left >= _screen->getDisplayWidth()) continue; Common::Rect clipRect, translatedClipRect; clipRect = itemEntry->celRect; if (view && view->isSci2Hires()) { clipRect.clip(it->upscaledPlaneClipRect); translatedClipRect = clipRect; translatedClipRect.translate(it->upscaledPlaneRect.left, it->upscaledPlaneRect.top); } else { // QFG4 passes invalid rectangles when a battle is starting if (!clipRect.isValidRect()) continue; clipRect.clip(it->planeClipRect); translatedClipRect = clipRect; translatedClipRect.translate(it->planeRect.left, it->planeRect.top); } if (view) { if (!clipRect.isEmpty()) { if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) view->draw(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires()); else view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY); } } // Draw text, if it exists if (lookupSelector(_segMan, itemEntry->object, SELECTOR(text), NULL, NULL) == kSelectorVariable) { g_sci->_gfxText32->drawTextBitmap(itemEntry->x, itemEntry->y, it->planeRect, itemEntry->object); } } } for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) { if (pictureIt->object == planeObject) { delete[] pictureIt->pictureCels; pictureIt->pictureCels = 0; } } } showCurrentScrollText(); _screen->copyToScreen(); g_sci->getEngineState()->_throttleTrigger = true; }
void GfxFrameout::kernelFrameout() { if (g_sci->_robotDecoder->isVideoLoaded()) { bool skipVideo = false; RobotDecoder *videoDecoder = g_sci->_robotDecoder; uint16 x = videoDecoder->getPos().x; uint16 y = videoDecoder->getPos().y; if (videoDecoder->hasDirtyPalette()) videoDecoder->setSystemPalette(); while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { if (videoDecoder->needsUpdate()) { const Graphics::Surface *frame = videoDecoder->decodeNextFrame(); if (frame) { g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); if (videoDecoder->hasDirtyPalette()) videoDecoder->setSystemPalette(); g_system->updateScreen(); } } Common::Event event; while (g_system->getEventManager()->pollEvent(event)) { if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) skipVideo = true; } g_system->delayMillis(10); } return; } _palette->palVaryUpdate(); for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) { reg_t planeObject = it->object; uint16 planeLastPriority = it->lastPriority; // Update priority here, sq6 sets it w/o UpdatePlane uint16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority)); it->lastPriority = planePriority; if (planePriority == 0xffff) { // Plane currently not meant to be shown // If plane was shown before, delete plane rect if (planePriority != planeLastPriority) _paint32->fillRect(it->planeRect, 0); continue; } if (it->planeBack) _paint32->fillRect(it->planeRect, it->planeBack); GuiResourceId planeMainPictureId = it->pictureId; _coordAdjuster->pictureSetDisplayArea(it->planeRect); _palette->drewPicture(planeMainPictureId); FrameoutList itemList; // Copy screen items of the current frame to the list of items to be drawn for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) { reg_t itemPlane = readSelector(_segMan, (*listIterator)->object, SELECTOR(plane)); if (planeObject == itemPlane) { kernelUpdateScreenItem((*listIterator)->object); // TODO: Why is this necessary? itemList.push_back(*listIterator); } } for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) { if (pictureIt->object == planeObject) { GfxPicture *planePicture = pictureIt->picture; // Allocate memory for picture cels pictureIt->pictureCels = new FrameoutEntry[planePicture->getSci32celCount()]; // Add following cels to the itemlist FrameoutEntry *picEntry = pictureIt->pictureCels; int planePictureCels = planePicture->getSci32celCount(); for (int pictureCelNr = 0; pictureCelNr < planePictureCels; pictureCelNr++) { picEntry->celNo = pictureCelNr; picEntry->object = NULL_REG; picEntry->picture = planePicture; picEntry->y = planePicture->getSci32celY(pictureCelNr); picEntry->x = planePicture->getSci32celX(pictureCelNr); picEntry->picStartX = pictureIt->startX; picEntry->priority = planePicture->getSci32celPriority(pictureCelNr); itemList.push_back(picEntry); picEntry++; } } } // Now sort our itemlist Common::sort(itemList.begin(), itemList.end(), sortHelper); // warning("Plane %s", _segMan->getObjectName(planeObject)); for (FrameoutList::iterator listIterator = itemList.begin(); listIterator != itemList.end(); listIterator++) { FrameoutEntry *itemEntry = *listIterator; if (itemEntry->object.isNull()) { // Picture cel data itemEntry->y = ((itemEntry->y * _screen->getHeight()) / scriptsRunningHeight); itemEntry->x = ((itemEntry->x * _screen->getWidth()) / scriptsRunningWidth); itemEntry->picStartX = ((itemEntry->picStartX * _screen->getWidth()) / scriptsRunningWidth); // Out of view int16 pictureCelStartX = itemEntry->picStartX + itemEntry->x; int16 pictureCelEndX = pictureCelStartX + itemEntry->picture->getSci32celWidth(itemEntry->celNo); int16 planeStartX = it->planeOffsetX; int16 planeEndX = planeStartX + it->planeRect.width(); if (pictureCelEndX < planeStartX) continue; if (pictureCelStartX > planeEndX) continue; int16 pictureOffsetX = it->planeOffsetX; int16 pictureX = itemEntry->x; if ((it->planeOffsetX) || (itemEntry->picStartX)) { if (it->planeOffsetX <= itemEntry->picStartX) { pictureX += itemEntry->picStartX - it->planeOffsetX; pictureOffsetX = 0; } else { pictureOffsetX = it->planeOffsetX - itemEntry->picStartX; } } itemEntry->picture->drawSci32Vga(itemEntry->celNo, pictureX, itemEntry->y, pictureOffsetX, it->planePictureMirrored); // warning("picture cel %d %d", itemEntry->celNo, itemEntry->priority); } else if (itemEntry->viewId != 0xFFFF) { GfxView *view = _cache->getView(itemEntry->viewId); // warning("view %s %04x:%04x", _segMan->getObjectName(itemEntry->object), PRINT_REG(itemEntry->object)); if (view->isSci2Hires()) { int16 dummyX = 0; _screen->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x); _screen->adjustToUpscaledCoordinates(itemEntry->z, dummyX); } else if (getSciVersion() == SCI_VERSION_2_1) { itemEntry->y = (itemEntry->y * _screen->getHeight()) / scriptsRunningHeight; itemEntry->x = (itemEntry->x * _screen->getWidth()) / scriptsRunningWidth; itemEntry->z = (itemEntry->z * _screen->getHeight()) / scriptsRunningHeight; } // Adjust according to current scroll position itemEntry->x -= it->planeOffsetX; uint16 useInsetRect = readSelectorValue(_segMan, itemEntry->object, SELECTOR(useInsetRect)); if (useInsetRect) { itemEntry->celRect.top = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inTop)); itemEntry->celRect.left = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inLeft)); itemEntry->celRect.bottom = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inBottom)) + 1; itemEntry->celRect.right = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inRight)) + 1; if (view->isSci2Hires()) { _screen->adjustToUpscaledCoordinates(itemEntry->celRect.top, itemEntry->celRect.left); _screen->adjustToUpscaledCoordinates(itemEntry->celRect.bottom, itemEntry->celRect.right); } itemEntry->celRect.translate(itemEntry->x, itemEntry->y); // TODO: maybe we should clip the cels rect with this, i'm not sure // the only currently known usage is game menu of gk1 } else { if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) view->getCelRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect); else view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, itemEntry->scaleY, itemEntry->celRect); Common::Rect nsRect = itemEntry->celRect; // Translate back to actual coordinate within scrollable plane nsRect.translate(it->planeOffsetX, 0); if (view->isSci2Hires()) { _screen->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left); _screen->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right); } else if (getSciVersion() == SCI_VERSION_2_1) { nsRect.top = (nsRect.top * scriptsRunningHeight) / _screen->getHeight(); nsRect.left = (nsRect.left * scriptsRunningWidth) / _screen->getWidth(); nsRect.bottom = (nsRect.bottom * scriptsRunningHeight) / _screen->getHeight(); nsRect.right = (nsRect.right * scriptsRunningWidth) / _screen->getWidth(); } writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsLeft), nsRect.left); writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsTop), nsRect.top); writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsRight), nsRect.right); writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsBottom), nsRect.bottom); } int16 screenHeight = _screen->getHeight(); int16 screenWidth = _screen->getWidth(); if (view->isSci2Hires()) { screenHeight = _screen->getDisplayHeight(); screenWidth = _screen->getDisplayWidth(); } if (itemEntry->celRect.bottom < 0 || itemEntry->celRect.top >= screenHeight) continue; if (itemEntry->celRect.right < 0 || itemEntry->celRect.left >= screenWidth) continue; Common::Rect clipRect, translatedClipRect; clipRect = itemEntry->celRect; if (view->isSci2Hires()) { clipRect.clip(it->upscaledPlaneClipRect); translatedClipRect = clipRect; translatedClipRect.translate(it->upscaledPlaneRect.left, it->upscaledPlaneRect.top); } else { clipRect.clip(it->planeClipRect); translatedClipRect = clipRect; translatedClipRect.translate(it->planeRect.left, it->planeRect.top); } if (!clipRect.isEmpty()) { if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) view->draw(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires()); else view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY); } } else { // Most likely a text entry // This draws text the "SCI0-SCI11" way. In SCI2, text is prerendered in kCreateTextBitmap // TODO: rewrite this the "SCI2" way (i.e. implement the text buffer to draw inside kCreateTextBitmap) if (lookupSelector(_segMan, itemEntry->object, SELECTOR(text), NULL, NULL) == kSelectorVariable) { reg_t stringObject = readSelector(_segMan, itemEntry->object, SELECTOR(text)); // The object in the text selector of the item can be either a raw string // or a Str object. In the latter case, we need to access the object's data // selector to get the raw string. if (_segMan->isHeapObject(stringObject)) stringObject = readSelector(_segMan, stringObject, SELECTOR(data)); Common::String text = _segMan->getString(stringObject); GfxFont *font = _cache->getFont(readSelectorValue(_segMan, itemEntry->object, SELECTOR(font))); bool dimmed = readSelectorValue(_segMan, itemEntry->object, SELECTOR(dimmed)); uint16 foreColor = readSelectorValue(_segMan, itemEntry->object, SELECTOR(fore)); itemEntry->y = ((itemEntry->y * _screen->getHeight()) / scriptsRunningHeight); itemEntry->x = ((itemEntry->x * _screen->getWidth()) / scriptsRunningWidth); uint16 startX = itemEntry->x + it->planeRect.left; uint16 curY = itemEntry->y + it->planeRect.top; const char *txt = text.c_str(); // HACK. The plane sometimes doesn't contain the correct width. This // hack breaks the dialog options when speaking with Grace, but it's // the best we got up to now. This happens because of the unimplemented // kTextWidth function in SCI32. // TODO: Remove this once kTextWidth has been implemented. uint16 w = it->planeRect.width() >= 20 ? it->planeRect.width() : _screen->getWidth() - 10; int16 charCount; // Upscale the coordinates/width if the fonts are already upscaled if (_screen->fontIsUpscaled()) { startX = startX * _screen->getDisplayWidth() / _screen->getWidth(); curY = curY * _screen->getDisplayHeight() / _screen->getHeight(); w = w * _screen->getDisplayWidth() / _screen->getWidth(); } while (*txt) { charCount = GetLongest(txt, w, font); if (charCount == 0) break; uint16 curX = startX; for (int i = 0; i < charCount; i++) { unsigned char curChar = txt[i]; font->draw(curChar, curY, curX, foreColor, dimmed); curX += font->getCharWidth(curChar); } curY += font->getHeight(); txt += charCount; while (*txt == ' ') txt++; // skip over breaking spaces } } } } for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) { if (pictureIt->object == planeObject) { delete[] pictureIt->pictureCels; pictureIt->pictureCels = 0; } } } _screen->copyToScreen(); g_sci->getEngineState()->_throttleTrigger = true; }