aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/graphics')
-rw-r--r--engines/sci/graphics/animate.cpp2
-rw-r--r--engines/sci/graphics/cursor.cpp10
-rw-r--r--engines/sci/graphics/font.cpp2
-rw-r--r--engines/sci/graphics/frameout.cpp356
-rw-r--r--engines/sci/graphics/frameout.h53
-rw-r--r--engines/sci/graphics/maciconbar.cpp4
-rw-r--r--engines/sci/graphics/menu.cpp8
-rw-r--r--engines/sci/graphics/paint16.cpp10
-rw-r--r--engines/sci/graphics/palette.cpp94
-rw-r--r--engines/sci/graphics/palette.h21
-rw-r--r--engines/sci/graphics/screen.cpp48
-rw-r--r--engines/sci/graphics/screen.h1
-rw-r--r--engines/sci/graphics/text32.cpp74
-rw-r--r--engines/sci/graphics/text32.h6
-rw-r--r--engines/sci/graphics/view.cpp30
15 files changed, 563 insertions, 156 deletions
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp
index 983e697481..ee28c5ca31 100644
--- a/engines/sci/graphics/animate.cpp
+++ b/engines/sci/graphics/animate.cpp
@@ -723,7 +723,7 @@ void GfxAnimate::printAnimateList(Console *con) {
const AnimateList::iterator end = _list.end();
for (it = _list.begin(); it != end; ++it) {
- Script *scr = _s->_segMan->getScriptIfLoaded(it->object.segment);
+ Script *scr = _s->_segMan->getScriptIfLoaded(it->object.getSegment());
int16 scriptNo = scr ? scr->getScriptNumber() : -1;
con->DebugPrintf("%04x:%04x (%s), script %d, view %d (%d, %d), pal %d, "
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index 71f4598afc..ce77cf6ed3 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -148,11 +148,13 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) {
colorMapping[1] = _screen->getColorWhite(); // White is also hardcoded
colorMapping[2] = SCI_CURSOR_SCI0_TRANSPARENCYCOLOR;
colorMapping[3] = _palette->matchColor(170, 170, 170); // Grey
- // Special case for the magnifier cursor in LB1 (bug #3487092).
- // No other SCI0 game has a cursor resource of 1, so this is handled
- // specifically for LB1.
+ // TODO: Figure out if the grey color is hardcoded
+ // HACK for the magnifier cursor in LB1, fixes its color (bug #3487092)
if (g_sci->getGameId() == GID_LAURABOW && resourceId == 1)
colorMapping[3] = _screen->getColorWhite();
+ // HACK for Longbow cursors, fixes the shade of grey they're using (bug #3489101)
+ if (g_sci->getGameId() == GID_LONGBOW)
+ colorMapping[3] = _palette->matchColor(223, 223, 223); // Light Grey
// Seek to actual data
resourceData += 4;
@@ -409,7 +411,7 @@ void GfxCursor::refreshPosition() {
}
}
- CursorMan.replaceCursor((const byte *)_cursorSurface, cursorCelInfo->width, cursorCelInfo->height, cursorHotspot.x, cursorHotspot.y, cursorCelInfo->clearKey);
+ CursorMan.replaceCursor(_cursorSurface, cursorCelInfo->width, cursorCelInfo->height, cursorHotspot.x, cursorHotspot.y, cursorCelInfo->clearKey);
}
}
diff --git a/engines/sci/graphics/font.cpp b/engines/sci/graphics/font.cpp
index fcdd057509..30184cc091 100644
--- a/engines/sci/graphics/font.cpp
+++ b/engines/sci/graphics/font.cpp
@@ -54,7 +54,7 @@ GfxFontFromResource::GfxFontFromResource(ResourceManager *resMan, GfxScreen *scr
}
GfxFontFromResource::~GfxFontFromResource() {
- delete []_chars;
+ delete[] _chars;
_resMan->unlockResource(_resource);
}
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index b12413ab69..968014c032 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -28,9 +28,11 @@
#include "common/system.h"
#include "common/textconsole.h"
#include "engines/engine.h"
+#include "graphics/palette.h"
#include "graphics/surface.h"
#include "sci/sci.h"
+#include "sci/console.h"
#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
@@ -52,12 +54,18 @@ namespace Sci {
// TODO/FIXME: This is all guesswork
+enum SciSpeciaPlanelPictureCodes {
+ kPlaneTranslucent = 0xfffe, // -2
+ kPlanePlainColored = 0xffff // -1
+};
+
GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxCache *cache, GfxScreen *screen, GfxPalette *palette, GfxPaint32 *paint32)
: _segMan(segMan), _resMan(resMan), _cache(cache), _screen(screen), _palette(palette), _paint32(paint32) {
_coordAdjuster = (GfxCoordAdjuster32 *)coordAdjuster;
- _scriptsRunningWidth = 320;
- _scriptsRunningHeight = 200;
+ _curScrollText = -1;
+ _showScrollText = false;
+ _maxScrollTexts = 0;
}
GfxFrameout::~GfxFrameout() {
@@ -68,32 +76,88 @@ void GfxFrameout::clear() {
deletePlaneItems(NULL_REG);
_planes.clear();
deletePlanePictures(NULL_REG);
+ clearScrollTexts();
+}
+
+void GfxFrameout::clearScrollTexts() {
+ _scrollTexts.clear();
+ _curScrollText = -1;
+}
+
+void GfxFrameout::addScrollTextEntry(Common::String &text, reg_t kWindow, uint16 x, uint16 y, bool replace) {
+ //reg_t bitmapHandle = g_sci->_gfxText32->createScrollTextBitmap(text, kWindow);
+ // HACK: We set the container dimensions manually
+ reg_t bitmapHandle = g_sci->_gfxText32->createScrollTextBitmap(text, kWindow, 480, 70);
+ ScrollTextEntry textEntry;
+ textEntry.bitmapHandle = bitmapHandle;
+ textEntry.kWindow = kWindow;
+ textEntry.x = x;
+ textEntry.y = y;
+ if (!replace || _scrollTexts.size() == 0) {
+ if (_scrollTexts.size() > _maxScrollTexts) {
+ _scrollTexts.remove_at(0);
+ _curScrollText--;
+ }
+ _scrollTexts.push_back(textEntry);
+ _curScrollText++;
+ } else {
+ _scrollTexts.pop_back();
+ _scrollTexts.push_back(textEntry);
+ }
+}
+
+void GfxFrameout::showCurrentScrollText() {
+ if (!_showScrollText || _curScrollText < 0)
+ return;
+
+ uint16 size = (uint16)_scrollTexts.size();
+ if (size > 0) {
+ assert(_curScrollText < size);
+ ScrollTextEntry textEntry = _scrollTexts[_curScrollText];
+ g_sci->_gfxText32->drawScrollTextBitmap(textEntry.kWindow, textEntry.bitmapHandle, textEntry.x, textEntry.y);
+ }
}
+extern void showScummVMDialog(const Common::String &message);
+
void GfxFrameout::kernelAddPlane(reg_t object) {
PlaneEntry newPlane;
if (_planes.empty()) {
// There has to be another way for sierra sci to do this or maybe script resolution is compiled into
// interpreter (TODO)
- uint16 tmpRunningWidth = readSelectorValue(_segMan, object, SELECTOR(resX));
- uint16 tmpRunningHeight = readSelectorValue(_segMan, object, SELECTOR(resY));
+ uint16 scriptWidth = readSelectorValue(_segMan, object, SELECTOR(resX));
+ uint16 scriptHeight = readSelectorValue(_segMan, object, SELECTOR(resY));
- // The above can be 0 in SCI3 (e.g. Phantasmagoria 2)
- if (tmpRunningWidth > 0 && tmpRunningHeight > 0) {
- _scriptsRunningWidth = tmpRunningWidth;
- _scriptsRunningHeight = tmpRunningHeight;
+ // Phantasmagoria 2 doesn't specify a script width/height
+ if (g_sci->getGameId() == GID_PHANTASMAGORIA2) {
+ scriptWidth = 640;
+ scriptHeight = 480;
}
- _coordAdjuster->setScriptsResolution(_scriptsRunningWidth, _scriptsRunningHeight);
+ assert(scriptWidth > 0 && scriptHeight > 0);
+ _coordAdjuster->setScriptsResolution(scriptWidth, scriptHeight);
+ }
+
+ // Import of QfG character files dialog is shown in QFG4.
+ // Display additional popup information before letting user use it.
+ // For the SCI0-SCI1.1 version of this, check kDrawControl().
+ if (g_sci->inQfGImportRoom() && !strcmp(_segMan->getObjectName(object), "DSPlane")) {
+ showScummVMDialog("Characters saved inside ScummVM are shown "
+ "automatically. Character files saved in the original "
+ "interpreter need to be put inside ScummVM's saved games "
+ "directory and a prefix needs to be added depending on which "
+ "game it was saved in: 'qfg1-' for Quest for Glory 1, 'qfg2-' "
+ "for Quest for Glory 2, 'qfg3-' for Quest for Glory 3. "
+ "Example: 'qfg2-thief.sav'.");
}
newPlane.object = object;
newPlane.priority = readSelectorValue(_segMan, object, SELECTOR(priority));
- newPlane.lastPriority = 0xFFFF; // hidden
+ newPlane.lastPriority = -1; // hidden
newPlane.planeOffsetX = 0;
newPlane.planeOffsetY = 0;
- newPlane.pictureId = 0xFFFF;
+ newPlane.pictureId = kPlanePlainColored;
newPlane.planePictureMirrored = false;
newPlane.planeBack = 0;
_planes.push_back(newPlane);
@@ -111,7 +175,8 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) {
if (lastPictureId != it->pictureId) {
// picture got changed, load new picture
deletePlanePictures(object);
- if ((it->pictureId != 0xFFFF) && (it->pictureId != 0xFFFE)) {
+ // Draw the plane's picture if it's not a translucent/plane colored frame
+ if ((it->pictureId != kPlanePlainColored) && (it->pictureId != kPlaneTranslucent)) {
// SQ6 gives us a bad picture number for the control menu
if (_resMan->testResource(ResourceId(kResourceTypePic, it->pictureId)))
addPlanePicture(object, it->pictureId, 0);
@@ -122,11 +187,8 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) {
it->planeRect.bottom = readSelectorValue(_segMan, object, SELECTOR(bottom));
it->planeRect.right = readSelectorValue(_segMan, object, SELECTOR(right));
- Common::Rect screenRect(_screen->getWidth(), _screen->getHeight());
- it->planeRect.top = (it->planeRect.top * screenRect.height()) / _scriptsRunningHeight;
- it->planeRect.left = (it->planeRect.left * screenRect.width()) / _scriptsRunningWidth;
- it->planeRect.bottom = (it->planeRect.bottom * screenRect.height()) / _scriptsRunningHeight;
- it->planeRect.right = (it->planeRect.right * screenRect.width()) / _scriptsRunningWidth;
+ _coordAdjuster->fromScriptToDisplay(it->planeRect.top, it->planeRect.left);
+ _coordAdjuster->fromScriptToDisplay(it->planeRect.bottom, it->planeRect.right);
// We get negative left in kq7 in scrolling rooms
if (it->planeRect.left < 0) {
@@ -191,11 +253,9 @@ void GfxFrameout::kernelDeletePlane(reg_t object) {
planeRect.bottom = readSelectorValue(_segMan, object, SELECTOR(bottom));
planeRect.right = readSelectorValue(_segMan, object, SELECTOR(right));
- Common::Rect screenRect(_screen->getWidth(), _screen->getHeight());
- planeRect.top = (planeRect.top * screenRect.height()) / _scriptsRunningHeight;
- planeRect.left = (planeRect.left * screenRect.width()) / _scriptsRunningWidth;
- planeRect.bottom = (planeRect.bottom * screenRect.height()) / _scriptsRunningHeight;
- planeRect.right = (planeRect.right * screenRect.width()) / _scriptsRunningWidth;
+ _coordAdjuster->fromScriptToDisplay(planeRect.top, planeRect.left);
+ _coordAdjuster->fromScriptToDisplay(planeRect.bottom, planeRect.right);
+
// Blackout removed plane rect
_paint32->fillRect(planeRect, 0);
return;
@@ -204,6 +264,9 @@ void GfxFrameout::kernelDeletePlane(reg_t object) {
}
void GfxFrameout::addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX, uint16 startY) {
+ if (pictureId == kPlanePlainColored || pictureId == kPlaneTranslucent) // sanity check
+ return;
+
PlanePictureEntry newPicture;
newPicture.object = object;
newPicture.pictureId = pictureId;
@@ -228,15 +291,76 @@ void GfxFrameout::deletePlanePictures(reg_t object) {
}
}
+// Provides the same functionality as kGraph(DrawLine)
+reg_t GfxFrameout::addPlaneLine(reg_t object, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control) {
+ for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
+ if (it->object == object) {
+ PlaneLineEntry line;
+ line.hunkId = _segMan->allocateHunkEntry("PlaneLine()", 1); // we basically use this for a unique ID
+ line.startPoint = startPoint;
+ line.endPoint = endPoint;
+ line.color = color;
+ line.priority = priority;
+ line.control = control;
+ it->lines.push_back(line);
+ return line.hunkId;
+ }
+ }
+
+ return NULL_REG;
+}
+
+void GfxFrameout::updatePlaneLine(reg_t object, reg_t hunkId, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control) {
+ // Check if we're asked to update a line that was never added
+ if (hunkId.isNull())
+ return;
+
+ for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
+ if (it->object == object) {
+ for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) {
+ if (it2->hunkId == hunkId) {
+ it2->startPoint = startPoint;
+ it2->endPoint = endPoint;
+ it2->color = color;
+ it2->priority = priority;
+ it2->control = control;
+ return;
+ }
+ }
+ }
+ }
+}
+
+void GfxFrameout::deletePlaneLine(reg_t object, reg_t hunkId) {
+ // Check if we're asked to delete a line that was never added (happens during the intro of LSL6)
+ if (hunkId.isNull())
+ return;
+
+ for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
+ if (it->object == object) {
+ for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) {
+ if (it2->hunkId == hunkId) {
+ _segMan->freeHunkEntry(hunkId);
+ it2 = it->lines.erase(it2);
+ return;
+ }
+ }
+ }
+ }
+}
+
void GfxFrameout::kernelAddScreenItem(reg_t object) {
// Ignore invalid items
- if (!_segMan->isObject(object))
+ if (!_segMan->isObject(object)) {
+ warning("kernelAddScreenItem: Attempt to add an invalid object (%04x:%04x)", PRINT_REG(object));
return;
+ }
FrameoutEntry *itemEntry = new FrameoutEntry();
memset(itemEntry, 0, sizeof(FrameoutEntry));
itemEntry->object = object;
itemEntry->givenOrderNr = _screenItems.size();
+ itemEntry->visible = true;
_screenItems.push_back(itemEntry);
kernelUpdateScreenItem(object);
@@ -244,8 +368,10 @@ void GfxFrameout::kernelAddScreenItem(reg_t object) {
void GfxFrameout::kernelUpdateScreenItem(reg_t object) {
// Ignore invalid items
- if (!_segMan->isObject(object))
+ if (!_segMan->isObject(object)) {
+ warning("kernelUpdateScreenItem: Attempt to update an invalid object (%04x:%04x)", PRINT_REG(object));
return;
+ }
FrameoutEntry *itemEntry = findScreenItem(object);
if (!itemEntry) {
@@ -266,14 +392,18 @@ void GfxFrameout::kernelUpdateScreenItem(reg_t object) {
itemEntry->signal = readSelectorValue(_segMan, object, SELECTOR(signal));
itemEntry->scaleX = readSelectorValue(_segMan, object, SELECTOR(scaleX));
itemEntry->scaleY = readSelectorValue(_segMan, object, SELECTOR(scaleY));
+ itemEntry->visible = true;
+
+ // Check if the entry can be hidden
+ if (lookupSelector(_segMan, object, SELECTOR(visible), NULL, NULL) != kSelectorNone)
+ itemEntry->visible = readSelectorValue(_segMan, object, SELECTOR(visible));
}
void GfxFrameout::kernelDeleteScreenItem(reg_t object) {
FrameoutEntry *itemEntry = findScreenItem(object);
- if (!itemEntry) {
- warning("kernelDeleteScreenItem: invalid object %04x:%04x", PRINT_REG(object));
+ // If the item could not be found, it may already have been deleted
+ if (!itemEntry)
return;
- }
_screenItems.remove(itemEntry);
delete itemEntry;
@@ -330,15 +460,10 @@ bool sortHelper(const FrameoutEntry* entry1, const FrameoutEntry* entry2) {
}
bool planeSortHelper(const PlaneEntry &entry1, const PlaneEntry &entry2) {
-// SegManager *segMan = g_sci->getEngineState()->_segMan;
-
-// uint16 plane1Priority = readSelectorValue(segMan, entry1, SELECTOR(priority));
-// uint16 plane2Priority = readSelectorValue(segMan, entry2, SELECTOR(priority));
-
- if (entry1.priority == 0xffff)
+ if (entry1.priority < 0)
return true;
- if (entry2.priority == 0xffff)
+ if (entry2.priority < 0)
return false;
return entry1.priority < entry2.priority;
@@ -357,23 +482,6 @@ void GfxFrameout::sortPlanes() {
Common::sort(_planes.begin(), _planes.end(), planeSortHelper);
}
-int16 GfxFrameout::upscaleHorizontalCoordinate(int16 coordinate) {
- return ((coordinate * _screen->getWidth()) / _scriptsRunningWidth);
-}
-
-int16 GfxFrameout::upscaleVerticalCoordinate(int16 coordinate) {
- return ((coordinate * _screen->getHeight()) / _scriptsRunningHeight);
-}
-
-Common::Rect GfxFrameout::upscaleRect(Common::Rect &rect) {
- rect.top = (rect.top * _scriptsRunningHeight) / _screen->getHeight();
- rect.left = (rect.left * _scriptsRunningWidth) / _screen->getWidth();
- rect.bottom = (rect.bottom * _scriptsRunningHeight) / _screen->getHeight();
- rect.right = (rect.right * _scriptsRunningWidth) / _screen->getWidth();
-
- return rect;
-}
-
void GfxFrameout::showVideo() {
bool skipVideo = false;
RobotDecoder *videoDecoder = g_sci->_robotDecoder;
@@ -381,16 +489,16 @@ void GfxFrameout::showVideo() {
uint16 y = videoDecoder->getPos().y;
if (videoDecoder->hasDirtyPalette())
- videoDecoder->setSystemPalette();
+ g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256);
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);
+ g_system->copyRectToScreen(frame->pixels, frame->pitch, x, y, frame->w, frame->h);
if (videoDecoder->hasDirtyPalette())
- videoDecoder->setSystemPalette();
+ g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256);
g_system->updateScreen();
}
@@ -433,6 +541,7 @@ void GfxFrameout::createPlaneItemList(reg_t planeObject, FrameoutList &itemList)
picEntry->x = planePicture->getSci32celX(pictureCelNr);
picEntry->picStartX = pictureIt->startX;
picEntry->picStartY = pictureIt->startY;
+ picEntry->visible = true;
picEntry->priority = planePicture->getSci32celPriority(pictureCelNr);
@@ -507,13 +616,26 @@ void GfxFrameout::kernelFrameout() {
for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) {
reg_t planeObject = it->object;
- uint16 planeLastPriority = it->lastPriority;
+
+ // 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
- uint16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority));
+ int16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority));
it->lastPriority = planePriority;
- if (planePriority == 0xffff) { // Plane currently not meant to be shown
+ 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);
@@ -523,44 +645,40 @@ void GfxFrameout::kernelFrameout() {
// 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 == 0xffff) // FIXME: This is what SSCI does, and fixes the intro of LSL7, but breaks the dialogs in GK1 (adds black boxes)
- if (it->planeBack)
+ //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);
- GuiResourceId planeMainPictureId = it->pictureId;
-
_coordAdjuster->pictureSetDisplayArea(it->planeRect);
- _palette->drewPicture(planeMainPictureId);
+ _palette->drewPicture(it->pictureId);
FrameoutList itemList;
createPlaneItemList(planeObject, itemList);
-// warning("Plane %s", _segMan->getObjectName(planeObject));
-
for (FrameoutList::iterator listIterator = itemList.begin(); listIterator != itemList.end(); listIterator++) {
FrameoutEntry *itemEntry = *listIterator;
+ if (!itemEntry->visible)
+ continue;
+
if (itemEntry->object.isNull()) {
// Picture cel data
- itemEntry->x = upscaleHorizontalCoordinate(itemEntry->x);
- itemEntry->y = upscaleVerticalCoordinate(itemEntry->y);
- itemEntry->picStartX = upscaleHorizontalCoordinate(itemEntry->picStartX);
- itemEntry->picStartY = upscaleVerticalCoordinate(itemEntry->picStartY);
+ _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()) {
- int16 dummyX = 0;
view->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x);
view->adjustToUpscaledCoordinates(itemEntry->z, dummyX);
- } else if (getSciVersion() == SCI_VERSION_2_1) {
- itemEntry->x = upscaleHorizontalCoordinate(itemEntry->x);
- itemEntry->y = upscaleVerticalCoordinate(itemEntry->y);
- itemEntry->z = upscaleVerticalCoordinate(itemEntry->z);
+ } else if (getSciVersion() >= SCI_VERSION_2_1) {
+ _coordAdjuster->fromScriptToDisplay(itemEntry->y, itemEntry->x);
+ _coordAdjuster->fromScriptToDisplay(itemEntry->z, dummyX);
}
// Adjust according to current scroll position
@@ -581,13 +699,13 @@ void GfxFrameout::kernelFrameout() {
// 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) {
- 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);
+ 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
@@ -596,8 +714,9 @@ void GfxFrameout::kernelFrameout() {
if (view && view->isSci2Hires()) {
view->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left);
view->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right);
- } else if (getSciVersion() == SCI_VERSION_2_1) {
- nsRect = upscaleRect(nsRect);
+ } else if (getSciVersion() >= SCI_VERSION_2_1) {
+ _coordAdjuster->fromDisplayToScript(nsRect.top, nsRect.left);
+ _coordAdjuster->fromDisplayToScript(nsRect.bottom, nsRect.right);
}
if (g_sci->getGameId() == GID_PHANTASMAGORIA2) {
@@ -610,17 +729,11 @@ void GfxFrameout::kernelFrameout() {
g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect);
}
- int16 screenHeight = _screen->getHeight();
- int16 screenWidth = _screen->getWidth();
- if (view && 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)
+ // 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;
@@ -631,6 +744,9 @@ void GfxFrameout::kernelFrameout() {
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);
@@ -662,9 +778,61 @@ void GfxFrameout::kernelFrameout() {
}
}
+ showCurrentScrollText();
+
_screen->copyToScreen();
g_sci->getEngineState()->_throttleTrigger = true;
}
+void GfxFrameout::printPlaneList(Console *con) {
+ for (PlaneList::const_iterator it = _planes.begin(); it != _planes.end(); ++it) {
+ PlaneEntry p = *it;
+ Common::String curPlaneName = _segMan->getObjectName(p.object);
+ Common::Rect r = p.upscaledPlaneRect;
+ Common::Rect cr = p.upscaledPlaneClipRect;
+
+ con->DebugPrintf("%04x:%04x (%s): prio %d, lastprio %d, offsetX %d, offsetY %d, pic %d, mirror %d, back %d\n",
+ PRINT_REG(p.object), curPlaneName.c_str(),
+ (int16)p.priority, (int16)p.lastPriority,
+ p.planeOffsetX, p.planeOffsetY, p.pictureId,
+ p.planePictureMirrored, p.planeBack);
+ con->DebugPrintf(" rect: (%d, %d, %d, %d), clip rect: (%d, %d, %d, %d)\n",
+ r.left, r.top, r.right, r.bottom,
+ cr.left, cr.top, cr.right, cr.bottom);
+
+ if (p.pictureId != 0xffff && p.pictureId != 0xfffe) {
+ con->DebugPrintf("Pictures:\n");
+
+ for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) {
+ if (pictureIt->object == p.object) {
+ con->DebugPrintf(" Picture %d: x %d, y %d\n", pictureIt->pictureId, pictureIt->startX, pictureIt->startY);
+ }
+ }
+ }
+ }
+}
+
+void GfxFrameout::printPlaneItemList(Console *con, reg_t planeObject) {
+ for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
+ FrameoutEntry *e = *listIterator;
+ reg_t itemPlane = readSelector(_segMan, e->object, SELECTOR(plane));
+
+ if (planeObject == itemPlane) {
+ Common::String curItemName = _segMan->getObjectName(e->object);
+ Common::Rect icr = e->celRect;
+ GuiResourceId picId = e->picture ? e->picture->getResourceId() : 0;
+
+ con->DebugPrintf("%d: %04x:%04x (%s), view %d, loop %d, cel %d, x %d, y %d, z %d, "
+ "signal %d, scale signal %d, scaleX %d, scaleY %d, rect (%d, %d, %d, %d), "
+ "pic %d, picX %d, picY %d, visible %d\n",
+ e->givenOrderNr, PRINT_REG(e->object), curItemName.c_str(),
+ e->viewId, e->loopNo, e->celNo, e->x, e->y, e->z,
+ e->signal, e->scaleSignal, e->scaleX, e->scaleY,
+ icr.left, icr.top, icr.right, icr.bottom,
+ picId, e->picStartX, e->picStartY, e->visible);
+ }
+ }
+}
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index 8c3cc261d5..5fd2824224 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -27,10 +27,21 @@ namespace Sci {
class GfxPicture;
+struct PlaneLineEntry {
+ reg_t hunkId;
+ Common::Point startPoint;
+ Common::Point endPoint;
+ byte color;
+ byte priority;
+ byte control;
+};
+
+typedef Common::List<PlaneLineEntry> PlaneLineList;
+
struct PlaneEntry {
reg_t object;
- uint16 priority;
- uint16 lastPriority;
+ int16 priority;
+ int16 lastPriority;
int16 planeOffsetX;
int16 planeOffsetY;
GuiResourceId pictureId;
@@ -40,6 +51,7 @@ struct PlaneEntry {
Common::Rect upscaledPlaneClipRect;
bool planePictureMirrored;
byte planeBack;
+ PlaneLineList lines;
};
typedef Common::List<PlaneEntry> PlaneList;
@@ -60,6 +72,7 @@ struct FrameoutEntry {
GfxPicture *picture;
int16 picStartX;
int16 picStartY;
+ bool visible;
};
typedef Common::List<FrameoutEntry *> FrameoutList;
@@ -75,6 +88,15 @@ struct PlanePictureEntry {
typedef Common::List<PlanePictureEntry> PlanePictureList;
+struct ScrollTextEntry {
+ reg_t bitmapHandle;
+ reg_t kWindow;
+ uint16 x;
+ uint16 y;
+};
+
+typedef Common::Array<ScrollTextEntry> ScrollTextList;
+
class GfxCache;
class GfxCoordAdjuster32;
class GfxPaint32;
@@ -102,16 +124,30 @@ public:
void addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX, uint16 startY = 0);
void deletePlanePictures(reg_t object);
+ reg_t addPlaneLine(reg_t object, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control);
+ void updatePlaneLine(reg_t object, reg_t hunkId, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control);
+ void deletePlaneLine(reg_t object, reg_t hunkId);
void clear();
+ // Scroll text functions
+ void addScrollTextEntry(Common::String &text, reg_t kWindow, uint16 x, uint16 y, bool replace);
+ void showCurrentScrollText();
+ void initScrollText(uint16 maxItems) { _maxScrollTexts = maxItems; }
+ void clearScrollTexts();
+ void firstScrollText() { if (_scrollTexts.size() > 0) _curScrollText = 0; }
+ void lastScrollText() { if (_scrollTexts.size() > 0) _curScrollText = _scrollTexts.size() - 1; }
+ void prevScrollText() { if (_curScrollText > 0) _curScrollText--; }
+ void nextScrollText() { if (_curScrollText + 1 < (uint16)_scrollTexts.size()) _curScrollText++; }
+ void toggleScrollText(bool show) { _showScrollText = show; }
+
+ void printPlaneList(Console *con);
+ void printPlaneItemList(Console *con, reg_t planeObject);
+
private:
void showVideo();
void createPlaneItemList(reg_t planeObject, FrameoutList &itemList);
bool isPictureOutOfView(FrameoutEntry *itemEntry, Common::Rect planeRect, int16 planeOffsetX, int16 planeOffsetY);
void drawPicture(FrameoutEntry *itemEntry, int16 planeOffsetX, int16 planeOffsetY, bool planePictureMirrored);
- int16 upscaleHorizontalCoordinate(int16 coordinate);
- int16 upscaleVerticalCoordinate(int16 coordinate);
- Common::Rect upscaleRect(Common::Rect &rect);
SegManager *_segMan;
ResourceManager *_resMan;
@@ -124,11 +160,12 @@ private:
FrameoutList _screenItems;
PlaneList _planes;
PlanePictureList _planePictures;
+ ScrollTextList _scrollTexts;
+ int16 _curScrollText;
+ bool _showScrollText;
+ uint16 _maxScrollTexts;
void sortPlanes();
-
- uint16 _scriptsRunningWidth;
- uint16 _scriptsRunningHeight;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp
index 7ecba5a24d..dfb50b0edb 100644
--- a/engines/sci/graphics/maciconbar.cpp
+++ b/engines/sci/graphics/maciconbar.cpp
@@ -129,7 +129,7 @@ void GfxMacIconBar::drawIcon(uint16 iconIndex, bool selected) {
void GfxMacIconBar::drawEnabledImage(Graphics::Surface *surface, const Common::Rect &rect) {
if (surface)
- g_system->copyRectToScreen((byte *)surface->pixels, surface->pitch, rect.left, rect.top, rect.width(), rect.height());
+ g_system->copyRectToScreen(surface->pixels, surface->pitch, rect.left, rect.top, rect.width(), rect.height());
}
void GfxMacIconBar::drawDisabledImage(Graphics::Surface *surface, const Common::Rect &rect) {
@@ -153,7 +153,7 @@ void GfxMacIconBar::drawDisabledImage(Graphics::Surface *surface, const Common::
*((byte *)newSurf.getBasePtr(j, i)) = 0;
}
- g_system->copyRectToScreen((byte *)newSurf.pixels, newSurf.pitch, rect.left, rect.top, rect.width(), rect.height());
+ g_system->copyRectToScreen(newSurf.pixels, newSurf.pitch, rect.left, rect.top, rect.width(), rect.height());
newSurf.free();
}
diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp
index 47f34cf99d..bfecc296a2 100644
--- a/engines/sci/graphics/menu.cpp
+++ b/engines/sci/graphics/menu.cpp
@@ -219,7 +219,7 @@ void GfxMenu::kernelAddEntry(Common::String title, Common::String content, reg_t
}
}
itemEntry->textVmPtr = contentVmPtr;
- itemEntry->textVmPtr.offset += beginPos;
+ itemEntry->textVmPtr.incOffset(beginPos);
if (rightAlignedPos) {
rightAlignedPos++;
@@ -297,13 +297,13 @@ void GfxMenu::kernelSetAttribute(uint16 menuId, uint16 itemId, uint16 attributeI
// We assume here that no script ever creates a separatorLine dynamically
break;
case SCI_MENU_ATTRIBUTE_KEYPRESS:
- itemEntry->keyPress = tolower(value.offset);
+ itemEntry->keyPress = tolower(value.getOffset());
itemEntry->keyModifier = 0;
// TODO: Find out how modifier is handled
- debug("setAttr keypress %X %X", value.segment, value.offset);
+ debug("setAttr keypress %X %X", value.getSegment(), value.getOffset());
break;
case SCI_MENU_ATTRIBUTE_TAG:
- itemEntry->tag = value.offset;
+ itemEntry->tag = value.getOffset();
break;
default:
// Happens when loading a game in LSL3 - attribute 1A
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index c951f3349d..89f3625e2c 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -491,10 +491,10 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
// processing codes in argv
while (argc > 0) {
displayArg = argv[0];
- if (displayArg.segment)
- displayArg.offset = 0xFFFF;
+ if (displayArg.getSegment())
+ displayArg.setOffset(0xFFFF);
argc--; argv++;
- switch (displayArg.offset) {
+ switch (displayArg.getOffset()) {
case SCI_DISPLAY_MOVEPEN:
_ports->moveTo(argv[0].toUint16(), argv[1].toUint16());
argc -= 2; argv += 2;
@@ -547,9 +547,9 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
if (!(g_sci->getGameId() == GID_LONGBOW && g_sci->isDemo()) &&
!(g_sci->getGameId() == GID_QFG1 && g_sci->isDemo()) &&
!(g_sci->getGameId() == GID_PQ2))
- error("Unknown kDisplay argument %d", displayArg.offset);
+ error("Unknown kDisplay argument %d", displayArg.getOffset());
- if (displayArg.offset == SCI_DISPLAY_DUMMY2) {
+ if (displayArg.getOffset() == SCI_DISPLAY_DUMMY2) {
if (!argc)
error("No parameter left for kDisplay(115)");
argc--; argv++;
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 47d1647c6c..53d69cdcca 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -100,6 +100,9 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen)
default:
error("GfxPalette: Unknown view type");
}
+
+ _remapOn = false;
+ resetRemapping();
}
GfxPalette::~GfxPalette() {
@@ -140,8 +143,9 @@ void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut)
memset(paletteOut, 0, sizeof(Palette));
// Setup 1:1 mapping
- for (colorNo = 0; colorNo < 256; colorNo++)
+ for (colorNo = 0; colorNo < 256; colorNo++) {
paletteOut->mapping[colorNo] = colorNo;
+ }
if (bytesLeft < 37) {
// This happens when loading palette of picture 0 in sq5 - the resource is broken and doesn't contain a full
@@ -329,6 +333,79 @@ void GfxPalette::set(Palette *newPalette, bool force, bool forceRealMerge) {
}
}
+byte GfxPalette::remapColor(byte remappedColor, byte screenColor) {
+ assert(_remapOn);
+ if (_remappingType[remappedColor] == kRemappingByRange)
+ return _remappingByRange[screenColor];
+ else if (_remappingType[remappedColor] == kRemappingByPercent)
+ return _remappingByPercent[screenColor];
+ else
+ error("remapColor(): Color %d isn't remapped", remappedColor);
+
+ return 0; // should never reach here
+}
+
+void GfxPalette::resetRemapping() {
+ _remapOn = false;
+ _remappingPercentToSet = 0;
+
+ for (int i = 0; i < 256; i++) {
+ _remappingType[i] = kRemappingNone;
+ _remappingByPercent[i] = i;
+ _remappingByRange[i] = i;
+ }
+}
+
+void GfxPalette::setRemappingPercent(byte color, byte percent) {
+ _remapOn = true;
+
+ // We need to defer the setup of the remapping table every time the screen
+ // palette is changed, so that kernelFindColor() can find the correct
+ // colors. Set it once here, in case the palette stays the same and update
+ // it on each palette change by copySysPaletteToScreen().
+ _remappingPercentToSet = percent;
+
+ for (int i = 0; i < 256; i++) {
+ byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100;
+ byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100;
+ byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100;
+ _remappingByPercent[i] = kernelFindColor(r, g, b);
+ }
+
+ _remappingType[color] = kRemappingByPercent;
+}
+
+void GfxPalette::setRemappingPercentGray(byte color, byte percent) {
+ _remapOn = true;
+
+ // We need to defer the setup of the remapping table every time the screen
+ // palette is changed, so that kernelFindColor() can find the correct
+ // colors. Set it once here, in case the palette stays the same and update
+ // it on each palette change by copySysPaletteToScreen().
+ _remappingPercentToSet = percent;
+
+ // Note: This is not what the original does, but the results are the same visually
+ for (int i = 0; i < 256; i++) {
+ byte rComponent = _sysPalette.colors[i].r * _remappingPercentToSet * 0.30 / 100;
+ byte gComponent = _sysPalette.colors[i].g * _remappingPercentToSet * 0.59 / 100;
+ byte bComponent = _sysPalette.colors[i].b * _remappingPercentToSet * 0.11 / 100;
+ byte luminosity = rComponent + gComponent + bComponent;
+ _remappingByPercent[i] = kernelFindColor(luminosity, luminosity, luminosity);
+ }
+
+ _remappingType[color] = kRemappingByPercent;
+}
+
+void GfxPalette::setRemappingRange(byte color, byte from, byte to, byte base) {
+ _remapOn = true;
+
+ for (int i = from; i <= to; i++) {
+ _remappingByRange[i] = i + base;
+ }
+
+ _remappingType[color] = kRemappingByRange;
+}
+
bool GfxPalette::insert(Palette *newPalette, Palette *destPalette) {
bool paletteChanged = false;
@@ -491,6 +568,16 @@ void GfxPalette::copySysPaletteToScreen() {
}
}
+ // Check if we need to reset remapping by percent with the new colors.
+ if (_remappingPercentToSet) {
+ for (int i = 0; i < 256; i++) {
+ byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100;
+ byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100;
+ byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100;
+ _remappingByPercent[i] = kernelFindColor(r, g, b);
+ }
+ }
+
g_system->getPaletteManager()->setPalette(bpal, 0, 256);
}
@@ -698,7 +785,7 @@ void GfxPalette::palVaryInit() {
}
bool GfxPalette::palVaryLoadTargetPalette(GuiResourceId resourceId) {
- _palVaryResourceId = resourceId;
+ _palVaryResourceId = (resourceId != 65535) ? resourceId : -1;
Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
if (palResource) {
// Load and initialize destination palette
@@ -999,8 +1086,9 @@ bool GfxPalette::loadClut(uint16 clutId) {
memset(&pal, 0, sizeof(Palette));
// Setup 1:1 mapping
- for (int i = 0; i < 256; i++)
+ for (int i = 0; i < 256; i++) {
pal.mapping[i] = i;
+ }
// Now load in the palette
for (int i = 1; i <= 236; i++) {
diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h
index a9ea1c32de..e974781d49 100644
--- a/engines/sci/graphics/palette.h
+++ b/engines/sci/graphics/palette.h
@@ -31,6 +31,12 @@ namespace Sci {
class ResourceManager;
class GfxScreen;
+enum ColorRemappingType {
+ kRemappingNone = 0,
+ kRemappingByRange = 1,
+ kRemappingByPercent = 2
+};
+
/**
* Palette class, handles palette operations like changing intensity, setting up the palette, merging different palettes
*/
@@ -53,6 +59,15 @@ public:
void getSys(Palette *pal);
uint16 getTotalColorCount() const { return _totalScreenColors; }
+ void resetRemapping();
+ void setRemappingPercent(byte color, byte percent);
+ void setRemappingPercentGray(byte color, byte percent);
+ void setRemappingRange(byte color, byte from, byte to, byte base);
+ bool isRemapped(byte color) const {
+ return _remapOn && (_remappingType[color] != kRemappingNone);
+ }
+ byte remapColor(byte remappedColor, byte screenColor);
+
void setOnScreen();
void copySysPaletteToScreen();
@@ -123,6 +138,12 @@ private:
int _palVarySignal;
uint16 _totalScreenColors;
+ bool _remapOn;
+ ColorRemappingType _remappingType[256];
+ byte _remappingByPercent[256];
+ byte _remappingByRange[256];
+ uint16 _remappingPercentToSet;
+
void loadMacIconBarPalette();
byte *_macClut;
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 4020518b72..246b6bfff9 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -53,23 +53,35 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
#ifdef ENABLE_SCI32
// GK1 Mac uses a 640x480 resolution too
- if (g_sci->getGameId() == GID_GK1 && g_sci->getPlatform() == Common::kPlatformMacintosh)
- _upscaledHires = GFX_SCREEN_UPSCALED_640x480;
+ if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
+ if (g_sci->getGameId() == GID_GK1)
+ _upscaledHires = GFX_SCREEN_UPSCALED_640x480;
+ }
#endif
if (_resMan->detectHires()) {
_width = 640;
+ _pitch = 640;
_height = 480;
} else {
_width = 320;
+ _pitch = 320;
_height = getLowResScreenHeight();
}
+#ifdef ENABLE_SCI32
+ // Phantasmagoria 1 sets a window area of 630x450
+ if (g_sci->getGameId() == GID_PHANTASMAGORIA) {
+ _width = 630;
+ _height = 450;
+ }
+#endif
+
// Japanese versions of games use hi-res font on upscaled version of the game.
if ((g_sci->getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1))
_upscaledHires = GFX_SCREEN_UPSCALED_640x400;
- _pixels = _width * _height;
+ _pixels = _pitch * _height;
switch (_upscaledHires) {
case GFX_SCREEN_UPSCALED_640x400:
@@ -91,7 +103,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
_upscaledMapping[i] = (i * 12) / 5;
break;
default:
- _displayWidth = _width;
+ _displayWidth = _pitch;
_displayHeight = _height;
memset(&_upscaledMapping, 0, sizeof(_upscaledMapping) );
break;
@@ -207,7 +219,7 @@ byte GfxScreen::getDrawingMask(byte color, byte prio, byte control) {
}
void GfxScreen::putPixel(int x, int y, byte drawMask, byte color, byte priority, byte control) {
- int offset = y * _width + x;
+ int offset = y * _pitch + x;
if (drawMask & GFX_SCREEN_MASK_VISUAL) {
_visualScreen[offset] = color;
@@ -240,7 +252,7 @@ void GfxScreen::putFontPixel(int startingY, int x, int y, byte color) {
// Do not scale ourselves, but put it on the display directly
putPixelOnDisplay(x, y + startingY, color);
} else {
- int offset = (startingY + y) * _width + x;
+ int offset = (startingY + y) * _pitch + x;
_visualScreen[offset] = color;
if (!_upscaledHires) {
@@ -342,19 +354,19 @@ void GfxScreen::putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, u
}
byte GfxScreen::getVisual(int x, int y) {
- return _visualScreen[y * _width + x];
+ return _visualScreen[y * _pitch + x];
}
byte GfxScreen::getPriority(int x, int y) {
- return _priorityScreen[y * _width + x];
+ return _priorityScreen[y * _pitch + x];
}
byte GfxScreen::getControl(int x, int y) {
- return _controlScreen[y * _width + x];
+ return _controlScreen[y * _pitch + x];
}
byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) {
- int offset = y * _width + x;
+ int offset = y * _pitch + x;
byte match = 0;
if (screenMask & GFX_SCREEN_MASK_VISUAL) {
@@ -415,14 +427,14 @@ void GfxScreen::bitsSave(Common::Rect rect, byte mask, byte *memoryPtr) {
memcpy(memoryPtr, (void *)&mask, sizeof(mask)); memoryPtr += sizeof(mask);
if (mask & GFX_SCREEN_MASK_VISUAL) {
- bitsSaveScreen(rect, _visualScreen, _width, memoryPtr);
+ bitsSaveScreen(rect, _visualScreen, _pitch, memoryPtr);
bitsSaveDisplayScreen(rect, memoryPtr);
}
if (mask & GFX_SCREEN_MASK_PRIORITY) {
- bitsSaveScreen(rect, _priorityScreen, _width, memoryPtr);
+ bitsSaveScreen(rect, _priorityScreen, _pitch, memoryPtr);
}
if (mask & GFX_SCREEN_MASK_CONTROL) {
- bitsSaveScreen(rect, _controlScreen, _width, memoryPtr);
+ bitsSaveScreen(rect, _controlScreen, _pitch, memoryPtr);
}
if (mask & GFX_SCREEN_MASK_DISPLAY) {
if (!_upscaledHires)
@@ -475,14 +487,14 @@ void GfxScreen::bitsRestore(byte *memoryPtr) {
memcpy((void *)&mask, memoryPtr, sizeof(mask)); memoryPtr += sizeof(mask);
if (mask & GFX_SCREEN_MASK_VISUAL) {
- bitsRestoreScreen(rect, memoryPtr, _visualScreen, _width);
+ bitsRestoreScreen(rect, memoryPtr, _visualScreen, _pitch);
bitsRestoreDisplayScreen(rect, memoryPtr);
}
if (mask & GFX_SCREEN_MASK_PRIORITY) {
- bitsRestoreScreen(rect, memoryPtr, _priorityScreen, _width);
+ bitsRestoreScreen(rect, memoryPtr, _priorityScreen, _pitch);
}
if (mask & GFX_SCREEN_MASK_CONTROL) {
- bitsRestoreScreen(rect, memoryPtr, _controlScreen, _width);
+ bitsRestoreScreen(rect, memoryPtr, _controlScreen, _pitch);
}
if (mask & GFX_SCREEN_MASK_DISPLAY) {
if (!_upscaledHires)
@@ -560,7 +572,7 @@ void GfxScreen::dither(bool addToFlag) {
if (!_unditheringEnabled) {
// Do dithering on visual and display-screen
for (y = 0; y < _height; y++) {
- for (x = 0; x < _width; x++) {
+ for (x = 0; x < _pitch; x++) {
color = *visualPtr;
if (color & 0xF0) {
color ^= color << 4;
@@ -585,7 +597,7 @@ void GfxScreen::dither(bool addToFlag) {
memset(&_ditheredPicColors, 0, sizeof(_ditheredPicColors));
// Do dithering on visual screen and put decoded but undithered byte onto display-screen
for (y = 0; y < _height; y++) {
- for (x = 0; x < _width; x++) {
+ for (x = 0; x < _pitch; x++) {
color = *visualPtr;
if (color & 0xF0) {
color ^= color << 4;
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index 73ea596ba1..01fb899edb 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -132,6 +132,7 @@ public:
private:
uint16 _width;
+ uint16 _pitch;
uint16 _height;
uint _pixels;
uint16 _displayWidth;
diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp
index 7894c7109c..f14ae2ef0b 100644
--- a/engines/sci/graphics/text32.cpp
+++ b/engines/sci/graphics/text32.cpp
@@ -49,9 +49,12 @@ GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen)
GfxText32::~GfxText32() {
}
+reg_t GfxText32::createScrollTextBitmap(Common::String text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) {
+ return createTextBitmapInternal(text, textObject, maxWidth, maxHeight, prevHunk);
+
+}
reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) {
reg_t stringObject = readSelector(_segMan, textObject, 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.
@@ -59,6 +62,11 @@ reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxH
stringObject = readSelector(_segMan, stringObject, SELECTOR(data));
Common::String text = _segMan->getString(stringObject);
+
+ return createTextBitmapInternal(text, textObject, maxWidth, maxHeight, prevHunk);
+}
+
+reg_t GfxText32::createTextBitmapInternal(Common::String &text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) {
// HACK: The character offsets of the up and down arrow buttons are off by one
// in GK1, for some unknown reason. Fix them here.
if (text.size() == 1 && (text[0] == 29 || text[0] == 30)) {
@@ -91,7 +99,11 @@ reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxH
reg_t memoryId = NULL_REG;
if (prevHunk.isNull()) {
memoryId = _segMan->allocateHunkEntry("TextBitmap()", entrySize);
- writeSelector(_segMan, textObject, SELECTOR(bitmap), memoryId);
+
+ // Scroll text objects have no bitmap selector!
+ ObjVarRef varp;
+ if (lookupSelector(_segMan, textObject, SELECTOR(bitmap), &varp, NULL) == kSelectorVariable)
+ writeSelector(_segMan, textObject, SELECTOR(bitmap), memoryId);
} else {
memoryId = prevHunk;
}
@@ -153,10 +165,24 @@ reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxH
warning("Invalid alignment %d used in TextBox()", alignment);
}
+ byte curChar;
+
for (int i = 0; i < charCount; i++) {
- unsigned char curChar = txt[i];
- font->drawToBuffer(curChar, curY + offsetY, curX + offsetX, foreColor, dimmed, bitmap, width, height);
- curX += font->getCharWidth(curChar);
+ curChar = txt[i];
+
+ switch (curChar) {
+ case 0x0A:
+ case 0x0D:
+ case 0:
+ break;
+ case 0x7C:
+ warning("Code processing isn't implemented in SCI32");
+ break;
+ default:
+ font->drawToBuffer(curChar, curY + offsetY, curX + offsetX, foreColor, dimmed, bitmap, width, height);
+ curX += font->getCharWidth(curChar);
+ break;
+ }
}
curX = 0;
@@ -175,7 +201,25 @@ void GfxText32::disposeTextBitmap(reg_t hunkId) {
void GfxText32::drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t textObject) {
reg_t hunkId = readSelector(_segMan, textObject, SELECTOR(bitmap));
- uint16 backColor = readSelectorValue(_segMan, textObject, SELECTOR(back));
+ drawTextBitmapInternal(x, y, planeRect, textObject, hunkId);
+}
+
+void GfxText32::drawScrollTextBitmap(reg_t textObject, reg_t hunkId, uint16 x, uint16 y) {
+ /*reg_t plane = readSelector(_segMan, textObject, SELECTOR(plane));
+ Common::Rect planeRect;
+ planeRect.top = readSelectorValue(_segMan, plane, SELECTOR(top));
+ planeRect.left = readSelectorValue(_segMan, plane, SELECTOR(left));
+ planeRect.bottom = readSelectorValue(_segMan, plane, SELECTOR(bottom));
+ planeRect.right = readSelectorValue(_segMan, plane, SELECTOR(right));
+
+ drawTextBitmapInternal(x, y, planeRect, textObject, hunkId);*/
+
+ // HACK: we pretty much ignore the plane rect and x, y...
+ drawTextBitmapInternal(0, 0, Common::Rect(20, 390, 600, 460), textObject, hunkId);
+}
+
+void GfxText32::drawTextBitmapInternal(int16 x, int16 y, Common::Rect planeRect, reg_t textObject, reg_t hunkId) {
+ int16 backColor = (int16)readSelectorValue(_segMan, textObject, SELECTOR(back));
// Sanity check: Check if the hunk is set. If not, either the game scripts
// didn't set it, or an old saved game has been loaded, where it wasn't set.
if (hunkId.isNull())
@@ -187,13 +231,17 @@ void GfxText32::drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t t
byte *memoryPtr = _segMan->getHunkPointer(hunkId);
- if (!memoryPtr)
- error("Attempt to draw an invalid text bitmap");
+ if (!memoryPtr) {
+ // Happens when restoring in some SCI32 games (e.g. SQ6).
+ // Commented out to reduce console spam
+ //warning("Attempt to draw an invalid text bitmap");
+ return;
+ }
byte *surface = memoryPtr + BITMAP_HEADER_SIZE;
int curByte = 0;
- uint16 skipColor = readSelectorValue(_segMan, textObject, SELECTOR(skip));
+ int16 skipColor = (int16)readSelectorValue(_segMan, textObject, SELECTOR(skip));
uint16 textX = planeRect.left + x;
uint16 textY = planeRect.top + y;
// Get totalWidth, totalHeight
@@ -206,10 +254,13 @@ void GfxText32::drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t t
textY = textY * _screen->getDisplayHeight() / _screen->getHeight();
}
+ bool translucent = (skipColor == -1 && backColor == -1);
+
for (int curY = 0; curY < height; curY++) {
for (int curX = 0; curX < width; curX++) {
byte pixel = surface[curByte++];
- if (pixel != skipColor && pixel != backColor)
+ if ((!translucent && pixel != skipColor && pixel != backColor) ||
+ (translucent && pixel != 0xFF))
_screen->putFontPixel(textY, curX + textX, curY, pixel);
}
}
@@ -265,7 +316,7 @@ void GfxText32::StringWidth(const char *str, GuiResourceId fontId, int16 &textWi
}
void GfxText32::Width(const char *text, int16 from, int16 len, GuiResourceId fontId, int16 &textWidth, int16 &textHeight, bool restoreFont) {
- uint16 curChar;
+ byte curChar;
textWidth = 0; textHeight = 0;
GfxFont *font = _cache->getFont(fontId);
@@ -277,7 +328,6 @@ void GfxText32::Width(const char *text, int16 from, int16 len, GuiResourceId fon
switch (curChar) {
case 0x0A:
case 0x0D:
- case 0x9781: // this one is used by SQ4/japanese as line break as well
textHeight = MAX<int16> (textHeight, font->getHeight());
break;
case 0x7C:
diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h
index 3505de85eb..ce78003fdf 100644
--- a/engines/sci/graphics/text32.h
+++ b/engines/sci/graphics/text32.h
@@ -33,13 +33,17 @@ public:
GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen);
~GfxText32();
reg_t createTextBitmap(reg_t textObject, uint16 maxWidth = 0, uint16 maxHeight = 0, reg_t prevHunk = NULL_REG);
- void disposeTextBitmap(reg_t hunkId);
+ reg_t createScrollTextBitmap(Common::String text, reg_t textObject, uint16 maxWidth = 0, uint16 maxHeight = 0, reg_t prevHunk = NULL_REG);
void drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t textObject);
+ void drawScrollTextBitmap(reg_t textObject, reg_t hunkId, uint16 x, uint16 y);
+ void disposeTextBitmap(reg_t hunkId);
int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font);
void kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight);
private:
+ reg_t createTextBitmapInternal(Common::String &text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t hunkId);
+ void drawTextBitmapInternal(int16 x, int16 y, Common::Rect planeRect, reg_t textObject, reg_t hunkId);
int16 Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth);
void Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont);
void StringWidth(const char *str, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight);
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index a77bcccc52..36aaae9232 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -720,6 +720,19 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const
bitmap += (clipRect.top - rect.top) * celWidth + (clipRect.left - rect.left);
+ // WORKAROUND: EcoQuest French and German draw the fish and anemone sprites
+ // with priority 15 in scene 440. Afterwards, a dialog is shown on top of
+ // these sprites with priority 15 as well. This is undefined behavior
+ // actually, as the sprites and dialog share the same priority, so in our
+ // implementation the sprites get drawn incorrectly on top of the dialog.
+ // Perhaps this worked by mistake in SSCI because of subtle differences in
+ // how sprites are drawn. We compensate for this by resetting the priority
+ // of all sprites that have a priority of 15 in scene 440 to priority 14,
+ // so that the speech bubble can be drawn correctly on top of them. Fixes
+ // bug #3040625.
+ if (g_sci->getGameId() == GID_ECOQUEST && g_sci->getEngineState()->currentRoomNumber() == 440 && priority == 15)
+ priority = 14;
+
if (!_EGAmapping) {
for (y = 0; y < height; y++, bitmap += celWidth) {
for (x = 0; x < width; x++) {
@@ -728,8 +741,14 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const
const int x2 = clipRectTranslated.left + x;
const int y2 = clipRectTranslated.top + y;
if (!upscaledHires) {
- if (priority >= _screen->getPriority(x2, y2))
- _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
+ if (priority >= _screen->getPriority(x2, y2)) {
+ if (!_palette->isRemapped(palette->mapping[color])) {
+ _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
+ } else {
+ byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2));
+ _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0);
+ }
+ }
} else {
// UpscaledHires means view is hires and is supposed to
// get drawn onto lowres screen.
@@ -838,7 +857,12 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect,
const int x2 = clipRectTranslated.left + x;
const int y2 = clipRectTranslated.top + y;
if (color != clearKey && priority >= _screen->getPriority(x2, y2)) {
- _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
+ if (!_palette->isRemapped(palette->mapping[color])) {
+ _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
+ } else {
+ byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2));
+ _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0);
+ }
}
}
}