scummvm/engines/lab/graphics.cpp
2015-12-23 21:33:52 +01:00

1168 lines
37 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "lab/lab.h"
#include "lab/anim.h"
#include "lab/parsetypes.h"
#include "lab/image.h"
#include "lab/labfun.h"
#include "lab/parsefun.h"
#include "lab/text.h"
#include "lab/resource.h"
#include "lab/graphics.h"
namespace Lab {
BitMap bit1, bit2, *DispBitMap = &bit1, *DrawBitMap = &bit1;
DisplayMan::DisplayMan(LabEngine *vm) : _vm(vm) {
_longWinInFront = false;
_lastMessageLong = false;
_doNotDrawMessage = false;
_screenBytesPerPage = 65536;
_curapen = 0;
_curBitmap = nullptr;
_displayBuffer = nullptr;
_currentDisplayBuffer = nullptr;
_tempScrollData = nullptr;
FadePalette = nullptr;
_screenWidth = 0;
_screenHeight = 0;
for (int i = 0; i < 256 * 3; i++)
_curvgapal[i] = 0;
}
DisplayMan::~DisplayMan() {
freePict();
}
/*****************************************************************************/
/* Scales the x co-ordinates to that of the new display. In the room parser */
/* file, co-ordinates are set up on a 360x336 display. */
/*****************************************************************************/
uint16 DisplayMan::scaleX(uint16 x) {
if (_vm->_isHiRes)
return (uint16)((x * 16) / 9);
else
return (uint16)((x * 8) / 9);
}
/*****************************************************************************/
/* Scales the y co-ordinates to that of the new display. In the room parser */
/* file, co-ordinates are set up on a 368x336 display. */
/*****************************************************************************/
uint16 DisplayMan::scaleY(uint16 y) {
if (_vm->_isHiRes)
return (y + (y / 14));
else
return ((y * 10) / 24);
}
/*****************************************************************************/
/* Scales the VGA cords to SVGA if necessary; otherwise, returns VGA cords. */
/*****************************************************************************/
int16 DisplayMan::VGAScaleX(int16 x) {
if (_vm->_isHiRes)
return (x * 2);
else
return x;
}
/*****************************************************************************/
/* Scales the VGA cords to SVGA if necessary; otherwise, returns VGA cords. */
/*****************************************************************************/
int16 DisplayMan::VGAScaleY(int16 y) {
if (_vm->_isHiRes)
return ((y * 12) / 5);
else
return y;
}
uint16 DisplayMan::SVGACord(uint16 cord) {
if (_vm->_isHiRes)
return cord;
else
return 0;
}
/*---------------------------------------------------------------------------*/
/*------ From readPict.c. Reads in pictures and animations from disk. ------*/
/*---------------------------------------------------------------------------*/
void DisplayMan::loadPict(const char *filename) {
Common::File *bitmapFile = _vm->_resource->openDataFile(filename);
freePict();
_curBitmap = new byte[bitmapFile->size()];
bitmapFile->read(_curBitmap, bitmapFile->size());
delete bitmapFile;
}
/*****************************************************************************/
/* Reads in a picture into the dest bitmap. */
/*****************************************************************************/
bool DisplayMan::readPict(const char *filename, bool playOnce) {
_vm->_anim->stopDiff();
loadPict(filename);
_vm->_music->updateMusic();
if (!_vm->_music->_doNotFilestopSoundEffect)
_vm->_music->stopSoundEffect();
DispBitMap->_bytesPerRow = _screenWidth;
DispBitMap->_rows = _screenHeight;
DispBitMap->_flags = BITMAPF_VIDEO;
_vm->_anim->readDiff(_curBitmap, playOnce);
return true;
}
/*****************************************************************************/
/* Reads in a picture into buffer memory. */
/*****************************************************************************/
byte *DisplayMan::readPictToMem(const char *filename, uint16 x, uint16 y) {
_vm->_anim->stopDiff();
loadPict(filename);
_vm->_music->updateMusic();
if (!_vm->_music->_doNotFilestopSoundEffect)
_vm->_music->stopSoundEffect();
DispBitMap->_bytesPerRow = x;
DispBitMap->_rows = y;
DispBitMap->_flags = BITMAPF_NONE;
DispBitMap->_planes[0] = _curBitmap;
DispBitMap->_planes[1] = DispBitMap->_planes[0] + 0x10000;
DispBitMap->_planes[2] = DispBitMap->_planes[1] + 0x10000;
DispBitMap->_planes[3] = DispBitMap->_planes[2] + 0x10000;
DispBitMap->_planes[4] = DispBitMap->_planes[3] + 0x10000;
_vm->_anim->readDiff(_curBitmap, true);
return _curBitmap;
}
void DisplayMan::freePict() {
delete _curBitmap;
_curBitmap = NULL;
}
/*---------------------------------------------------------------------------*/
/*------------ Does all the text rendering to the message boxes. ------------*/
/*---------------------------------------------------------------------------*/
/*----- The flowText routines -----*/
/******************************************************************************/
/* Extracts the first word from a string. */
/******************************************************************************/
static void getWord(char *wordBuffer, const char *mainBuffer, uint16 *wordWidth) {
uint16 width = 0;
while ((mainBuffer[width] != ' ') && mainBuffer[width] &&
(mainBuffer[width] != '\n')) {
wordBuffer[width] = mainBuffer[width];
width++;
}
wordBuffer[width] = 0;
*wordWidth = width;
}
/******************************************************************************/
/* Gets a line of text for flowText; makes sure that its length is less than */
/* or equal to the maximum width. */
/******************************************************************************/
static void getLine(TextFont *tf, char *lineBuffer, const char **mainBuffer, uint16 lineWidth) {
uint16 curWidth = 0, wordWidth;
char wordBuffer[100];
bool doit = true;
lineWidth += textLength(tf, " ", 1);
lineBuffer[0] = 0;
while ((*mainBuffer)[0] && doit) {
getWord(wordBuffer, *mainBuffer, &wordWidth);
strcat(wordBuffer, " ");
if ((curWidth + textLength(tf, wordBuffer, wordWidth + 1)) <= lineWidth) {
strcat(lineBuffer, wordBuffer);
(*mainBuffer) += wordWidth;
if ((*mainBuffer)[0] == '\n')
doit = false;
if ((*mainBuffer)[0])
(*mainBuffer)++;
curWidth = textLength(tf, lineBuffer, strlen(lineBuffer));
} else
doit = false;
}
}
/******************************************************************************/
/* Dumps a chunk of text to an arbitrary box; flows it within that box and */
/* optionally centers it. Returns the number of characters that were */
/* processed. */
/* */
/* Note: Every individual word MUST be int16 enough to fit on a line, and */
/* each line less than 255 characters. */
/******************************************************************************/
uint32 DisplayMan::flowText(void *font, /* the TextAttr pointer */
int16 spacing, /* How much vertical spacing between the lines */
byte pencolor, /* pen number to use for text */
byte backpen, /* the background color */
bool fillback, /* Whether to fill the background */
bool centerh, /* Whether to center the text horizontally */
bool centerv, /* Whether to center the text vertically */
bool output, /* Whether to output any text */
uint16 x1, /* Cords */
uint16 y1, uint16 x2, uint16 y2, const char *str) { /* The text itself */
TextFont *_msgFont = (TextFont *)font;
char linebuffer[256];
const char *temp;
uint16 numlines, actlines, fontheight, width;
uint16 x, y;
if (fillback) {
setAPen(backpen);
rectFill(x1, y1, x2, y2);
}
if (str == NULL)
return 0L;
setAPen(pencolor);
fontheight = textHeight(_msgFont) + spacing;
numlines = (y2 - y1 + 1) / fontheight;
width = x2 - x1 + 1;
y = y1;
if (centerv && output) {
temp = str;
actlines = 0;
while (temp[0]) {
getLine(_msgFont, linebuffer, &temp, width);
actlines++;
}
if (actlines <= numlines)
y += ((y2 - y1 + 1) - (actlines * fontheight)) / 2;
}
temp = str;
while (numlines && str[0]) {
getLine(_msgFont, linebuffer, &str, width);
x = x1;
if (centerh)
x += (width - textLength(_msgFont, linebuffer, strlen(linebuffer))) / 2;
if (output)
text(_msgFont, x, y, pencolor, linebuffer, strlen(linebuffer));
numlines--;
y += fontheight;
}
return (str - temp);
}
uint32 DisplayMan::flowTextScaled(void *font, /* the TextAttr pointer */
int16 spacing, /* How much vertical spacing between the lines */
byte pencolor, /* pen number to use for text */
byte backpen, /* the background color */
bool fillback, /* Whether to fill the background */
bool centerh, /* Whether to center the text horizontally */
bool centerv, /* Whether to center the text vertically */
bool output, /* Whether to output any text */
uint16 x1, /* Cords */
uint16 y1, uint16 x2, uint16 y2, const char *str) {
return flowText(font, spacing, pencolor, backpen, fillback, centerh, centerv, output, VGAScaleX(x1), VGAScaleY(y1), VGAScaleX(x2), VGAScaleY(y2), str);
}
/******************************************************************************/
/* Calls flowText, but flows it to memory. Same restrictions as flowText. */
/******************************************************************************/
uint32 DisplayMan::flowTextToMem(Image *destIm, void *font, /* the TextAttr pointer */
int16 spacing, /* How much vertical spacing between the lines */
byte pencolor, /* pen number to use for text */
byte backpen, /* the background color */
bool fillback, /* Whether to fill the background */
bool centerh, /* Whether to center the text horizontally */
bool centerv, /* Whether to center the text vertically */
bool output, /* Whether to output any text */
uint16 x1, /* Cords */
uint16 y1, uint16 x2, uint16 y2, const char *str) { /* The text itself */
uint32 res, vgabyte = _screenBytesPerPage;
byte *tmp = _currentDisplayBuffer;
_currentDisplayBuffer = destIm->_imageData;
_screenBytesPerPage = (uint32)destIm->_width * (int32)destIm->_height;
res = flowText(font, spacing, pencolor, backpen, fillback, centerh, centerv, output, x1, y1, x2, y2, str);
_screenBytesPerPage = vgabyte;
_currentDisplayBuffer = tmp;
return res;
}
/*----- The control panel stuff -----*/
void DisplayMan::createBox(uint16 y2) {
setAPen(7); /* Message box area */
rectFillScaled(4, 154, 315, y2 - 2);
setAPen(0); /* Box around message area */
drawHLine(VGAScaleX(2), VGAScaleY(152), VGAScaleX(317));
drawVLine(VGAScaleX(317), VGAScaleY(152), VGAScaleY(y2));
drawHLine(VGAScaleX(2), VGAScaleY(y2), VGAScaleX(317));
drawVLine(VGAScaleX(2), VGAScaleY(152), VGAScaleY(y2));
}
int32 DisplayMan::longDrawMessage(const char *str) {
char newText[512];
if (str == NULL)
return 0;
_vm->_event->attachGadgetList(NULL);
_vm->_event->mouseHide();
strcpy(newText, str);
if (!_longWinInFront) {
_longWinInFront = true;
setAPen(3); /* Clear Area */
rectFill(0, VGAScaleY(149) + SVGACord(2), VGAScaleX(319), VGAScaleY(199));
}
createBox(198);
_vm->_event->mouseShow();
return flowTextScaled(_vm->_msgFont, 0, 1, 7, false, true, true, true, 6, 155, 313, 195, str);
}
/******************************************************************************/
/* Draws a message to the message box. */
/******************************************************************************/
void DisplayMan::drawMessage(const char *str) {
if (_doNotDrawMessage) {
_doNotDrawMessage = false;
return;
}
if (str) {
if ((textLength(_vm->_msgFont, str, strlen(str)) > VGAScaleX(306))) {
longDrawMessage(str);
_lastMessageLong = true;
} else {
if (_longWinInFront) {
_longWinInFront = false;
drawPanel();
}
_vm->_event->mouseHide();
createBox(168);
text(_vm->_msgFont, VGAScaleX(7), VGAScaleY(155) + SVGACord(2), 1, str, strlen(str));
_vm->_event->mouseShow();
_lastMessageLong = false;
}
}
}
/*---------------------------------------------------------------------------*/
/*--------------------------- All the wipe stuff. ---------------------------*/
/*---------------------------------------------------------------------------*/
#define TRANSWIPE 1
#define SCROLLWIPE 2
#define SCROLLBLACK 3
#define SCROLLBOUNCE 4
#define TRANSPORTER 5
#define READFIRSTFRAME 6
#define READNEXTFRAME 7
/*****************************************************************************/
/* Scrolls the display to black. */
/*****************************************************************************/
void DisplayMan::doScrollBlack() {
byte *tempmem;
Image im;
uint32 size, copysize;
byte *baseAddr;
uint16 width = VGAScaleX(320);
uint16 height = VGAScaleY(149) + SVGACord(2);
byte *mem = new byte[width * height];
_vm->_event->mouseHide();
im._width = width;
im._height = height;
im._imageData = mem;
_vm->_music->updateMusic();
im.readScreenImage(0, 0);
_vm->_music->updateMusic();
baseAddr = getCurrentDrawingBuffer();
uint16 by = VGAScaleX(4);
uint16 nheight = height;
while (nheight) {
_vm->_music->updateMusic();
if (!_vm->_isHiRes)
_vm->waitTOF();
baseAddr = getCurrentDrawingBuffer();
if (by > nheight)
by = nheight;
mem += by * width;
nheight -= by;
size = (int32)nheight * (int32)width;
tempmem = mem;
while (size) {
if (size > _screenBytesPerPage)
copysize = _screenBytesPerPage;
else
copysize = size;
size -= copysize;
memcpy(baseAddr, tempmem, copysize);
tempmem += copysize;
}
setAPen(0);
rectFill(0, nheight, width - 1, nheight + by - 1);
screenUpdate();
if (!_vm->_isHiRes) {
if (nheight <= (height / 8))
by = 1;
else if (nheight <= (height / 4))
by = 2;
else if (nheight <= (height / 2))
by = 3;
}
}
delete[] mem;
freePict();
_vm->_event->mouseShow();
}
void DisplayMan::copyPage(uint16 width, uint16 height, uint16 nheight, uint16 startline, byte *mem) {
byte *baseAddr = getCurrentDrawingBuffer();
uint32 size = (int32)(height - nheight) * (int32)width;
mem += startline * width;
uint16 curPage = ((int32)nheight * (int32)width) / _screenBytesPerPage;
uint32 offSet = ((int32)nheight * (int32)width) - (curPage * _screenBytesPerPage);
while (size) {
uint32 copysize;
if (size > (_screenBytesPerPage - offSet))
copysize = _screenBytesPerPage - offSet;
else
copysize = size;
size -= copysize;
memcpy(baseAddr + (offSet >> 2), mem, copysize);
mem += copysize;
curPage++;
offSet = 0;
}
}
/*****************************************************************************/
/* Scrolls the display to a new picture from a black screen. */
/*****************************************************************************/
void DisplayMan::doScrollWipe(char *filename) {
uint16 startline = 0, onrow = 0;
_vm->_event->mouseHide();
uint16 width = VGAScaleX(320);
uint16 height = VGAScaleY(149) + SVGACord(2);
while (_vm->_music->isSoundEffectActive()) {
_vm->_music->updateMusic();
_vm->waitTOF();
}
_vm->_anim->_isBM = true;
readPict(filename, true);
setPalette(_vm->_anim->_diffPalette, 256);
_vm->_anim->_isBM = false;
byte *mem = _vm->_anim->_rawDiffBM._planes[0];
_vm->_music->updateMusic();
uint16 by = VGAScaleX(3);
uint16 nheight = height;
while (onrow < _vm->_anim->_headerdata._height) {
_vm->_music->updateMusic();
if ((by > nheight) && nheight)
by = nheight;
if ((startline + by) > (_vm->_anim->_headerdata._height - height - 1))
break;
if (nheight)
nheight -= by;
copyPage(width, height, nheight, startline, mem);
screenUpdate();
if (!nheight)
startline += by;
onrow += by;
if (nheight <= (height / 4))
by = VGAScaleX(5);
else if (nheight <= (height / 3))
by = VGAScaleX(4);
else if (nheight <= (height / 2))
by = VGAScaleX(3);
}
_vm->_event->mouseShow();
}
/*****************************************************************************/
/* Does the scroll bounce. Assumes bitmap already in memory. */
/*****************************************************************************/
void DisplayMan::doScrollBounce() {
const uint16 *newby, *newby1;
const uint16 newbyd[5] = {5, 4, 3, 2, 1}, newby1d[8] = {3, 3, 2, 2, 2, 1, 1, 1};
const uint16 newbyw[5] = {10, 8, 6, 4, 2}, newby1w[8] = {6, 6, 4, 4, 4, 2, 2, 2};
if (_vm->getPlatform() != Common::kPlatformWindows) {
newby = newbyd;
newby1 = newby1d;
} else {
newby = newbyw;
newby1 = newby1w;
}
_vm->_event->mouseHide();
int width = VGAScaleX(320);
int height = VGAScaleY(149) + SVGACord(2);
byte *mem = _vm->_anim->_rawDiffBM._planes[0];
_vm->_music->updateMusic();
int startline = _vm->_anim->_headerdata._height - height - 1;
for (int i = 0; i < 5; i++) {
_vm->_music->updateMusic();
startline -= newby[i];
copyPage(width, height, 0, startline, mem);
screenUpdate();
_vm->waitTOF();
}
for (int i = 8; i > 0; i--) {
_vm->_music->updateMusic();
startline += newby1[i - 1];
copyPage(width, height, 0, startline, mem);
screenUpdate();
_vm->waitTOF();
}
_vm->_event->mouseShow();
}
/*****************************************************************************/
/* Does the transporter wipe. */
/*****************************************************************************/
void DisplayMan::doTransWipe(CloseDataPtr *cPtr, char *filename) {
uint16 lastY, curY, linesdone = 0, lineslast;
Image imSource, imDest;
if (_vm->_isHiRes) {
lineslast = 3;
lastY = 358;
} else {
lineslast = 1;
lastY = 148;
}
for (uint16 i = 0; i < 2; i++) {
curY = i * 2;
while (curY < lastY) {
if (linesdone >= lineslast) {
_vm->_music->updateMusic();
_vm->waitTOF();
linesdone = 0;
}
overlayRect(0, 0, curY, _screenWidth - 1, curY + 1);
curY += 4;
linesdone++;
}
}
setAPen(0);
for (uint16 i = 0; i < 2; i++) {
curY = i * 2;
while (curY <= lastY) {
if (linesdone >= lineslast) {
_vm->_music->updateMusic();
_vm->waitTOF();
linesdone = 0;
}
rectFill(0, curY, _screenWidth - 1, curY + 1);
curY += 4;
linesdone++;
}
}
if (filename == NULL)
_vm->_curFileName = getPictName(cPtr);
else if (filename[0] > ' ')
_vm->_curFileName = filename;
else
_vm->_curFileName = getPictName(cPtr);
byte *BitMapMem = readPictToMem(_vm->_curFileName, _screenWidth, lastY + 5);
setPalette(_vm->_anim->_diffPalette, 256);
if (BitMapMem) {
imSource._width = _screenWidth;
imSource._height = lastY;
imSource._imageData = BitMapMem;
imDest._width = _screenWidth;
imDest._height = _screenHeight;
imDest._imageData = getCurrentDrawingBuffer();
for (uint16 i = 0; i < 2; i++) {
curY = i * 2;
while (curY < lastY) {
if (linesdone >= lineslast) {
_vm->_music->updateMusic();
_vm->waitTOF();
linesdone = 0;
}
imDest._imageData = getCurrentDrawingBuffer();
imSource.blitBitmap(0, curY, &imDest, 0, curY, _screenWidth, 2, false);
overlayRect(0, 0, curY, _screenWidth - 1, curY + 1);
curY += 4;
linesdone++;
}
}
for (uint16 i = 0; i < 2; i++) {
curY = i * 2;
while (curY <= lastY) {
if (linesdone >= lineslast) {
_vm->_music->updateMusic();
_vm->waitTOF();
linesdone = 0;
}
imDest._imageData = getCurrentDrawingBuffer();
if (curY == lastY)
imSource.blitBitmap(0, curY, &imDest, 0, curY, _screenWidth, 1, false);
else
imSource.blitBitmap(0, curY, &imDest, 0, curY, _screenWidth, 2, false);
curY += 4;
linesdone++;
}
}
}
}
/*****************************************************************************/
/* Does a certain number of pre-programmed wipes. */
/*****************************************************************************/
void DisplayMan::doWipe(uint16 wipeType, CloseDataPtr *cPtr, char *filename) {
if ((wipeType == TRANSWIPE) || (wipeType == TRANSPORTER))
doTransWipe(cPtr, filename);
else if (wipeType == SCROLLWIPE)
doScrollWipe(filename);
else if (wipeType == SCROLLBLACK)
doScrollBlack();
else if (wipeType == SCROLLBOUNCE)
doScrollBounce();
else if (wipeType == READFIRSTFRAME)
readPict(filename, false);
else if (wipeType == READNEXTFRAME)
_vm->_anim->diffNextFrame();
}
/*****************************************************************************/
/* Changes the front screen to black. */
/*****************************************************************************/
void DisplayMan::blackScreen() {
byte pal[256 * 3];
memset(pal, 0, 248 * 3);
writeColorRegs(pal, 8, 248);
g_system->delayMillis(32);
}
/*****************************************************************************/
/* Changes the front screen to white. */
/*****************************************************************************/
void DisplayMan::whiteScreen() {
byte pal[256 * 3];
memset(pal, 255, 248 * 3);
writeColorRegs(pal, 8, 248);
}
/*****************************************************************************/
/* Changes the entire screen to black. */
/*****************************************************************************/
void DisplayMan::blackAllScreen() {
byte pal[256 * 3];
memset(pal, 0, 256 * 3);
writeColorRegs(pal, 0, 256);
g_system->delayMillis(32);
}
/******************************************************************************/
/* Draws the control panel display. */
/******************************************************************************/
void DisplayMan::drawPanel() {
_vm->_event->mouseHide();
setAPen(3); /* Clear Area */
rectFill(0, VGAScaleY(149) + SVGACord(2), VGAScaleX(319), VGAScaleY(199));
setAPen(0); /* First Line */
drawHLine(0, VGAScaleY(149) + SVGACord(2), VGAScaleX(319));
setAPen(5); /* Second Line */
drawHLine(0, VGAScaleY(149) + 1 + SVGACord(2), VGAScaleX(319));
/* Gadget Separators */
setAPen(0);
drawHLine(0, VGAScaleY(170), VGAScaleX(319)); /* First black line to separate buttons */
if (!_vm->_alternate) {
setAPen(4);
drawHLine(0, VGAScaleY(170) + 1, VGAScaleX(319)); /* The horizontal lines under the black one */
drawGadgetList(&_vm->_moveGadgetList);
} else {
if (_vm->getPlatform() != Common::kPlatformWindows) {
drawVLine(VGAScaleX(124), VGAScaleY(170) + 1, VGAScaleY(199)); /* Vertical Black lines */
drawVLine(VGAScaleX(194), VGAScaleY(170) + 1, VGAScaleY(199));
} else {
drawVLine(VGAScaleX(90), VGAScaleY(170) + 1, VGAScaleY(199)); /* Vertical Black lines */
drawVLine(VGAScaleX(160), VGAScaleY(170) + 1, VGAScaleY(199));
drawVLine(VGAScaleX(230), VGAScaleY(170) + 1, VGAScaleY(199));
}
setAPen(4);
drawHLine(0, VGAScaleY(170) + 1, VGAScaleX(122)); /* The horizontal lines under the black one */
drawHLine(VGAScaleX(126), VGAScaleY(170) + 1, VGAScaleX(192));
drawHLine(VGAScaleX(196), VGAScaleY(170) + 1, VGAScaleX(319));
drawVLine(VGAScaleX(1), VGAScaleY(170) + 2, VGAScaleY(198)); /* The vertical high light lines */
if (_vm->getPlatform() != Common::kPlatformWindows) {
drawVLine(VGAScaleX(126), VGAScaleY(170) + 2, VGAScaleY(198));
drawVLine(VGAScaleX(196), VGAScaleY(170) + 2, VGAScaleY(198));
} else {
drawVLine(VGAScaleX(92), VGAScaleY(170) + 2, VGAScaleY(198));
drawVLine(VGAScaleX(162), VGAScaleY(170) + 2, VGAScaleY(198));
drawVLine(VGAScaleX(232), VGAScaleY(170) + 2, VGAScaleY(198));
}
drawGadgetList(&_vm->_invGadgetList);
}
_vm->_event->mouseShow();
}
/******************************************************************************/
/* Sets up the Labyrinth screens, and opens up the initial windows. */
/******************************************************************************/
bool DisplayMan::setUpScreens() {
if (!createScreen(_vm->_isHiRes))
return false;
Common::File *controlFile = _vm->_resource->openDataFile("P:Control");
for (uint16 i = 0; i < 20; i++)
_vm->_moveImages[i] = new Image(controlFile);
delete controlFile;
/* Creates the gadgets for the movement control panel */
uint16 y = VGAScaleY(173) - SVGACord(2);
// The key mapping was only set for the Windows version.
// It's very convenient to have those shortcut, so I added them
// for all versions. (Strangerke)
_vm->_moveGadgetList.push_back(createButton( 1, y, 0, 't', _vm->_moveImages[0], _vm->_moveImages[1]));
_vm->_moveGadgetList.push_back(createButton( 33, y, 1, 'm', _vm->_moveImages[2], _vm->_moveImages[3]));
_vm->_moveGadgetList.push_back(createButton( 65, y, 2, 'o', _vm->_moveImages[4], _vm->_moveImages[5]));
_vm->_moveGadgetList.push_back(createButton( 97, y, 3, 'c', _vm->_moveImages[6], _vm->_moveImages[7]));
_vm->_moveGadgetList.push_back(createButton(129, y, 4, 'l', _vm->_moveImages[8], _vm->_moveImages[9]));
_vm->_moveGadgetList.push_back(createButton(161, y, 5, 'i', _vm->_moveImages[12], _vm->_moveImages[13]));
_vm->_moveGadgetList.push_back(createButton(193, y, 6, VKEY_LTARROW, _vm->_moveImages[14], _vm->_moveImages[15]));
_vm->_moveGadgetList.push_back(createButton(225, y, 7, VKEY_UPARROW, _vm->_moveImages[16], _vm->_moveImages[17]));
_vm->_moveGadgetList.push_back(createButton(257, y, 8, VKEY_RTARROW, _vm->_moveImages[18], _vm->_moveImages[19]));
_vm->_moveGadgetList.push_back(createButton(289, y, 9, 'p', _vm->_moveImages[10], _vm->_moveImages[11]));
Common::File *invFile = _vm->_resource->openDataFile("P:Inv");
if (_vm->getPlatform() == Common::kPlatformWindows) {
for (uint16 imgIdx = 0; imgIdx < 10; imgIdx++)
_vm->_invImages[imgIdx] = new Image(invFile);
} else {
for (uint16 imgIdx = 0; imgIdx < 6; imgIdx++)
_vm->_invImages[imgIdx] = new Image(invFile);
}
_vm->_invGadgetList.push_back(createButton( 24, y, 0, 'm', _vm->_invImages[0], _vm->_invImages[1]));
_vm->_invGadgetList.push_back(createButton( 56, y, 1, 'g', _vm->_invImages[2], _vm->_invImages[3]));
_vm->_invGadgetList.push_back(createButton( 94, y, 2, 'u', _vm->_invImages[4], _vm->_invImages[5]));
_vm->_invGadgetList.push_back(createButton(126, y, 3, 'l', _vm->_moveImages[8], _vm->_moveImages[9]));
_vm->_invGadgetList.push_back(createButton(164, y, 4, VKEY_LTARROW, _vm->_moveImages[14], _vm->_moveImages[15]));
_vm->_invGadgetList.push_back(createButton(196, y, 5, VKEY_RTARROW, _vm->_moveImages[18], _vm->_moveImages[19]));
// The windows version has 2 extra gadgets for breadcrumb trail
// TODO: the game is really hard to play without those, maybe we could add something to enable that.
if (_vm->getPlatform() == Common::kPlatformWindows) {
_vm->_invGadgetList.push_back(createButton(234, y, 6, 'b', _vm->_invImages[6], _vm->_invImages[7]));
_vm->_invGadgetList.push_back(createButton(266, y, 7, 'f', _vm->_invImages[8], _vm->_invImages[9]));
}
delete invFile;
return true;
}
/*****************************************************************************/
/* Sets the pen number to use on all the drawing operations. */
/*****************************************************************************/
void DisplayMan::setAPen(byte pennum) {
_curapen = pennum;
}
/*****************************************************************************/
/* Fills in a rectangle. */
/*****************************************************************************/
void DisplayMan::rectFill(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
int w = x2 - x1 + 1;
int h = y2 - y1 + 1;
if (x1 + w > _screenWidth)
w = _screenWidth - x1;
if (y1 + h > _screenHeight)
h = _screenHeight - y1;
if ((w > 0) && (h > 0)) {
char *d = (char *)getCurrentDrawingBuffer() + y1 * _screenWidth + x1;
while (h-- > 0) {
char *dd = d;
int ww = w;
while (ww-- > 0) {
*dd++ = _curapen;
}
d += _screenWidth;
}
}
}
void DisplayMan::rectFillScaled(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
rectFill(VGAScaleX(x1), VGAScaleY(y1), VGAScaleX(x2), VGAScaleY(y2));
}
/*****************************************************************************/
/* Draws a horizontal line. */
/*****************************************************************************/
void DisplayMan::drawVLine(uint16 x, uint16 y1, uint16 y2) {
rectFill(x, y1, x, y2);
}
/*****************************************************************************/
/* Draws a vertical line. */
/*****************************************************************************/
void DisplayMan::drawHLine(uint16 x1, uint16 y, uint16 x2) {
rectFill(x1, y, x2, y);
}
void DisplayMan::screenUpdate() {
g_system->copyRectToScreen(_displayBuffer, _screenWidth, 0, 0, _screenWidth, _screenHeight);
g_system->updateScreen();
_vm->_event->processInput();
}
/*****************************************************************************/
/* Sets up either a low-res or a high-res 256 color screen. */
/*****************************************************************************/
bool DisplayMan::createScreen(bool hiRes) {
if (hiRes) {
_screenWidth = 640;
_screenHeight = 480;
} else {
_screenWidth = 320;
_screenHeight = 200;
}
_screenBytesPerPage = _screenWidth * _screenHeight;
_displayBuffer = new byte[_screenBytesPerPage]; // FIXME: Memory leak!
return true;
}
/*****************************************************************************/
/* Converts an Amiga palette (up to 16 colors) to a VGA palette, then sets */
/* the VGA palette. */
/*****************************************************************************/
void DisplayMan::setAmigaPal(uint16 *pal, uint16 numColors) {
byte vgaPal[16 * 3];
uint16 vgaIdx = 0;
if (numColors > 16)
numColors = 16;
for (uint16 i = 0; i < numColors; i++) {
vgaPal[vgaIdx++] = (byte)(((pal[i] & 0xf00) >> 8) << 2);
vgaPal[vgaIdx++] = (byte)(((pal[i] & 0x0f0) >> 4) << 2);
vgaPal[vgaIdx++] = (byte)(((pal[i] & 0x00f)) << 2);
}
writeColorRegs(vgaPal, 0, 16);
_vm->waitTOF();
}
/*****************************************************************************/
/* Writes any number of the 256 color registers. */
/* first: the number of the first color register to write. */
/* numreg: the number of registers to write */
/* buf: a char pointer which contains the selected color registers. */
/* Each value representing a color register occupies 3 bytes in */
/* the array. The order is red, green then blue. The first byte */
/* in the array is the red component of the first element selected.*/
/* The length of the buffer is 3 times the number of registers */
/* selected. */
/*****************************************************************************/
void DisplayMan::writeColorRegs(byte *buf, uint16 first, uint16 numreg) {
byte tmp[256 * 3];
for (int i = 0; i < 256 * 3; i++) {
tmp[i] = buf[i] * 4;
}
g_system->getPaletteManager()->setPalette(tmp, first, numreg);
memcpy(&(_curvgapal[first * 3]), buf, numreg * 3);
}
void DisplayMan::setPalette(void *cmap, uint16 numcolors) {
if (memcmp(cmap, _curvgapal, numcolors * 3) != 0)
writeColorRegs((byte *)cmap, 0, numcolors);
}
/*****************************************************************************/
/* Returns the base address of the current VGA display. */
/*****************************************************************************/
byte *DisplayMan::getCurrentDrawingBuffer() {
if (_currentDisplayBuffer)
return _currentDisplayBuffer;
return _displayBuffer;
}
/*****************************************************************************/
/* Overlays a region on the screen using the desired pen color. */
/*****************************************************************************/
void DisplayMan::overlayRect(uint16 pencolor, uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
int w = x2 - x1 + 1;
int h = y2 - y1 + 1;
if (x1 + w > _screenWidth)
w = _screenWidth - x1;
if (y1 + h > _screenHeight)
h = _screenHeight - y1;
if ((w > 0) && (h > 0)) {
char *d = (char *)getCurrentDrawingBuffer() + y1 * _screenWidth + x1;
while (h-- > 0) {
char *dd = d;
int ww = w;
if (y1 & 1) {
dd++;
ww--;
}
while (ww > 0) {
*dd = pencolor;
dd += 2;
ww -= 2;
}
d += _screenWidth;
y1++;
}
}
}
/*****************************************************************************/
/* Scrolls the display in the x direction by blitting. */
/* The _tempScrollData variable must be initialized to some memory, or this */
/* function will fail. */
/*****************************************************************************/
void DisplayMan::scrollDisplayX(int16 dx, uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
Image im;
uint16 temp;
im._imageData = _tempScrollData;
if (x1 > x2) {
temp = x2;
x2 = x1;
x1 = temp;
}
if (y1 > y2) {
temp = y2;
y2 = y1;
y1 = temp;
}
im._width = x2 - x1 + 1 - dx;
im._height = y2 - y1 + 1;
im.readScreenImage(x1, y1);
im.drawImage(x1 + dx, y1);
setAPen(0);
rectFill(x1, y1, x1 + dx - 1, y2);
}
/*****************************************************************************/
/* Scrolls the display in the y direction by blitting. */
/*****************************************************************************/
void DisplayMan::scrollDisplayY(int16 dy, uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
Image im;
uint16 temp;
im._imageData = _tempScrollData;
if (x1 > x2) {
temp = x2;
x2 = x1;
x1 = temp;
}
if (y1 > y2) {
temp = y2;
y2 = y1;
y1 = temp;
}
im._width = x2 - x1 + 1;
im._height = y2 - y1 + 1 - dy;
im.readScreenImage(x1, y1);
im.drawImage(x1, y1 + dy);
setAPen(0);
rectFill(x1, y1, x2, y1 + dy - 1);
}
/*****************************************************************************/
/* Does the fading of the Palette on the screen. */
/*****************************************************************************/
uint16 DisplayMan::fadeNumIn(uint16 num, uint16 res, uint16 counter) {
return (num - ((((int32)(15 - counter)) * ((int32)(num - res))) / 15));
}
uint16 DisplayMan::fadeNumOut(uint16 num, uint16 res, uint16 counter) {
return (num - ((((int32) counter) * ((int32)(num - res))) / 15));
}
void DisplayMan::fade(bool fadein, uint16 res) {
uint16 newpal[16];
for (uint16 i = 0; i < 16; i++) {
for (uint16 palIdx = 0; palIdx < 16; palIdx++) {
if (fadein)
newpal[palIdx] = (0x00F & fadeNumIn(0x00F & FadePalette[palIdx], 0x00F & res, i)) +
(0x0F0 & fadeNumIn(0x0F0 & FadePalette[palIdx], 0x0F0 & res, i)) +
(0xF00 & fadeNumIn(0xF00 & FadePalette[palIdx], 0xF00 & res, i));
else
newpal[palIdx] = (0x00F & fadeNumOut(0x00F & FadePalette[palIdx], 0x00F & res, i)) +
(0x0F0 & fadeNumOut(0x0F0 & FadePalette[palIdx], 0x0F0 & res, i)) +
(0xF00 & fadeNumOut(0xF00 & FadePalette[palIdx], 0xF00 & res, i));
}
g_lab->_graphics->setAmigaPal(newpal, 16);
g_lab->waitTOF();
g_lab->_music->updateMusic();
}
}
} // End of namespace Lab