From 673537bad93f0b440172a0cc263ebf19cc95ffc0 Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Wed, 15 Oct 2014 11:33:14 +0300 Subject: MADS: Move all the anim and text view code into a common class The animation and text players are more or less common among all MADS games --- engines/mads/debugger.cpp | 4 +- engines/mads/dialogs.cpp | 73 +++ engines/mads/dialogs.h | 32 ++ engines/mads/menu_views.cpp | 782 +++++++++++++++++++++++++++++++ engines/mads/menu_views.h | 226 +++++++++ engines/mads/module.mk | 1 + engines/mads/nebular/dialogs_nebular.cpp | 61 --- engines/mads/nebular/dialogs_nebular.h | 32 -- engines/mads/nebular/menu_nebular.cpp | 753 +---------------------------- engines/mads/nebular/menu_nebular.h | 190 +------- 10 files changed, 1118 insertions(+), 1036 deletions(-) create mode 100644 engines/mads/menu_views.cpp create mode 100644 engines/mads/menu_views.h (limited to 'engines/mads') diff --git a/engines/mads/debugger.cpp b/engines/mads/debugger.cpp index 3b77d5332b..99251f9fbb 100644 --- a/engines/mads/debugger.cpp +++ b/engines/mads/debugger.cpp @@ -362,7 +362,7 @@ bool Debugger::Cmd_PlayAnim(int argc, const char **argv) { Common::File f; if (f.exists(resName) || f.exists(resName + ".res")) { - Nebular::AnimationView::execute(_vm, resName); + AnimationView::execute(_vm, resName); return false; } else { debugPrintf("Could not find resource file\n"); @@ -382,7 +382,7 @@ bool Debugger::Cmd_PlayText(int argc, const char **argv) { Common::File f; if (f.exists(resName) || f.exists(resName + ".txr")) { - Nebular::TextView::execute(_vm, resName); + TextView::execute(_vm, resName); return false; } else { debugPrintf("Could not find resource file\n"); diff --git a/engines/mads/dialogs.cpp b/engines/mads/dialogs.cpp index 7e6909d113..5ea8fb115c 100644 --- a/engines/mads/dialogs.cpp +++ b/engines/mads/dialogs.cpp @@ -395,4 +395,77 @@ Dialogs::Dialogs(MADSEngine *vm) _pendingDialog = DIALOG_NONE; } +/*------------------------------------------------------------------------*/ + +FullScreenDialog::FullScreenDialog(MADSEngine *vm) : _vm(vm) { + switch (_vm->getGameID()) { + case GType_RexNebular: + _screenId = 990; + break; + case GType_Phantom: + _screenId = 920; + break; + case GType_Dragonsphere: + _screenId = 922; + break; + default: + error("FullScreenDialog:Unknown game"); + } + _palFlag = true; +} + +FullScreenDialog::~FullScreenDialog() { + _vm->_screen.resetClipBounds(); + _vm->_game->_scene.restrictScene(); +} + +void FullScreenDialog::display() { + Game &game = *_vm->_game; + Scene &scene = game._scene; + + int nextSceneId = scene._nextSceneId; + int currentSceneId = scene._currentSceneId; + int priorSceneId = scene._priorSceneId; + + if (_screenId > 0) { + SceneInfo *sceneInfo = SceneInfo::init(_vm); + sceneInfo->load(_screenId, 0, "", 0, scene._depthSurface, scene._backgroundSurface); + } + + scene._priorSceneId = priorSceneId; + scene._currentSceneId = currentSceneId; + scene._nextSceneId = nextSceneId; + + _vm->_events->initVars(); + game._kernelMode = KERNEL_ROOM_INIT; + + byte pal[768]; + if (_vm->_screenFade) { + Common::fill(&pal[0], &pal[PALETTE_SIZE], 0); + _vm->_palette->setFullPalette(pal); + } else { + _vm->_palette->getFullPalette(pal); + _vm->_palette->fadeOut(pal, nullptr, 0, PALETTE_COUNT, 0, 1, 1, 16); + } + + // Set Fx state and palette entries + game._fx = _vm->_screenFade == SCREEN_FADE_SMOOTH ? kTransitionFadeIn : kCenterVertTransition; + game._trigger = 0; + + // Clear the screen and draw the upper and lower horizontal lines + _vm->_screen.empty(); + _vm->_palette->setLowRange(); + _vm->_screen.hLine(0, 20, MADS_SCREEN_WIDTH, 2); + _vm->_screen.hLine(0, 179, MADS_SCREEN_WIDTH, 2); + _vm->_screen.copyRectToScreen(Common::Rect(0, 0, MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT)); + + // Restrict the screen to the area between the two lines + _vm->_screen.setClipBounds(Common::Rect(0, DIALOG_TOP, MADS_SCREEN_WIDTH, + DIALOG_TOP + MADS_SCENE_HEIGHT)); + _vm->_game->_scene.restrictScene(); + + if (_screenId > 0) + scene._spriteSlots.fullRefresh(); +} + } // End of namespace MADS diff --git a/engines/mads/dialogs.h b/engines/mads/dialogs.h index c586a6f1fe..317c7bd792 100644 --- a/engines/mads/dialogs.h +++ b/engines/mads/dialogs.h @@ -30,6 +30,8 @@ namespace MADS { +#define DIALOG_TOP 22 + class Dialog { private: void setDialogPalette(); @@ -226,6 +228,36 @@ public: virtual bool show(int messageId, int objectId = -1) = 0; }; +class FullScreenDialog: public EventTarget { +protected: + /** + * Engine reference + */ + MADSEngine *_vm; + + /** + * Screen/scene to show background from + */ + int _screenId; + + /** + * Flag for palette initialization + */ + bool _palFlag; + + /** + * Handles displaying the screen background and dialog + */ + virtual void display(); +public: + /** + * Constructor + */ + FullScreenDialog(MADSEngine *vm); + + virtual ~FullScreenDialog(); +}; + } // End of namespace MADS #endif /* MADS_DIALOGS_H */ diff --git a/engines/mads/menu_views.cpp b/engines/mads/menu_views.cpp new file mode 100644 index 0000000000..6acf6cdf9f --- /dev/null +++ b/engines/mads/menu_views.cpp @@ -0,0 +1,782 @@ +/* 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. + * + */ + +#include "common/scummsys.h" +#include "mads/game.h" +#include "mads/mads.h" +#include "mads/menu_views.h" +#include "mads/resources.h" +#include "mads/scene.h" +#include "mads/screen.h" + +namespace MADS { + +MenuView::MenuView(MADSEngine *vm) : FullScreenDialog(vm) { + _breakFlag = false; + _redrawFlag = true; + _palFlag = false; +} + +void MenuView::show() { + Scene &scene = _vm->_game->_scene; + EventsManager &events = *_vm->_events; + _vm->_screenFade = SCREEN_FADE_FAST; + + scene._spriteSlots.reset(true); + display(); + + events.setEventTarget(this); + events.hideCursor(); + + while (!_breakFlag && !_vm->shouldQuit()) { + if (_redrawFlag) { + scene._kernelMessages.update(); + + _vm->_game->_scene.drawElements(_vm->_game->_fx, _vm->_game->_fx); + _redrawFlag = false; + } + + _vm->_events->waitForNextFrame(); + _vm->_game->_fx = kTransitionNone; + doFrame(); + } + + events.setEventTarget(nullptr); + _vm->_sound->stop(); +} + +void MenuView::display() { + _vm->_palette->resetGamePalette(4, 8); + + FullScreenDialog::display(); +} + +bool MenuView::onEvent(Common::Event &event) { + if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN) { + _breakFlag = true; + _vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU; + return true; + } + + return false; +} + +/*------------------------------------------------------------------------*/ + +char TextView::_resourceName[100]; +#define TEXTVIEW_LINE_SPACING 2 +#define TEXT_ANIMATION_DELAY 100 +#define TV_NUM_FADE_STEPS 40 +#define TV_FADE_DELAY_MILLI 50 + +void TextView::execute(MADSEngine *vm, const Common::String &resName) { + assert(resName.size() < 100); + Common::strlcpy(_resourceName, resName.c_str(), sizeof(_resourceName)); + vm->_dialogs->_pendingDialog = DIALOG_TEXTVIEW; +} + +TextView::TextView(MADSEngine *vm) : MenuView(vm) { + _animating = false; + _panSpeed = 0; + _spareScreen = nullptr; + _scrollCount = 0; + _lineY = -1; + _scrollTimeout = 0; + _panCountdown = 0; + _translationX = 0; + _screenId = -1; + + _font = _vm->_font->getFont(FONT_CONVERSATION); + _vm->_palette->resetGamePalette(4, 0); + + load(); +} + +TextView::~TextView() { +} + +void TextView::load() { + Common::String scriptName(_resourceName); + scriptName += ".txr"; + + if (!_script.open(scriptName)) + error("Could not open resource %s", _resourceName); + + processLines(); +} + +void TextView::processLines() { + if (_script.eos()) + error("Attempted to read past end of response file"); + + while (!_script.eos()) { + // Read in the next line + _script.readLine(_currentLine, 79); + char *p = _currentLine + strlen(_currentLine) - 1; + if (*p == '\n') + *p = '\0'; + + // 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 + Common::strlcpy(_currentLine, cEnd + 1, sizeof(_currentLine)); + + cStart = strchr(_currentLine, '['); + } + + if (_currentLine[0]) { + processText(); + break; + } + + } else { + processText(); + break; + } + } +} + +void TextView::processCommand() { + Scene &scene = _vm->_game->_scene; + Common::String scriptLine(_currentLine + 1); + scriptLine.toUppercase(); + const char *paramP; + const char *commandStr = scriptLine.c_str(); + + if (!strncmp(commandStr, "BACKGROUND", 10)) { + // Set the background + paramP = commandStr + 10; + resetPalette(); + int screenId = getParameter(¶mP); + + SceneInfo *sceneInfo = SceneInfo::init(_vm); + sceneInfo->load(screenId, 0, "", 0, scene._depthSurface, scene._backgroundSurface); + scene._spriteSlots.fullRefresh(); + _redrawFlag = true; + + } else if (!strncmp(commandStr, "GO", 2)) { + _animating = 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)) { + _pan = Common::Point(panX, panY); + _panSpeed = panSpeed; + } + + } else if (!strncmp(commandStr, "DRIVER", 6)) { + // Set the driver to use + paramP = commandStr + 7; + + if (!strncmp(paramP, "#SOUND.00", 9)) { + int driverNum = paramP[9] - '0'; + _vm->_sound->init(driverNum); + } + } else if (!strncmp(commandStr, "SOUND", 5)) { + // Set sound number + paramP = commandStr + 5; + int soundId = getParameter(¶mP); + _vm->_sound->command(soundId); + + } else if (!strncmp(commandStr, "COLOR", 5) && ((commandStr[5] == '0') || + (commandStr[5] == '1'))) { + // Set the text colors + int index = commandStr[5] - '0'; + paramP = commandStr + 6; + + byte r = getParameter(¶mP); + byte g = getParameter(¶mP); + byte b = getParameter(¶mP); + + _vm->_palette->setEntry(5 + index, r, g, b); + + } 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'; + assert(spareIndex < 4); + int screenId = getParameter(¶mP); + + // Load the spare background + SceneInfo *sceneInfo = SceneInfo::init(_vm); + sceneInfo->_width = MADS_SCREEN_WIDTH; + sceneInfo->_height = MADS_SCENE_HEIGHT; + _spareScreens[spareIndex].setSize(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT); + sceneInfo->loadMadsV1Background(screenId, "", SCENEFLAG_TRANSLATE, + _spareScreens[spareIndex]); + delete sceneInfo; + + } 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].getPixels() != nullptr) { + _spareScreen = &_spareScreens[spareIndex]; + _translationX = 0; + } + + } else { + error("Unknown response command: '%s'", commandStr); + } +} + +int TextView::getParameter(const 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 TextView::processText() { + int xStart; + + if (!strcmp(_currentLine, "***")) { + // Special signifier for end of script + _scrollCount = _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 = (MADS_SCREEN_WIDTH / 2) - _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 { + int lineWidth = _font->getWidth(_currentLine); + xStart = (MADS_SCREEN_WIDTH - lineWidth) / 2; + } + + // Add the new line to the list of pending lines + TextLine tl; + tl._pos = Common::Point(xStart, MADS_SCENE_HEIGHT); + tl._line = _currentLine; + tl._textDisplayIndex = -1; + _textLines.push_back(tl); +} + +void TextView::display() { + FullScreenDialog::display(); +} + +void TextView::resetPalette() { + _vm->_palette->resetGamePalette(8, 8); + _vm->_palette->setEntry(5, 0, 63, 63); + _vm->_palette->setEntry(6, 0, 45, 45); +} + +void TextView::doFrame() { + Scene &scene = _vm->_game->_scene; + 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 *bgP = scene._backgroundSurface.getBasePtr(_translationX, 0); + byte *screenP = (byte *)_vm->_screen.getBasePtr(_translationX, 0); + + for (int y = 0; y < MADS_SCENE_HEIGHT; ++y, srcP += MADS_SCREEN_WIDTH, + bgP += MADS_SCREEN_WIDTH, screenP += MADS_SCREEN_WIDTH) { + *bgP = *srcP; + *screenP = *srcP; + } + + // Flag the column of the screen is modified + _vm->_screen.copyRectToScreen(Common::Rect(_translationX, 0, + _translationX + 1, MADS_SCENE_HEIGHT)); + + // Keep moving the column to copy to the right + if (++_translationX == MADS_SCREEN_WIDTH) { + // Surface transition is complete + _spareScreen = nullptr; + } + } + + // Make sure it's time for an update + if (currTime < _scrollTimeout) + return; + _scrollTimeout = g_system->getMillis() + TEXT_ANIMATION_DELAY; + _redrawFlag = true; + + // If any panning values are set, pan the background surface + if ((_pan.x != 0) || (_pan.y != 0)) { + if (_panCountdown > 0) { + --_panCountdown; + return; + } + + // Handle horizontal panning + if (_pan.x != 0) { + byte *lineTemp = new byte[_pan.x]; + for (int y = 0; y < MADS_SCENE_HEIGHT; ++y) { + byte *pixelsP = (byte *)scene._backgroundSurface.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 + _pan.x, lineTemp); + Common::copy(pixelsP + _pan.x, pixelsP + MADS_SCREEN_WIDTH, pixelsP); + Common::copy(lineTemp, lineTemp + _pan.x, pixelsP + MADS_SCREEN_WIDTH - _pan.x); + } + + delete[] lineTemp; + } + + // Handle vertical panning + if (_pan.y != 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[_pan.y * MADS_SCREEN_WIDTH]; + byte *pixelsP = (byte *)scene._backgroundSurface.getBasePtr(0, MADS_SCENE_HEIGHT - _pan.y); + Common::copy(pixelsP, pixelsP + MADS_SCREEN_WIDTH * _pan.y, linesTemp); + + for (int y = MADS_SCENE_HEIGHT - 1; y >= _pan.y; --y) { + byte *destP = (byte *)scene._backgroundSurface.getBasePtr(0, y); + byte *srcP = (byte *)scene._backgroundSurface.getBasePtr(0, y - _pan.y); + Common::copy(srcP, srcP + MADS_SCREEN_WIDTH, destP); + } + + Common::copy(linesTemp, linesTemp + _pan.y * MADS_SCREEN_WIDTH, + (byte *)scene._backgroundSurface.getPixels()); + delete[] linesTemp; + } + + // Flag for a full screen refresh + scene._spriteSlots.fullRefresh(); + } + + // Scroll all active text lines up + for (int i = _textLines.size() - 1; i >= 0; --i) { + TextLine &tl = _textLines[i]; + if (tl._textDisplayIndex != -1) + // Expire the text line that's already on-screen + scene._textDisplay.expire(tl._textDisplayIndex); + + tl._pos.y--; + if (tl._pos.y < 0) { + _textLines.remove_at(i); + } else { + tl._textDisplayIndex = scene._textDisplay.add(tl._pos.x, tl._pos.y, + 0x605, -1, tl._line, _font); + } + } + + if (_scrollCount > 0) { + // Handling final scrolling of text off of screen + if (--_scrollCount == 0) { + scriptDone(); + return; + } + } else { + // Handling a text row + if (++_lineY == (_font->getHeight() + TEXTVIEW_LINE_SPACING)) + processLines(); + } +} + +void TextView::scriptDone() { + _breakFlag = true; + _vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU; +} + +/*------------------------------------------------------------------------*/ + +char AnimationView::_resourceName[100]; + +void AnimationView::execute(MADSEngine *vm, const Common::String &resName) { + assert(resName.size() < 100); + Common::strlcpy(_resourceName, resName.c_str(), sizeof(_resourceName)); + vm->_dialogs->_pendingDialog = DIALOG_ANIMVIEW; +} + +AnimationView::AnimationView(MADSEngine *vm) : MenuView(vm) { + _redrawFlag = false; + + _soundDriverLoaded = false; + _previousUpdate = 0; + _screenId = -1; + _resetPalette = false; + _resyncMode = NEVER; + _v1 = 0; + _v2 = -1; + _resourceIndex = -1; + _currentAnimation = nullptr; + _sfx = 0; + _soundFlag = _bgLoadFlag = true; + _showWhiteBars = true; + _manualFrameNumber = 0; + _manualSpriteSet = nullptr; + _manualStartFrame = _manualEndFrame = 0; + _manualFrame2 = 0; + _animFrameNumber = 0; + _nextCyclingActive = false; + _sceneInfo = SceneInfo::init(_vm); + + load(); +} + +AnimationView::~AnimationView() { + delete _currentAnimation; + delete _sceneInfo; +} + +void AnimationView::load() { + Common::String resName(_resourceName); + if (!resName.hasSuffix(".")) + resName += ".res"; + + if (!_script.open(resName)) + error("Could not open resource %s", resName.c_str()); + + processLines(); +} + +void AnimationView::display() { + Scene &scene = _vm->_game->_scene; + _vm->_palette->initPalette(); + Common::fill(&_vm->_palette->_cyclingPalette[0], &_vm->_palette->_cyclingPalette[PALETTE_SIZE], 0); + + _vm->_palette->resetGamePalette(1, 8); + scene._spriteSlots.reset(); + scene._paletteCycles.clear(); + + MenuView::display(); +} + +bool AnimationView::onEvent(Common::Event &event) { + // Wait for the Escape key or a mouse press + if (((event.type == Common::EVENT_KEYDOWN) && (event.kbd.keycode == Common::KEYCODE_ESCAPE)) || + (event.type == Common::EVENT_RBUTTONUP)) { + scriptDone(); + return true; + } + + return false; +} + +void AnimationView::doFrame() { + Scene &scene = _vm->_game->_scene; + + if (_resourceIndex == -1 || _currentAnimation->freeFlag()) { + if (++_resourceIndex == (int)_resources.size()) { + scriptDone(); + } else { + scene._frameStartTime = 0; + loadNextResource(); + } + } else if (_currentAnimation->getCurrentFrame() == 1) { + scene._cyclingActive = _nextCyclingActive; + } + + if (_currentAnimation) { + ++scene._frameStartTime; + _currentAnimation->update(); + _redrawFlag = true; + } +} + +void AnimationView::loadNextResource() { + Scene &scene = _vm->_game->_scene; + Palette &palette = *_vm->_palette; + ResourceEntry &resEntry = _resources[_resourceIndex]; + Common::Array paletteCycles; + + if (resEntry._bgFlag) + palette.resetGamePalette(1, 8); + + palette._mainPalette[253 * 3] = palette._mainPalette[253 * 3 + 1] + = palette._mainPalette[253 * 3 + 2] = 0xb4; + palette.setPalette(&palette._mainPalette[253 * 3], 253, 1); + + // Free any previous messages + scene._kernelMessages.reset(); + + // Handle the bars at the top/bottom + if (resEntry._showWhiteBars) { + // For animations the screen has been clipped to the middle 156 rows. + // So although it's slightly messy, bypass our screen class entirely, + // and draw the horizontal lines directly on the physiacl screen surface + Graphics::Surface *s = g_system->lockScreen(); + s->hLine(0, 20, MADS_SCREEN_WIDTH, 253); + s->hLine(0, 179, MADS_SCREEN_WIDTH, 253); + g_system->unlockScreen(); + } + + // Load the new animation + delete _currentAnimation; + _currentAnimation = Animation::init(_vm, &scene); + int flags = ANIMFLAG_ANIMVIEW | (resEntry._bgFlag ? ANIMFLAG_LOAD_BACKGROUND : 0); + _currentAnimation->load(scene._backgroundSurface, scene._depthSurface, + resEntry._resourceName, flags, &paletteCycles, _sceneInfo); + + // Signal for a screen refresh + scene._spriteSlots.fullRefresh(); + + // If a sound driver has been specified, then load the correct one + if (!_currentAnimation->_header._soundName.empty()) { + const char *chP = strchr(_currentAnimation->_header._soundName.c_str(), '.'); + assert(chP); + + // Handle both Rex naming (xxx.009) and naming in later games (e.g. xxx.ph9) + int driverNum = atoi(chP + 3); + // HACK for Dragon + if (_currentAnimation->_header._soundName == "#SOUND.DRG") + driverNum = 9; + _vm->_sound->init(driverNum); + } + + // Handle any manual setup + if (_currentAnimation->_header._manualFlag) { + _manualFrameNumber = _currentAnimation->_header._spritesIndex; + _manualSpriteSet = _currentAnimation->getSpriteSet(_manualFrameNumber); + } + + // Set the sound data for the animation + _vm->_sound->setEnabled(resEntry._soundFlag); + + Common::String dsrName = _currentAnimation->_header._dsrName; + if (!dsrName.empty()) + _vm->_audio->setSoundGroup(dsrName); + + // Start the new animation + _currentAnimation->startAnimation(0); + + // Handle the palette and cycling palette + scene._cyclingActive = false; + Common::copy(&palette._mainPalette[0], &palette._mainPalette[PALETTE_SIZE], + &palette._cyclingPalette[0]); + + _vm->_game->_fx = (ScreenTransition)resEntry._fx; + _nextCyclingActive = paletteCycles.size() > 0; + if (!_vm->_game->_fx) { + palette.setFullPalette(palette._mainPalette); + } + + scene.initPaletteAnimation(paletteCycles, _nextCyclingActive && !_vm->_game->_fx); +} + +void AnimationView::scriptDone() { + _breakFlag = true; + _vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU; +} + +void AnimationView::processLines() { + if (_script.eos()) { + // end of script, end animation + scriptDone(); + return; + } + + while (!_script.eos()) { + // Get in next line + _currentLine.clear(); + char c; + while (!_script.eos() && (c = _script.readByte()) != '\n') { + if (c != '\r' && c != '\0') + _currentLine += c; + } + + // Process the line + while (!_currentLine.empty()) { + if (_currentLine.hasPrefix("-")) { + _currentLine.deleteChar(0); + + processCommand(); + } else { + // Get resource name + Common::String resName; + while (!_currentLine.empty() && (c = _currentLine[0]) != ' ') { + _currentLine.deleteChar(0); + resName += c; + } + + // Add resource into list along with any set state information + _resources.push_back(ResourceEntry(resName, _sfx, _soundFlag, + _bgLoadFlag, _showWhiteBars)); + + // Fx resets between resource entries + _sfx = 0; + } + + // Skip any spaces + while (_currentLine.hasPrefix(" ")) + _currentLine.deleteChar(0); + } + } +} + +void AnimationView::processCommand() { + // Get the command character + char commandChar = toupper(_currentLine[0]); + _currentLine.deleteChar(0); + + // Handle the command + switch (commandChar) { + case 'B': + _soundFlag = !_soundFlag; + break; + case 'H': + // -h[:ex] Disable EMS / XMS high memory support + if (_currentLine.hasPrefix(":")) + _currentLine.deleteChar(0); + while (_currentLine.hasPrefix("e") || _currentLine.hasPrefix("x")) + _currentLine.deleteChar(0); + break; + case 'O': + // -o:xxx Specify opening special effect + assert(_currentLine[0] == ':'); + _currentLine.deleteChar(0); + _sfx = getParameter(); + break; + case 'P': + // Switch to CONCAT mode, which is ignored anyway + break; + case 'R': { + // Resynch timer (always, beginning, never) + assert(_currentLine[0] == ':'); + _currentLine.deleteChar(0); + + char v = toupper(_currentLine[0]); + _currentLine.deleteChar(0); + if (v == 'N') + _resyncMode = NEVER; + else if (v == 'A') + _resyncMode = ALWAYS; + else if (v == 'B') + _resyncMode = BEGINNING; + else + error("Unknown parameter"); + break; + } + case 'W': + // Switch white bars being visible + _showWhiteBars = !_showWhiteBars; + break; + case 'X': + // Exit after animation finishes. Ignore + break; + case 'D': + // Unimplemented and ignored in the original. Ignore as well + break; + case 'Y': + // Reset palette on startup + _resetPalette = true; + break; + default: + error("Unknown command char: '%c'", commandChar); + } +} + +int AnimationView::getParameter() { + int result = 0; + + while (!_currentLine.empty()) { + char c = _currentLine[0]; + + if (c >= '0' && c <= '9') { + _currentLine.deleteChar(0); + result = result * 10 + (c - '0'); + } else { + break; + } + } + + return result; +} + +void AnimationView::checkResource(const Common::String &resourceName) { + //bool hasSuffix = false; + +} + +int AnimationView::scanResourceIndex(const Common::String &resourceName) { + int foundIndex = -1; + + if (_v1) { + const char *chP = strchr(resourceName.c_str(), '\\'); + if (!chP) { + chP = strchr(resourceName.c_str(), '*'); + } + + Common::String resName = chP ? Common::String(chP + 1) : resourceName; + + if (_v2 != 3) { + assert(_resIndex.size() == 0); + } + + // Scan for the resource name + for (uint resIndex = 0; resIndex < _resIndex.size(); ++resIndex) { + ResIndexEntry &resEntry = _resIndex[resIndex]; + if (resEntry._resourceName.compareToIgnoreCase(resourceName)) { + foundIndex = resIndex; + break; + } + } + } + + if (foundIndex >= 0) { + // TODO + } + return -1; +} + +} // End of namespace MADS diff --git a/engines/mads/menu_views.h b/engines/mads/menu_views.h new file mode 100644 index 0000000000..6faa665bff --- /dev/null +++ b/engines/mads/menu_views.h @@ -0,0 +1,226 @@ +/* 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. + * + */ + +#ifndef MADS_MENU_VIEWS_H +#define MADS_MENU_VIEWS_H + +#include "common/scummsys.h" +#include "mads/dialogs.h" +#include "mads/game.h" +#include "mads/msurface.h" + +namespace MADS { + +class MADSEngine; + +class MenuView: public FullScreenDialog { +protected: + bool _breakFlag; + bool _redrawFlag; + + virtual void doFrame() = 0; + + virtual void display(); + + /** + * Event handler + */ + virtual bool onEvent(Common::Event &event); +public: + MenuView(MADSEngine *vm); + + virtual ~MenuView() {} + + virtual void show(); +}; + +struct TextLine { + Common::Point _pos; + Common::String _line; + int _textDisplayIndex; +}; + +/** + * Scrolling text view + */ +class TextView : public MenuView { +private: + static char _resourceName[100]; + + bool _animating; + Common::Array _textLines; + Common::Point _pan; + int _panSpeed; + MSurface _spareScreens[4]; + int _scrollCount; + int _lineY; + uint32 _scrollTimeout; + int _panCountdown; + int _translationX; + Common::File _script; + char _currentLine[80]; + MSurface *_spareScreen; + Font *_font; +private: + /** + * Load the text resource + */ + void load(); + + /** + * Process the lines + */ + void processLines(); + + /** + * Process a command from the script file + */ + void processCommand(); + + /** + * Process text from the script file + */ + void processText(); + + /** + * Get a parameter from line + */ + int getParameter(const char **paramP); + + /** + * Called when the script is finished + */ + void scriptDone(); + + /** + * Reset the game palette + */ + void resetPalette(); +protected: + virtual void display(); + + virtual void doFrame(); +public: + /** + * Queue the given text resource for display + */ + static void execute(MADSEngine *vm, const Common::String &resName); + + TextView(MADSEngine *vm); + + virtual ~TextView(); +}; + +enum ResyncMode { NEVER, ALWAYS, BEGINNING }; + +struct ResourceEntry { + Common::String _resourceName; + int _fx; + bool _soundFlag; + bool _bgFlag; + bool _showWhiteBars; + + ResourceEntry() {} + ResourceEntry(const Common::String &resName, int fx, bool soundFlag, + bool bgFlag, bool showWhiteBars) { + _resourceName = resName; + _fx = fx; + _soundFlag = soundFlag; + _bgFlag = bgFlag; + _showWhiteBars = showWhiteBars; + } +}; + +struct ResIndexEntry { + int _id; + int _v; + Common::String _resourceName; + + ResIndexEntry() {} +}; + +/** +* Animation cutscene view +*/ +class AnimationView : public MenuView { +private: + static char _resourceName[100]; + + Common::File _script; + uint32 _previousUpdate; + Common::String _currentLine; + bool _soundDriverLoaded; + bool _resetPalette; + ResyncMode _resyncMode; + int _sfx; + bool _soundFlag; + bool _bgLoadFlag; + bool _showWhiteBars; + Common::Array _resources; + Common::Array _resIndex; + int _v1; + int _v2; + int _resourceIndex; + SceneInfo *_sceneInfo; + Animation *_currentAnimation; + int _manualFrameNumber; + SpriteAsset *_manualSpriteSet; + int _manualStartFrame, _manualEndFrame; + int _manualFrame2; + int _animFrameNumber; + bool _nextCyclingActive; +private: + void checkResource(const Common::String &resourceName); + + int scanResourceIndex(const Common::String &resourceName); + + void load(); + + void processLines(); + + void processCommand(); + + int getParameter(); + + void scriptDone(); + + void loadNextResource(); +protected: + virtual void display(); + + virtual void doFrame(); + + virtual bool onEvent(Common::Event &event); +public: + /** + * Queue the given text resource for display + */ + static void execute(MADSEngine *vm, const Common::String &resName); + + AnimationView(MADSEngine *vm); + + virtual ~AnimationView(); +}; + +} // End of namespace MADS + +#endif /* MADS_MENU_VIEWS_H */ diff --git a/engines/mads/module.mk b/engines/mads/module.mk index 8a792a70a4..7cb7a91e8c 100644 --- a/engines/mads/module.mk +++ b/engines/mads/module.mk @@ -37,6 +37,7 @@ MODULE_OBJS := \ hotspots.o \ inventory.o \ mads.o \ + menu_views.o \ messages.o \ msurface.o \ palette.o \ diff --git a/engines/mads/nebular/dialogs_nebular.cpp b/engines/mads/nebular/dialogs_nebular.cpp index 35a7d3bdc6..86244bd3bb 100644 --- a/engines/mads/nebular/dialogs_nebular.cpp +++ b/engines/mads/nebular/dialogs_nebular.cpp @@ -553,67 +553,6 @@ void PictureDialog::restore() { /*------------------------------------------------------------------------*/ -FullScreenDialog::FullScreenDialog(MADSEngine *vm) : _vm(vm) { - _screenId = 990; - _palFlag = true; -} - -FullScreenDialog::~FullScreenDialog() { - _vm->_screen.resetClipBounds(); - _vm->_game->_scene.restrictScene(); -} - -void FullScreenDialog::display() { - Game &game = *_vm->_game; - Scene &scene = game._scene; - - int nextSceneId = scene._nextSceneId; - int currentSceneId = scene._currentSceneId; - int priorSceneId = scene._priorSceneId; - - if (_screenId > 0) { - SceneInfo *sceneInfo = SceneInfo::init(_vm); - sceneInfo->load(_screenId, 0, "", 0, scene._depthSurface, scene._backgroundSurface); - } - - scene._priorSceneId = priorSceneId; - scene._currentSceneId = currentSceneId; - scene._nextSceneId = nextSceneId; - - _vm->_events->initVars(); - game._kernelMode = KERNEL_ROOM_INIT; - - byte pal[768]; - if (_vm->_screenFade) { - Common::fill(&pal[0], &pal[PALETTE_SIZE], 0); - _vm->_palette->setFullPalette(pal); - } else { - _vm->_palette->getFullPalette(pal); - _vm->_palette->fadeOut(pal, nullptr, 0, PALETTE_COUNT, 0, 1, 1, 16); - } - - // Set Fx state and palette entries - game._fx = _vm->_screenFade == SCREEN_FADE_SMOOTH ? kTransitionFadeIn : kCenterVertTransition; - game._trigger = 0; - - // Clear the screen and draw the upper and lower horizontal lines - _vm->_screen.empty(); - _vm->_palette->setLowRange(); - _vm->_screen.hLine(0, 20, MADS_SCREEN_WIDTH, 2); - _vm->_screen.hLine(0, 179, MADS_SCREEN_WIDTH, 2); - _vm->_screen.copyRectToScreen(Common::Rect(0, 0, MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT)); - - // Restrict the screen to the area between the two lines - _vm->_screen.setClipBounds(Common::Rect(0, DIALOG_TOP, MADS_SCREEN_WIDTH, - DIALOG_TOP + MADS_SCENE_HEIGHT)); - _vm->_game->_scene.restrictScene(); - - if (_screenId > 0) - scene._spriteSlots.fullRefresh(); -} - -/*------------------------------------------------------------------------*/ - GameDialog::DialogLine::DialogLine() { _active = true; _state = DLGSTATE_UNSELECTED; diff --git a/engines/mads/nebular/dialogs_nebular.h b/engines/mads/nebular/dialogs_nebular.h index f64f992611..d00cd87ead 100644 --- a/engines/mads/nebular/dialogs_nebular.h +++ b/engines/mads/nebular/dialogs_nebular.h @@ -31,8 +31,6 @@ namespace MADS { namespace Nebular { -#define DIALOG_TOP 22 - enum CapitalizationMode { kUppercase = 0, kLowercase = 1, kUpperAndLower = 2 }; class DialogsNebular : public Dialogs { @@ -109,36 +107,6 @@ enum DialogTextAlign { ALIGN_NONE = 0, ALIGN_CENTER = -1, ALIGN_AT_CENTER = -2, enum DialogState { DLGSTATE_UNSELECTED = 0, DLGSTATE_SELECTED = 1, DLGSTATE_FOCUSED = 2 }; -class FullScreenDialog: public EventTarget { -protected: - /** - * Engine reference - */ - MADSEngine *_vm; - - /** - * Screen/scene to show background from - */ - int _screenId; - - /** - * Flag for palette initialization - */ - bool _palFlag; - - /** - * Handles displaying the screen background and dialog - */ - virtual void display(); -public: - /** - * Constructor - */ - FullScreenDialog(MADSEngine *vm); - - virtual ~FullScreenDialog(); -}; - class GameDialog: public FullScreenDialog { struct DialogLine { bool _active; diff --git a/engines/mads/nebular/menu_nebular.cpp b/engines/mads/nebular/menu_nebular.cpp index 6a13cea195..717e3f6cf9 100644 --- a/engines/mads/nebular/menu_nebular.cpp +++ b/engines/mads/nebular/menu_nebular.cpp @@ -23,6 +23,7 @@ #include "common/scummsys.h" #include "mads/game.h" #include "mads/mads.h" +#include "mads/menu_views.h" #include "mads/resources.h" #include "mads/scene.h" #include "mads/screen.h" @@ -36,58 +37,6 @@ namespace Nebular { #define MADS_MENU_Y ((MADS_SCREEN_HEIGHT - MADS_SCENE_HEIGHT) / 2) #define MADS_MENU_ANIM_DELAY 70 -MenuView::MenuView(MADSEngine *vm) : FullScreenDialog(vm) { - _breakFlag = false; - _redrawFlag = true; - _palFlag = false; -} - -void MenuView::show() { - Scene &scene = _vm->_game->_scene; - EventsManager &events = *_vm->_events; - _vm->_screenFade = SCREEN_FADE_FAST; - - scene._spriteSlots.reset(true); - display(); - - events.setEventTarget(this); - events.hideCursor(); - - while (!_breakFlag && !_vm->shouldQuit()) { - if (_redrawFlag) { - scene._kernelMessages.update(); - - _vm->_game->_scene.drawElements(_vm->_game->_fx, _vm->_game->_fx); - _redrawFlag = false; - } - - _vm->_events->waitForNextFrame(); - _vm->_game->_fx = kTransitionNone; - doFrame(); - } - - events.setEventTarget(nullptr); - _vm->_sound->stop(); -} - -void MenuView::display() { - _vm->_palette->resetGamePalette(4, 8); - - FullScreenDialog::display(); -} - -bool MenuView::onEvent(Common::Event &event) { - if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN) { - _breakFlag = true; - _vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU; - return true; - } - - return false; -} - -/*------------------------------------------------------------------------*/ - MainMenu::MainMenu(MADSEngine *vm): MenuView(vm) { Common::fill(&_menuItems[0], &_menuItems[7], (SpriteAsset *)nullptr); Common::fill(&_menuItemIndexes[0], &_menuItemIndexes[7], -1); @@ -426,706 +375,6 @@ bool AdvertView::onEvent(Common::Event &event) { return false; } -/*------------------------------------------------------------------------*/ - -char TextView::_resourceName[100]; -#define TEXTVIEW_LINE_SPACING 2 -#define TEXT_ANIMATION_DELAY 100 -#define TV_NUM_FADE_STEPS 40 -#define TV_FADE_DELAY_MILLI 50 - -void TextView::execute(MADSEngine *vm, const Common::String &resName) { - assert(resName.size() < 100); - Common::strlcpy(_resourceName, resName.c_str(), sizeof(_resourceName)); - vm->_dialogs->_pendingDialog = DIALOG_TEXTVIEW; -} - -TextView::TextView(MADSEngine *vm) : MenuView(vm) { - _animating = false; - _panSpeed = 0; - _spareScreen = nullptr; - _scrollCount = 0; - _lineY = -1; - _scrollTimeout = 0; - _panCountdown = 0; - _translationX = 0; - _screenId = -1; - - _font = _vm->_font->getFont(FONT_CONVERSATION); - _vm->_palette->resetGamePalette(4, 0); - - load(); -} - -TextView::~TextView() { -} - -void TextView::load() { - Common::String scriptName(_resourceName); - scriptName += ".txr"; - - if (!_script.open(scriptName)) - error("Could not open resource %s", _resourceName); - - processLines(); -} - -void TextView::processLines() { - if (_script.eos()) - error("Attempted to read past end of response file"); - - while (!_script.eos()) { - // Read in the next line - _script.readLine(_currentLine, 79); - char *p = _currentLine + strlen(_currentLine) - 1; - if (*p == '\n') - *p = '\0'; - - // 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 - Common::strlcpy(_currentLine, cEnd + 1, sizeof(_currentLine)); - - cStart = strchr(_currentLine, '['); - } - - if (_currentLine[0]) { - processText(); - break; - } - - } else { - processText(); - break; - } - } -} - -void TextView::processCommand() { - Scene &scene = _vm->_game->_scene; - Common::String scriptLine(_currentLine + 1); - scriptLine.toUppercase(); - const char *paramP; - const char *commandStr = scriptLine.c_str(); - - if (!strncmp(commandStr, "BACKGROUND", 10)) { - // Set the background - paramP = commandStr + 10; - resetPalette(); - int screenId = getParameter(¶mP); - - SceneInfo *sceneInfo = SceneInfo::init(_vm); - sceneInfo->load(screenId, 0, "", 0, scene._depthSurface, scene._backgroundSurface); - scene._spriteSlots.fullRefresh(); - _redrawFlag = true; - - } else if (!strncmp(commandStr, "GO", 2)) { - _animating = 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)) { - _pan = Common::Point(panX, panY); - _panSpeed = panSpeed; - } - - } else if (!strncmp(commandStr, "DRIVER", 6)) { - // Set the driver to use - paramP = commandStr + 7; - - if (!strncmp(paramP, "#SOUND.00", 9)) { - int driverNum = paramP[9] - '0'; - _vm->_sound->init(driverNum); - } - } else if (!strncmp(commandStr, "SOUND", 5)) { - // Set sound number - paramP = commandStr + 5; - int soundId = getParameter(¶mP); - _vm->_sound->command(soundId); - - } else if (!strncmp(commandStr, "COLOR", 5) && ((commandStr[5] == '0') || - (commandStr[5] == '1'))) { - // Set the text colors - int index = commandStr[5] - '0'; - paramP = commandStr + 6; - - byte r = getParameter(¶mP); - byte g = getParameter(¶mP); - byte b = getParameter(¶mP); - - _vm->_palette->setEntry(5 + index, r, g, b); - - } 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'; - assert(spareIndex < 4); - int screenId = getParameter(¶mP); - - // Load the spare background - SceneInfo *sceneInfo = SceneInfo::init(_vm); - sceneInfo->_width = MADS_SCREEN_WIDTH; - sceneInfo->_height = MADS_SCENE_HEIGHT; - _spareScreens[spareIndex].setSize(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT); - sceneInfo->loadMadsV1Background(screenId, "", SCENEFLAG_TRANSLATE, - _spareScreens[spareIndex]); - delete sceneInfo; - - } 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].getPixels() != nullptr) { - _spareScreen = &_spareScreens[spareIndex]; - _translationX = 0; - } - - } else { - error("Unknown response command: '%s'", commandStr); - } -} - -int TextView::getParameter(const 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 TextView::processText() { - int xStart; - - if (!strcmp(_currentLine, "***")) { - // Special signifier for end of script - _scrollCount = _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 = (MADS_SCREEN_WIDTH / 2) - _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 { - int lineWidth = _font->getWidth(_currentLine); - xStart = (MADS_SCREEN_WIDTH - lineWidth) / 2; - } - - // Add the new line to the list of pending lines - TextLine tl; - tl._pos = Common::Point(xStart, MADS_SCENE_HEIGHT); - tl._line = _currentLine; - tl._textDisplayIndex = -1; - _textLines.push_back(tl); -} - -void TextView::display() { - FullScreenDialog::display(); -} - -void TextView::resetPalette() { - _vm->_palette->resetGamePalette(8, 8); - _vm->_palette->setEntry(5, 0, 63, 63); - _vm->_palette->setEntry(6, 0, 45, 45); -} - -void TextView::doFrame() { - Scene &scene = _vm->_game->_scene; - 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 *bgP = scene._backgroundSurface.getBasePtr(_translationX, 0); - byte *screenP = (byte *)_vm->_screen.getBasePtr(_translationX, 0); - - for (int y = 0; y < MADS_SCENE_HEIGHT; ++y, srcP += MADS_SCREEN_WIDTH, - bgP += MADS_SCREEN_WIDTH, screenP += MADS_SCREEN_WIDTH) { - *bgP = *srcP; - *screenP = *srcP; - } - - // Flag the column of the screen is modified - _vm->_screen.copyRectToScreen(Common::Rect(_translationX, 0, - _translationX + 1, MADS_SCENE_HEIGHT)); - - // Keep moving the column to copy to the right - if (++_translationX == MADS_SCREEN_WIDTH) { - // Surface transition is complete - _spareScreen = nullptr; - } - } - - // Make sure it's time for an update - if (currTime < _scrollTimeout) - return; - _scrollTimeout = g_system->getMillis() + TEXT_ANIMATION_DELAY; - _redrawFlag = true; - - // If any panning values are set, pan the background surface - if ((_pan.x != 0) || (_pan.y != 0)) { - if (_panCountdown > 0) { - --_panCountdown; - return; - } - - // Handle horizontal panning - if (_pan.x != 0) { - byte *lineTemp = new byte[_pan.x]; - for (int y = 0; y < MADS_SCENE_HEIGHT; ++y) { - byte *pixelsP = (byte *)scene._backgroundSurface.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 + _pan.x, lineTemp); - Common::copy(pixelsP + _pan.x, pixelsP + MADS_SCREEN_WIDTH, pixelsP); - Common::copy(lineTemp, lineTemp + _pan.x, pixelsP + MADS_SCREEN_WIDTH - _pan.x); - } - - delete[] lineTemp; - } - - // Handle vertical panning - if (_pan.y != 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[_pan.y * MADS_SCREEN_WIDTH]; - byte *pixelsP = (byte *)scene._backgroundSurface.getBasePtr(0, MADS_SCENE_HEIGHT - _pan.y); - Common::copy(pixelsP, pixelsP + MADS_SCREEN_WIDTH * _pan.y, linesTemp); - - for (int y = MADS_SCENE_HEIGHT - 1; y >= _pan.y; --y) { - byte *destP = (byte *)scene._backgroundSurface.getBasePtr(0, y); - byte *srcP = (byte *)scene._backgroundSurface.getBasePtr(0, y - _pan.y); - Common::copy(srcP, srcP + MADS_SCREEN_WIDTH, destP); - } - - Common::copy(linesTemp, linesTemp + _pan.y * MADS_SCREEN_WIDTH, - (byte *)scene._backgroundSurface.getPixels()); - delete[] linesTemp; - } - - // Flag for a full screen refresh - scene._spriteSlots.fullRefresh(); - } - - // Scroll all active text lines up - for (int i = _textLines.size() - 1; i >= 0; --i) { - TextLine &tl = _textLines[i]; - if (tl._textDisplayIndex != -1) - // Expire the text line that's already on-screen - scene._textDisplay.expire(tl._textDisplayIndex); - - tl._pos.y--; - if (tl._pos.y < 0) { - _textLines.remove_at(i); - } else { - tl._textDisplayIndex = scene._textDisplay.add(tl._pos.x, tl._pos.y, - 0x605, -1, tl._line, _font); - } - } - - if (_scrollCount > 0) { - // Handling final scrolling of text off of screen - if (--_scrollCount == 0) { - scriptDone(); - return; - } - } else { - // Handling a text row - if (++_lineY == (_font->getHeight() + TEXTVIEW_LINE_SPACING)) - processLines(); - } -} - -void TextView::scriptDone() { - _breakFlag = true; - _vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU; -} - -/*------------------------------------------------------------------------*/ - -char AnimationView::_resourceName[100]; - -void AnimationView::execute(MADSEngine *vm, const Common::String &resName) { - assert(resName.size() < 100); - Common::strlcpy(_resourceName, resName.c_str(), sizeof(_resourceName)); - vm->_dialogs->_pendingDialog = DIALOG_ANIMVIEW; -} - -AnimationView::AnimationView(MADSEngine *vm) : MenuView(vm) { - _redrawFlag = false; - - _soundDriverLoaded = false; - _previousUpdate = 0; - _screenId = -1; - _resetPalette = false; - _resyncMode = NEVER; - _v1 = 0; - _v2 = -1; - _resourceIndex = -1; - _currentAnimation = nullptr; - _sfx = 0; - _soundFlag = _bgLoadFlag = true; - _showWhiteBars = true; - _manualFrameNumber = 0; - _manualSpriteSet = nullptr; - _manualStartFrame = _manualEndFrame = 0; - _manualFrame2 = 0; - _animFrameNumber = 0; - _nextCyclingActive = false; - _sceneInfo = SceneInfo::init(_vm); - - load(); -} - -AnimationView::~AnimationView() { - delete _currentAnimation; - delete _sceneInfo; -} - -void AnimationView::load() { - Common::String resName(_resourceName); - if (!resName.hasSuffix(".")) - resName += ".res"; - - if (!_script.open(resName)) - error("Could not open resource %s", resName.c_str()); - - processLines(); -} - -void AnimationView::display() { - Scene &scene = _vm->_game->_scene; - _vm->_palette->initPalette(); - Common::fill(&_vm->_palette->_cyclingPalette[0], &_vm->_palette->_cyclingPalette[PALETTE_SIZE], 0); - - _vm->_palette->resetGamePalette(1, 8); - scene._spriteSlots.reset(); - scene._paletteCycles.clear(); - - MenuView::display(); -} - -bool AnimationView::onEvent(Common::Event &event) { - // Wait for the Escape key or a mouse press - if (((event.type == Common::EVENT_KEYDOWN) && (event.kbd.keycode == Common::KEYCODE_ESCAPE)) || - (event.type == Common::EVENT_RBUTTONUP)) { - scriptDone(); - return true; - } - - return false; -} - -void AnimationView::doFrame() { - Scene &scene = _vm->_game->_scene; - - if (_resourceIndex == -1 || _currentAnimation->freeFlag()) { - if (++_resourceIndex == (int)_resources.size()) { - scriptDone(); - } else { - scene._frameStartTime = 0; - loadNextResource(); - } - } else if (_currentAnimation->getCurrentFrame() == 1) { - scene._cyclingActive = _nextCyclingActive; - } - - if (_currentAnimation) { - ++scene._frameStartTime; - _currentAnimation->update(); - _redrawFlag = true; - } -} - -void AnimationView::loadNextResource() { - Scene &scene = _vm->_game->_scene; - Palette &palette = *_vm->_palette; - ResourceEntry &resEntry = _resources[_resourceIndex]; - Common::Array paletteCycles; - - if (resEntry._bgFlag) - palette.resetGamePalette(1, 8); - - palette._mainPalette[253 * 3] = palette._mainPalette[253 * 3 + 1] - = palette._mainPalette[253 * 3 + 2] = 0xb4; - palette.setPalette(&palette._mainPalette[253 * 3], 253, 1); - - // Free any previous messages - scene._kernelMessages.reset(); - - // Handle the bars at the top/bottom - if (resEntry._showWhiteBars) { - // For animations the screen has been clipped to the middle 156 rows. - // So although it's slightly messy, bypass our screen class entirely, - // and draw the horizontal lines directly on the physiacl screen surface - Graphics::Surface *s = g_system->lockScreen(); - s->hLine(0, 20, MADS_SCREEN_WIDTH, 253); - s->hLine(0, 179, MADS_SCREEN_WIDTH, 253); - g_system->unlockScreen(); - } - - // Load the new animation - delete _currentAnimation; - _currentAnimation = Animation::init(_vm, &scene); - int flags = ANIMFLAG_ANIMVIEW | (resEntry._bgFlag ? ANIMFLAG_LOAD_BACKGROUND : 0); - _currentAnimation->load(scene._backgroundSurface, scene._depthSurface, - resEntry._resourceName, flags, &paletteCycles, _sceneInfo); - - // Signal for a screen refresh - scene._spriteSlots.fullRefresh(); - - // If a sound driver has been specified, then load the correct one - if (!_currentAnimation->_header._soundName.empty()) { - const char *chP = strchr(_currentAnimation->_header._soundName.c_str(), '.'); - assert(chP); - - // Handle both Rex naming (xxx.009) and naming in later games (e.g. xxx.ph9) - int driverNum = atoi(chP + 3); - // HACK for Dragon - if (_currentAnimation->_header._soundName == "#SOUND.DRG") - driverNum = 9; - _vm->_sound->init(driverNum); - } - - // Handle any manual setup - if (_currentAnimation->_header._manualFlag) { - _manualFrameNumber = _currentAnimation->_header._spritesIndex; - _manualSpriteSet = _currentAnimation->getSpriteSet(_manualFrameNumber); - } - - // Set the sound data for the animation - _vm->_sound->setEnabled(resEntry._soundFlag); - - Common::String dsrName = _currentAnimation->_header._dsrName; - if (!dsrName.empty()) - _vm->_audio->setSoundGroup(dsrName); - - // Start the new animation - _currentAnimation->startAnimation(0); - - // Handle the palette and cycling palette - scene._cyclingActive = false; - Common::copy(&palette._mainPalette[0], &palette._mainPalette[PALETTE_SIZE], - &palette._cyclingPalette[0]); - - _vm->_game->_fx = (ScreenTransition)resEntry._fx; - _nextCyclingActive = paletteCycles.size() > 0; - if (!_vm->_game->_fx) { - palette.setFullPalette(palette._mainPalette); - } - - scene.initPaletteAnimation(paletteCycles, _nextCyclingActive && !_vm->_game->_fx); -} - -void AnimationView::scriptDone() { - _breakFlag = true; - _vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU; -} - -void AnimationView::processLines() { - if (_script.eos()) { - // end of script, end animation - scriptDone(); - return; - } - - while (!_script.eos()) { - // Get in next line - _currentLine.clear(); - char c; - while (!_script.eos() && (c = _script.readByte()) != '\n') { - if (c != '\r' && c != '\0') - _currentLine += c; - } - - // Process the line - while (!_currentLine.empty()) { - if (_currentLine.hasPrefix("-")) { - _currentLine.deleteChar(0); - - processCommand(); - } else { - // Get resource name - Common::String resName; - while (!_currentLine.empty() && (c = _currentLine[0]) != ' ') { - _currentLine.deleteChar(0); - resName += c; - } - - // Add resource into list along with any set state information - _resources.push_back(ResourceEntry(resName, _sfx, _soundFlag, - _bgLoadFlag, _showWhiteBars)); - - // Fx resets between resource entries - _sfx = 0; - } - - // Skip any spaces - while (_currentLine.hasPrefix(" ")) - _currentLine.deleteChar(0); - } - } -} - -void AnimationView::processCommand() { - // Get the command character - char commandChar = toupper(_currentLine[0]); - _currentLine.deleteChar(0); - - // Handle the command - switch (commandChar) { - case 'B': - _soundFlag = !_soundFlag; - break; - case 'H': - // -h[:ex] Disable EMS / XMS high memory support - if (_currentLine.hasPrefix(":")) - _currentLine.deleteChar(0); - while (_currentLine.hasPrefix("e") || _currentLine.hasPrefix("x")) - _currentLine.deleteChar(0); - break; - case 'O': - // -o:xxx Specify opening special effect - assert(_currentLine[0] == ':'); - _currentLine.deleteChar(0); - _sfx = getParameter(); - break; - case 'P': - // Switch to CONCAT mode, which is ignored anyway - break; - case 'R': { - // Resynch timer (always, beginning, never) - assert(_currentLine[0] == ':'); - _currentLine.deleteChar(0); - - char v = toupper(_currentLine[0]); - _currentLine.deleteChar(0); - if (v == 'N') - _resyncMode = NEVER; - else if (v == 'A') - _resyncMode = ALWAYS; - else if (v == 'B') - _resyncMode = BEGINNING; - else - error("Unknown parameter"); - break; - } - case 'W': - // Switch white bars being visible - _showWhiteBars = !_showWhiteBars; - break; - case 'X': - // Exit after animation finishes. Ignore - break; - case 'D': - // Unimplemented and ignored in the original. Ignore as well - break; - case 'Y': - // Reset palette on startup - _resetPalette = true; - break; - default: - error("Unknown command char: '%c'", commandChar); - } -} - -int AnimationView::getParameter() { - int result = 0; - - while (!_currentLine.empty()) { - char c = _currentLine[0]; - - if (c >= '0' && c <= '9') { - _currentLine.deleteChar(0); - result = result * 10 + (c - '0'); - } else { - break; - } - } - - return result; -} - -void AnimationView::checkResource(const Common::String &resourceName) { - //bool hasSuffix = false; - -} - -int AnimationView::scanResourceIndex(const Common::String &resourceName) { - int foundIndex = -1; - - if (_v1) { - const char *chP = strchr(resourceName.c_str(), '\\'); - if (!chP) { - chP = strchr(resourceName.c_str(), '*'); - } - - Common::String resName = chP ? Common::String(chP + 1) : resourceName; - - if (_v2 != 3) { - assert(_resIndex.size() == 0); - } - - // Scan for the resource name - for (uint resIndex = 0; resIndex < _resIndex.size(); ++resIndex) { - ResIndexEntry &resEntry = _resIndex[resIndex]; - if (resEntry._resourceName.compareToIgnoreCase(resourceName)) { - foundIndex = resIndex; - break; - } - } - } - - if (foundIndex >= 0) { - // TODO - } - return -1; -} - - } // End of namespace Nebular } // End of namespace MADS diff --git a/engines/mads/nebular/menu_nebular.h b/engines/mads/nebular/menu_nebular.h index c05c87258e..29777a7a7c 100644 --- a/engines/mads/nebular/menu_nebular.h +++ b/engines/mads/nebular/menu_nebular.h @@ -25,6 +25,7 @@ #include "common/scummsys.h" #include "mads/game.h" +#include "mads/menu_views.h" #include "mads/msurface.h" #include "mads/nebular/dialogs_nebular.h" @@ -36,27 +37,6 @@ namespace Nebular { enum MADSGameAction { START_GAME, RESUME_GAME, SHOW_INTRO, CREDITS, QUOTES, EXIT }; -class MenuView: public FullScreenDialog { -protected: - bool _breakFlag; - bool _redrawFlag; - - virtual void doFrame() = 0; - - virtual void display(); - - /** - * Event handler - */ - virtual bool onEvent(Common::Event &event); -public: - MenuView(MADSEngine *vm); - - virtual ~MenuView() {} - - virtual void show(); -}; - class MainMenu: public MenuView { private: SpriteAsset *_menuItems[7]; @@ -148,174 +128,6 @@ public: void show(); }; -struct TextLine { - Common::Point _pos; - Common::String _line; - int _textDisplayIndex; -}; - -/** - * Scrolling text view - */ -class TextView : public MenuView { -private: - static char _resourceName[100]; - - bool _animating; - Common::Array _textLines; - Common::Point _pan; - int _panSpeed; - MSurface _spareScreens[4]; - int _scrollCount; - int _lineY; - uint32 _scrollTimeout; - int _panCountdown; - int _translationX; - Common::File _script; - char _currentLine[80]; - MSurface *_spareScreen; - Font *_font; -private: - /** - * Load the text resource - */ - void load(); - - /** - * Process the lines - */ - void processLines(); - - /** - * Process a command from the script file - */ - void processCommand(); - - /** - * Process text from the script file - */ - void processText(); - - /** - * Get a parameter from line - */ - int getParameter(const char **paramP); - - /** - * Called when the script is finished - */ - void scriptDone(); - - /** - * Reset the game palette - */ - void resetPalette(); -protected: - virtual void display(); - - virtual void doFrame(); -public: - /** - * Queue the given text resource for display - */ - static void execute(MADSEngine *vm, const Common::String &resName); - - TextView(MADSEngine *vm); - - virtual ~TextView(); -}; - -enum ResyncMode { NEVER, ALWAYS, BEGINNING }; - -struct ResourceEntry { - Common::String _resourceName; - int _fx; - bool _soundFlag; - bool _bgFlag; - bool _showWhiteBars; - - ResourceEntry() {} - ResourceEntry(const Common::String &resName, int fx, bool soundFlag, - bool bgFlag, bool showWhiteBars) { - _resourceName = resName; - _fx = fx; - _soundFlag = soundFlag; - _bgFlag = bgFlag; - _showWhiteBars = showWhiteBars; - } -}; - -struct ResIndexEntry { - int _id; - int _v; - Common::String _resourceName; - - ResIndexEntry() {} -}; - -/** -* Animation cutscene view -*/ -class AnimationView : public MenuView { -private: - static char _resourceName[100]; - - Common::File _script; - uint32 _previousUpdate; - Common::String _currentLine; - bool _soundDriverLoaded; - bool _resetPalette; - ResyncMode _resyncMode; - int _sfx; - bool _soundFlag; - bool _bgLoadFlag; - bool _showWhiteBars; - Common::Array _resources; - Common::Array _resIndex; - int _v1; - int _v2; - int _resourceIndex; - SceneInfo *_sceneInfo; - Animation *_currentAnimation; - int _manualFrameNumber; - SpriteAsset *_manualSpriteSet; - int _manualStartFrame, _manualEndFrame; - int _manualFrame2; - int _animFrameNumber; - bool _nextCyclingActive; -private: - void checkResource(const Common::String &resourceName); - - int scanResourceIndex(const Common::String &resourceName); - - void load(); - - void processLines(); - - void processCommand(); - - int getParameter(); - - void scriptDone(); - - void loadNextResource(); -protected: - virtual void display(); - - virtual void doFrame(); - - virtual bool onEvent(Common::Event &event); -public: - /** - * Queue the given text resource for display - */ - static void execute(MADSEngine *vm, const Common::String &resName); - - AnimationView(MADSEngine *vm); - - virtual ~AnimationView(); -}; - } // End of namespace Nebular } // End of namespace MADS -- cgit v1.2.3