diff options
Diffstat (limited to 'engines/mads')
43 files changed, 2014 insertions, 802 deletions
diff --git a/engines/mads/animation.cpp b/engines/mads/animation.cpp index 512a3979f9..2b999fa305 100644 --- a/engines/mads/animation.cpp +++ b/engines/mads/animation.cpp @@ -32,10 +32,8 @@ void AAHeader::load(Common::SeekableReadStream *f) { _miscEntriesCount = f->readUint16LE(); _frameEntriesCount = f->readUint16LE(); _messagesCount = f->readUint16LE(); - f->skip(1); - _flags = f->readByte(); - - f->skip(2); + _loadFlags = f->readUint16LE(); + _charSpacing = f->readSint16LE(); _bgType = (AnimBgType)f->readUint16LE(); _roomNumber = f->readUint16LE(); f->skip(2); @@ -49,7 +47,7 @@ void AAHeader::load(Common::SeekableReadStream *f) { char buffer[FILENAME_SIZE]; f->read(buffer, FILENAME_SIZE); buffer[FILENAME_SIZE - 1] = '\0'; - _interfaceFile = Common::String(buffer); + _backgroundFile = Common::String(buffer); for (int i = 0; i < 50; ++i) { f->read(buffer, FILENAME_SIZE); @@ -134,7 +132,8 @@ void AnimMiscEntry::load(Common::SeekableReadStream *f) { _numTicks = f->readUint16LE(); _posAdjust.x = f->readSint16LE(); _posAdjust.y = f->readSint16LE(); - _field8 = f->readUint16LE(); + _scroll.x = f->readSByte(); + _scroll.y = f->readSByte(); } /*------------------------------------------------------------------------*/ @@ -160,6 +159,7 @@ Animation *Animation::init(MADSEngine *vm, Scene *scene) { } Animation::Animation(MADSEngine *vm, Scene *scene) : _vm(vm), _scene(scene) { + _flags = 0; _font = nullptr; _resetFlag = false; _messageCtr = 0; @@ -175,6 +175,8 @@ Animation::Animation(MADSEngine *vm, Scene *scene) : _vm(vm), _scene(scene) { _actionDetails._indirectObjectId = -1; _currentFrame = 0; _oldFrameEntry = 0; + _rgbResult = -1; + _palIndex1 = _palIndex2 = -1; } Animation::~Animation() { @@ -189,7 +191,7 @@ Animation::~Animation() { } } -void Animation::load(UserInterface &interfaceSurface, DepthSurface &depthSurface, +void Animation::load(MSurface &backSurface, DepthSurface &depthSurface, const Common::String &resName, int flags, Common::Array<PaletteCycle> *palCycles, SceneInfo *sceneInfo) { Common::String resourceName = resName; @@ -205,9 +207,10 @@ void Animation::load(UserInterface &interfaceSurface, DepthSurface &depthSurface if (_header._bgType == ANIMBG_INTERFACE) flags |= PALFLAG_RESERVED; + _flags = flags; if (flags & ANIMFLAG_LOAD_BACKGROUND) { - loadInterface(interfaceSurface, depthSurface, _header, flags, palCycles, sceneInfo); + loadBackground(backSurface, depthSurface, _header, flags, palCycles, sceneInfo); } if (flags & ANIMFLAG_LOAD_BACKGROUND_ONLY) { // No data @@ -243,7 +246,7 @@ void Animation::load(UserInterface &interfaceSurface, DepthSurface &depthSurface for (int i = 0; i < _header._frameEntriesCount; i++) { AnimFrameEntry rec; - rec.load(frameStream, flags & ANIMFLAG_LOAD_BACKGROUND); + rec.load(frameStream, _header._bgType == ANIMBG_INTERFACE); _frameEntries.push_back(rec); } @@ -256,7 +259,7 @@ void Animation::load(UserInterface &interfaceSurface, DepthSurface &depthSurface // Chunk 4: Misc Data Common::SeekableReadStream *miscStream = madsPack.getItemStream(streamIndex++); - if (flags & ANIMFLAG_LOAD_BACKGROUND) { + if (_header._bgType == ANIMBG_INTERFACE) { for (int i = 0; i < _header._miscEntriesCount; ++i) { AnimUIEntry rec; rec.load(miscStream); @@ -275,7 +278,7 @@ void Animation::load(UserInterface &interfaceSurface, DepthSurface &depthSurface // If the animation specifies a font, then load it for access delete _font; - if (_header._flags & ANIMFLAG_CUSTOM_FONT) { + if (_header._loadFlags & ANIMFLAG_CUSTOM_FONT) { Common::String fontName = "*" + _header._fontResource; _font = _vm->_font->getFont(fontName.c_str()); } else { @@ -337,9 +340,6 @@ void Animation::startAnimation(int endTrigger) { _unkIndex = -1; //SpriteAsset *asset = _scene->_sprites[_spriteListIndexes[_header._spritesIndex]]; - // TODO: Weird stuff with _unkList. Seems like it's treated as pointers - // here, but in processText, it's used as POINTs? - loadFrame(1); } @@ -385,12 +385,12 @@ bool Animation::drawFrame(SpriteAsset &spriteSet, const Common::Point &pt, int f return 0; } -void Animation::loadInterface(UserInterface &interfaceSurface, DepthSurface &depthSurface, +void Animation::loadBackground(MSurface &backSurface, DepthSurface &depthSurface, AAHeader &header, int flags, Common::Array<PaletteCycle> *palCycles, SceneInfo *sceneInfo) { _scene->_depthStyle = 0; if (header._bgType <= ANIMBG_FULL_SIZE) { _vm->_palette->_paletteUsage.setEmpty(); - sceneInfo->load(header._roomNumber, flags, header._interfaceFile, 0, depthSurface, interfaceSurface); + sceneInfo->load(header._roomNumber, 0, header._backgroundFile, flags, depthSurface, backSurface); _scene->_depthStyle = sceneInfo->_depthStyle == 2 ? 1 : 0; if (palCycles) { palCycles->clear(); @@ -399,8 +399,8 @@ void Animation::loadInterface(UserInterface &interfaceSurface, DepthSurface &dep } } else if (header._bgType == ANIMBG_INTERFACE) { // Load a scene interface - Common::String resourceName = "*" + header._interfaceFile; - interfaceSurface.load(resourceName); + Common::String resourceName = "*" + header._backgroundFile; + backSurface.load(resourceName); if (palCycles) palCycles->clear(); @@ -415,6 +415,7 @@ bool Animation::hasScroll() const { void Animation::update() { Scene &scene = _vm->_game->_scene; + Palette &palette = *_vm->_palette; if (_header._manualFlag) { int spriteListIndex = _spriteListIndexes[_header._spritesIndex]; @@ -534,28 +535,43 @@ void Animation::update() { // Start displaying the message AnimMessage &me = _messages[idx]; - // The color index to use is dependant on how many messages are currently on-screen - uint8 colIndex; - switch (_messageCtr) { - case 1: - colIndex = 252; - break; - case 2: - colIndex = 16; - break; - default: - colIndex = 250; - break; - } + if (_flags & ANIMFLAG_ANIMVIEW) { + _rgbResult = palette._paletteUsage.checkRGB(me._rgb1, -1, true, &_palIndex1); + _rgbResult = palette._paletteUsage.checkRGB(me._rgb2, _rgbResult, true, &_palIndex2); + + // Update the palette with the two needed colors + int palStart = MIN(_palIndex1, _palIndex2); + int palCount = ABS(_palIndex2 - _palIndex1) + 1; + palette.setPalette(&palette._mainPalette[palStart * 3], palStart, palCount); + } else { + // The color index to use is dependant on how many messages are currently on-screen + switch (_messageCtr) { + case 1: + _palIndex1 = 252; + break; + case 2: + _palIndex1 = 16; + break; + default: + _palIndex1 = 250; + break; + } + _palIndex2 = _palIndex1 + 1; - _vm->_palette->setEntry(colIndex, me._rgb1[0], me._rgb1[1], me._rgb1[2]); - _vm->_palette->setEntry(colIndex + 1, me._rgb2[0], me._rgb2[1], me._rgb2[2]); + _vm->_palette->setEntry(_palIndex1, me._rgb1[0], me._rgb1[1], me._rgb1[2]); + _vm->_palette->setEntry(_palIndex2, me._rgb2[0], me._rgb2[1], me._rgb2[2]); + } // Add a kernel message to display the given text - me._kernelMsgIndex = scene._kernelMessages.add(me._pos, colIndex * 0x101 + 0x100, + me._kernelMsgIndex = scene._kernelMessages.add(me._pos, + _palIndex1 | (_palIndex2 << 8), 0, 0, INDEFINITE_TIMEOUT, me._msg); assert(me._kernelMsgIndex >= 0); ++_messageCtr; + + // If there's an accompanying sound, also play it + if (me._soundId > 0) + _vm->_audio->playSound(me._soundId - 1); } } diff --git a/engines/mads/animation.h b/engines/mads/animation.h index 15086d3e41..8b85a5370d 100644 --- a/engines/mads/animation.h +++ b/engines/mads/animation.h @@ -34,10 +34,11 @@ namespace MADS { enum AnimFlag { - ANIMFLAG_DITHER = 0x0001, // Dither to 16 colors - ANIMFLAG_CUSTOM_FONT = 0x0020, // Load ccustom font + ANIMFLAG_DITHER = 0x1000, // Dither to 16 colors + ANIMFLAG_CUSTOM_FONT = 0x2000, // Load ccustom font ANIMFLAG_LOAD_BACKGROUND = 0x0100, // Load background - ANIMFLAG_LOAD_BACKGROUND_ONLY = 0x0200 // Load background only + ANIMFLAG_LOAD_BACKGROUND_ONLY = 0x0200, // Load background only + ANIMFLAG_ANIMVIEW = 0x4000 // Cutscene animation }; enum AnimBgType { @@ -82,7 +83,7 @@ public: int _msgIndex; int _numTicks; Common::Point _posAdjust; - int _field8; + Common::Point _scroll; /** * Loads data for the record @@ -116,14 +117,15 @@ public: int _miscEntriesCount; int _frameEntriesCount; int _messagesCount; - byte _flags; + int _loadFlags; + int _charSpacing; AnimBgType _bgType; int _roomNumber; bool _manualFlag; int _spritesIndex; Common::Point _scrollPosition; uint32 _scrollTicks; - Common::String _interfaceFile; + Common::String _backgroundFile; Common::StringArray _spriteSetNames; Common::String _lbmFilename; Common::String _spritesFilename; @@ -154,6 +156,9 @@ private: uint32 _nextScrollTimer; int _messageCtr; int _trigger; + int _flags; + int _rgbResult; + int _palIndex1, _palIndex2; TriggerMode _triggerMode; ActionDetails _actionDetails; @@ -166,9 +171,9 @@ private: bool drawFrame(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber); /** - * Load the user interface display for an animation + * Load the user interface display or background for an animation */ - void loadInterface(UserInterface &interfaceSurface, DepthSurface &depthSurface, + void loadBackground(MSurface &backSurface, DepthSurface &depthSurface, AAHeader &header, int flags, Common::Array<PaletteCycle> *palCycles, SceneInfo *sceneInfo); /** @@ -196,7 +201,7 @@ public: /** * Loads animation data */ - void load(UserInterface &interfaceSurface, DepthSurface &depthSurface, const Common::String &resName, + void load(MSurface &backSurface, DepthSurface &depthSurface, const Common::String &resName, int flags, Common::Array<PaletteCycle> *palCycles, SceneInfo *sceneInfo); /** @@ -223,6 +228,8 @@ public: int roomNumber() const { return _header._roomNumber; } void resetSpriteSetsCount() { _header._spriteSetsCount = 0; } // CHECKME: See if it doesn't leak the memory when the destructor is called + + SpriteAsset *getSpriteSet(int idx) { return _spriteSets[idx]; } }; } // End of namespace MADS diff --git a/engines/mads/audio.cpp b/engines/mads/audio.cpp index 1c61e13957..def2cd6c62 100644 --- a/engines/mads/audio.cpp +++ b/engines/mads/audio.cpp @@ -37,6 +37,7 @@ AudioPlayer::AudioPlayer(Audio::Mixer *mixer, uint32 gameID) : _mixer(mixer), _g AudioPlayer::~AudioPlayer() { _dsrEntries.clear(); + _filename = ""; } bool AudioPlayer::isPlaying() const { @@ -65,25 +66,27 @@ void AudioPlayer::setDefaultSoundGroup() { } void AudioPlayer::setSoundGroup(const Common::String &filename) { - _dsrEntries.clear(); - - _filename = filename; - _dsrFile.open(filename); - - // Read header - uint16 entryCount = _dsrFile.readUint16LE(); - - for (uint16 i = 0; i < entryCount; i++) { - DSREntry newEntry; - newEntry.frequency = _dsrFile.readUint16LE(); - newEntry.channels = _dsrFile.readUint32LE(); - newEntry.compSize = _dsrFile.readUint32LE(); - newEntry.uncompSize = _dsrFile.readUint32LE(); - newEntry.offset = _dsrFile.readUint32LE(); - _dsrEntries.push_back(newEntry); + if (_filename != filename) { + _dsrEntries.clear(); + + _filename = filename; + _dsrFile.open(filename); + + // Read header + uint16 entryCount = _dsrFile.readUint16LE(); + + for (uint16 i = 0; i < entryCount; i++) { + DSREntry newEntry; + newEntry.frequency = _dsrFile.readUint16LE(); + newEntry.channels = _dsrFile.readUint32LE(); + newEntry.compSize = _dsrFile.readUint32LE(); + newEntry.uncompSize = _dsrFile.readUint32LE(); + newEntry.offset = _dsrFile.readUint32LE(); + _dsrEntries.push_back(newEntry); + } + + _dsrFile.close(); } - - _dsrFile.close(); } void AudioPlayer::playSound(int soundIndex, bool loop) { @@ -126,4 +129,8 @@ void AudioPlayer::playSound(int soundIndex, bool loop) { */ } +void AudioPlayer::stop() { + _mixer->stopHandle(_handle); +} + } // End of namespace M4 diff --git a/engines/mads/audio.h b/engines/mads/audio.h index 21f4bed59a..13c540bf85 100644 --- a/engines/mads/audio.h +++ b/engines/mads/audio.h @@ -46,6 +46,7 @@ public: void setSoundGroup(const Common::String &filename); void setDefaultSoundGroup(); void playSound(int soundIndex, bool loop = false); + void stop(); void setVolume(int volume); bool isPlaying() const; diff --git a/engines/mads/debugger.cpp b/engines/mads/debugger.cpp index 6bc6cf572d..99251f9fbb 100644 --- a/engines/mads/debugger.cpp +++ b/engines/mads/debugger.cpp @@ -24,6 +24,7 @@ #include "mads/compression.h" #include "mads/mads.h" #include "mads/debugger.h" +#include "mads/nebular/menu_nebular.h" namespace MADS { @@ -46,6 +47,8 @@ Debugger::Debugger(MADSEngine *vm) : GUI::Debugger(), _vm(vm) { registerCmd("show_item", WRAP_METHOD(Debugger, Cmd_ShowItem)); registerCmd("dump_items", WRAP_METHOD(Debugger, Cmd_DumpItems)); registerCmd("item", WRAP_METHOD(Debugger, Cmd_Item)); + registerCmd("play_anim", WRAP_METHOD(Debugger, Cmd_PlayAnim)); + registerCmd("play_text", WRAP_METHOD(Debugger, Cmd_PlayText)); } static int strToInt(const char *s) { @@ -348,4 +351,44 @@ bool Debugger::Cmd_Item(int argc, const char **argv) { } } +bool Debugger::Cmd_PlayAnim(int argc, const char **argv) { + if (argc != 2) { + debugPrintf("Usage: %s <anim name>\n", argv[0]); + return true; + } else { + Common::String resName = argv[1]; + if (resName.hasPrefix("@")) + resName.deleteChar(0); + + Common::File f; + if (f.exists(resName) || f.exists(resName + ".res")) { + AnimationView::execute(_vm, resName); + return false; + } else { + debugPrintf("Could not find resource file\n"); + return true; + } + } +} + +bool Debugger::Cmd_PlayText(int argc, const char **argv) { + if (argc != 2) { + debugPrintf("Usage: %s <text name>\n", argv[0]); + return true; + } else { + Common::String resName = argv[1]; + if (resName.hasPrefix("@")) + resName.deleteChar(0); + + Common::File f; + if (f.exists(resName) || f.exists(resName + ".txr")) { + TextView::execute(_vm, resName); + return false; + } else { + debugPrintf("Could not find resource file\n"); + return true; + } + } +} + } // End of namespace MADS diff --git a/engines/mads/debugger.h b/engines/mads/debugger.h index 351eb13615..c8fee5f5b2 100644 --- a/engines/mads/debugger.h +++ b/engines/mads/debugger.h @@ -49,6 +49,8 @@ protected: bool Cmd_ShowItem(int argc, const char **argv); bool Cmd_DumpItems(int argc, const char **argv); bool Cmd_Item(int argc, const char **argv); + bool Cmd_PlayAnim(int argc, const char **argv); + bool Cmd_PlayText(int argc, const char **argv); public: bool _showMousePos; public: diff --git a/engines/mads/dialogs.cpp b/engines/mads/dialogs.cpp index 7e6909d113..5e38f34fc6 100644 --- a/engines/mads/dialogs.cpp +++ b/engines/mads/dialogs.cpp @@ -395,4 +395,78 @@ 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.resetClipBounds(); + _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/game.cpp b/engines/mads/game.cpp index b544eff2db..94653f9a39 100644 --- a/engines/mads/game.cpp +++ b/engines/mads/game.cpp @@ -433,8 +433,6 @@ void Game::handleKeypress(const Common::Event &event) { default: break; } - - warning("TODO: handleKeypress - %d", (int)event.kbd.keycode); } void Game::synchronize(Common::Serializer &s, bool phase1) { @@ -558,7 +556,7 @@ void Game::writeSavegameHeader(Common::OutSaveFile *out, MADSSavegameHeader &hea if (!_saveThumb) createThumbnail(); Graphics::saveThumbnail(*out, *_saveThumb); - + _saveThumb->free(); delete _saveThumb; _saveThumb = nullptr; diff --git a/engines/mads/mads.cpp b/engines/mads/mads.cpp index 59eec40bcc..52a0b40561 100644 --- a/engines/mads/mads.cpp +++ b/engines/mads/mads.cpp @@ -68,6 +68,8 @@ MADSEngine::~MADSEngine() { delete _resources; delete _sound; delete _audio; + + _mixer->stopAll(); } void MADSEngine::initialize() { @@ -103,9 +105,6 @@ Common::Error MADSEngine::run() { // Run the game _game->run(); - // Dummy loop to keep application active - _events->delay(9999); - return Common::kNoError; } diff --git a/engines/mads/menu_views.cpp b/engines/mads/menu_views.cpp new file mode 100644 index 0000000000..ee4268a650 --- /dev/null +++ b/engines/mads/menu_views.cpp @@ -0,0 +1,768 @@ +/* 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; +} + +Common::String MenuView::getResourceName() { + Common::String s(_filename); + s.toLowercase(); + while (s.contains('.')) + s.deleteLastChar(); + + return s; +} + +/*------------------------------------------------------------------------*/ + +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() { + // Turn off palette cycling as well as any playing sound + Scene &scene = _vm->_game->_scene; + scene._cyclingActive = false; + _vm->_sound->stop(); +} + +void TextView::load() { + Common::String scriptName(_resourceName); + scriptName += ".txr"; + + _filename = scriptName; + 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() { + // Turn off palette cycling as well as any playing sound + Scene &scene = _vm->_game->_scene; + scene._cyclingActive = false; + _vm->_sound->stop(); + _vm->_audio->stop(); + + // Delete data + delete _currentAnimation; + delete _sceneInfo; +} + +void AnimationView::load() { + Common::String resName(_resourceName); + if (!resName.hasSuffix(".")) + resName += ".res"; + + _filename = resName; + 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_LBUTTONUP)) { + 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<PaletteCycle> 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; +} + +} // End of namespace MADS diff --git a/engines/mads/menu_views.h b/engines/mads/menu_views.h new file mode 100644 index 0000000000..cc5a13006f --- /dev/null +++ b/engines/mads/menu_views.h @@ -0,0 +1,225 @@ +/* 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; + Common::String _filename; + + virtual void doFrame() = 0; + + virtual void display(); + + /** + * Event handler + */ + virtual bool onEvent(Common::Event &event); +public: + MenuView(MADSEngine *vm); + + virtual ~MenuView() {} + + virtual void show(); + + Common::String getResourceName(); +}; + +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<TextLine> _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); + + /** + * Reset the game palette + */ + void resetPalette(); +protected: + virtual void display(); + + virtual void doFrame(); + + /** + * Called when the script is finished + */ + virtual void scriptDone(); +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<ResourceEntry> _resources; + Common::Array<ResIndexEntry> _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 load(); + + void processLines(); + + void processCommand(); + + int getParameter(); + + void loadNextResource(); +protected: + virtual void display(); + + virtual void doFrame(); + + virtual bool onEvent(Common::Event &event); + + virtual void scriptDone(); +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/messages.cpp b/engines/mads/messages.cpp index d41696044b..e83b69d210 100644 --- a/engines/mads/messages.cpp +++ b/engines/mads/messages.cpp @@ -546,8 +546,7 @@ void TextDisplayList::draw(MSurface *s) { for (uint idx = 0; idx < size(); ++idx) { TextDisplay &td = (*this)[idx]; if (td._active && (td._expire >= 0)) { - Common::Point destPos(td._bounds.left + _vm->_screen._offset.x, - td._bounds.top + _vm->_screen._offset.y); + Common::Point destPos(td._bounds.left, td._bounds.top); td._font->setColors(0xFF, td._color1, td._color2, 0); td._font->writeString(s, td._msg, destPos, td._spacing, td._bounds.width()); } diff --git a/engines/mads/module.mk b/engines/mads/module.mk index 96353e9ae5..fc04a2f8ba 100644 --- a/engines/mads/module.mk +++ b/engines/mads/module.mk @@ -35,6 +35,7 @@ MODULE_OBJS := \ hotspots.o \ inventory.o \ mads.o \ + menu_views.o \ messages.o \ msurface.o \ palette.o \ diff --git a/engines/mads/msurface.cpp b/engines/mads/msurface.cpp index 349f4a5f23..39824bac4b 100644 --- a/engines/mads/msurface.cpp +++ b/engines/mads/msurface.cpp @@ -87,7 +87,6 @@ void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Commo // rectangle is always 0, 0 assert(clipRect.top == 0 && clipRect.left == 0); - // TODO: Put err* and scaled* into SpriteInfo int errX = info.hotX * info.scaleX % 100; int errY = info.hotY * info.scaleY % 100; int scaledWidth = scaleValue(info.width, info.scaleX, errX); @@ -160,7 +159,6 @@ void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Commo if (status == kStatusDraw && clipY == 0) { // Draw previously scaled line - // TODO Implement different drawing types (depth, shadow etc.) byte *tempDst = dst; for (int lineX = 0; lineX < scaledWidth; lineX++) { byte pixel = scaledLineBuf[lineX]; @@ -186,8 +184,6 @@ void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Commo } dst += pitch; heightAmt--; - // TODO depth etc. - //depthAddress += Destination -> Width; errY += 100; if (errY >= 0) @@ -266,11 +262,11 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth, int highestDim = MAX(frameWidth, frameHeight); bool lineDist[MADS_SCREEN_WIDTH]; - int distIndex = 0; int distXCount = 0, distYCount = 0; if (scale != -1) { int distCtr = 0; + int distIndex = 0; do { distCtr += scale; if (distCtr < 100) { @@ -356,9 +352,10 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth, if (widthAmount > 0) spriteWidth -= widthAmount; - int spriteRight = spriteLeft + spriteWidth; if (spriteWidth <= 0) return; + + int spriteRight = spriteLeft + spriteWidth; if (flipped) { destX += distXCount - 1; spriteLeft = -(distXCount - spriteRight); diff --git a/engines/mads/msurface.h b/engines/mads/msurface.h index 23e0a03985..650d7fdaee 100644 --- a/engines/mads/msurface.h +++ b/engines/mads/msurface.h @@ -51,10 +51,9 @@ struct SpriteInfo { * MADS graphics surface */ class MSurface : public Graphics::Surface { -private: - bool _freeFlag; protected: static MADSEngine *_vm; + bool _freeFlag; public: /** * Sets the engine refrence used all surfaces @@ -65,6 +64,11 @@ public: * Helper method for calculating new dimensions when scaling a sprite */ static int scaleValue(int value, int scale, int err); + + /** + * Base method for descendents to load their contents + */ + virtual void load(const Common::String &resName) {} public: /** * Basic constructor @@ -219,8 +223,6 @@ public: }; class DepthSurface : public MSurface { -private: - MADSEngine *_vm; public: /** * Depth style @@ -230,7 +232,7 @@ public: /** * Constructor */ - DepthSurface(MADSEngine *vm) : _vm(vm) {} + DepthSurface() : _depthStyle(0) {} /** * Returns the depth at a given position diff --git a/engines/mads/nebular/dialogs_nebular.cpp b/engines/mads/nebular/dialogs_nebular.cpp index d4b277d856..f5355517bd 100644 --- a/engines/mads/nebular/dialogs_nebular.cpp +++ b/engines/mads/nebular/dialogs_nebular.cpp @@ -313,6 +313,18 @@ void DialogsNebular::showDialog() { delete dlg; break; } + case DIALOG_TEXTVIEW: { + TextView *dlg = new RexTextView(_vm); + dlg->show(); + delete dlg; + break; + } + case DIALOG_ANIMVIEW: { + AnimationView *dlg = new RexAnimationView(_vm); + dlg->show(); + delete dlg; + break; + } default: break; } @@ -334,7 +346,6 @@ void DialogsNebular::showScummVMSaveDialog() { } scene->_spriteSlots.reset(); - _vm->_screen._offset.y = 0; scene->loadScene(scene->_currentSceneId, game._aaName, true); scene->_userInterface.noInventoryAnim(); game._scene.drawElements(kTransitionFadeIn, false); @@ -542,60 +553,6 @@ void PictureDialog::restore() { /*------------------------------------------------------------------------*/ -FullScreenDialog::FullScreenDialog(MADSEngine *vm) : _vm(vm) { - _screenId = 990; - _palFlag = true; -} - -FullScreenDialog::~FullScreenDialog() { - _vm->_screen._offset.y = 0; -} - -void FullScreenDialog::display() { - Game &game = *_vm->_game; - Scene &scene = game._scene; - - int nextSceneId = scene._nextSceneId; - int currentSceneId = scene._currentSceneId; - int priorSceneId = scene._priorSceneId; - - scene.loadScene(_screenId, game._aaName, _palFlag); - - scene._priorSceneId = priorSceneId; - scene._currentSceneId = currentSceneId; - scene._nextSceneId = nextSceneId; - - _vm->_screen._offset.y = 22; - _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); - } - - _vm->_screen.empty(); - _vm->_screen.hLine(0, 20, MADS_SCREEN_WIDTH, 2); - _vm->_screen.hLine(0, 179, MADS_SCREEN_WIDTH, 2); - game._scene._spriteSlots.fullRefresh(); - - game._fx = _vm->_screenFade == SCREEN_FADE_SMOOTH ? kTransitionFadeIn : kCenterVertTransition; - game._trigger = 0; - - _vm->_palette->setEntry(10, 0, 63, 0); - _vm->_palette->setEntry(11, 0, 45, 0); - _vm->_palette->setEntry(12, 63, 63, 0); - _vm->_palette->setEntry(13, 45, 45, 0); - _vm->_palette->setEntry(14, 63, 63, 63); - _vm->_palette->setEntry(15, 45, 45, 45); -} - -/*------------------------------------------------------------------------*/ - GameDialog::DialogLine::DialogLine() { _active = true; _state = DLGSTATE_UNSELECTED; @@ -637,12 +594,24 @@ GameDialog::GameDialog(MADSEngine *vm) : FullScreenDialog(vm) { _vm->_events->waitCursor(); scene.clearVocab(); scene._dynamicHotspots.clear(); + // Clear scene sprites and objects + scene._spriteSlots.reset(); + _vm->_game->_screenObjects.clear(); _vm->_dialogs->_defaultPosition = Common::Point(-1, -1); + _menuSpritesIndex = 0; } void GameDialog::display() { FullScreenDialog::display(); + Palette &palette = *_vm->_palette; + palette.setEntry(10, 0, 63, 0); + palette.setEntry(11, 0, 45, 0); + palette.setEntry(12, 63, 63, 0); + palette.setEntry(13, 45, 45, 0); + palette.setEntry(14, 63, 63, 63); + palette.setEntry(15, 45, 45, 45); + Scene &scene = _vm->_game->_scene; SpriteAsset *menuSprites = new SpriteAsset(_vm, "*MENU", 0); _menuSpritesIndex = scene._sprites.add(menuSprites); @@ -654,7 +623,7 @@ void GameDialog::display() { } GameDialog::~GameDialog() { - _vm->_screen._offset.y = 0; + _vm->_screen.resetClipBounds(); } void GameDialog::clearLines() { @@ -855,10 +824,11 @@ void GameDialog::handleEvents() { _vm->_events->pollEvents(); // Scan for objects in the dialog - int objIndex = screenObjects.scan(events.currentPos() - _vm->_screen._offset, LAYER_GUI); + Common::Point mousePos = events.currentPos() - Common::Point(0, DIALOG_TOP); + int objIndex = screenObjects.scan(mousePos, LAYER_GUI); if (_movedFlag) { - int yp = events.currentPos().y - _vm->_screen._offset.y; + int yp = mousePos.y; if (yp < screenObjects[1]._bounds.top) { if (!events._mouseReleased) _lines[1]._state = DLGSTATE_SELECTED; @@ -938,7 +908,7 @@ void GameDialog::refreshText() { } if (!skipFlag) { - _lines[i]._textDisplayIndex = scene._textDisplay.add(_lines[i]._pos.x, _lines[i]._pos.y, + _lines[i]._textDisplayIndex = scene._textDisplay.add(_lines[i]._pos.x, _lines[i]._pos.y, fontColor, _lines[i]._widthAdjust, _lines[i]._msg, _lines[i]._font); } } @@ -949,6 +919,7 @@ void GameDialog::refreshText() { DifficultyDialog::DifficultyDialog(MADSEngine *vm) : GameDialog(vm) { setLines(); + _vm->_palette->resetGamePalette(18, 10); } void DifficultyDialog::setLines() { @@ -1093,6 +1064,14 @@ void OptionsDialog::display() { void OptionsDialog::show() { Nebular::GameNebular &game = *(Nebular::GameNebular *)_vm->_game; + + // Previous options, restored when cancel is selected + bool prevEasyMouse = _vm->_easyMouse; + bool prevInvObjectsAnimated = _vm->_invObjectsAnimated; + bool prevTextWindowStill = _vm->_textWindowStill; + ScreenFade prevScreenFade = _vm->_screenFade; + StoryMode prevStoryMode = game._storyMode; + do { _selectedLine = 0; GameDialog::show(); @@ -1137,10 +1116,15 @@ void OptionsDialog::show() { switch (_selectedLine) { case 8: // Done - // TODO: Copy from temporary config + // New options will be applied break; case 9: // Cancel - // TODO: Ignore all changes to temporary config + // Revert all options from the saved ones + _vm->_easyMouse = prevEasyMouse; + _vm->_invObjectsAnimated = prevInvObjectsAnimated; + _vm->_textWindowStill = prevTextWindowStill; + _vm->_screenFade = prevScreenFade; + game._storyMode = prevStoryMode; break; default: break; diff --git a/engines/mads/nebular/dialogs_nebular.h b/engines/mads/nebular/dialogs_nebular.h index 1468db38c8..5dbe4da6f0 100644 --- a/engines/mads/nebular/dialogs_nebular.h +++ b/engines/mads/nebular/dialogs_nebular.h @@ -107,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; @@ -146,7 +116,7 @@ class GameDialog: public FullScreenDialog { Common::String _msg; Font *_font; int _widthAdjust; - + DialogLine(); DialogLine(const Common::String &s); }; @@ -160,7 +130,7 @@ protected: int _menuSpritesIndex; int _lineIndex; int _textLineCount; - + /** * Display the dialog */ diff --git a/engines/mads/nebular/game_nebular.cpp b/engines/mads/nebular/game_nebular.cpp index 902f42507a..fd669bc5cf 100644 --- a/engines/mads/nebular/game_nebular.cpp +++ b/engines/mads/nebular/game_nebular.cpp @@ -27,6 +27,7 @@ #include "mads/game.h" #include "mads/screen.h" #include "mads/msurface.h" +#include "mads/menu_views.h" #include "mads/nebular/game_nebular.h" #include "mads/nebular/dialogs_nebular.h" #include "mads/nebular/globals_nebular.h" @@ -309,6 +310,31 @@ void GameNebular::setSectionHandler() { } void GameNebular::checkShowDialog() { + // Handling to start endgame sequences if the win/lose type has been set + switch (_winStatus) { + case 1: + // No shields failure ending + AnimationView::execute(_vm, "rexend1"); + break; + case 2: + // Shields, but no targetting failure ending + AnimationView::execute(_vm, "rexend2"); + break; + case 3: + // Completed game successfully, so activate quotes item on the main menu + ConfMan.setBool("ShowQuotes", true); + ConfMan.flushToDisk(); + + AnimationView::execute(_vm, "rexend3"); + break; + case 4: + // Decompression ending + TextView::execute(_vm, "ending4"); + break; + } + _winStatus = 0; + + // Loop for showing dialogs, if any need to be shown if (_vm->_dialogs->_pendingDialog && _player._stepEnabled && !_globals[kCopyProtectFailed]) { _player.releasePlayerSprites(); @@ -599,7 +625,7 @@ void GameNebular::doObjectAction() { _objects.addToInventory(OBJ_DURAFAIL_CELLS); if (_difficulty == DIFFICULTY_HARD) { dialogs.showItem(OBJ_DURAFAIL_CELLS, 416); - } + } _globals[kHandsetCellStatus] = 0; break; case 3: diff --git a/engines/mads/nebular/game_nebular.h b/engines/mads/nebular/game_nebular.h index da607d47ee..efa21a2e73 100644 --- a/engines/mads/nebular/game_nebular.h +++ b/engines/mads/nebular/game_nebular.h @@ -133,18 +133,16 @@ public: virtual void synchronize(Common::Serializer &s, bool phase1); }; - +// Section handlers aren't needed in ScummVM implementation class Section1Handler : public SectionHandler { public: Section1Handler(MADSEngine *vm) : SectionHandler(vm) {} - // TODO: Properly implement handler methods virtual void preLoadSection() {} virtual void sectionPtr2() {} virtual void postLoadSection() {} }; -// TODO: Properly implement handler classes typedef Section1Handler Section2Handler; typedef Section1Handler Section3Handler; typedef Section1Handler Section4Handler; diff --git a/engines/mads/nebular/menu_nebular.cpp b/engines/mads/nebular/menu_nebular.cpp index cb8f56bd05..28de4e5650 100644 --- a/engines/mads/nebular/menu_nebular.cpp +++ b/engines/mads/nebular/menu_nebular.cpp @@ -21,9 +21,12 @@ */ #include "common/scummsys.h" +#include "common/config-manager.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" #include "mads/nebular/menu_nebular.h" @@ -35,44 +38,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; - display(); - - events.setEventTarget(this); - events.hideCursor(); - - while (!_breakFlag && !_vm->shouldQuit()) { - if (_redrawFlag) { - scene.drawElements(_vm->_game->_fx, _vm->_game->_fx); - - _vm->_screen.copyRectToScreen(Common::Rect(0, 0, 320, 200)); - _redrawFlag = false; - } - - _vm->_events->waitForNextFrame(); - _vm->_game->_fx = kTransitionNone; - doFrame(); - } - - events.setEventTarget(nullptr); -} - -void MenuView::display() { - _vm->_palette->resetGamePalette(4, 8); - - FullScreenDialog::display(); -} - -/*------------------------------------------------------------------------*/ - MainMenu::MainMenu(MADSEngine *vm): MenuView(vm) { Common::fill(&_menuItems[0], &_menuItems[7], (SpriteAsset *)nullptr); Common::fill(&_menuItemIndexes[0], &_menuItemIndexes[7], -1); @@ -83,9 +48,23 @@ MainMenu::MainMenu(MADSEngine *vm): MenuView(vm) { _highlightedIndex = -1; _selectedIndex = -1; _buttonDown = false; + + for (int i = 0; i < 7; ++i) + _menuItems[i] = nullptr; } MainMenu::~MainMenu() { + Scene &scene = _vm->_game->_scene; + for (int i = 0; i < 7; ++i) { + if (_menuItemIndexes[i] != -1) + scene._sprites.remove(_menuItemIndexes[i]); + } + + scene._spriteSlots.reset(); +} + +bool MainMenu::shouldShowQuotes() { + return ConfMan.hasKey("ShowQuotes") && ConfMan.getBool("ShowQuotes"); } void MainMenu::display() { @@ -104,10 +83,10 @@ void MainMenu::display() { // Register the menu item area in the screen objects MSprite *frame0 = _menuItems[i]->getFrame(0); Common::Point pt(frame0->_offset.x - (frame0->w / 2), - frame0->_offset.y - frame0->h + _vm->_screen._offset.y); + frame0->_offset.y - frame0->h); screenObjects.add( - Common::Rect(pt.x, pt.y, pt.x + frame0->w, pt.y + frame0->h), - LAYER_GUI, CAT_COMMAND, i); + Common::Rect(pt.x, pt.y + DIALOG_TOP, pt.x + frame0->w, + pt.y + frame0->h + DIALOG_TOP), LAYER_GUI, CAT_COMMAND, i); } // Set the cursor for when it's shown @@ -127,6 +106,9 @@ void MainMenu::doFrame() { handleAction((MADSGameAction)_selectedIndex); } else { for (_menuItemIndex = 0; _menuItemIndex < 6; ++_menuItemIndex) { + if (_menuItemIndex == 4 && !shouldShowQuotes()) + continue; + if (_menuItemIndex != _selectedIndex) { addSpriteSlot(); } @@ -144,8 +126,11 @@ void MainMenu::doFrame() { // If the user has chosen to skip the animation, show the full menu immediately if (_skipFlag && _menuItemIndex >= 0) { - // Quickly loop through all the menu items to display each's final frame + // Quickly loop through all the menu items to display each's final frame for (; _menuItemIndex < 6; ++_menuItemIndex) { + if (_menuItemIndex == 4 && !shouldShowQuotes()) + continue; + // Draw the final frame of the menuitem _frameIndex = 0; addSpriteSlot(); @@ -155,9 +140,12 @@ void MainMenu::doFrame() { } else { if ((_menuItemIndex == -1) || (_frameIndex == 0)) { if (++_menuItemIndex == 6) { + // Reached end of display animation _vm->_events->showCursor(); return; + } else if (_menuItemIndex == 4 && !shouldShowQuotes()) { + ++_menuItemIndex; } _frameIndex = _menuItems[_menuItemIndex]->getCount() - 1; @@ -173,7 +161,7 @@ void MainMenu::doFrame() { void MainMenu::addSpriteSlot() { Scene &scene = _vm->_game->_scene; SpriteSlots &spriteSlots = scene._spriteSlots; - + int seqIndex = (_menuItemIndex < 6) ? _menuItemIndex : _frameIndex; spriteSlots.deleteTimer(seqIndex); @@ -267,7 +255,7 @@ bool MainMenu::onEvent(Common::Event &event) { } return true; - case Common::EVENT_MOUSEMOVE: + case Common::EVENT_MOUSEMOVE: if (_buttonDown) { int menuIndex = getHighlightedItem(event.mouse); if (menuIndex != _highlightedIndex) { @@ -299,7 +287,7 @@ bool MainMenu::onEvent(Common::Event &event) { default: break; } - + return false; } @@ -329,13 +317,13 @@ void MainMenu::handleAction(MADSGameAction action) { break; case RESUME_GAME: - // The original resumed the most recently saved game. Instead, + // The original resumed the most recently saved game. Instead, // just show the load game scren _vm->_dialogs->_pendingDialog = DIALOG_RESTORE; return; case SHOW_INTRO: - AnimationView::execute(_vm, "@rexopen"); + AnimationView::execute(_vm, "rexopen"); break; case CREDITS: @@ -366,12 +354,14 @@ void AdvertView::show() { uint32 expiryTime = g_system->getMillis() + 10 * 1000; _vm->_palette->resetGamePalette(4, 8); - + // Load the advert background onto the screen SceneInfo *sceneInfo = SceneInfo::init(_vm); sceneInfo->load(screenId, 0, Common::String(), 0, _vm->_game->_scene._depthSurface, _vm->_screen); _vm->_screen.copyRectToScreen(_vm->_screen.getBounds()); + _vm->_palette->setFullPalette(_vm->_palette->_mainPalette); + delete sceneInfo; EventsManager &events = *_vm->_events; @@ -387,6 +377,7 @@ void AdvertView::show() { events.setEventTarget(nullptr); _vm->quitGame(); + events.pollEvents(); } bool AdvertView::onEvent(Common::Event &event) { @@ -400,374 +391,16 @@ bool AdvertView::onEvent(Common::Event &event) { /*------------------------------------------------------------------------*/ -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); - strcpy(_resourceName, resName.c_str()); - vm->_dialogs->_pendingDialog = DIALOG_TEXTVIEW; -} - -TextView::TextView(MADSEngine *vm) : MenuView(vm), - _textSurface(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT + _vm->_font->getHeight()) { - _animating = false; - _panSpeed = 0; - Common::fill(&_spareScreens[0], &_spareScreens[10], 0); - _spareScreen = nullptr; - _scrollCount = 0; - _lineY = -1; - _scrollTimeout = 0; - _panCountdown = 0; - _translationX = 0; -} - -TextView::~TextView() { - delete _spareScreen; -} - -void TextView::load() { - if (!_script.open(_resourceName)) - 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()) { - _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 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; - int screenId = getParameter(¶mP); - - SceneInfo *sceneInfo = SceneInfo::init(_vm); - sceneInfo->load(screenId, 0, Common::String(), 0, scene._depthSurface, - scene._backgroundSurface); - - } else if (!strncmp(commandStr, "GO", 2)) { - _animating = true; - - // Grab what the final palete will be - byte destPalette[PALETTE_SIZE]; - _vm->_palette->grabPalette(destPalette, 0, 256); - - // Copy the loaded background, if any, to the view surface - //int yp = 22; - //scene._backgroundSurface.copyTo(this, 0, 22); - - // Handle fade-in - //byte srcPalette[768]; - //Common::fill(&srcPalette[0], &srcPalette[PALETTE_SIZE], 0); - //_vm->_palette->fadeIn(srcPalette, destPalette, 0, PALETTE_COUNT, 0, 0, - // TV_FADE_DELAY_MILLI, TV_NUM_FADE_STEPS); - _vm->_game->_fx = kTransitionFadeIn; - - } 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 + 6; - int driverNum = getParameter(¶mP); - _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 palEntry[3]; - palEntry[0] = getParameter(¶mP) << 2; - palEntry[1] = getParameter(¶mP) << 2; - palEntry[2] = getParameter(¶mP) << 2; - _vm->_palette->setPalette(&palEntry[0], 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 MSurface(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT); - //_spareScreen->loadBackground(_spareScreens[spareIndex], &_bgSpare); - - _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 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 = (MADS_SCREEN_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 = (MADS_SCREEN_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.h - _vm->_font->getHeight() - TEXTVIEW_LINE_SPACING; - _textSurface.fillRect(Common::Rect(0, yp, MADS_SCREEN_WIDTH, _textSurface.h), 0); - _vm->_font->writeString(&_textSurface, _currentLine, Common::Point(xStart, yp)); -} - -/*------------------------------------------------------------------------*/ - -char AnimationView::_resourceName[100]; - -void AnimationView::execute(MADSEngine *vm, const Common::String &resName) { - assert(resName.size() < 100); - strcpy(_resourceName, resName.c_str()); - vm->_dialogs->_pendingDialog = DIALOG_ANIMVIEW; -} - -AnimationView::AnimationView(MADSEngine *vm) : MenuView(vm) { - _soundDriverLoaded = false; -} - -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 RexAnimationView::scriptDone() { + AnimationView::scriptDone(); -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; - 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; - } - - char bgFile[10]; - 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 (Common::File::exists(bgFile)) { - _vm->_palette->resetGamePalette(4, 8); - SceneInfo *sceneInfo = SceneInfo::init(_vm); - sceneInfo->load(bgNumber, 0, Common::String(), 0, scene._depthSurface, - scene._backgroundSurface); - } - - // Read next line - processLines(); -} - -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()) { - _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); - warning("File: %s\n", _currentLine); - break; - } - } -} - -void AnimationView::processCommand() { - Common::String commandLine(_currentLine + 1); - commandLine.toUppercase(); - const char *commandStr = commandLine.c_str(); - const 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)) { - // Set the transition effect - param = param + 2; - _vm->_game->_fx = (ScreenTransition)atoi(param); - } else { - error("Unknown response command: '%s'", commandStr); + Common::String s = getResourceName(); + if (s == "rexend1") { + TextView::execute(_vm, "ending1"); + } else if (s == "rexend2") { + TextView::execute(_vm, "ending2"); + } else if (s == "rexend3") { + TextView::execute(_vm, "credits"); } } diff --git a/engines/mads/nebular/menu_nebular.h b/engines/mads/nebular/menu_nebular.h index 6e877a8a24..77b8b6fc6e 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,22 +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(); -public: - MenuView(MADSEngine *vm); - - virtual ~MenuView() {} - - virtual void show(); -}; - class MainMenu: public MenuView { private: SpriteAsset *_menuItems[7]; @@ -95,6 +80,8 @@ private: * Add a sprite slot for the current menuitem frame */ void addSpriteSlot(); + + bool shouldShowQuotes(); protected: /** * Display the menu @@ -143,95 +130,16 @@ public: void show(); }; -/** - * Scrolling text view - */ -class TextView : public MenuView { -private: - static char _resourceName[100]; - - bool _animating; - Common::Point _pan; - int _panSpeed; - int _spareScreens[10]; - int _scrollCount; - int _lineY; - uint32 _scrollTimeout; - int _panCountdown; - int _translationX; - Common::File _script; - char _currentLine[80]; - MSurface _textSurface; - MSurface *_spareScreen; -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); +class RexAnimationView : public AnimationView { +protected: + virtual void scriptDone(); public: - /** - * Queue the given text resource for display - */ - static void execute(MADSEngine *vm, const Common::String &resName); - - TextView(MADSEngine *vm); - - virtual ~TextView(); + RexAnimationView(MADSEngine *vm) : AnimationView(vm) {} }; -/** -* Animation cutscene view -*/ -class AnimationView : public MenuView { -private: - static char _resourceName[100]; - - Common::File _script; - uint32 _previousUpdate; - char _currentLine[80]; - char _currentFile[10]; - bool _soundDriverLoaded; -private: - void load(); - - void processLines(); - - void processCommand(); - - void scriptDone(); - - void doFrame(); -protected: - virtual bool onEvent(Common::Event &event); +class RexTextView : public TextView { public: - /** - * Queue the given text resource for display - */ - static void execute(MADSEngine *vm, const Common::String &resName); - - AnimationView(MADSEngine *vm); - - virtual ~AnimationView() {} + RexTextView(MADSEngine *vm) : TextView(vm) {} }; } // End of namespace Nebular diff --git a/engines/mads/nebular/nebular_scenes.cpp b/engines/mads/nebular/nebular_scenes.cpp index c71512ed4c..b5e2491624 100644 --- a/engines/mads/nebular/nebular_scenes.cpp +++ b/engines/mads/nebular/nebular_scenes.cpp @@ -331,7 +331,7 @@ void SceneInfoNebular::loadCodes(MSurface &depthSurface, Common::SeekableReadStr byte runValue = stream->readByte(); // Write out the run length - Common::fill(destP, destP + runLength, runValue); + Common::fill(destP, MIN(endP, destP + runLength), runValue); destP += runLength; // Get the next run length diff --git a/engines/mads/nebular/nebular_scenes5.cpp b/engines/mads/nebular/nebular_scenes5.cpp index 5a67d1541f..66d8294fc6 100644 --- a/engines/mads/nebular/nebular_scenes5.cpp +++ b/engines/mads/nebular/nebular_scenes5.cpp @@ -2847,8 +2847,6 @@ void Scene551::actions() { _vm->_dialogs->show(55113); else if (_action.isAction(VERB_LOOK, NOUN_TELEPORTER)) _vm->_dialogs->show(55114); - else if (_action.isAction(VERB_LOOK, NOUN_BUILDING)) - _vm->_dialogs->show(55115); else if (_action.isAction(VERB_LOOK, NOUN_SIDEWALK_TO_WEST)) { if (_game._visitedScenes.exists(505)) _vm->_dialogs->show(55116); diff --git a/engines/mads/nebular/nebular_scenes6.cpp b/engines/mads/nebular/nebular_scenes6.cpp index d33675c578..679039535f 100644 --- a/engines/mads/nebular/nebular_scenes6.cpp +++ b/engines/mads/nebular/nebular_scenes6.cpp @@ -643,7 +643,8 @@ void Scene603::actions() { _game._player._visible = true; _game._player._stepEnabled = true; } - } + } else + _vm->_dialogs->show(60323); } else if (_action._lookFlag) _vm->_dialogs->show(60310); else if (_action.isAction(VERB_LOOK, NOUN_BED)) @@ -670,8 +671,6 @@ void Scene603::actions() { _vm->_dialogs->show(60321); else if (_action.isAction(VERB_TAKE, NOUN_PERFUME)) _vm->_dialogs->show(60322); - else if (_action.isAction(VERB_TAKE, NOUN_NOTE)) - _vm->_dialogs->show(60323); else if (_action.isAction(VERB_LOOK, NOUN_NOTE)) { if (_game._objects[OBJ_NOTE]._roomNumber == _scene->_currentSceneId) _vm->_dialogs->show(60324); @@ -3156,7 +3155,7 @@ bool Scene611::check2ChargedBatteries() { } bool Scene611::check4ChargedBatteries() { - if (_game._objects.isInInventory(OBJ_DURAFAIL_CELLS) && _game._objects.isInInventory(OBJ_PHONE_CELLS) + if (_game._objects.isInInventory(OBJ_DURAFAIL_CELLS) && _game._objects.isInInventory(OBJ_PHONE_CELLS) && _globals[kDurafailRecharged]) return true; diff --git a/engines/mads/nebular/nebular_scenes7.cpp b/engines/mads/nebular/nebular_scenes7.cpp index 930bb7c250..0f019c4b19 100644 --- a/engines/mads/nebular/nebular_scenes7.cpp +++ b/engines/mads/nebular/nebular_scenes7.cpp @@ -2616,7 +2616,7 @@ void Scene752::actions() { default: break; } - } else if (_action.isAction(VERB_TAKE, NOUN_BONES) && (_action._savedFields._mainObjectSource == CAT_HOTSPOT) && + } else if (_action.isAction(VERB_TAKE, NOUN_BONES) && (_action._savedFields._mainObjectSource == CAT_HOTSPOT) && (!_game._objects.isInInventory(OBJ_BONES) || _game._trigger)) { switch (_game._trigger) { case 0: diff --git a/engines/mads/nebular/nebular_scenes8.cpp b/engines/mads/nebular/nebular_scenes8.cpp index 14f36756de..62a1a262b0 100644 --- a/engines/mads/nebular/nebular_scenes8.cpp +++ b/engines/mads/nebular/nebular_scenes8.cpp @@ -1098,7 +1098,7 @@ void Scene804::actions() { _action.isAction(VERB_OPEN, NOUN_SERVICE_PANEL)) { _scene->_nextSceneId = 805; } else if ((_action.isAction(VERB_ACTIVATE, NOUN_REMOTE)) && _globals[kTopButtonPushed]) { - if (!_globals[kInSpace]) { + if (!_globals[kInSpace]) { // Top button pressed on panel in hanger control if (!_globals[kBeamIsUp]) { _globals[kFromCockpit] = true; diff --git a/engines/mads/nebular/sound_nebular.cpp b/engines/mads/nebular/sound_nebular.cpp index fc2755db2f..0a054440b2 100644 --- a/engines/mads/nebular/sound_nebular.cpp +++ b/engines/mads/nebular/sound_nebular.cpp @@ -24,6 +24,7 @@ #include "audio/decoders/raw.h" #include "common/algorithm.h" #include "common/debug.h" +#include "common/md5.h" #include "common/memstream.h" #include "mads/sound.h" #include "mads/nebular/sound_nebular.h" @@ -149,7 +150,7 @@ AdlibSample::AdlibSample(Common::SeekableReadStream &s) { /*-----------------------------------------------------------------------*/ -ASound::ASound(Audio::Mixer *mixer, const Common::String &filename, int dataOffset) { +ASound::ASound(Audio::Mixer *mixer, FM_OPL *opl, const Common::String &filename, int dataOffset) { // Open up the appropriate sound file if (!_soundFile.open(filename)) error("Could not open file - %s", filename.c_str()); @@ -197,8 +198,7 @@ ASound::ASound(Audio::Mixer *mixer, const Common::String &filename, int dataOffs // Store passed parameters, and setup OPL _dataOffset = dataOffset; _mixer = mixer; - _opl = OPL::Config::create(); - assert(_opl); + _opl = opl; _opl->init(getRate()); _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, @@ -217,7 +217,32 @@ ASound::~ASound() { delete[] (*i)._data; _mixer->stopHandle(_soundHandle); - delete _opl; +} + +void ASound::validate() { + Common::File f; + static const char *const MD5[] = { + "205398468de2c8873b7d4d73d5be8ddc", + "f9b2d944a2fb782b1af5c0ad592306d3", + "7431f8dad77d6ddfc24e6f3c0c4ac7df", + "eb1f3f5a4673d3e73d8ac1818c957cf4", + "f936dd853073fa44f3daac512e91c476", + "3dc139d3e02437a6d9b732072407c366", + "af0edab2934947982e9a405476702e03", + "8cbc25570b50ba41c9b5361cad4fbedc", + "a31e4783e098f633cbb6689adb41dd4f" + }; + + for (int i = 1; i <= 9; ++i) { + Common::String filename = Common::String::format("ASOUND.00%d", i); + if (!f.open(filename)) + error("Could not process - %s", filename.c_str()); + Common::String md5str = Common::computeStreamMD5AsString(f, 8192); + f.close(); + + if (md5str != MD5[i - 1]) + error("Invalid sound file - %s", filename.c_str()); + } } void ASound::adlibInit() { @@ -941,8 +966,8 @@ const ASound1::CommandPtr ASound1::_commandList[42] = { &ASound1::command40, &ASound1::command41 }; -ASound1::ASound1(Audio::Mixer *mixer) - : ASound(mixer, "asound.001", 0x1520) { +ASound1::ASound1(Audio::Mixer *mixer, FM_OPL *opl) + : ASound(mixer, opl, "asound.001", 0x1520) { _cmd23Toggle = false; // Load sound samples @@ -1242,7 +1267,7 @@ const ASound2::CommandPtr ASound2::_commandList[44] = { &ASound2::command40, &ASound2::command41, &ASound2::command42, &ASound2::command43 }; -ASound2::ASound2(Audio::Mixer *mixer) : ASound(mixer, "asound.002", 0x15E0) { +ASound2::ASound2(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.002", 0x15E0) { _command12Param = 0xFD; // Load sound samples @@ -1613,7 +1638,7 @@ const ASound3::CommandPtr ASound3::_commandList[61] = { &ASound3::command60 }; -ASound3::ASound3(Audio::Mixer *mixer) : ASound(mixer, "asound.003", 0x15B0) { +ASound3::ASound3(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.003", 0x15B0) { _command39Flag = false; // Load sound samples @@ -2017,7 +2042,7 @@ const ASound4::CommandPtr ASound4::_commandList[61] = { &ASound4::command60 }; -ASound4::ASound4(Audio::Mixer *mixer) : ASound(mixer, "asound.004", 0x14F0) { +ASound4::ASound4(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.004", 0x14F0) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 210; ++i) @@ -2273,7 +2298,7 @@ const ASound5::CommandPtr ASound5::_commandList[42] = { &ASound5::command40, &ASound5::command41 }; -ASound5::ASound5(Audio::Mixer *mixer) : ASound(mixer, "asound.002", 0x15E0) { +ASound5::ASound5(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.002", 0x15E0) { // Load sound samples _soundFile.seek(_dataOffset + 0x144); for (int i = 0; i < 164; ++i) @@ -2514,7 +2539,7 @@ const ASound6::CommandPtr ASound6::_commandList[30] = { &ASound6::nullCommand, &ASound6::command29 }; -ASound6::ASound6(Audio::Mixer *mixer) : ASound(mixer, "asound.006", 0x1390) { +ASound6::ASound6(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.006", 0x1390) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 200; ++i) @@ -2670,7 +2695,7 @@ const ASound7::CommandPtr ASound7::_commandList[38] = { &ASound7::command36, &ASound7::command37 }; -ASound7::ASound7(Audio::Mixer *mixer) : ASound(mixer, "asound.007", 0x1460) { +ASound7::ASound7(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.007", 0x1460) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 214; ++i) @@ -2876,7 +2901,7 @@ const ASound8::CommandPtr ASound8::_commandList[38] = { &ASound8::command36, &ASound8::command37 }; -ASound8::ASound8(Audio::Mixer *mixer) : ASound(mixer, "asound.008", 0x1490) { +ASound8::ASound8(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.008", 0x1490) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 174; ++i) @@ -3114,6 +3139,313 @@ int ASound8::command37() { return 0; } +/*-----------------------------------------------------------------------*/ + +const ASound9::CommandPtr ASound9::_commandList[52] = { + &ASound9::command0, &ASound9::command1, &ASound9::command2, &ASound9::command3, + &ASound9::command4, &ASound9::command5, &ASound9::command6, &ASound9::command7, + &ASound9::command8, &ASound9::command9, &ASound9::command10, &ASound9::command11, + &ASound9::command12, &ASound9::command13, &ASound9::command14, &ASound9::command15, + &ASound9::command16, &ASound9::command17, &ASound9::command18, &ASound9::command19, + &ASound9::command20, &ASound9::command21, &ASound9::command22, &ASound9::command23, + &ASound9::command24, &ASound9::command25, &ASound9::command26, &ASound9::command27, + &ASound9::command28, &ASound9::command29, &ASound9::command30, &ASound9::command31, + &ASound9::command32, &ASound9::command33, &ASound9::command34, &ASound9::command35, + &ASound9::command36, &ASound9::command37, &ASound9::command38, &ASound9::command39, + &ASound9::command40, &ASound9::command41, &ASound9::command42, &ASound9::command43, + &ASound9::command44_46, &ASound9::command45, &ASound9::command44_46, &ASound9::command47, + &ASound9::command48, &ASound9::command49, &ASound9::command50, &ASound9::command51 +}; + +ASound9::ASound9(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.009", 0x16F0) { + _v1 = _v2 = 0; + _soundPtr = nullptr; + + // Load sound samples + _soundFile.seek(_dataOffset + 0x50); + for (int i = 0; i < 94; ++i) + _samples.push_back(AdlibSample(_soundFile)); +} + +int ASound9::command(int commandId, int param) { + if (commandId > 51) + return 0; + + _commandParam = param; + _frameCounter = 0; + return (this->*_commandList[commandId])(); +} + +int ASound9::command9() { + _v1 = 1848; + _v2 = 84; + _channels[0].load(loadData(0xAA4, 470)); + _channels[1].load(loadData(0xE4C, 450)); + _channels[2].load(loadData(0x1466, 702)); + _channels[3].load(loadData(0x137E, 232)); + _channels[4].load(loadData(0x1014, 65)); + _channels[5].load(loadData(0x11C4, 44)); + _channels[6].load(loadData(0XC7A, 466)); + return 0; +} + +int ASound9::command10() { + _channels[0].load(loadData(0x1724, 24)); + _channels[1].load(loadData(0x173C, 24)); + _channels[2].load(loadData(0x1754, 20)); + _channels[3].load(loadData(0x1768, 20)); + _channels[4].load(loadData(0x177C, 20)); + _channels[5].load(loadData(0x1790, 20)); + return 0; +} + +int ASound9::command11() { + playSound(0x8232, 168); + playSound(0x82DA, 170); + return 0; +} + +int ASound9::command12() { + playSound(0x80DA, 12); + playSound(0x80E6, 12); + return 0; +} + +int ASound9::command13() { + playSound(0x80F2, 38); + playSound(0x8118, 42); + return 0; +} + +int ASound9::command14() { + playSound(0x81F6, 22); + return 0; +} + +int ASound9::command15() { + playSound(0x818A, 32); + playSound(0x81AA, 32); + return 0; +} + +int ASound9::command16() { + playSound(0x8022, 36); + playSound(0x8046, 42); + return 0; +} + +int ASound9::command17() { + command29(); + playSound(0x858C, 11); + return 0; +} + +int ASound9::command18() { + playSound(0x80C2, 24); + return 0; +} + +int ASound9::command19() { + playSound(0x80A0, 34); + return 0; +} + +int ASound9::command20() { + int v = (getRandomNumber() & 0x10) | 0x4D; + byte *pData = loadData(0x8142, 8); + pData[4] = v & 0x7F; + playSoundData(pData); + return 0; +} + +int ASound9::command21() { + playSound(0x815A, 16); + return 0; +} + +int ASound9::command22() { + playSound(0x816A, 16); + return 0; +} + +int ASound9::command23() { + playSound(0x814A, 16); + return 0; +} + +int ASound9::command24() { + playSound(0x7FE2, 34); + return 0; +} + +int ASound9::command25() { + playSound(0x8004, 30); + return 0; +} + +int ASound9::command26() { + _channels[6].load(loadData(0x8384, 156)); + _channels[7].load(loadData(0x8420, 160)); + return 0; +} + +int ASound9::command27() { + playSound(0x84C0, 140); + return 0; +} + +int ASound9::command28() { + playSound(0x81CA, 10); + return 0; +} + +int ASound9::command29() { + playSound(0x81D4, 10); + return 0; +} + +int ASound9::command30() { + playSound(0x817A, 16); + return 0; +} + +int ASound9::command31() { + playSound(0x820C, 14); + playSound(0x821A, 24); + return 0; +} + +int ASound9::command32() { + playSound(0x8070, 8); + return 0; +} + +int ASound9::command33() { + playSound(0x8078, 16); + playSound(0x8088, 16); + return 0; +} + +int ASound9::command34() { + // Skipped stuff in original + _channels[0].load(loadData(0x17A4, 24)); + _channels[1].load(loadData(0x1CDE, 62)); + _channels[2].load(loadData(0x2672, 980)); + _channels[3].load(loadData(0x3336, 1000)); + _channels[4].load(loadData(0x469E, 176)); + _channels[5].load(loadData(0x57F2, 138)); + + return 0; +} + +int ASound9::command35() { + playSound(0x854C, 64); + return 0; +} + +int ASound9::command36() { + playSound(0x81DE, 10); + playSound(0x81E8, 14); + return 0; +} + +int ASound9::command37() { + byte *pData = loadData(0x8098, 8); + int v = getRandomNumber(); + if ((v &= 0x40) != 0) + v |= 8; + else + v += 0x4A; + + pData[6] = v; + playSoundData(pData); + return 0; +} + +int ASound9::command38() { + playSound(0x100E, 6); + return 0; +} + +int ASound9::command39() { + _soundPtr = loadData(0x1055, 128); + return 0; +} + +int ASound9::command40() { + _soundPtr = loadData(0x118C, 50); + return 0; +} + +int ASound9::command41() { + _soundPtr = loadData(0x11BE, 6); + return 0; +} + +int ASound9::command42() { + _soundPtr = loadData(0x11F0, 50); + return 0; +} + +int ASound9::command43() { + _v1 = _v2 = 80; + _channels[0].load(loadData(0x626A, 90)); + _channels[1].load(loadData(0x67F2, 92)); + _channels[2].load(loadData(0x6CFE, 232)); + _channels[3].load(loadData(0x7146, 236)); + + return 0; +} + +int ASound9::command44_46() { + _soundPtr = loadData(0x10D5, 38); + return 0; +} + +int ASound9::command45() { + _soundPtr = loadData(0x10FB, 38); + return 0; +} + +int ASound9::command47() { + _soundPtr = loadData(0x1121, 107); + return 0; +} + +int ASound9::command48() { + playSound(0x7FD0, 8); + playSound(0x7FD8, 10); + return 0; +} + +int ASound9::command49() { + _channels[0].load(loadData(0x7AD6, 92)); + _channels[1].load(loadData(0x7B32, 90)); + _channels[2].load(loadData(0x7B8C, 738)); + _channels[3].load(loadData(0x7E6E, 28)); + _channels[4].load(loadData(0x7E8A, 30)); + _channels[5].load(loadData(0x7EA8, 30)); + _channels[6].load(loadData(0x7EC6, 195)); + return 0; +} + +int ASound9::command50() { + _soundPtr = loadData(0x1222, 348); + return 0; +} + +int ASound9::command51() { + // Skipped stuff in original + _channels[0].load(loadData(0x17BC, 1282)); + _channels[1].load(loadData(0x1CFC, 2422)); + _channels[2].load(loadData(0x2A46, 2288)); + _channels[3].load(loadData(0x371E, 3964)); + _channels[4].load(loadData(0x474E, 1863)); + _channels[5].load(loadData(0x587C, 2538)); + return 0; +} + + } // End of namespace Nebular } // End of namespace MADS diff --git a/engines/mads/nebular/sound_nebular.h b/engines/mads/nebular/sound_nebular.h index c485bd7955..ccfd40ad52 100644 --- a/engines/mads/nebular/sound_nebular.h +++ b/engines/mads/nebular/sound_nebular.h @@ -305,10 +305,12 @@ public: public: /** * Constructor + * @param mixer Mixer + * @param opl OPL * @param filename Specifies the adlib sound player file to use * @param dataOffset Offset in the file of the data segment */ - ASound(Audio::Mixer *mixer, const Common::String &filename, int dataOffset); + ASound(Audio::Mixer *mixer, FM_OPL *opl, const Common::String &filename, int dataOffset); /** * Destructor @@ -316,6 +318,11 @@ public: virtual ~ASound(); /** + * Validates the Adlib sound files + */ + static void validate(); + + /** * Execute a player command. Most commands represent sounds to play, but some * low number commands also provide control operations. * @param commandId Player ommand to execute. @@ -408,7 +415,7 @@ private: void command111213(); int command2627293032(); public: - ASound1(Audio::Mixer *mixer); + ASound1(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -460,7 +467,7 @@ private: void command9Randomize(); void command9Apply(byte *data, int val, int incr); public: - ASound2(Audio::Mixer *mixer); + ASound2(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -520,7 +527,7 @@ private: void command9Randomize(); void command9Apply(byte *data, int val, int incr); public: - ASound3(Audio::Mixer *mixer); + ASound3(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -558,7 +565,7 @@ private: void method1(); public: - ASound4(Audio::Mixer *mixer); + ASound4(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -604,7 +611,7 @@ private: int command42(); int command43(); public: - ASound5(Audio::Mixer *mixer); + ASound5(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -633,7 +640,7 @@ private: int command25(); int command29(); public: - ASound6(Audio::Mixer *mixer); + ASound6(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -665,7 +672,7 @@ private: int command36(); int command37(); public: - ASound7(Audio::Mixer *mixer); + ASound7(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -708,7 +715,66 @@ private: void method1(byte *pData); void adjustRange(byte *pData, byte v, int incr); public: - ASound8(Audio::Mixer *mixer); + ASound8(Audio::Mixer *mixer, FM_OPL *opl); + + virtual int command(int commandId, int param); +}; + +class ASound9 : public ASound { +private: + int _v1, _v2; + byte *_soundPtr; + + typedef int (ASound9::*CommandPtr)(); + static const CommandPtr _commandList[52]; + + int command9(); + int command10(); + int command11(); + int command12(); + int command13(); + int command14(); + int command15(); + int command16(); + int command17(); + int command18(); + int command19(); + int command20(); + int command21(); + int command22(); + int command23(); + int command24(); + int command25(); + int command26(); + int command27(); + int command28(); + int command29(); + int command30(); + int command31(); + int command32(); + int command33(); + int command34(); + int command35(); + int command36(); + int command37(); + int command38(); + int command39(); + int command40(); + int command41(); + int command42(); + int command43(); + int command44_46(); + int command45(); + int command47(); + int command48(); + int command49(); + int command50(); + int command51(); + int command57(); + int command59(); + int command60(); +public: + ASound9(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; diff --git a/engines/mads/palette.cpp b/engines/mads/palette.cpp index eedbf36ddd..836d04f7c0 100644 --- a/engines/mads/palette.cpp +++ b/engines/mads/palette.cpp @@ -143,7 +143,7 @@ int PaletteUsage::process(Common::Array<RGB6> &palette, uint flags) { for (uint palIndex = 0; palIndex < palette.size(); ++palIndex) { bool changed = false; - int newPalIndex = -1; + int newPalIndex = 0xFF; int v1 = palRange[palIndex]._v2; if (palette[v1]._flags & 8) { @@ -229,8 +229,11 @@ int PaletteUsage::process(Common::Array<RGB6> &palette, uint flags) { // In at least scene 318, when the doctor knocks you with the blackjack, // the changed flag can be false //assert(changed); - assert(newPalIndex != -1); - + + // CHECKME: When pressing on F1 in the first screen, newPalIndex is set to 0xFF at this point + // which is a valid value for the index. Maybe a better check would be "< 256" ? + //assert(newPalIndex != -1); + int var52 = (noUsageFlag && palette[palIndex]._u2) ? 2 : 0; _vm->_palette->_palFlags[newPalIndex] |= var52 | rgbMask; @@ -314,6 +317,62 @@ int PaletteUsage::rgbFactor(byte *palEntry, RGB6 &pal6) { return total; } +int PaletteUsage::checkRGB(const byte *rgb, int palStart, bool flag, int *palIndex) { + Palette &palette = *_vm->_palette; + bool match = false; + int result; + if (palStart >= 0) { + result = palStart; + } else { + result = -1; + for (int i = 0; i < palette._highRange; ++i) { + if (!palette._rgbList[i]) { + result = i; + break; + } + } + } + + if (result >= 0) { + int mask = 1 << result; + byte *palP = &palette._mainPalette[0]; + uint32 *flagsP = &palette._palFlags[0]; + + for (; flagsP < &palette._palFlags[PALETTE_COUNT]; ++flagsP, ++result) { + if ((!(*flagsP & 1) || flag) && !(*flagsP & 2)) { + if (!memcmp(palP, rgb, 3)) { + *flagsP |= mask; + + if (palIndex) + *palIndex = result; + match = true; + break; + } + } + } + + if (!match) { + palP = &palette._mainPalette[0]; + flagsP = &palette._palFlags[0]; + + for (int i = 0; i < PALETTE_COUNT; ++i, palP += 3, ++flagsP) { + if (!*flagsP) { + Common::copy(rgb, rgb + 3, palP); + *flagsP |= mask; + + if (palIndex) + *palIndex = i; + match = true; + break; + } + } + } + } + + assert(match); + return result; +} + /*------------------------------------------------------------------------*/ void RGBList::clear() { diff --git a/engines/mads/palette.h b/engines/mads/palette.h index 9b8b7146db..27d25f266b 100644 --- a/engines/mads/palette.h +++ b/engines/mads/palette.h @@ -136,6 +136,8 @@ public: void updateUsage(Common::Array<int> &usageList, int sceneUsageIndex); void resetPalFlags(int idx); + + int checkRGB(const byte *rgb, int palStart, bool flag, int *palIndex); }; class RGBList { diff --git a/engines/mads/phantom/game_phantom.cpp b/engines/mads/phantom/game_phantom.cpp index ba2179fcbf..0b2531ef65 100644 --- a/engines/mads/phantom/game_phantom.cpp +++ b/engines/mads/phantom/game_phantom.cpp @@ -50,11 +50,12 @@ void GamePhantom::startGame() { } void GamePhantom::initializeGlobals() { - //int count, count2; - //int bad; - _globals.reset(); - //_globals[kTalkInanimateCount] = 8; + + warning("TODO: sub_316DA()"); + + _player._facing = FACING_NORTH; + _player._turnToFacing = FACING_NORTH; /* Section #1 variables */ // TODO @@ -74,11 +75,7 @@ void GamePhantom::initializeGlobals() { /* Section #9 variables */ // TODO - _player._facing = FACING_NORTH; - _player._turnToFacing = FACING_NORTH; - - //Player::preloadSequences("RXM", 1); - //Player::preloadSequences("ROX", 1); + Player::preloadSequences("RAL", 1); } void GamePhantom::setSectionHandler() { diff --git a/engines/mads/scene.cpp b/engines/mads/scene.cpp index 1f95749fd8..d2b4b29622 100644 --- a/engines/mads/scene.cpp +++ b/engines/mads/scene.cpp @@ -31,7 +31,7 @@ namespace MADS { Scene::Scene(MADSEngine *vm) - : _vm(vm), _action(_vm), _depthSurface(vm), + : _vm(vm), _action(_vm), _depthSurface(), _dirtyAreas(_vm), _dynamicHotspots(vm), _hotspots(vm), _kernelMessages(vm), _sequences(vm), _sprites(vm), _spriteSlots(vm), _textDisplay(vm), _userInterface(vm) { @@ -63,8 +63,7 @@ Scene::Scene(MADSEngine *vm) _paletteUsageF.push_back(PaletteUsage::UsageEntry(0xF)); // Set up a scene surface that maps to our physical screen drawing surface - _sceneSurface.init(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT, MADS_SCREEN_WIDTH, - _vm->_screen.getPixels(), Graphics::PixelFormat::createFormatCLUT8()); + restrictScene(); // Set up the verb list _verbList.push_back(VerbInit(VERB_LOOK, VERB_THAT, PREP_NONE)); @@ -82,6 +81,12 @@ Scene::Scene(MADSEngine *vm) Scene::~Scene() { delete _sceneLogic; delete _sceneInfo; + delete _animationData; +} + +void Scene::restrictScene() { + _sceneSurface.init(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT, MADS_SCREEN_WIDTH, + _vm->_screen.getPixels(), Graphics::PixelFormat::createFormatCLUT8()); } void Scene::clearVocab() { @@ -177,7 +182,7 @@ void Scene::loadScene(int sceneId, const Common::String &prefix, bool palFlag) { flags |= ANIMFLAG_LOAD_BACKGROUND_ONLY; _animationData = Animation::init(_vm, this); - DepthSurface depthSurface(_vm); + DepthSurface depthSurface; _animationData->load(_userInterface, depthSurface, prefix, flags, nullptr, nullptr); _vm->_palette->_paletteUsage.load(&_scenePaletteUsage); @@ -355,6 +360,9 @@ void Scene::loop() { if (_vm->_dialogs->_pendingDialog != DIALOG_NONE && !_vm->_game->_trigger && _vm->_game->_player._stepEnabled) _reloadSceneFlag = true; + + if (_vm->_game->_winStatus) + break; } } @@ -510,7 +518,7 @@ void Scene::drawElements(ScreenTransition transitionType, bool surfaceFlag) { _vm->_sound->startQueuedCommands(); } else { // Copy dirty areas to the screen - _dirtyAreas.copyToScreen(_vm->_screen._offset); + _dirtyAreas.copyToScreen(); } _spriteSlots.cleanUp(); @@ -603,7 +611,7 @@ void Scene::loadAnimation(const Common::String &resName, int trigger) { if (_activeAnimation) freeAnimation(); - DepthSurface depthSurface(_vm); + DepthSurface depthSurface; UserInterface interfaceSurface(_vm); _activeAnimation = Animation::init(_vm, this); diff --git a/engines/mads/scene.h b/engines/mads/scene.h index 407d70dc85..9fd99ad8e5 100644 --- a/engines/mads/scene.h +++ b/engines/mads/scene.h @@ -52,11 +52,6 @@ private: */ void loadVocabStrings(); - /* - * Initializes the data for palette animation within the scene - */ - void initPaletteAnimation(Common::Array<PaletteCycle> &palCycles, bool animFlag); - /** * Handles a single frame within the game scene */ @@ -142,6 +137,8 @@ public: */ ~Scene(); + void restrictScene(); + /** * Clear the vocabulary list */ @@ -202,6 +199,11 @@ public: */ void drawElements(ScreenTransition transitionType, bool surfaceFlag); + /* + * Initializes the data for palette animation within the scene + */ + void initPaletteAnimation(Common::Array<PaletteCycle> &palCycles, bool animFlag); + /** * Handles cycling palette colors for the scene */ diff --git a/engines/mads/scene_data.cpp b/engines/mads/scene_data.cpp index e874468345..b5e219ed04 100644 --- a/engines/mads/scene_data.cpp +++ b/engines/mads/scene_data.cpp @@ -217,7 +217,7 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, int width = _width; int height = _height; - if (!bgSurface.getPixels()) { + if (!bgSurface.getPixels() || (bgSurface.w != width) || (bgSurface.h != height)) { bgSurface.setSize(width, height); } @@ -232,11 +232,11 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, infoFile.close(); if (_vm->getGameID() == GType_RexNebular) { - loadMadsV1Background(sceneId, resName, flags, bgSurface); - loadPalette(sceneId, _artFileNum, resName, flags, bgSurface); + loadMadsV1Background(_artFileNum, resName, flags, bgSurface); + loadPalette(_sceneId, _artFileNum, resName, flags, bgSurface); } else { - loadMadsV2Background(sceneId, resName, flags, bgSurface); - loadPalette(sceneId, sceneId, resName, flags, bgSurface); + loadMadsV2Background(_sceneId, resName, flags, bgSurface); + loadPalette(_sceneId, _sceneId, resName, flags, bgSurface); } Common::Array<SpriteAsset *> spriteSets; @@ -264,7 +264,7 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, assert(asset && _depthStyle != 2); MSprite *spr = asset->getFrame(asset->getCount() - 1); - bgSurface.copyFrom(spr, si._position, si._depth, &depthSurface, + bgSurface.copyFrom(spr, si._position, si._depth, &depthSurface, si._scale, spr->getTransparencyIndex()); } @@ -299,6 +299,7 @@ void SceneInfo::loadPalette(int sceneId, int artFileNum, const Common::String &r delete stream; // Copy out the palette animation data + _paletteCycles.clear(); for (uint i = 0; i < artHeader._paletteCycles.size(); ++i) _paletteCycles.push_back(artHeader._paletteCycles[i]); @@ -333,7 +334,7 @@ void SceneInfo::loadMadsV1Background(int sceneId, const Common::String &resName, // Get the ART resource if (sceneFlag) { - resourceName = Resources::formatName(RESPREFIX_RM, _artFileNum, ".ART"); + resourceName = Resources::formatName(RESPREFIX_RM, sceneId, ".ART"); } else { resourceName = "*" + Resources::formatResource(resName, resName); } @@ -342,13 +343,33 @@ void SceneInfo::loadMadsV1Background(int sceneId, const Common::String &resName, File artFile(resourceName); MadsPack artResource(&artFile); - // Read in the background surface data - assert(_width == bgSurface.w && _height == bgSurface.h); + // Read inhh the background surface data + assert(_width && _height == bgSurface.h); stream = artResource.getItemStream(1); stream->read(bgSurface.getPixels(), bgSurface.w * bgSurface.h); + delete stream; + + if (flags & SCENEFLAG_TRANSLATE) { + // Load in the palette and translate it + Common::SeekableReadStream *palStream = artResource.getItemStream(0); + Common::Array<RGB6> palette; + + _width = palStream->readUint16LE(); + _height = palStream->readUint16LE(); + + int numColors = palStream->readUint16LE(); + assert(numColors <= 252); + palette.resize(numColors); + for (int i = 0; i < numColors; ++i) + palette[i].load(palStream); + delete palStream; + + // Translate the surface + _vm->_palette->_paletteUsage.process(palette, 0); + bgSurface.translate(palette); + } // Close the ART file - delete stream; artFile.close(); } diff --git a/engines/mads/scene_data.h b/engines/mads/scene_data.h index 783a9ab8a9..41e094b8f5 100644 --- a/engines/mads/scene_data.h +++ b/engines/mads/scene_data.h @@ -55,7 +55,8 @@ class SpriteSlot; enum { SCENEFLAG_DITHER = 0x01, // Dither to 16 colors - SCENEFLAG_LOAD_SHADOW = 0x10 // Load hard shadows + SCENEFLAG_LOAD_SHADOW = 0x10, // Load hard shadows + SCENEFLAG_TRANSLATE = 0x10000 // Translate palette of loaded background }; class VerbInit { diff --git a/engines/mads/screen.cpp b/engines/mads/screen.cpp index ab5dff56ff..c9a0863d85 100644 --- a/engines/mads/screen.cpp +++ b/engines/mads/screen.cpp @@ -212,8 +212,7 @@ void DirtyAreas::copy(MSurface *srcSurface, MSurface *destSurface, const Common: Common::Rect bounds(srcBounds.left + posAdjust.x, srcBounds.top + posAdjust.y, srcBounds.right + posAdjust.x, srcBounds.bottom + posAdjust.y); - Common::Point destPos(bounds.left + _vm->_screen._offset.x, - bounds.top + _vm->_screen._offset.y); + Common::Point destPos(srcBounds.left, srcBounds.top); if ((*this)[i]._active && bounds.isValidRect()) { srcSurface->copyTo(destSurface, bounds, destPos); @@ -221,17 +220,14 @@ void DirtyAreas::copy(MSurface *srcSurface, MSurface *destSurface, const Common: } } -void DirtyAreas::copyToScreen(const Common::Point &posAdjust) { +void DirtyAreas::copyToScreen() { for (uint i = 0; i < size(); ++i) { - const Common::Rect &srcBounds = (*this)[i]._bounds; + const Common::Rect &bounds = (*this)[i]._bounds; // Check if this is a sane rectangle before attempting to create it - if (srcBounds.left >= srcBounds.right || srcBounds.top >= srcBounds.bottom) + if (bounds.left >= bounds.right || bounds.top >= bounds.bottom) continue; - Common::Rect bounds(srcBounds.left + posAdjust.x, srcBounds.top + posAdjust.y, - srcBounds.right + posAdjust.x, srcBounds.bottom + posAdjust.y); - if ((*this)[i]._active && (*this)[i]._bounds.isValidRect()) { _vm->_screen.copyRectToScreen(bounds); } @@ -561,23 +557,32 @@ void ScreenObjects::synchronize(Common::Serializer &s) { ScreenSurface::ScreenSurface() { _shakeCountdown = -1; _random = 0x4D2; + _surfacePixels = nullptr; } void ScreenSurface::init() { - setSize(g_system->getWidth(), g_system->getHeight()); -} + // Set the size for the screen + setSize(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT); -void ScreenSurface::copyRectToScreen(const Common::Point &destPos, - const Common::Rect &bounds) { - const byte *buf = getBasePtr(destPos.x, destPos.y); + // Store a copy of the raw pixels pointer for the screen, since the surface + // itself may be later changed to only a subset of the screen + _surfacePixels = (byte *)getPixels(); + _freeFlag = false; +} - if (bounds.width() != 0 && bounds.height() != 0) - g_system->copyRectToScreen(buf, this->pitch, bounds.left, bounds.top, - bounds.width(), bounds.height()); +ScreenSurface::~ScreenSurface() { + delete[] _surfacePixels; } void ScreenSurface::copyRectToScreen(const Common::Rect &bounds) { - copyRectToScreen(Common::Point(bounds.left, bounds.top), bounds); + const byte *buf = getBasePtr(bounds.left, bounds.top); + + Common::Rect destBounds = bounds; + destBounds.translate(_clipBounds.left, _clipBounds.top); + + if (bounds.width() != 0 && bounds.height() != 0) + g_system->copyRectToScreen(buf, this->pitch, destBounds.left, destBounds.top, + destBounds.width(), destBounds.height()); } void ScreenSurface::updateScreen() { @@ -659,4 +664,15 @@ void ScreenSurface::transition(ScreenTransition transitionType, bool surfaceFlag } } +void ScreenSurface::setClipBounds(const Common::Rect &r) { + _clipBounds = r; + setPixels(_surfacePixels + pitch * r.top + r.left, r.width(), r.height()); + this->pitch = MADS_SCREEN_WIDTH; +} + +void ScreenSurface::resetClipBounds() { + setClipBounds(Common::Rect(0, 0, MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT)); +} + + } // End of namespace MADS diff --git a/engines/mads/screen.h b/engines/mads/screen.h index 7937e15456..9d01ca82e3 100644 --- a/engines/mads/screen.h +++ b/engines/mads/screen.h @@ -117,8 +117,8 @@ public: /** * Use the lsit of dirty areas to copy areas of the screen surface to * the physical screen - * @param posAdjust Position adjustment */ - void copyToScreen(const Common::Point &posAdjust); + */ + void copyToScreen(); void reset(); }; @@ -205,8 +205,9 @@ public: class ScreenSurface : public MSurface { private: uint16 _random; + byte *_surfacePixels; + Common::Rect _clipBounds; public: - Common::Point _offset; int _shakeCountdown; public: /** @@ -215,17 +216,14 @@ public: ScreenSurface(); /** - * Initialize the surface + * Destructor */ - void init(); + ~ScreenSurface(); /** - * Copys an area of the screen surface to a given destination position on - * the ScummVM physical screen buffer - * @param destPos Destination position - * @param bounds Area of screen surface to copy + * Initialize the surface */ - void copyRectToScreen(const Common::Point &destPos, const Common::Rect &bounds); + void init(); /** * Copys an area of the screen surface to the ScmmVM physical screen buffer @@ -239,6 +237,12 @@ public: void updateScreen(); void transition(ScreenTransition transitionType, bool surfaceFlag); + + void setClipBounds(const Common::Rect &r); + + void resetClipBounds(); + + const Common::Rect &getClipBounds() { return _clipBounds; } }; } // End of namespace MADS diff --git a/engines/mads/sequence.cpp b/engines/mads/sequence.cpp index 07b1451718..05f00afb5a 100644 --- a/engines/mads/sequence.cpp +++ b/engines/mads/sequence.cpp @@ -473,7 +473,7 @@ int SequenceList::startReverseCycle(int srcSpriteIndex, bool flipped, int numTic int depth = _vm->_game->_scene._depthSurface.getDepth(Common::Point( frame->_offset.x + frame->w / 2, frame->_offset.y + frame->h / 2)); - return add(srcSpriteIndex, flipped, sprites->getCount(), triggerCountdown, timeoutTicks, + return add(srcSpriteIndex, flipped, sprites->getCount(), triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, true, 100, depth - 1, -1, ANIMTYPE_REVERSIBLE, 0, 0); } diff --git a/engines/mads/sound.cpp b/engines/mads/sound.cpp index bd99aed2f4..1652550ba3 100644 --- a/engines/mads/sound.cpp +++ b/engines/mads/sound.cpp @@ -36,10 +36,27 @@ SoundManager::SoundManager(MADSEngine *vm, Audio::Mixer *mixer) { _pollSoundEnabled = false; _soundPollFlag = false; _newSoundsPaused = false; + + _opl = OPL::Config::create(); + _opl->init(11025); + + // Validate sound files + switch (_vm->getGameID()) { + case GType_RexNebular: + Nebular::ASound::validate(); + break; + default: + break; + } } SoundManager::~SoundManager() { - delete _driver; + if (_driver) { + _driver->stop(); + delete _driver; + } + + delete _opl; } void SoundManager::init(int sectionNumber) { @@ -49,31 +66,32 @@ void SoundManager::init(int sectionNumber) { case GType_RexNebular: switch (sectionNumber) { case 1: - _driver = new Nebular::ASound1(_mixer); + _driver = new Nebular::ASound1(_mixer, _opl); break; case 2: - _driver = new Nebular::ASound2(_mixer); + _driver = new Nebular::ASound2(_mixer, _opl); break; case 3: - _driver = new Nebular::ASound3(_mixer); + _driver = new Nebular::ASound3(_mixer, _opl); break; case 4: - _driver = new Nebular::ASound4(_mixer); + _driver = new Nebular::ASound4(_mixer, _opl); break; case 5: - _driver = new Nebular::ASound5(_mixer); + _driver = new Nebular::ASound5(_mixer, _opl); break; case 6: - _driver = new Nebular::ASound6(_mixer); + _driver = new Nebular::ASound6(_mixer, _opl); break; case 7: - _driver = new Nebular::ASound7(_mixer); + _driver = new Nebular::ASound7(_mixer, _opl); break; case 8: - _driver = new Nebular::ASound8(_mixer); + _driver = new Nebular::ASound8(_mixer, _opl); break; case 9: - error("Sound driver 9 not implemented"); + _driver = new Nebular::ASound9(_mixer, _opl); + break; default: _driver = nullptr; break; diff --git a/engines/mads/sound.h b/engines/mads/sound.h index 9a251f9dd0..72bb21a812 100644 --- a/engines/mads/sound.h +++ b/engines/mads/sound.h @@ -37,6 +37,7 @@ class SoundManager { private: MADSEngine *_vm; Audio::Mixer *_mixer; + FM_OPL *_opl; Nebular::ASound *_driver; bool _pollSoundEnabled; bool _soundPollFlag; diff --git a/engines/mads/sprites.cpp b/engines/mads/sprites.cpp index 2bf13eeb5a..fd73930475 100644 --- a/engines/mads/sprites.cpp +++ b/engines/mads/sprites.cpp @@ -262,7 +262,7 @@ void SpriteSlots::drawBackground() { scene._backgroundSurface.copyFrom(frame, pt, spriteSlot._depth, &scene._depthSurface, -1, false, frame->getTransparencyIndex()); } else { - error("Unsupported depth style"); + frame->copyTo(&scene._backgroundSurface, pt, frame->getTransparencyIndex()); } } } @@ -331,8 +331,6 @@ void SpriteSlots::drawSprites(MSurface *s) { xp = slot._position.x - (sprite->w / 2) - scene._posAdjust.x; yp = slot._position.y - sprite->h - scene._posAdjust.y + 1; } - xp += _vm->_screen._offset.x; - yp += _vm->_screen._offset.y; if (slot._depth > 1) { // Draw the frame with depth processing @@ -406,9 +404,9 @@ void SpriteSets::remove(int idx) { delete (*this)[idx]; (*this)[idx] = nullptr; } else { - while (size() > 0 && (*this)[size() - 1] == nullptr) { + do { remove_at(size() - 1); - } + } while (size() > 0 && (*this)[size() - 1] == nullptr); } if (_assetCount > 0) diff --git a/engines/mads/user_interface.h b/engines/mads/user_interface.h index f251441e40..89044c9bf1 100644 --- a/engines/mads/user_interface.h +++ b/engines/mads/user_interface.h @@ -225,7 +225,7 @@ public: /** * Loads an interface from a specified resource */ - void load(const Common::String &resName); + virtual void load(const Common::String &resName); /** * Set up the interface |
