aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gilbert2014-03-03 23:40:23 -0500
committerPaul Gilbert2014-03-03 23:40:23 -0500
commit9e356dd945863a4c4dfa89f2a94dd1a56c2d03a6 (patch)
tree6480a01d1dfb99a7fd5f73237f9e7b523187ab34
parent3a3a295758a87817e9d66d3c06df56859ef55529 (diff)
downloadscummvm-rg350-9e356dd945863a4c4dfa89f2a94dd1a56c2d03a6.tar.gz
scummvm-rg350-9e356dd945863a4c4dfa89f2a94dd1a56c2d03a6.tar.bz2
scummvm-rg350-9e356dd945863a4c4dfa89f2a94dd1a56c2d03a6.zip
MADS: Implemented extra message and dirty area classes
-rw-r--r--engines/mads/animation.cpp4
-rw-r--r--engines/mads/animation.h5
-rw-r--r--engines/mads/debugger.cpp12
-rw-r--r--engines/mads/debugger.h6
-rw-r--r--engines/mads/font.cpp1
-rw-r--r--engines/mads/game.cpp18
-rw-r--r--engines/mads/game.h5
-rw-r--r--engines/mads/messages.cpp384
-rw-r--r--engines/mads/messages.h148
-rw-r--r--engines/mads/module.mk1
-rw-r--r--engines/mads/scene.cpp20
-rw-r--r--engines/mads/scene.h8
-rw-r--r--engines/mads/scene_data.cpp164
-rw-r--r--engines/mads/scene_data.h79
14 files changed, 802 insertions, 53 deletions
diff --git a/engines/mads/animation.cpp b/engines/mads/animation.cpp
index 8ea95ee168..0546fc3d34 100644
--- a/engines/mads/animation.cpp
+++ b/engines/mads/animation.cpp
@@ -297,4 +297,8 @@ void Animation::loadInterface(InterfaceSurface &interfaceSurface, MSurface &dept
}
}
+void Animation::update() {
+ warning("TODO: Animation::update");
+}
+
} // End of namespace MADS
diff --git a/engines/mads/animation.h b/engines/mads/animation.h
index ce145cdd35..1f0e1fda65 100644
--- a/engines/mads/animation.h
+++ b/engines/mads/animation.h
@@ -135,6 +135,11 @@ public:
*/
void load(MSurface &depthSurface, InterfaceSurface &interfaceSurface, const Common::String &resName,
int flags, Common::Array<RGB4> *palAnimData, SceneInfo *sceneInfo);
+
+ /**
+ * Update the animation
+ */
+ void update();
};
} // End of namespace MADS
diff --git a/engines/mads/debugger.cpp b/engines/mads/debugger.cpp
index ceaeeaa5dd..34b12c1680 100644
--- a/engines/mads/debugger.cpp
+++ b/engines/mads/debugger.cpp
@@ -26,6 +26,8 @@
namespace MADS {
Debugger::Debugger(MADSEngine *vm) : GUI::Debugger(), _vm(vm) {
+ _showMousePos = false;
+
DCmd_Register("continue", WRAP_METHOD(Debugger, Cmd_Exit));
}
/*
@@ -46,4 +48,14 @@ static int strToInt(const char *s) {
}
*/
+bool Debugger::Cmd_Mouse(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Format: mouse [ on | off ]\n");
+ } else {
+ _showMousePos = strcmp(argv[1], "on") == 0;
+ }
+
+ return true;
+}
+
} // End of namespace MADS
diff --git a/engines/mads/debugger.h b/engines/mads/debugger.h
index 044151c0bb..983b9931fc 100644
--- a/engines/mads/debugger.h
+++ b/engines/mads/debugger.h
@@ -33,11 +33,13 @@ class MADSEngine;
class Debugger : public GUI::Debugger {
private:
MADSEngine *_vm;
+protected:
+ bool Cmd_Mouse(int argc, const char **argv);
+public:
+ bool _showMousePos;
public:
Debugger(MADSEngine *vm);
virtual ~Debugger() {}
-
-protected:
};
} // End of namespace MADS
diff --git a/engines/mads/font.cpp b/engines/mads/font.cpp
index 10d51fbbc0..0ff01905ce 100644
--- a/engines/mads/font.cpp
+++ b/engines/mads/font.cpp
@@ -93,7 +93,6 @@ void Font::setColors(uint8 v1, uint8 v2, uint8 v3, uint8 v4) {
_fontColors[0] = v1;
_fontColors[1] = v2;
_fontColors[2] = v3;
- _fontColors[3] = v4;
}
int Font::write(MSurface *surface, const Common::String &msg, const Common::Point &pt, int width, int spaceWidth, uint8 colors[]) {
diff --git a/engines/mads/game.cpp b/engines/mads/game.cpp
index 37027685dd..479c48cc2f 100644
--- a/engines/mads/game.cpp
+++ b/engines/mads/game.cpp
@@ -278,4 +278,22 @@ void Game::loadResourceSequence(const Common::String prefix, int v) {
warning("TODO: loadResourceSequence");
}
+Common::String Game::getQuote(int quoteId) {
+ if (_quotes && *_quotes) {
+ // Loop through the list of quotes
+ char *p = (char *)_quotes;
+ while (*p) {
+ // Get a pointer to the quote Id after the string
+ char *idP = p + strlen(p) + 1;
+ if (READ_LE_UINT16(idP) == quoteId)
+ // Found the correct string, so return it
+ return Common::String(p);
+
+ p = idP + 2;
+ }
+ }
+
+ return Common::String();
+}
+
} // End of namespace MADS
diff --git a/engines/mads/game.h b/engines/mads/game.h
index 148fc121cd..47eed34393 100644
--- a/engines/mads/game.h
+++ b/engines/mads/game.h
@@ -124,6 +124,11 @@ public:
* Run the game
*/
void run();
+
+ /**
+ * Get a quote
+ */
+ Common::String getQuote(int quoteId);
};
} // End of namespace MADS
diff --git a/engines/mads/messages.cpp b/engines/mads/messages.cpp
new file mode 100644
index 0000000000..c08f29fd91
--- /dev/null
+++ b/engines/mads/messages.cpp
@@ -0,0 +1,384 @@
+/* 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/mads.h"
+#include "mads/font.h"
+#include "mads/graphics.h"
+#include "mads/messages.h"
+#include "mads/scene_data.h"
+
+namespace MADS {
+
+KernelMessages::KernelMessages(MADSEngine *vm): _vm(vm) {
+ Scene &scene = _vm->_game->_scene;
+
+ for (int i = 0; i < KERNEL_MESSAGES_SIZE; ++i) {
+ KernelMessage rec;
+ _entries.push_back(rec);
+ }
+
+ scene._textSpacing = -1;
+ _talkFont = _vm->_font->getFont(FONT_CONVERSATION);
+ word_8469E = 0;
+}
+
+void KernelMessages::clear() {
+ Scene &scene = _vm->_game->_scene;
+
+ for (uint i = 0; i < _entries.size(); ++i)
+ _entries[i]._flags = 0;
+
+ scene._textSpacing = -1;
+ _talkFont = _vm->_font->getFont(FONT_CONVERSATION);
+}
+
+int KernelMessages::add(const Common::Point &pt, uint fontColor, uint8 flags,
+ uint8 abortTimers, uint32 timeout, const Common::String &msg) {
+ Scene &scene = _vm->_game->_scene;
+
+ // Find a free slot
+ uint idx = 0;
+ while ((idx < _entries.size()) && ((_entries[idx]._flags & KMSG_ACTIVE) != 0))
+ ++idx;
+ if (idx == _entries.size()) {
+ if (abortTimers == 0)
+ return -1;
+
+ error("KernelMessages overflow");
+ }
+
+ KernelMessage &rec = _entries[idx];
+ rec._msg = msg;
+ rec._flags = flags | KMSG_ACTIVE;
+ rec._color1 = fontColor & 0xff;
+ rec._color2 = fontColor >> 8;
+ rec._position = pt;
+ rec._textDisplayIndex = -1;
+ rec._timeout = timeout;
+ rec._frameTimer = _vm->_game->_currentTimer;
+ rec._abortTimers = abortTimers;
+ rec._abortMode = _vm->_game->_abortTimersMode2;
+
+ rec._actionDetails = scene._action._activeAction;
+
+ if (flags & KMSG_PLAYER_TIMEOUT)
+ rec._frameTimer = _vm->_game->_player._ticksAmount +
+ _vm->_game->_player._priorTimer;
+
+ return idx;
+}
+
+int KernelMessages::addQuote(int quoteId, int abortTimers, uint32 timeout) {
+ Common::String quoteStr = _vm->_game->getQuote(quoteId);
+ return add(Common::Point(), 0x1110, KMSG_PLAYER_TIMEOUT | KMSG_CENTER_ALIGN, abortTimers, timeout, quoteStr);
+}
+
+void KernelMessages::scrollMessage(int msgIndex, int numTicks, bool quoted) {
+ if (msgIndex < 0)
+ return;
+
+ _entries[msgIndex]._flags |= quoted ? (KMSG_SCROLL | KMSG_QUOTED) : KMSG_SCROLL;
+ _entries[msgIndex]._msgOffset = 0;
+ _entries[msgIndex]._numTicks = numTicks;
+ _entries[msgIndex]._frameTimer2 = _vm->_game->_currentTimer;
+
+ Common::String msg = _entries[msgIndex]._msg;
+ _entries[msgIndex]._asciiChar = msg[0];
+ _entries[msgIndex]._asciiChar2 = msg[1];
+
+ if (_entries[msgIndex]._flags & KMSG_PLAYER_TIMEOUT)
+ _entries[msgIndex]._frameTimer2 = _vm->_game->_player._ticksAmount +
+ _vm->_game->_player._priorTimer;
+
+ _entries[msgIndex]._frameTimer = _entries[msgIndex]._frameTimer2;
+}
+
+void KernelMessages::setSeqIndex(int msgIndex, int seqIndex) {
+ if (msgIndex >= 0) {
+ _entries[msgIndex]._flags |= KMSG_SEQ_ENTRY;
+ _entries[msgIndex]._sequenceIndex = seqIndex;
+ }
+}
+
+void KernelMessages::remove(int msgIndex) {
+ KernelMessage &rec = _entries[msgIndex];
+ Scene &scene = _vm->_game->_scene;
+
+ if (rec._flags & KMSG_ACTIVE) {
+ if (rec._flags & KMSG_SCROLL) {
+ rec._msg.setChar(rec._asciiChar, rec._msgOffset);
+ rec._msg.setChar(rec._asciiChar2, rec._msgOffset + 1);
+ }
+
+ if (rec._textDisplayIndex >= 0)
+ scene._textDisplay.expire(rec._textDisplayIndex);
+
+ rec._flags &= ~KMSG_ACTIVE;
+ }
+}
+
+void KernelMessages::reset() {
+ for (uint i = 0; i < _entries.size(); ++i)
+ remove(i);
+
+ warning("TODO: KernelMessages::reset - sub_20454");
+}
+
+void KernelMessages::update() {
+ uint32 currentTimer = _vm->_game->_currentTimer;
+
+ for (uint i = 0; i < _entries.size(); ++i) {
+ if (((_entries[i]._flags & KMSG_ACTIVE) != 0) &&
+ (currentTimer >= _entries[i]._frameTimer))
+ processText(i);
+ }
+}
+
+void KernelMessages::processText(int msgIndex) {
+ Scene &scene = _vm->_game->_scene;
+ KernelMessage &msg = _entries[msgIndex];
+ uint32 currentTimer = _vm->_game->_currentTimer;
+ bool flag = false;
+
+ if ((msg._flags & KMSG_EXPIRE) != 0) {
+ scene._textDisplay.expire(msg._textDisplayIndex);
+ msg._flags &= !KMSG_ACTIVE;
+ return;
+ }
+
+ if ((msg._flags & KMSG_SCROLL) == 0) {
+ msg._timeout -= 3;
+ }
+
+ if (msg._flags & KMSG_SEQ_ENTRY) {
+ SequenceEntry &seqEntry = scene._sequences[msg._sequenceIndex];
+ if (seqEntry._doneFlag || !seqEntry._active)
+ msg._timeout = 0;
+ }
+
+ if ((msg._timeout <= 0) && (_vm->_game->_abortTimers == 0)) {
+ msg._flags |= KMSG_EXPIRE;
+ if (msg._abortTimers != 0) {
+ _vm->_game->_abortTimers = msg._abortTimers;
+ _vm->_game->_abortTimersMode = msg._abortMode;
+
+ if (_vm->_game->_abortTimersMode != ABORTMODE_1) {
+ scene._action._activeAction = msg._actionDetails;
+ }
+ }
+ }
+
+ msg._frameTimer = currentTimer + 3;
+ int x1 = 0, y1 = 0;
+
+ if (msg._flags & KMSG_SEQ_ENTRY) {
+ SequenceEntry &seqEntry = scene._sequences[msg._sequenceIndex];
+ if (!seqEntry._nonFixed) {
+ SpriteAsset &spriteSet = scene._spriteSlots.getSprite(seqEntry._spritesIndex);
+ MSprite *frame = spriteSet.getFrame(seqEntry._frameIndex - 1);
+ x1 = frame->getBounds().left;
+ y1 = frame->getBounds().top;
+ } else {
+ x1 = seqEntry._msgPos.x;
+ y1 = seqEntry._msgPos.y;
+ }
+ }
+
+ if (msg._flags & KMSG_PLAYER_TIMEOUT) {
+ if (word_8469E != 0) {
+ warning("TODO: KernelMessages::processText");
+ // TODO: Figure out various flags
+ } else {
+ x1 = 160;
+ y1 = 78;
+ }
+ }
+
+ x1 += msg._position.x;
+ y1 += msg._position.y;
+
+ if ((msg._flags & KMSG_SCROLL) && (msg._frameTimer >= currentTimer)) {
+ msg._msg.setChar(msg._asciiChar, msg._msgOffset);
+
+ ++msg._msgOffset;
+ msg._msg.setChar(msg._asciiChar2, msg._msgOffset);
+ msg._asciiChar = msg._msg[msg._msgOffset];
+ msg._asciiChar2 = msg._msg[msg._msgOffset + 1];
+
+ if (!msg._asciiChar) {
+ // End of message
+ msg._msg.setChar('\0', msg._msgOffset);
+ msg._flags &= ~KMSG_SCROLL;
+ } else if (msg._flags & KMSG_QUOTED) {
+ msg._msg.setChar('"', msg._msgOffset);
+ msg._msg.setChar('\0', msg._msgOffset + 1);
+ }
+
+ msg._frameTimer = msg._frameTimer2 = currentTimer + msg._numTicks;
+ flag = true;
+ }
+
+ int strWidth = _talkFont->getWidth(msg._msg, scene._textSpacing);
+
+ if (msg._flags & (KMSG_RIGHT_ALIGN | KMSG_CENTER_ALIGN)) {
+ x1 -= (msg._flags & KMSG_CENTER_ALIGN) ? strWidth / 2 : strWidth;
+ }
+
+ // Make sure text appears entirely on-screen
+ int x2 = x1 + strWidth;
+ if (x2 > MADS_SCREEN_WIDTH)
+ x1 -= x2 - MADS_SCREEN_WIDTH;
+ if (x1 > (MADS_SCREEN_WIDTH - 1))
+ x1 = MADS_SCREEN_WIDTH - 1;
+ if (x1 < 0)
+ x1 = 0;
+
+ if (y1 >(MADS_SCENE_HEIGHT - 1))
+ y1 = MADS_SCENE_HEIGHT - 1;
+ if (y1 < 0)
+ y1 = 0;
+
+ if (msg._textDisplayIndex >= 0) {
+ TextDisplay &textEntry = scene._textDisplay[msg._textDisplayIndex];
+
+ if (flag || (textEntry._bounds.left != x1) || (textEntry._bounds.top != y1)) {
+ // Mark the associated text entry as deleted, so it can be re-created
+ scene._textDisplay.expire(msg._textDisplayIndex);
+ msg._textDisplayIndex = -1;
+ }
+ }
+
+ if (msg._textDisplayIndex < 0) {
+ // Need to create a new text display entry for this message
+ int idx = scene._textDisplay.add(x1, y1, msg._color1 | (msg._color2 << 8),
+ scene._textSpacing, msg._msg, _talkFont);
+ if (idx >= 0)
+ msg._textDisplayIndex = idx;
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+TextDisplay::TextDisplay() {
+ _active = false;
+ _expire = 0;
+ _spacing = 0;
+ _color1 = 0;
+ _color2 = 0;
+ _font = nullptr;
+}
+
+/*------------------------------------------------------------------------*/
+
+TextDisplayList::TextDisplayList(MADSEngine *vm) : _vm(vm) {
+ for (int i = 0; i < TEXT_DISPLAY_SIZE; ++i) {
+ TextDisplay rec;
+ rec._active = false;
+ rec._expire = 0;
+ _entries.push_back(rec);
+ }
+}
+
+void TextDisplayList::clear() {
+ for (int i = 0; i < TEXT_DISPLAY_SIZE; ++i)
+ _entries[i]._active = false;
+}
+
+int TextDisplayList::add(int xp, int yp, uint fontColor, int charSpacing,
+ const Common::String &msg, Font *font) {
+ int usedSlot = -1;
+
+ for (int idx = 0; idx < TEXT_DISPLAY_SIZE; ++idx) {
+ if (!_entries[idx]._active) {
+ usedSlot = idx;
+
+ _entries[idx]._bounds.left = xp;
+ _entries[idx]._bounds.top = yp;
+ _entries[idx]._font = font;
+ _entries[idx]._msg = msg;
+ _entries[idx]._bounds.setWidth(font->getWidth(msg, charSpacing));
+ _entries[idx]._bounds.setHeight(font->getHeight());
+ _entries[idx]._color1 = fontColor & 0xff;
+ _entries[idx]._color2 = fontColor >> 8;
+ _entries[idx]._spacing = charSpacing;
+ _entries[idx]._expire = 1;
+ _entries[idx]._active = true;
+ break;
+ }
+ }
+
+ return usedSlot;
+}
+
+void TextDisplayList::setDirtyAreas() {
+ Scene &scene = _vm->_game->_scene;
+
+ for (uint idx = 0, dirtyIdx = DIRTY_AREAS_TEXT_DISPLAY_IDX; dirtyIdx < DIRTY_AREAS_SIZE; ++idx, ++dirtyIdx) {
+ if ((_entries[idx]._expire >= 0) || !_entries[idx]._active)
+ scene._dirtyAreas[dirtyIdx]._active = false;
+ else {
+ scene._dirtyAreas[dirtyIdx]._textActive = true;
+ scene._dirtyAreas.setTextDisplay(dirtyIdx, _entries[idx]);
+ }
+ }
+}
+
+void TextDisplayList::setDirtyAreas2() {
+ Scene &scene = _vm->_game->_scene;
+
+ for (uint idx = 0, dirtyIdx = DIRTY_AREAS_TEXT_DISPLAY_IDX; dirtyIdx < DIRTY_AREAS_SIZE; ++idx, ++dirtyIdx) {
+ if (_entries[idx]._active && (_entries[idx]._expire >= 0)) {
+ scene._dirtyAreas.setTextDisplay(dirtyIdx, _entries[idx]);
+ scene._dirtyAreas[dirtyIdx]._textActive = (_entries[idx]._expire <= 0) ? 0 : 1;
+ }
+ }
+}
+
+void TextDisplayList::draw(MSurface *view) {
+ error("TODO");
+ /*
+ for (uint idx = 0; idx < _entries.size(); ++idx) {
+ if (_entries[idx]._active && (_entries[idx]._expire >= 0)) {
+ _entries[idx]._font->setColors(_entries[idx]._color1, _entries[idx]._color2, 0);
+ _entries[idx]._font->writeString(view, _entries[idx]._msg,
+ Common::Point(_entries[idx]._bounds.left, _entries[idx]._bounds.top),
+ _entries[idx]._bounds.width(), _entries[idx]._spacing);
+ }
+ }
+ */
+}
+
+void TextDisplayList::cleanUp() {
+ for (uint idx = 0; idx < _entries.size(); ++idx) {
+ if (_entries[idx]._expire < 0) {
+ _entries[idx]._active = false;
+ _entries[idx]._expire = 0;
+ }
+ }
+}
+
+void TextDisplayList::expire(int idx) {
+ _entries[idx]._expire = -1;
+}
+
+} // End of namespace MADS
diff --git a/engines/mads/messages.h b/engines/mads/messages.h
new file mode 100644
index 0000000000..b60ca8cedc
--- /dev/null
+++ b/engines/mads/messages.h
@@ -0,0 +1,148 @@
+/* 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_MESSAGES_H
+#define MADS_MESSAGES_H
+
+#include "common/scummsys.h"
+#include "common/array.h"
+#include "mads/action.h"
+#include "mads/font.h"
+#include "mads/msurface.h"
+
+namespace MADS {
+
+#define KERNEL_MESSAGES_SIZE 10
+#define INDEFINITE_TIMEOUT 9999999
+#define TEXT_DISPLAY_SIZE 40
+
+enum KernelMessageFlags {
+ KMSG_QUOTED = 1, KMSG_PLAYER_TIMEOUT = 2, KMSG_SEQ_ENTRY = 4, KMSG_SCROLL = 8,
+ KMSG_RIGHT_ALIGN = 0x10, KMSG_CENTER_ALIGN = 0x20, KMSG_EXPIRE = 0x40,
+ KMSG_ACTIVE = 0x80
+};
+
+class MADSEngine;
+
+class KernelMessage {
+public:
+ uint8 _flags;
+ int _sequenceIndex;
+ char _asciiChar;
+ char _asciiChar2;
+ int _color1;
+ int _color2;
+ Common::Point _position;
+ int _textDisplayIndex;
+ int _msgOffset;
+ int _numTicks;
+ uint32 _frameTimer2;
+ uint32 _frameTimer;
+ uint32 _timeout;
+ int _abortTimers;
+ AbortTimerMode _abortMode;
+ ActionDetails _actionDetails;
+ Common::String _msg;
+
+ KernelMessage();
+};
+
+class KernelMessages {
+private:
+ MADSEngine *_vm;
+ Common::Array<KernelMessage> _entries;
+ Font *_talkFont;
+public:
+ int word_8469E;
+public:
+ KernelMessages(MADSEngine *vm);
+
+ void clear();
+ int add(const Common::Point &pt, uint fontColor, uint8 flags, uint8 abortTimers,
+ uint32 timeout, const Common::String &msg);
+ int addQuote(int quoteId, int abortTimers, uint32 timeout);
+ void scrollMessage(int msgIndex, int numTicks, bool quoted);
+ void setSeqIndex(int msgIndex, int seqIndex);
+ void remove(int msgIndex);
+ void reset();
+ void update();
+ void processText(int msgIndex);
+};
+
+class TextDisplay {
+public:
+ bool _active;
+ int _expire;
+ int _spacing;
+ Common::Rect _bounds;
+ uint8 _color1;
+ uint8 _color2;
+ Font *_font;
+ Common::String _msg;
+
+ TextDisplay();
+};
+
+#define TEXT_DISPLAY_SIZE 40
+
+class TextDisplayList {
+private:
+ MADSEngine *_vm;
+ Common::Array<TextDisplay> _entries;
+
+ /**
+ * Determine dirty areas for active text areas
+ */
+ void setDirtyAreas2();
+public:
+ TextDisplayList(MADSEngine *vm);
+
+ /**
+ * Item operator
+ */
+ TextDisplay &operator[](int idx) {
+ return _entries[idx];
+ }
+
+ /**
+ * Expire a given text display entry
+ */
+ void expire(int idx);
+
+ int add(int xp, int yp, uint fontColor, int charSpacing, const Common::String &, Font *font);
+ void clear();
+ void draw(MSurface *view);
+
+ /**
+ * Determine dirty areas for active text areas
+ */
+ void setDirtyAreas();
+
+ /**
+ * Deactivates any text display entries that are finished
+ */
+ void cleanUp();
+};
+
+} // End of namespace MADS
+
+#endif /* MADS_MESSAGES_H */
diff --git a/engines/mads/module.mk b/engines/mads/module.mk
index 826e54f8fd..36d0c49ff7 100644
--- a/engines/mads/module.mk
+++ b/engines/mads/module.mk
@@ -19,6 +19,7 @@ MODULE_OBJS := \
game_data.o \
graphics.o \
mads.o \
+ messages.o \
msprite.o \
msurface.o \
palette.o \
diff --git a/engines/mads/scene.cpp b/engines/mads/scene.cpp
index 6efbdf5072..a0888f7d68 100644
--- a/engines/mads/scene.cpp
+++ b/engines/mads/scene.cpp
@@ -29,8 +29,8 @@
namespace MADS {
Scene::Scene(MADSEngine *vm): _vm(vm), _spriteSlots(vm), _action(_vm),
- _dynamicHotspots(vm), _screenObjects(vm), _interface(vm),
- _sequences(vm) {
+ _dirtyAreas(_vm), _dynamicHotspots(vm), _interface(vm), _messages(vm),
+ _screenObjects(vm), _sequences(vm), _textDisplay(vm) {
_priorSceneId = 0;
_nextSceneId = 0;
_currentSceneId = 0;
@@ -45,6 +45,8 @@ Scene::Scene(MADSEngine *vm): _vm(vm), _spriteSlots(vm), _action(_vm),
_reloadSceneFlag = false;
_destFacing = 0;
_freeAnimationFlag = false;
+ _animation = nullptr;
+ _activeAnimation = nullptr;
_verbList.push_back(VerbInit(VERB_LOOK, 2, 0));
_verbList.push_back(VerbInit(VERB_TAKE, 2, 0));
@@ -356,9 +358,21 @@ void Scene::doFrame() {
_vm->_events->setCursor(cursorId);
}
- if (!_vm->_game->_abortTimers)
+ if (!_vm->_game->_abortTimers) {
+ // Handle any active sequences
_sequences.tick();
+ // Handle any active animation
+ if (_activeAnimation)
+ _activeAnimation->update();
+ }
+
+ // If the debugget flag is set, show the mouse position
+ if (_vm->_debugger->_showMousePos) {
+ Common::Point pt = _vm->_events->mousePos();
+ Common::String msg = Common::String::format("(%d,%d)", pt.x, pt.y);
+ _messages.add(Common::Point(5, 5), 0x203, 0, 0, 1, msg);
+ }
// TODO: Rest of Scene::doFrame
}
diff --git a/engines/mads/scene.h b/engines/mads/scene.h
index 71bfe317d9..f6eecbf093 100644
--- a/engines/mads/scene.h
+++ b/engines/mads/scene.h
@@ -27,6 +27,7 @@
#include "common/array.h"
#include "common/rect.h"
#include "mads/assets.h"
+#include "mads/messages.h"
#include "mads/msurface.h"
#include "mads/scene_data.h"
#include "mads/animation.h"
@@ -78,7 +79,7 @@ public:
int _nextSceneId;
int _currentSceneId;
Common::Array<VerbInit> _verbList;
- Common::Array<TextDisplay> _textDisplay;
+ TextDisplayList _textDisplay;
SpriteSlots _spriteSlots;
SpriteSets _sprites;
int _spritesIndex;
@@ -86,11 +87,12 @@ public:
byte *_vocabBuffer;
Common::Array<int> _activeVocabs;
SequenceList _sequences;
- Common::Array<KernelMessage> _messages;
+ KernelMessages _messages;
Common::String _talkFont;
int _textSpacing;
Common::Array<Hotspot> _hotspots;
ScreenObjects _screenObjects;
+ DirtyAreas _dirtyAreas;
int _v1;
SceneInfo *_sceneInfo;
MSurface _backgroundSurface;
@@ -104,6 +106,7 @@ public:
SceneNodeList _nodes;
Common::StringArray _vocabStrings;
Animation *_animation;
+ Animation *_activeAnimation;
bool _freeAnimationFlag;
int _depthStyle;
int _bandsRange;
@@ -118,6 +121,7 @@ public:
bool _reloadSceneFlag;
Common::Point _destPos;
int _destFacing;
+ Common::Point _posAdjust;
/**
* Constructor
diff --git a/engines/mads/scene_data.cpp b/engines/mads/scene_data.cpp
index 6fa2f5b326..985b961e00 100644
--- a/engines/mads/scene_data.cpp
+++ b/engines/mads/scene_data.cpp
@@ -202,11 +202,152 @@ int SpriteSets::add(SpriteAsset *asset, int idx) {
/*------------------------------------------------------------------------*/
-TextDisplay::TextDisplay() {
- _active = false;
- _spacing = 0;
- _expire = 0;
- _col1 = _col2 = 0;
+void DirtyArea::setArea(int width, int height, int maxWidth, int maxHeight) {
+ if (_bounds.left % 2) {
+ --_bounds.left;
+ ++width;
+ }
+
+ if (_bounds.left < 0)
+ _bounds.left = 0;
+ else if (_bounds.left > maxWidth)
+ _bounds.left = maxWidth;
+ int right = _bounds.left + width;
+ if (right < 0)
+ right = 0;
+ if (right > maxWidth)
+ right = maxWidth;
+
+ _bounds.right = right;
+ _bounds2.left = _bounds.width() / 2;
+ _bounds2.right = _bounds.left + (_bounds.width() + 1) / 2 - 1;
+
+ if (_bounds.top < 0)
+ _bounds.top = 0;
+ else if (_bounds.top > maxHeight)
+ _bounds.top = maxHeight;
+ int bottom = _bounds.top + height;
+ if (bottom < 0)
+ bottom = 0;
+ if (bottom > maxHeight)
+ bottom = maxHeight;
+
+ _bounds.bottom = bottom;
+ _bounds2.top = _bounds.height() / 2;
+ _bounds2.bottom = _bounds.top + (_bounds.height() + 1) / 2 - 1;
+
+ _active = true;
+}
+
+/*------------------------------------------------------------------------*/
+
+DirtyAreas::DirtyAreas(MADSEngine *vm) : _vm(vm) {
+ for (int i = 0; i < DIRTY_AREAS_SIZE; ++i) {
+ DirtyArea rec;
+ rec._active = false;
+ _entries.push_back(rec);
+ }
+}
+
+void DirtyAreas::setSpriteSlot(int dirtyIdx, const SpriteSlot &spriteSlot) {
+ int width, height;
+ DirtyArea &dirtyArea = _entries[dirtyIdx];
+ Scene &scene = _vm->_game->_scene;
+
+ if (spriteSlot._spriteType == ST_FULL_SCREEN_REFRESH) {
+ // Special entry to refresh the entire screen
+ dirtyArea._bounds.left = 0;
+ dirtyArea._bounds.top = 0;
+ width = MADS_SCREEN_WIDTH;
+ height = MADS_SCENE_HEIGHT;
+ } else {
+ // Standard sprite slots
+ dirtyArea._bounds.left = spriteSlot._position.x - scene._posAdjust.x;
+ dirtyArea._bounds.top = spriteSlot._position.y - scene._posAdjust.y;
+
+ SpriteAsset &spriteSet = scene._spriteSlots.getSprite(spriteSlot._spritesIndex);
+ MSprite *frame = spriteSet.getFrame(((spriteSlot._frameNumber & 0x7fff) - 1) & 0x7f);
+
+ if (spriteSlot._scale == -1) {
+ width = frame->getWidth();
+ height = frame->getHeight();
+ } else {
+ width = frame->getWidth() * spriteSlot._scale / 100;
+ height = frame->getHeight() * spriteSlot._scale / 100;
+
+ dirtyArea._bounds.left -= width / 2;
+ dirtyArea._bounds.top += -(height - 1);
+ }
+ }
+
+ dirtyArea.setArea(width, height, MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT);
+}
+
+void DirtyAreas::setTextDisplay(int dirtyIdx, const TextDisplay &textDisplay) {
+ DirtyArea &dirtyArea = _entries[dirtyIdx];
+ dirtyArea._bounds.left = textDisplay._bounds.left;
+ dirtyArea._bounds.top = textDisplay._bounds.top;
+
+ dirtyArea.setArea(textDisplay._bounds.width(), textDisplay._bounds.height(),
+ MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT);
+}
+
+void DirtyAreas::merge(int startIndex, int count) {
+ error("TODO: DirtyAreas::merge");
+ if (startIndex >= count)
+ return;
+
+ for (int outerCtr = startIndex - 1, idx = 0; idx < count; ++outerCtr, ++idx) {
+ if (!_entries[outerCtr]._active)
+ continue;
+
+ for (int innerCtr = outerCtr + 1; innerCtr < count; ++innerCtr) {
+ if (!_entries[innerCtr]._active || !intersects(outerCtr, innerCtr))
+ continue;
+
+ if (_entries[outerCtr]._textActive && _entries[innerCtr]._textActive)
+ mergeAreas(outerCtr, innerCtr);
+ }
+ }
+}
+
+/**
+* Returns true if two dirty areas intersect
+*/
+bool DirtyAreas::intersects(int idx1, int idx2) {
+ return _entries[idx1]._bounds2.intersects(_entries[idx2]._bounds2);
+}
+
+void DirtyAreas::mergeAreas(int idx1, int idx2) {
+ DirtyArea &da1 = _entries[idx1];
+ DirtyArea &da2 = _entries[idx2];
+
+ da1._bounds.extend(da2._bounds);
+
+ da1._bounds2.left = da1._bounds.width() / 2;
+ da1._bounds2.right = da1._bounds.left + (da1._bounds.width() + 1) / 2 - 1;
+ da1._bounds2.top = da1._bounds.height() / 2;
+ da1._bounds2.bottom = da1._bounds.top + (da1._bounds.height() + 1) / 2 - 1;
+
+ da2._active = false;
+ da1._textActive = true;
+}
+
+void DirtyAreas::copy(MSurface *dest, MSurface *src, const Common::Point &posAdjust) {
+ for (uint i = 0; i < _entries.size(); ++i) {
+ const Common::Rect &srcBounds = _entries[i]._bounds;
+
+ Common::Rect bounds(srcBounds.left + posAdjust.x, srcBounds.top + posAdjust.y,
+ srcBounds.right + posAdjust.x, srcBounds.bottom + posAdjust.y);
+
+ if (_entries[i]._active && _entries[i]._bounds.isValidRect())
+ src->copyTo(dest, bounds, Common::Point(_entries[i]._bounds.left, _entries[i]._bounds.top));
+ }
+}
+
+void DirtyAreas::clear() {
+ for (uint i = 0; i < _entries.size(); ++i)
+ _entries[i]._active = false;
}
/*------------------------------------------------------------------------*/
@@ -315,18 +456,21 @@ void DynamicHotspots::refresh() {
KernelMessage::KernelMessage() {
_flags = 0;
- _seqInex = 0;
+ _sequenceIndex = 0;
_asciiChar = '\0';
_asciiChar2 = '\0';
- _colors = 0;
+ _color1 = 0;
+ _color2 = 0;
_msgOffset = 0;
_numTicks = 0;
_frameTimer2 = 0;
_frameTimer = 0;
_timeout = 0;
- _field1C = 0;
- _abortMode = 0;
- _nounList[0] = _nounList[1] = _nounList[2] = 0;
+ _abortTimers = 0;
+ _abortMode = ABORTMODE_0;
+ _actionDetails._verbId = 0;
+ _actionDetails._objectNameId = 0;
+ _actionDetails._indirectObjectId = 0;
}
/*------------------------------------------------------------------------*/
diff --git a/engines/mads/scene_data.h b/engines/mads/scene_data.h
index dc59bff4cf..f26069ca7e 100644
--- a/engines/mads/scene_data.h
+++ b/engines/mads/scene_data.h
@@ -32,6 +32,7 @@
#include "mads/assets.h"
#include "mads/events.h"
#include "mads/game_data.h"
+#include "mads/messages.h"
namespace MADS {
@@ -44,6 +45,9 @@ class Scene;
#define DEPTH_BANDS_SIZE 15
#define MAX_ROUTE_NODES 22
+#define DIRTY_AREAS_SIZE 90
+#define DIRTY_AREAS_TEXT_DISPLAY_IDX 50
+
enum ScrCategory {
CAT_NONE = 0, CAT_ACTION = 1, CAT_INV_LIST = 2, CAT_INV_VOCAB = 3,
CAT_HOTSPOT = 4, CAT_INV_ANIM = 5, CAT_6 = 6, CAT_INV_SCROLLER = 7,
@@ -175,20 +179,6 @@ public:
int add(SpriteAsset *asset, int idx = 0);
};
-class TextDisplay {
-public:
- bool _active;
- int _spacing;
- Common::Rect _bounds;
- int _expire;
- int _col1;
- int _col2;
- Common::String _fontName;
- Common::String _msg;
-
- TextDisplay();
-};
-
class DynamicHotspot {
public:
bool _active;
@@ -226,27 +216,6 @@ public:
void refresh();
};
-class KernelMessage {
-public:
- int _flags;
- int _seqInex;
- char _asciiChar;
- char _asciiChar2;
- int _colors;
- Common::Point _posiition;
- int _msgOffset;
- int _numTicks;
- int _frameTimer2;
- int _frameTimer;
- int _timeout;
- int _field1C;
- int _abortMode;
- int _nounList[3];
- Common::String _msg;
-
- KernelMessage();
-};
-
class Hotspot {
public:
Common::Rect _bounds;
@@ -261,6 +230,46 @@ public:
Hotspot(Common::SeekableReadStream &f);
};
+class DirtyArea {
+public:
+ Common::Rect _bounds;
+ Common::Rect _bounds2;
+ bool _textActive;
+ bool _active;
+
+ DirtyArea() { _active = false; }
+ void setArea(int width, int height, int maxWidth, int maxHeight);
+};
+
+class DirtyAreas {
+private:
+ MADSEngine *_vm;
+ Common::Array<DirtyArea> _entries;
+public:
+ DirtyAreas(MADSEngine *vm);
+
+ DirtyArea &operator[](uint idx) {
+ assert(idx < _entries.size());
+ return _entries[idx];
+ }
+
+ void setSpriteSlot(int dirtyIdx, const SpriteSlot &spriteSlot);
+
+ void setTextDisplay(int dirtyIdx, const TextDisplay &textDisplay);
+
+ /**
+ * Merge together any designated dirty areas that overlap
+ * @param startIndex 1-based starting dirty area starting index
+ * @param count Number of entries to process
+ */
+ void merge(int startIndex, int count);
+
+ bool intersects(int idx1, int idx2);
+ void mergeAreas(int idx1, int idx2);
+ void copy(MSurface *dest, MSurface *src, const Common::Point &posAdjust);
+ void clear();
+};
+
class SceneLogic {
protected:
Scene *_scene;