diff options
author | Filippos Karapetis | 2008-04-20 14:47:37 +0000 |
---|---|---|
committer | Filippos Karapetis | 2008-04-20 14:47:37 +0000 |
commit | 7ca439f410ac1c46a387567b30271ae4e4a2ed30 (patch) | |
tree | 4d4154169b074293581ad6a11ee821290418f4fb /engines/m4/mads_anim.cpp | |
parent | d0590a09eac68d5cde64d37fb2e5bbd1471a676a (diff) | |
download | scummvm-rg350-7ca439f410ac1c46a387567b30271ae4e4a2ed30.tar.gz scummvm-rg350-7ca439f410ac1c46a387567b30271ae4e4a2ed30.tar.bz2 scummvm-rg350-7ca439f410ac1c46a387567b30271ae4e4a2ed30.zip |
Initial import of the work in progress M4 engine
svn-id: r31600
Diffstat (limited to 'engines/m4/mads_anim.cpp')
-rw-r--r-- | engines/m4/mads_anim.cpp | 705 |
1 files changed, 705 insertions, 0 deletions
diff --git a/engines/m4/mads_anim.cpp b/engines/m4/mads_anim.cpp new file mode 100644 index 0000000000..4d481f9397 --- /dev/null +++ b/engines/m4/mads_anim.cpp @@ -0,0 +1,705 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "m4/mads_anim.h" +#include "m4/m4.h" + +namespace M4 { + +#define TEXTVIEW_LINE_SPACING 2 +#define TEXT_ANIMATION_DELAY 100 +#define TV_NUM_FADE_STEPS 40 +#define TV_FADE_DELAY_MILLI 50 + +TextviewView::TextviewView(M4Engine *vm): + View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())), + _bgSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT), + _textSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT + vm->_font->getHeight() + + TEXTVIEW_LINE_SPACING) { + + _screenType = VIEWID_TEXTVIEW; + _screenFlags.layer = LAYER_BACKGROUND; + _screenFlags.visible = true; + _screenFlags.get = SCREVENT_ALL; + _callback = NULL; + _script = NULL; + _spareScreen = NULL; + _bgCurrent = NULL; + _bgSpare = NULL; + reset(); + + // Set up system palette colors and the two colors for text display + _vm->_palette->setMadsSystemPalette(); + RGB8 palData[3]; + palData[0].r = palData[0].g = palData[0].b = 0; + palData[1].r = 0; palData[1].g = palData[1].b = 252; + palData[2].r = 0; palData[2].g = palData[2].b = 180; + _vm->_palette->setPalette(&palData[0], 4, 3); + _vm->_palette->blockRange(4, 3); + + _vm->_font->setColors(5, 6, 4); + + empty(); + _bgSurface.empty(); + _textSurface.empty(); + + int y = (height() - MADS_SURFACE_HEIGHT) / 2; + setColor(2); + hLine(0, width() - 1, y - 2); + hLine(0, width() - 1, height() - y + 1); +} + +TextviewView::~TextviewView() { + if (_script) + _vm->res()->toss(_resourceName); + if (_spareScreen) + delete _spareScreen; + if (_bgCurrent) + delete _bgCurrent; + if (_bgSpare) + delete _bgSpare; +} + +void TextviewView::reset() { + _bgSurface.empty(); + _textSurface.empty(); + _animating = false; + _panX = 0; + _panY = 0; + _panSpeed = 0; + _soundDriverLoaded = false; + Common::set_to(&_spareScreens[0], &_spareScreens[10], 0); + _scrollCount = 0; + _lineY = -1; + _scrollTimeout = 0; + _panCountdown = 0; + _processEvents = true; +} + +void TextviewView::setScript(const char *resourceName, TextviewCallback callback) { + _callback = callback; + if (_script) + _vm->res()->toss(_resourceName); + if (_spareScreen) { + delete _spareScreen; + _spareScreen = NULL; + } + + reset(); + + strncpy(_resourceName, resourceName, 15); + _resourceName[15] = '\0'; + if (!strchr(_resourceName, '.')) + strcat(_resourceName, ".txr"); + + _script = _vm->res()->get(_resourceName); + + processLines(); +} + +bool TextviewView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) { + if (!_processEvents) + return false; + + // Wait for the Escape key or a mouse press + if (((eventType == KEVENT_KEY) && (param == Common::KEYCODE_ESCAPE)) || + (eventType == MEVENT_LEFT_RELEASE) || (eventType == MEVENT_RIGHT_RELEASE)) { + scriptDone(); + captureEvents = false; + return true; + } + + return false; +} + +void TextviewView::updateState() { + if (!_animating) + return; + + // Only update state if wait period has expired + uint32 currTime = g_system->getMillis(); + + // If a screen transition is in progress and it's time for another column, handle it + if (_spareScreen) { + byte *srcP = _spareScreen->getBasePtr(_translationX, 0); + byte *destP = _bgSurface.getBasePtr(_translationX, 0); + + for (int y = 0; y < _bgSurface.height(); ++y, srcP += _spareScreen->width(), + destP += _bgSurface.width()) { + *destP = *srcP; + } + + if (++_translationX >= _bgSurface.width()) { + // Surface transition is complete + delete _spareScreen; + _spareScreen = NULL; + + _vm->_palette->deleteRange(_bgCurrent); + delete _bgCurrent; + _bgCurrent = _bgSpare; + _bgSpare = NULL; + } + } + + // Make sure it's time for an update + if (currTime < _scrollTimeout) + return; + _scrollTimeout = g_system->getMillis() + TEXT_ANIMATION_DELAY; + + // If any panning values are set, pan the background surface + if ((_panX != 0) || (_panY != 0)) { + if (_panCountdown > 0) { + --_panCountdown; + return; + } + + // Handle horizontal panning + if (_panX != 0) { + byte *lineTemp = new byte[_panX]; + for (int y = 0; y < _bgSurface.height(); ++y) { + byte *pixelsP = _bgSurface.getBasePtr(0, y); + + // Copy the first X pixels into temp buffer, move the rest of the line + // to the start of the line, and then move temp buffer pixels to end of line + Common::copy(pixelsP, pixelsP + _panX, lineTemp); + Common::copy(pixelsP + _panX, pixelsP + _bgSurface.width(), pixelsP); + Common::copy(lineTemp, lineTemp + _panX, pixelsP + _bgSurface.width() - _panX); + } + + delete[] lineTemp; + } + + // Handle vertical panning + if (_panY != 0) { + // Store the bottom Y lines into a temp buffer, move the rest of the lines down, + // and then copy the stored lines back to the top of the screen + byte *linesTemp = new byte[_panY * _bgSurface.width()]; + byte *pixelsP = _bgSurface.getBasePtr(0, _bgSurface.height() - _panY); + Common::copy(pixelsP, pixelsP + _bgSurface.width() * _panY, linesTemp); + + for (int y = _bgSurface.height() - 1; y >= _panY; --y) { + byte *destP = _bgSurface.getBasePtr(0, y); + byte *srcP = _bgSurface.getBasePtr(0, y - _panY); + Common::copy(srcP, srcP + _bgSurface.width(), destP); + } + + Common::copy(linesTemp, linesTemp + _panY * _bgSurface.width(), (byte *)_bgSurface.pixels); + delete[] linesTemp; + } + } + + // Scroll the text surface up by one row + byte *pixelsP = (byte *)_textSurface.pixels; + Common::copy(pixelsP + width(), pixelsP + _textSurface.width() * _textSurface.height(), pixelsP); + pixelsP = _textSurface.getBasePtr(0, _textSurface.height() - 1); + Common::set_to(pixelsP, pixelsP + _textSurface.width(), _vm->_palette->BLACK); + + if (_scrollCount > 0) { + // Handling final scrolling of text off of screen + if (--_scrollCount == 0) { + scriptDone(); + return; + } + } else { + // Handling a text row + if (++_lineY == (_vm->_font->getHeight() + TEXTVIEW_LINE_SPACING)) + processLines(); + } + + // Refresh the view + int yp = (height() - _bgSurface.height()) / 2; + _bgSurface.copyTo(this, 0, yp); + _textSurface.copyTo(this, Common::Rect(0, 0, _textSurface.width(), _bgSurface.height()), + 0, yp, _vm->_palette->BLACK); +} + +void TextviewView::scriptDone() { + TextviewCallback fn = _callback; + M4Engine *vm = _vm; + + // Remove this view from manager and destroy it + _vm->_viewManager->deleteView(this); + + if (fn) + fn(vm); +} + +void TextviewView::processLines() { + if (_script->eos()) + error("Attempted to read past end of response file"); + + while (!_script->eos()) { + _script->readLine(_currentLine, 79); + + // Commented out line, so go loop for another + if (_currentLine[0] == '#') + continue; + + // Process the line + char *cStart = strchr(_currentLine, '['); + if (cStart) { + while (cStart) { + // Loop for possible multiple commands on one line + char *cEnd = strchr(_currentLine, ']'); + if (!cEnd) + error("Unterminated command '%s' in response file", _currentLine); + + *cEnd = '\0'; + processCommand(); + + // Copy rest of line (if any) to start of buffer + strcpy(_currentLine, cEnd + 1); + + cStart = strchr(_currentLine, '['); + } + + if (_currentLine[0]) { + processText(); + break; + } + + } else { + processText(); + break; + } + } +} + +void TextviewView::processCommand() { + char commandStr[80]; + char *paramP; + strcpy(commandStr, _currentLine + 1); + str_upper(commandStr); + + if (!strncmp(commandStr, "BACKGROUND", 10)) { + // Set the background + paramP = commandStr + 10; + int screenId = getParameter(¶mP); + _bgSurface.loadBackground(screenId, &_bgCurrent); + _vm->_palette->addRange(_bgCurrent); + _bgSurface.translate(_bgCurrent); + + } else if (!strncmp(commandStr, "GO", 2)) { + _animating = true; + + // Grab what the final palete will be + RGB8 destPalette[256]; + _vm->_palette->grabPalette(destPalette, 0, 256); + + // Copy the loaded background, if any, to the view surface + int yp = (height() - _bgSurface.height()) / 2; + _bgSurface.copyTo(this, 0, yp); + + // Handle fade-in + _processEvents = false; // stop processing events during fade-in + _vm->_palette->fadeIn(TV_NUM_FADE_STEPS, TV_FADE_DELAY_MILLI, destPalette, 256); + _processEvents = true; + + } else if (!strncmp(commandStr, "PAN", 3)) { + // Set panning values + paramP = commandStr + 3; + int panX = getParameter(¶mP); + int panY = getParameter(¶mP); + int panSpeed = getParameter(¶mP); + + if ((panX != 0) || (panY != 0)) { + _panX = panX; + _panY = panY; + _panSpeed = panSpeed; + } + + } else if (!strncmp(commandStr, "DRIVER", 6)) { + // Set the driver to use + // TODO: Handling of the sound drivers + + } else if (!strncmp(commandStr, "SOUND", 5)) { + // Set sound number + paramP = commandStr + 5; + //int soundId = getParameter(¶mP); + + //TODO: Proper handling of the sound drivers/sounds + //if (!_soundDriverLoaded) + // error("Attempted to set sound without loading any driver\n"); + + } else if (!strncmp(commandStr, "COLOR", 5) && ((commandStr[5] == '0') || (commandStr[5] == '1'))) { + // Set the text colors + int index = commandStr[5] - '0'; + paramP = commandStr + 6; + + RGB8 palEntry; + palEntry.r = getParameter(¶mP) << 2; + palEntry.g = getParameter(¶mP) << 2; + palEntry.b = getParameter(¶mP) << 2; + _vm->_palette->setPalette(&palEntry, 5 + index, 1); + + } else if (!strncmp(commandStr, "SPARE", 5)) { + // Sets a secondary background number that can be later switched in with a PAGE command + paramP = commandStr + 6; + int spareIndex = commandStr[5] - '0'; + if ((spareIndex >= 0) && (spareIndex <= 9)) { + int screenId = getParameter(¶mP); + + _spareScreens[spareIndex] = screenId; + } + + } else if (!strncmp(commandStr, "PAGE", 4)) { + // Signals to change to a previous specified secondary background + paramP = commandStr + 4; + int spareIndex = getParameter(¶mP); + + // Only allow background switches if one isn't currently in progress + if (!_spareScreen && (_spareScreens[spareIndex] != 0)) { + _spareScreen = new M4Surface(width(), MADS_SURFACE_HEIGHT); + _spareScreen->loadBackground(_spareScreens[spareIndex], &_bgSpare); + _vm->_palette->addRange(_bgSpare); + _spareScreen->translate(_bgSpare); + + _translationX = 0; + } + + } else { + error("Unknown response command: '%s'", commandStr); + } +} + +int TextviewView::getParameter(char **paramP) { + if ((**paramP != '=') && (**paramP != ',')) + return 0; + + int result = 0; + ++*paramP; + while ((**paramP >= '0') && (**paramP <= '9')) { + result = result * 10 + (**paramP - '0'); + ++*paramP; + } + + return result; +} + +void TextviewView::processText() { + int lineWidth, xStart; + + if (!strcmp(_currentLine, "***")) { + // Special signifier for end of script + _scrollCount = _vm->_font->getHeight() * 13; + _lineY = -1; + return; + } + + _lineY = 0; + + // Lines are always centered, except if line contains a '@', in which case the + // '@' marks the position that must be horizontally centered + char *centerP = strchr(_currentLine, '@'); + if (centerP) { + *centerP = '\0'; + xStart = (width() / 2) - _vm->_font->getWidth(_currentLine); + + // Delete the @ character and shift back the remainder of the string + char *p = centerP + 1; + if (*p == ' ') ++p; + strcpy(centerP, p); + + } else { + lineWidth = _vm->_font->getWidth(_currentLine); + xStart = (width() - lineWidth) / 2; + } + + // Copy the text line onto the bottom of the textSurface surface, which will allow it + // to gradually scroll onto the screen + int yp = _textSurface.height() - _vm->_font->getHeight() - TEXTVIEW_LINE_SPACING; + _textSurface.fillRect(Common::Rect(0, yp, _textSurface.width(), _textSurface.height()), + _vm->_palette->BLACK); + _vm->_font->writeString(&_textSurface, _currentLine, xStart, yp); +} + + +//-------------------------------------------------------------------------- + +AnimviewView::AnimviewView(M4Engine *vm): + View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())), + _bgSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT) { + + _screenType = VIEWID_ANIMVIEW; + _screenFlags.layer = LAYER_BACKGROUND; + _screenFlags.visible = true; + _screenFlags.get = SCREVENT_ALL; + _callback = NULL; + _script = NULL; + _palData = NULL; + _previousUpdate = 0; + _transition = kTransitionNone; + reset(); + + // Set up system palette colors + _vm->_palette->setMadsSystemPalette(); + + empty(); + _bgSurface.empty(); + + int y = (height() - MADS_SURFACE_HEIGHT) / 2; + setColor(2); + hLine(0, width() - 1, y - 2); + hLine(0, width() - 1, height() - y + 1); +} + +AnimviewView::~AnimviewView() { + if (_script) + _vm->res()->toss(_resourceName); +} + +void AnimviewView::reset() { + _bgSurface.empty(); + _soundDriverLoaded = false; +} + +void AnimviewView::setScript(const char *resourceName, AnimviewCallback callback) { + _callback = callback; + if (_script) + _vm->res()->toss(_resourceName); + + reset(); + + strncpy(_resourceName, resourceName, 15); + _resourceName[15] = '\0'; + if (!strchr(_resourceName, '.')) + strcat(_resourceName, ".res"); + + _script = _vm->res()->get(_resourceName); + + processLines(); +} + +bool AnimviewView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) { + // Wait for the Escape key or a mouse press + if (((eventType == KEVENT_KEY) && (param == Common::KEYCODE_ESCAPE)) || + (eventType == MEVENT_LEFT_RELEASE) || (eventType == MEVENT_RIGHT_RELEASE)) { + scriptDone(); + captureEvents = false; + return true; + } + + return false; +} + +void AnimviewView::updateState() { + char bgFile[10]; + int bgNumber = 0; + + // Only update state if wait period has expired + if (_previousUpdate > 0) { + if (g_system->getMillis() - _previousUpdate < 3000) { + return; + } else { + // time for an update + _previousUpdate = g_system->getMillis(); + } + } else { + _previousUpdate = g_system->getMillis(); + return; + } + uint32 currTime = g_system->getMillis(); + + strncpy(bgFile, _currentFile, 5); + bgFile[0] = bgFile[2]; + bgFile[1] = bgFile[3]; + bgFile[2] = bgFile[4]; + bgFile[3] = '\0'; + bgNumber = atoi(bgFile); + sprintf(bgFile, "rm%i.art", bgNumber); + + // Not all scenes have a background. If there is one, refresh it + if (_vm->_resourceManager->resourceExists(bgFile)) { + if (_palData) { + _vm->_palette->deleteRange(_palData); + delete _palData; + } + _bgSurface.loadBackground(bgNumber, &_palData); + _vm->_palette->addRange(_palData); + _bgSurface.translate(_palData); + } + + // Grab what the final palete will be + RGB8 destPalette[256]; + _vm->_palette->grabPalette(destPalette, 0, 256); + + // Handle scene transition + switch (_transition) { + case kTransitionNone: + // nothing to do + break; + case kTransitionFadeIn: + case kTransitionFadeIn2: + _vm->_palette->fadeIn(TV_NUM_FADE_STEPS, TV_FADE_DELAY_MILLI, destPalette, 256); + break; + case kTransitionBoxInBottomLeft: + case kTransitionBoxInBottomRight: + case kTransitionBoxInTopLeft: + case kTransitionBoxInTopRight: + // unused + warning("Unsupported box in scene effect"); + break; + case kTransitionPanLeftToRight: + // TODO + break; + case kTransitionPanRightToLeft: + // TODO + break; + case kTransitionCircleIn: + // TODO + break; + default: + // nothing to do + break; + } + + // Refresh the view + int yp = (height() - _bgSurface.height()) / 2; + _bgSurface.copyTo(this, 0, yp); + + // Read next line + processLines(); +} + +void AnimviewView::scriptDone() { + AnimviewCallback fn = _callback; + M4Engine *vm = _vm; + + // Remove this view from manager and destroy it + _vm->_viewManager->deleteView(this); + + if (fn) + fn(vm); +} + +void AnimviewView::processLines() { + if (_script->eos()) { + // end of script, end animation + scriptDone(); + return; + } + + while (!_script->eos()) { + _script->readLine(_currentLine, 79); + + // Process the line + char *cStart = strchr(_currentLine, '-'); + if (cStart) { + while (cStart) { + // Loop for possible multiple commands on one line + char *cEnd = strchr(_currentLine, ' '); + if (!cEnd) + error("Unterminated command '%s' in response file", _currentLine); + + *cEnd = '\0'; + processCommand(); + + // Copy rest of line (if any) to start of buffer + // Don't use strcpy() here, because if the + // rest of the line is the longer of the two + // strings, the memory areas will overlap. + memmove(_currentLine, cEnd + 1, strlen(cEnd + 1) + 1); + + cStart = strchr(_currentLine, '-'); + } + + if (_currentLine[0]) { + sprintf(_currentFile, "%s", _currentLine); + //printf("File: %s\n", _currentLine); + break; + } + + } else { + sprintf(_currentFile, "%s", _currentLine); + //printf("File: %s\n", _currentLine); + break; + } + } +} + +/* +Switches are: (taken from the help of the original executable) + -b Toggle background load status off/on. + -c:char Specify sound card id letter. + -g Stay in graphics mode on exit. + -h[:ex] Disable EMS/XMS high memory support. + -i Switch sound interrupts mode off/on. + -j Wait for music to finish at end. + -k Keystroke jumps to end instead of abort. + -m Operate in non-MADS mode. + -o:xxx Specify opening special effect. + -p Switch MADS path mode to CONCAT. + -r[:abn] Resynch timer (always, beginning, never). + -s:file Specify sound file. + -u[:...] Use DMA speech [optional: addr,type,irq,drq]. + -w Toggle white bars off/on. + -x Exit immediately after last frame. + -y Do not clear screen initially + -z Display statistics after run. + + Opening special effects are: + 0: no effect + 1: fade in + 2: fade in (looks to be the same as 1) + 3: box in from bottom left (unused) + 4: box in from bottom right (unused) + 5: box in from top left (unused) + 6: box in from top right (unused) + 7: pan in from left to right + 8: pan in from right to left + 9: circle in (new scene appears in a circle that expands) + + Animview is ran like this from the original games: + animview.exe @resfilename -c:P,220,20 -u:220,20,07,01 -p -a:mainmenu -p + + Note that the first -p is necessary to watch the animation, otherwise + the program just exits + + To watch an animation within the *.res file, just run animview like this: + animview.exe -x -r:b -o:2 animfilename -p +*/ +void AnimviewView::processCommand() { + char commandStr[80]; + strcpy(commandStr, _currentLine + 1); + str_upper(commandStr); + char *param = commandStr; + + if (!strncmp(commandStr, "X", 1)) { + //printf("X "); + } else if (!strncmp(commandStr, "W", 1)) { + //printf("W "); + } else if (!strncmp(commandStr, "R", 1)) { + param = param + 2; + //printf("R:%s ", param); + } else if (!strncmp(commandStr, "O", 1)) { + param = param + 2; + //printf("O:%i ", atoi(param)); + _transition = atoi(param); + } else { + error("Unknown response command: '%s'", commandStr); + } +} + +} |