aboutsummaryrefslogtreecommitdiff
path: root/engines/mads
diff options
context:
space:
mode:
Diffstat (limited to 'engines/mads')
-rw-r--r--engines/mads/animation.cpp84
-rw-r--r--engines/mads/animation.h25
-rw-r--r--engines/mads/audio.cpp43
-rw-r--r--engines/mads/audio.h1
-rw-r--r--engines/mads/debugger.cpp43
-rw-r--r--engines/mads/debugger.h2
-rw-r--r--engines/mads/dialogs.cpp74
-rw-r--r--engines/mads/dialogs.h32
-rw-r--r--engines/mads/game.cpp4
-rw-r--r--engines/mads/mads.cpp5
-rw-r--r--engines/mads/menu_views.cpp768
-rw-r--r--engines/mads/menu_views.h225
-rw-r--r--engines/mads/messages.cpp3
-rw-r--r--engines/mads/module.mk1
-rw-r--r--engines/mads/msurface.cpp9
-rw-r--r--engines/mads/msurface.h12
-rw-r--r--engines/mads/nebular/dialogs_nebular.cpp106
-rw-r--r--engines/mads/nebular/dialogs_nebular.h34
-rw-r--r--engines/mads/nebular/game_nebular.cpp28
-rw-r--r--engines/mads/nebular/game_nebular.h4
-rw-r--r--engines/mads/nebular/menu_nebular.cpp463
-rw-r--r--engines/mads/nebular/menu_nebular.h110
-rw-r--r--engines/mads/nebular/nebular_scenes.cpp2
-rw-r--r--engines/mads/nebular/nebular_scenes5.cpp2
-rw-r--r--engines/mads/nebular/nebular_scenes6.cpp7
-rw-r--r--engines/mads/nebular/nebular_scenes7.cpp2
-rw-r--r--engines/mads/nebular/nebular_scenes8.cpp2
-rw-r--r--engines/mads/nebular/sound_nebular.cpp358
-rw-r--r--engines/mads/nebular/sound_nebular.h84
-rw-r--r--engines/mads/palette.cpp65
-rw-r--r--engines/mads/palette.h2
-rw-r--r--engines/mads/phantom/game_phantom.cpp15
-rw-r--r--engines/mads/scene.cpp20
-rw-r--r--engines/mads/scene.h12
-rw-r--r--engines/mads/scene_data.cpp41
-rw-r--r--engines/mads/scene_data.h3
-rw-r--r--engines/mads/screen.cpp50
-rw-r--r--engines/mads/screen.h24
-rw-r--r--engines/mads/sequence.cpp2
-rw-r--r--engines/mads/sound.cpp38
-rw-r--r--engines/mads/sound.h1
-rw-r--r--engines/mads/sprites.cpp8
-rw-r--r--engines/mads/user_interface.h2
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(&paramP);
+
+ 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(&paramP);
+ int panY = getParameter(&paramP);
+ int panSpeed = getParameter(&paramP);
+
+ 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(&paramP);
+ _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(&paramP);
+ byte g = getParameter(&paramP);
+ byte b = getParameter(&paramP);
+
+ _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(&paramP);
+
+ // 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(&paramP);
+
+ // 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(&paramP);
-
- 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(&paramP);
- int panY = getParameter(&paramP);
- int panSpeed = getParameter(&paramP);
-
- 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(&paramP);
- _vm->_sound->init(driverNum);
-
- } else if (!strncmp(commandStr, "SOUND", 5)) {
- // Set sound number
- paramP = commandStr + 5;
- int soundId = getParameter(&paramP);
- _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(&paramP) << 2;
- palEntry[1] = getParameter(&paramP) << 2;
- palEntry[2] = getParameter(&paramP) << 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(&paramP);
-
- _spareScreens[spareIndex] = screenId;
- }
-
- } else if (!strncmp(commandStr, "PAGE", 4)) {
- // Signals to change to a previous specified secondary background
- paramP = commandStr + 4;
- int spareIndex = getParameter(&paramP);
-
- // 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