aboutsummaryrefslogtreecommitdiff
path: root/engines/m4
diff options
context:
space:
mode:
authorPaul Gilbert2010-05-23 07:20:40 +0000
committerPaul Gilbert2010-05-23 07:20:40 +0000
commitdd9bf70761f2de9771912984209422f1f63bd3e8 (patch)
tree95923436f2aa71a906a1509d70b05c987938bb3d /engines/m4
parent7a6a2a62eebfea5f09bf86fee3935e1ad031959d (diff)
downloadscummvm-rg350-dd9bf70761f2de9771912984209422f1f63bd3e8.tar.gz
scummvm-rg350-dd9bf70761f2de9771912984209422f1f63bd3e8.tar.bz2
scummvm-rg350-dd9bf70761f2de9771912984209422f1f63bd3e8.zip
Major work done on the animation and support classes to match the original
svn-id: r49150
Diffstat (limited to 'engines/m4')
-rw-r--r--engines/m4/assets.cpp4
-rw-r--r--engines/m4/assets.h7
-rw-r--r--engines/m4/globals.h1
-rw-r--r--engines/m4/graphics.cpp81
-rw-r--r--engines/m4/graphics.h24
-rw-r--r--engines/m4/mads_logic.cpp41
-rw-r--r--engines/m4/mads_menus.cpp6
-rw-r--r--engines/m4/mads_scene.cpp5
-rw-r--r--engines/m4/mads_scene.h19
-rw-r--r--engines/m4/mads_views.cpp320
-rw-r--r--engines/m4/mads_views.h68
-rw-r--r--engines/m4/sprite.h12
12 files changed, 502 insertions, 86 deletions
diff --git a/engines/m4/assets.cpp b/engines/m4/assets.cpp
index e604019901..14857e6f2b 100644
--- a/engines/m4/assets.cpp
+++ b/engines/m4/assets.cpp
@@ -195,7 +195,9 @@ void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStre
_maxHeight = 0;
Common::SeekableReadStream *spriteStream = sprite.getItemStream(0);
- for (int i = 0; i < 19; i++) {
+
+ _assetType = spriteStream->readUint16LE();
+ for (int i = 0; i < 18; i++) {
spriteStream->readUint16LE();
}
_frameCount = spriteStream->readUint16LE();
diff --git a/engines/m4/assets.h b/engines/m4/assets.h
index 7b0ce24dc4..cd0ae6ba78 100644
--- a/engines/m4/assets.h
+++ b/engines/m4/assets.h
@@ -113,6 +113,7 @@ public:
int32 getFrameHeight(int index);
int32 getMaxFrameWidth() const { return _maxWidth; }
int32 getMaxFrameHeight() const { return _maxHeight; }
+ uint16 getAssetType() const { return _assetType; }
M4Sprite *getFrame(int frameIndex);
void loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX, int destY);
RGB8* getPalette() { return _palette; }
@@ -123,6 +124,7 @@ public:
int32 getFrameSize(int index);
M4Sprite *operator[](int index) { return getFrame(index); }
protected:
+ Common::SeekableReadStream *_stream;
RGB8 _palette[256];
uint32 _colorCount;
uint32 _srcSize;
@@ -132,7 +134,10 @@ protected:
Common::Array<uint32> _frameOffsets;
Common::Array<SpriteAssetFrame> _frames;
uint32 _frameStartOffset;
- Common::SeekableReadStream *_stream;
+
+ // MADS sprite set fields
+ uint16 _assetType;
+
int32 parseSprite(bool isBigEndian = false);
void loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndian = false);
private:
diff --git a/engines/m4/globals.h b/engines/m4/globals.h
index a052a4c868..de6e716ece 100644
--- a/engines/m4/globals.h
+++ b/engines/m4/globals.h
@@ -264,6 +264,7 @@ public:
// DEPRECATED: ScummVM re-implementation keeps all the quotes loaded, so the methods below are stubs
void clearQuotes() {}
void loadQuoteRange(int startNum, int endNum) {}
+ void loadQuoteSet(...) {}
void loadQuote(int quoteNum) {}
void loadMadsMessagesInfo();
diff --git a/engines/m4/graphics.cpp b/engines/m4/graphics.cpp
index 893c415576..fa0cd7ccd3 100644
--- a/engines/m4/graphics.cpp
+++ b/engines/m4/graphics.cpp
@@ -333,7 +333,7 @@ void M4Surface::fillRect(const Common::Rect &r, uint8 color) {
}
void M4Surface::copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY,
- int transparentColor) {
+ int transparentColour) {
// Validation of the rectangle and position
if ((destX >= w) || (destY >= h))
return;
@@ -362,13 +362,13 @@ void M4Surface::copyFrom(M4Surface *src, const Common::Rect &srcBounds, int dest
byte *destPtr = (byte *)pixels + (destY * width()) + destX;
for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) {
- if (transparentColor == -1)
+ if (transparentColour == -1)
// No transparency, so copy line over
Common::copy(srcPtr, srcPtr + copyRect.width(), destPtr);
else {
// Copy each byte one at a time checking for the transparency color
for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr)
- if (srcPtr[xCtr] != transparentColor) destPtr[xCtr] = srcPtr[xCtr];
+ if (srcPtr[xCtr] != transparentColour) destPtr[xCtr] = srcPtr[xCtr];
}
srcPtr += src->width();
@@ -378,6 +378,81 @@ void M4Surface::copyFrom(M4Surface *src, const Common::Rect &srcBounds, int dest
src->freeData();
}
+/**
+ * Copies a given image onto a destination surface with scaling, transferring only pixels that meet
+ * the specified depth requirement on a secondary surface contain depth information
+ */
+void M4Surface::copyFrom(M4Surface *src, int destX, int destY, int depth, M4Surface *depthsSurface,
+ int scale, int transparentColour) {
+ /* TODO: This isn't a straight re-implementation of the original draw routine. Double check in future
+ * whether this implementation provides equivalent functionality
+ */
+ Common::Rect copyRect(0, 0, src->width(), src->height());
+
+ if (destX < 0) {
+ copyRect.left += -destX;
+ destX = 0;
+ } else if (destX + copyRect.width() > w) {
+ copyRect.right -= destX + copyRect.width() - w;
+ }
+ if (destY < 0) {
+ copyRect.top += -destY;
+ destY = 0;
+ } else if (destY + copyRect.height() > h) {
+ copyRect.bottom -= destY + copyRect.height() - h;
+ }
+
+ if (!copyRect.isValidRect())
+ return;
+
+ // Copy the specified area
+
+ byte *data = src->getBasePtr();
+ byte *srcPtr = data + (src->width() * copyRect.top + copyRect.left);
+ byte *depthsData = depthsSurface->getBasePtr();
+ byte *depthsPtr = depthsData + (src->width() * copyRect.top + copyRect.left);
+ byte *destPtr = (byte *)pixels + (destY * width()) + destX;
+
+ if (scale == 100) {
+ // 100% scaling variation
+ for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) {
+ // Copy each byte one at a time checking against the depth
+ for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr) {
+ if ((depthsPtr[xCtr] > depth) && (srcPtr[xCtr] != transparentColour))
+ destPtr[xCtr] = srcPtr[xCtr];
+ }
+
+ srcPtr += src->width();
+ depthsPtr += depthsSurface->width();
+ destPtr += width();
+ }
+ } else {
+ // Scaled variation
+ for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) {
+ int currX = -1;
+
+ // Loop through the source pixels
+ for (int xCtr = 0, xTotal = 0; xCtr < copyRect.width(); ++xCtr, xTotal += (100 - scale)) {
+ int srcX = xTotal / 100;
+
+ if (srcX != currX) {
+ currX = srcX;
+
+ if ((depthsPtr[currX] > depth) && (srcPtr[xCtr] != transparentColour))
+ destPtr[currX] = srcPtr[xCtr];
+ }
+ }
+
+ srcPtr += src->width();
+ depthsPtr += depthsSurface->width();
+ destPtr += width();
+ }
+ }
+
+ src->freeData();
+ depthsSurface->freeData();
+}
+
void M4Surface::loadBackgroundRiddle(const char *sceneName) {
char resourceName[20];
Common::SeekableReadStream *stream;
diff --git a/engines/m4/graphics.h b/engines/m4/graphics.h
index 4c89c50b8a..6d0a82ad25 100644
--- a/engines/m4/graphics.h
+++ b/engines/m4/graphics.h
@@ -145,8 +145,10 @@ public:
void clear();
void frameRect(const Common::Rect &r, uint8 color);
void fillRect(const Common::Rect &r, uint8 color);
- void copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY,
- int transparentColor = -1);
+ void copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY,
+ int transparentColour = -1);
+ void copyFrom(M4Surface *src, int destX, int destY, int depth, M4Surface *depthSurface, int scale,
+ int transparentColour = -1);
void update() {
if (_isScreen) {
@@ -156,16 +158,22 @@ public:
}
// copyTo methods
- inline void copyTo(M4Surface *dest, int transparentColor = -1) {
- dest->copyFrom(this, Common::Rect(width(), height()), 0, 0, transparentColor);
+ inline void copyTo(M4Surface *dest, int transparentColour = -1) {
+ dest->copyFrom(this, Common::Rect(width(), height()), 0, 0, transparentColour);
}
- inline void copyTo(M4Surface *dest, int x, int y, int transparentColor = -1) {
- dest->copyFrom(this, Common::Rect(width(), height()), x, y, transparentColor);
+ inline void copyTo(M4Surface *dest, int x, int y, int transparentColour = -1) {
+ dest->copyFrom(this, Common::Rect(width(), height()), x, y, transparentColour);
}
inline void copyTo(M4Surface *dest, const Common::Rect &srcBounds, int destX, int destY,
- int transparentColor = -1) {
- dest->copyFrom(this, srcBounds, destX, destY, transparentColor);
+ int transparentColour = -1) {
+ dest->copyFrom(this, srcBounds, destX, destY, transparentColour);
}
+ inline void copyTo(M4Surface *dest, int destX, int destY, int depth, M4Surface *depthsSurface, int scale,
+ int transparentColour = -1) {
+ dest->copyFrom(this, destX, destY, depth, depthsSurface, scale, transparentColour);
+ }
+
+
void translate(RGBList *list, bool isTransparent = false);
};
diff --git a/engines/m4/mads_logic.cpp b/engines/m4/mads_logic.cpp
index d0b7021f38..1fe5f4beb3 100644
--- a/engines/m4/mads_logic.cpp
+++ b/engines/m4/mads_logic.cpp
@@ -78,7 +78,7 @@ uint16 MadsSceneLogic::loadSpriteSet(uint16 suffixNum, uint16 sepChar) {
}
uint16 MadsSceneLogic::startReversibleSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) {
- M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(1);
+ M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(0);
uint8 depth = _madsVm->_rails->getDepth(Common::Point(spriteFrame->x + (spriteFrame->width() / 2),
spriteFrame->y + (spriteFrame->height() / 2)));
@@ -87,8 +87,7 @@ uint16 MadsSceneLogic::startReversibleSpriteSequence(uint16 srcSpriteIdx, int v0
}
uint16 MadsSceneLogic::startCycledSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) {
- M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(1);
-warning("%d %dx%d %d/%d", srcSpriteIdx, spriteFrame->x, spriteFrame->y, spriteFrame->width(), spriteFrame->height());
+ M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(0);
uint8 depth = _madsVm->_rails->getDepth(Common::Point(spriteFrame->x + (spriteFrame->width() / 2),
spriteFrame->y + (spriteFrame->height() / 2)));
@@ -97,7 +96,7 @@ warning("%d %dx%d %d/%d", srcSpriteIdx, spriteFrame->x, spriteFrame->y, spriteFr
}
uint16 MadsSceneLogic::startSpriteSequence3(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) {
- M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(1);
+ M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(0);
uint8 depth = _madsVm->_rails->getDepth(Common::Point(spriteFrame->x + (spriteFrame->width() / 2),
spriteFrame->y + (spriteFrame->height() / 2)));
@@ -160,7 +159,7 @@ void MadsSceneLogic::selectScene(int sceneNum) {
assert(sceneNum == 101);
_sceneNumber = sceneNum;
-
+ Common::set_to(&_spriteIndexes[0], &_spriteIndexes[50], 0);
}
void MadsSceneLogic::setupScene() {
@@ -206,10 +205,38 @@ void MadsSceneLogic::enterScene() {
if (_madsVm->globals()->previousScene != -1)
_madsVm->globals()->_globals[10] = 0;
if (_madsVm->globals()->previousScene != -2) {
- //playerPos = (100, 152);
+ _madsVm->scene()->getSceneResources().playerPos = Common::Point(100, 152);
}
- // TODO: EXTRA STUFF
+ if ((_madsVm->globals()->previousScene == 112) ||
+ ((_madsVm->globals()->previousScene != -2) && (_spriteIndexes[29] != 0))) {
+ // Returning from probe cutscene?
+ _spriteIndexes[29] = -1;
+ _madsVm->scene()->getSceneResources().playerPos = Common::Point(161, 123);
+ _madsVm->scene()->getSceneResources().playerDir = 9;
+
+ // TODO: Extra flags setting
+ _spriteIndexes[25] = startCycledSpriteSequence(_spriteIndexes[10], 0, 3, 0, 0, 0);
+ _madsVm->scene()->_sequenceList.setAnimRange(_spriteIndexes[25], 17, 17);
+ activateHotspot(0x47, false); // CHAIR
+ /*timer_unk1 = */_madsVm->scene()->_dynamicHotspots.add(0x47, 0x13F /*SIT_IN*/, -1,
+ Common::Rect(159, 84, 159+33, 84+36));
+
+ //if (_madsVm->globals()->previousScene == 112)
+ // room101Check();
+ } else {
+ _spriteIndexes[26] = startCycledSpriteSequence(_spriteIndexes[11], 0, 6, 0, 0, 0);
+ }
+
+ _madsVm->globals()->loadQuoteSet(0x31, 0x32, 0x37, 0x38, 0x39, -1);
+
+ if (_madsVm->globals()->_globals[10]) {
+ // TODO: Load scene animation
+
+ _madsVm->scene()->getSceneResources().playerPos = Common::Point(68, 140);
+ _madsVm->scene()->getSceneResources().playerDir = 4;
+ // TODO: Flags setting
+ }
lowRoomsEntrySound();
}
diff --git a/engines/m4/mads_menus.cpp b/engines/m4/mads_menus.cpp
index 64f18fa11d..da8ac4230e 100644
--- a/engines/m4/mads_menus.cpp
+++ b/engines/m4/mads_menus.cpp
@@ -616,9 +616,9 @@ void RexDialogView::initialiseLines() {
}
_totalTextEntries = 0;
- // Set up a default sprite slot entry
+ // Set up a default sprite slot entry for a full screen refresh
_spriteSlots.startIndex = 1;
- _spriteSlots[0].spriteId = -2;
+ _spriteSlots[0].spriteType = FULL_SCREEN_REFRESH;
_spriteSlots[0].timerIndex = -1;
}
@@ -795,7 +795,7 @@ bool RexDialogView::onEvent(M4EventType eventType, int32 param1, int x, int y, b
void RexDialogView::setFrame(int frameNumber, int depth) {
int slotIndex = _spriteSlots.getIndex();
- _spriteSlots[slotIndex].spriteId = 1;
+ _spriteSlots[slotIndex].spriteType = FOREGROUND_SPRITE;
_spriteSlots[slotIndex].timerIndex = 1;
_spriteSlots[slotIndex].spriteListIndex = 0; //_menuSpritesIndex;
_spriteSlots[slotIndex].frameNumber = frameNumber;
diff --git a/engines/m4/mads_scene.cpp b/engines/m4/mads_scene.cpp
index efb495e823..4f28cdc6da 100644
--- a/engines/m4/mads_scene.cpp
+++ b/engines/m4/mads_scene.cpp
@@ -43,6 +43,8 @@ namespace M4 {
MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResources), MadsView(this) {
_vm = vm;
+ MadsView::_bgSurface = Scene::_backgroundSurface;
+ MadsView::_depthSurface = Scene::_walkSurface;
_interfaceSurface = new MadsInterfaceView(vm);
for (int i = 0; i < 3; ++i)
actionNouns[i] = 0;
@@ -277,9 +279,6 @@ void MadsScene::drawElements() {
void MadsScene::update() {
- // Copy the bare scene in
- _backgroundSurface->copyTo(this);
-
// Draw all the various elements
drawElements();
diff --git a/engines/m4/mads_scene.h b/engines/m4/mads_scene.h
index f7625bb761..c8a0da3aea 100644
--- a/engines/m4/mads_scene.h
+++ b/engines/m4/mads_scene.h
@@ -34,18 +34,6 @@ namespace M4 {
#define INTERFACE_HEIGHT 106
-struct SpriteSlot {
- int16 spriteId;
- int16 scale;
- uint16 spriteListIndex;
-};
-
-struct DirtyArea {
- bool active;
- bool active2;
- Common::Rect bounds;
-};
-
class MadsSceneResources: public SceneResources {
public:
@@ -60,8 +48,10 @@ public:
int walkSize;
byte *walkData;
+ Common::Point playerPos;
+ int playerDir;
- MadsSceneResources() { walkSize = 0; walkData = NULL; }
+ MadsSceneResources() { walkSize = 0; walkData = NULL; playerDir = 0; }
~MadsSceneResources() { delete walkData; }
void load(int sceneId);
};
@@ -101,8 +91,6 @@ public:
const char *statusText() const { return _statusText; }
};
-#define DIRTY_AREA_SIZE 90
-
class MadsScene : public Scene, public MadsView {
private:
MadsEngine *_vm;
@@ -111,7 +99,6 @@ private:
MadsSceneLogic _sceneLogic;
SpriteAsset *_playerSprites;
- DirtyArea _dirtyAreas[DIRTY_AREA_SIZE];
void drawElements();
void loadScene2(const char *aaName);
diff --git a/engines/m4/mads_views.cpp b/engines/m4/mads_views.cpp
index e50c35bc04..e82a9976a5 100644
--- a/engines/m4/mads_views.cpp
+++ b/engines/m4/mads_views.cpp
@@ -43,7 +43,7 @@ static const int SCROLLER_DELAY = 200;
//--------------------------------------------------------------------------
-MadsSpriteSlots::MadsSpriteSlots() {
+MadsSpriteSlots::MadsSpriteSlots(MadsView &owner): _owner(owner) {
for (int i = 0; i < SPRITE_SLOTS_SIZE; ++i) {
MadsSpriteSlot rec;
_entries.push_back(rec);
@@ -52,6 +52,16 @@ MadsSpriteSlots::MadsSpriteSlots() {
startIndex = 0;
}
+void MadsSpriteSlots::clear() {
+ _owner._textDisplay.clear();
+ _sprites.clear();
+
+ // Reset the sprite slots list back to a single entry for a full screen refresh
+ startIndex = 1;
+ _entries[0].spriteType = FULL_SCREEN_REFRESH;
+ _entries[0].timerIndex = -1;
+}
+
int MadsSpriteSlots::getIndex() {
if (startIndex == SPRITE_SLOTS_SIZE)
error("Run out of sprite slots");
@@ -77,7 +87,7 @@ int MadsSpriteSlots::addSprites(const char *resName) {
void MadsSpriteSlots::deleteTimer(int timerIndex) {
for (int idx = 0; idx < startIndex; ++idx) {
if (_entries[idx].timerIndex == timerIndex)
- _entries[idx].spriteId = -1;
+ _entries[idx].spriteType = -1;
}
}
@@ -95,12 +105,49 @@ bool sortHelper(const DepthEntry &entry1, const DepthEntry &entry2) {
typedef Common::List<DepthEntry> DepthList;
-void MadsSpriteSlots::draw(View *view) {
+void MadsSpriteSlots::drawBackground() {
+ // Draw all active sprites onto the background surface
+ for (int i = 0; i < startIndex; ++i) {
+ if (_entries[i].spriteType >= 0) {
+ _owner._dirtyAreas[i].active = false;
+ } else {
+ _owner._dirtyAreas[i].textActive = true;
+ _owner._dirtyAreas.setSpriteSlot(i, _entries[i]);
+
+ if (_entries[i].spriteType == BACKGROUND_SPRITE) {
+ SpriteAsset &spriteSet = getSprite(_entries[i].spriteListIndex);
+ M4Sprite *frame = spriteSet.getFrame((_entries[i].frameNumber & 0x7fff) - 1);
+ int xp = _entries[i].xp;
+ int yp = _entries[i].yp;
+
+ if (_entries[i].scale != -1) {
+ // Adjust position based on frame size
+ xp -= frame->width() / 2;
+ yp -= frame->height() / 2;
+ }
+
+ if (_entries[i].depth <= 1) {
+ // No depth, so simply copy the frame onto the background
+ frame->copyTo(_owner._bgSurface, xp, yp);
+ } else {
+ // Depth was specified, so draw frame using scene's depth information
+ frame->copyTo(_owner._bgSurface, xp, yp, _entries[i].depth, _owner._depthSurface, 100);
+ }
+ }
+ }
+ }
+
+ // Flag any remaining dirty areas as inactive
+ for (uint i = startIndex; i < DIRTY_AREAS_TEXT_DISPLAY_IDX; ++i)
+ _owner._dirtyAreas[i].active = false;
+}
+
+void MadsSpriteSlots::drawForeground(View *view) {
DepthList depthList;
// Get a list of sprite object depths for active objects
for (int i = 0; i < startIndex; ++i) {
- if (_entries[i].spriteId >= 0) {
+ if (_entries[i].spriteType >= 0) {
DepthEntry rec(_entries[i].depth, i);
depthList.push_back(rec);
}
@@ -121,28 +168,41 @@ void MadsSpriteSlots::draw(View *view) {
// Minimalised drawing
assert(slot.spriteListIndex < (int)_sprites.size());
M4Sprite *spr = spriteSet.getFrame(slot.frameNumber - 1);
- spr->draw1(view, slot.scale, slot.depth, slot.xp, slot.yp);
+ spr->copyTo(view, slot.xp, slot.yp, slot.depth, _owner._depthSurface, slot.scale, 0);
} else {
int xp, yp;
M4Sprite *spr = spriteSet.getFrame(slot.frameNumber - 1);
if (slot.scale == -1) {
- xp = slot.xp; // - widthAdjust;
- yp = slot.yp; // - heightAdjust;
+ xp = slot.xp - _owner._posAdjust.x;
+ yp = slot.yp - _owner._posAdjust.y;
} else {
- xp = slot.xp - (spr->width() / 2); // - widthAdjust;
- yp = slot.yp - spr->height() + 1; // - heightAdjust;
+ xp = slot.xp - (spr->width() / 2) - _owner._posAdjust.x;
+ yp = slot.yp - spr->height() - _owner._posAdjust.y + 1;
}
if (slot.depth > 1) {
- spr->draw2(view, slot.depth, xp, yp);
+ // Draw the frame with depth processing
+ spr->copyTo(view, xp, yp, slot.depth, _owner._depthSurface, 100, 0);
} else {
- spr->draw3(view, xp, yp);
+ // No depth, so simply draw the image
+ spr->copyTo(view, xp, yp, 0);
}
}
}
}
+void MadsSpriteSlots::setDirtyAreas() {
+ for (int i = 0; i < startIndex; ++i) {
+ if (_entries[i].spriteType >= 0) {
+ _owner._dirtyAreas.setSpriteSlot(i, _entries[i]);
+
+ _owner._dirtyAreas[i].textActive = (_entries[i].spriteType <= 0) ? 0 : 1;
+ _entries[i].spriteType = 0;
+ }
+ }
+}
+
/**
* Removes any sprite slots that are no longer needed
*/
@@ -150,7 +210,7 @@ void MadsSpriteSlots::cleanUp() {
// Delete any entries that aren't needed
int idx = 0;
while (idx < startIndex) {
- if (_entries[idx].spriteId >= 0) {
+ if (_entries[idx].spriteType < 0) {
_entries.remove_at(idx);
--startIndex;
} else {
@@ -168,7 +228,7 @@ void MadsSpriteSlots::cleanUp() {
//--------------------------------------------------------------------------
-MadsTextDisplay::MadsTextDisplay() {
+MadsTextDisplay::MadsTextDisplay(MadsView &owner): _owner(owner) {
for (int i = 0; i < TEXT_DISPLAY_SIZE; ++i) {
MadsTextDisplayEntry rec;
rec.active = false;
@@ -206,6 +266,28 @@ int MadsTextDisplay::add(int xp, int yp, uint fontColour, int charSpacing, const
return usedSlot;
}
+void MadsTextDisplay::setDirtyAreas() {
+ // Determine dirty areas for active text areas
+ for (uint idx = 0, dirtyIdx = DIRTY_AREAS_TEXT_DISPLAY_IDX; dirtyIdx < DIRTY_AREAS_SIZE; ++idx, ++dirtyIdx) {
+ if ((_entries[idx].expire < 0) || !_entries[idx].active)
+ _owner._dirtyAreas[dirtyIdx].active = false;
+ else {
+ _owner._dirtyAreas[dirtyIdx].textActive = true;
+ _owner._dirtyAreas.setTextDisplay(dirtyIdx, _entries[idx]);
+ }
+ }
+}
+
+void MadsTextDisplay::setDirtyAreas2() {
+ // Determine dirty areas for active text areas
+ for (uint idx = 0, dirtyIdx = DIRTY_AREAS_TEXT_DISPLAY_IDX; dirtyIdx < DIRTY_AREAS_SIZE; ++idx, ++dirtyIdx) {
+ if (_entries[idx].active && (_entries[idx].expire >= 0)) {
+ _owner._dirtyAreas.setTextDisplay(dirtyIdx, _entries[idx]);
+ _owner._dirtyAreas[dirtyIdx].textActive = (_entries[idx].expire <= 0) ? 0 : 1;
+ }
+ }
+}
+
void MadsTextDisplay::draw(View *view) {
for (uint idx = 0; idx < _entries.size(); ++idx) {
if (_entries[idx].active && (_entries[idx].expire >= 0)) {
@@ -405,21 +487,23 @@ MadsDynamicHotspots::MadsDynamicHotspots(MadsView &owner): _owner(owner) {
for (int i = 0; i < DYNAMIC_HOTSPOTS_SIZE; ++i) {
DynamicHotspot rec;
rec.active = false;
+ _entries.push_back(rec);
}
_flag = true;
_count = 0;
}
-int MadsDynamicHotspots::add(int descId, int field14, int timerIndex, const Common::Rect &bounds) {
+int MadsDynamicHotspots::add(int descId, int field14, int seqIndex, const Common::Rect &bounds) {
// Find a free slot
uint idx = 0;
- while ((idx < _entries.size()) && !_entries[idx].active)
+ while ((idx < _entries.size()) && _entries[idx].active)
++idx;
if (idx == _entries.size())
error("MadsDynamicHotspots overflow");
_entries[idx].active = true;
_entries[idx].descId = descId;
+ _entries[idx].seqIndex = seqIndex;
_entries[idx].bounds = bounds;
_entries[idx].pos.x = -3;
_entries[idx].pos.y = 0;
@@ -430,7 +514,9 @@ int MadsDynamicHotspots::add(int descId, int field14, int timerIndex, const Comm
++_count;
_flag = true;
- _owner._sequenceList[timerIndex].dynamicHotspotIndex = idx;
+
+ if (seqIndex >= 0)
+ _owner._sequenceList[seqIndex].dynamicHotspotIndex = idx;
return idx;
}
@@ -454,8 +540,8 @@ int MadsDynamicHotspots::set17(int index, int v) {
void MadsDynamicHotspots::remove(int index) {
if (_entries[index].active) {
- if (_entries[index].timerIndex >= 0)
- _owner._sequenceList[_entries[index].timerIndex].dynamicHotspotIndex = -1;
+ if (_entries[index].seqIndex >= 0)
+ _owner._sequenceList[_entries[index].seqIndex].dynamicHotspotIndex = -1;
_entries[index].active = false;
--_count;
@@ -473,6 +559,143 @@ void MadsDynamicHotspots::reset() {
/*--------------------------------------------------------------------------*/
+void MadsDirtyArea::setArea(int width, int height, int maxWidth, int maxHeight) {
+ if (bounds.left % 2) {
+ --bounds.left;
+ ++width;
+ }
+ int right = bounds.left + width;
+ if (bounds.left < 0)
+ bounds.left = 0;
+ 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;
+ 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;
+}
+
+/*--------------------------------------------------------------------------*/
+
+MadsDirtyAreas::MadsDirtyAreas(MadsView &owner): _owner(owner) {
+ for (int i = 0; i < DIRTY_AREAS_SIZE; ++i) {
+ MadsDirtyArea rec;
+ rec.active = false;
+ _entries.push_back(rec);
+ }
+}
+
+void MadsDirtyAreas::setSpriteSlot(int dirtyIdx, const MadsSpriteSlot &spriteSlot) {
+ int width, height;
+ MadsDirtyArea &dirtyArea = _entries[dirtyIdx];
+
+ if (spriteSlot.spriteType == FULL_SCREEN_REFRESH) {
+ // Special entry to refresh the entire screen
+ dirtyArea.bounds.left = 0;
+ dirtyArea.bounds.top = 0;
+ width = MADS_SURFACE_WIDTH;
+ height = MADS_SURFACE_HEIGHT;
+ } else {
+ // Standard sprite slots
+ dirtyArea.bounds.left = spriteSlot.xp - _owner._posAdjust.x;
+ dirtyArea.bounds.top = spriteSlot.yp - _owner._posAdjust.y;
+
+ SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(spriteSlot.spriteListIndex);
+ M4Sprite *frame = spriteSet.getFrame(((spriteSlot.frameNumber & 0x7fff) - 1) & 0x7f);
+
+ if (spriteSlot.scale == -1) {
+ width = frame->width();
+ height = frame->height();
+ } else {
+ width = frame->width() * spriteSlot.scale / 100;
+ height = frame->height() * spriteSlot.scale / 100;
+
+ dirtyArea.bounds.left -= width / 2;
+ dirtyArea.bounds.top += -(height - 1);
+ }
+ }
+
+ dirtyArea.setArea(width, height, MADS_SURFACE_WIDTH, MADS_SURFACE_HEIGHT);
+}
+
+void MadsDirtyAreas::setTextDisplay(int dirtyIdx, const MadsTextDisplayEntry &textDisplay) {
+ MadsDirtyArea &dirtyArea = _entries[dirtyIdx];
+ dirtyArea.bounds.left = textDisplay.bounds.left;
+ dirtyArea.bounds.top = textDisplay.bounds.top;
+
+ dirtyArea.setArea(textDisplay.bounds.width(), textDisplay.bounds.height(), MADS_SURFACE_WIDTH, MADS_SURFACE_HEIGHT);
+}
+
+/**
+ * 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 MadsDirtyAreas::merge(int startIndex, int count) {
+ 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 MadsDirtyAreas::intersects(int idx1, int idx2) {
+ return _entries[idx1].bounds2.intersects(_entries[idx2].bounds2);
+}
+
+void MadsDirtyAreas::mergeAreas(int idx1, int idx2) {
+ MadsDirtyArea &da1 = _entries[idx1];
+ MadsDirtyArea &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 MadsDirtyAreas::copy(M4Surface *dest, M4Surface *src) {
+ for (uint i = 0; i < _entries.size(); ++i) {
+ if (_entries[i].active && _entries[i].bounds.isValidRect())
+ src->copyTo(dest, _entries[i].bounds, _entries[i].bounds.left, _entries[i].bounds.top);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
MadsSequenceList::MadsSequenceList(MadsView &owner): _owner(owner) {
for (int i = 0; i < TIMER_LIST_SIZE; ++i) {
MadsSequenceEntry rec;
@@ -565,8 +788,7 @@ void MadsSequenceList::setSpriteSlot(int timerIndex, MadsSpriteSlot &spriteSlot)
MadsSequenceEntry &timerEntry = _entries[timerIndex];
SpriteAsset &sprite = _owner._spriteSlots.getSprite(timerEntry.spriteListIndex);
- // TODO: Figure out logic for spriteId value based on SPRITE_SLOT.field_0
- spriteSlot.spriteId = (0 /*field 0*/ == 1) ? -4 : 1;
+ spriteSlot.spriteType = sprite.getAssetType() == 1 ? BACKGROUND_SPRITE : FOREGROUND_SPRITE;
spriteSlot.timerIndex = timerIndex;
spriteSlot.spriteListIndex = timerEntry.spriteListIndex;
spriteSlot.frameNumber = ((timerEntry.field_2 == 1) ? 0x8000 : 0) | timerEntry.frameIndex;
@@ -727,10 +949,44 @@ void MadsSequenceList::delay(uint32 v1, uint32 v2) {
}
}
+void MadsSequenceList::setAnimRange(int timerIndex, int startVal, int endVal) {
+ MadsSequenceEntry &seqEntry = _entries[timerIndex];
+ SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(seqEntry.spriteListIndex);
+ int numSprites = spriteSet.getCount();
+ int tempStart = startVal, tempEnd = endVal;
+
+ switch (startVal) {
+ case -2:
+ tempStart = numSprites;
+ break;
+ case -1:
+ tempStart = 1;
+ break;
+ }
+
+ switch (endVal) {
+ case -2:
+ case 0:
+ tempEnd = numSprites;
+ break;
+ case -1:
+ tempEnd = 1;
+ break;
+ default:
+ tempEnd = numSprites;
+ break;
+ }
+
+ seqEntry.frameStart = tempStart;
+ seqEntry.numSprites = tempEnd;
+
+ seqEntry.frameIndex = (seqEntry.frameInc < 0) ? tempStart : tempEnd;
+}
+
//--------------------------------------------------------------------------
MadsView::MadsView(View *view): _view(view), _dynamicHotspots(*this), _sequenceList(*this),
- _kernelMessages(*this) {
+ _kernelMessages(*this), _spriteSlots(*this), _dirtyAreas(*this), _textDisplay(*this) {
_textSpacing = -1;
_ticksAmount = 3;
_newTimeout = 0;
@@ -738,11 +994,31 @@ MadsView::MadsView(View *view): _view(view), _dynamicHotspots(*this), _sequenceL
_abortTimers2 = 0;
_abortTimersMode = ABORTMODE_0;
_abortTimersMode2 = ABORTMODE_0;
+
+ _depthSurface = NULL;
+ _bgSurface = NULL;
}
void MadsView::refresh() {
// Draw any sprites
- _spriteSlots.draw(_view);
+ _spriteSlots.drawBackground();
+
+ // Process dirty areas
+ _textDisplay.setDirtyAreas();
+
+ // Merge any identified dirty areas
+ _dirtyAreas.merge(1, DIRTY_AREAS_SIZE);
+
+ // Copy dirty areas to the main display surface
+ _dirtyAreas.copy(_view, _bgSurface);
+
+ // Handle dirty areas for foreground objects
+ _spriteSlots.setDirtyAreas();
+ _textDisplay.setDirtyAreas2();
+ _dirtyAreas.merge(1, DIRTY_AREAS_SIZE);
+
+ // Draw foreground sprites
+ _spriteSlots.drawForeground(_view);
// Draw text elements onto the view
_textDisplay.draw(_view);
diff --git a/engines/m4/mads_views.h b/engines/m4/mads_views.h
index 926702a80b..3b64cc77f2 100644
--- a/engines/m4/mads_views.h
+++ b/engines/m4/mads_views.h
@@ -34,6 +34,7 @@
namespace M4 {
+#define MADS_SURFACE_WIDTH 320
#define MADS_SURFACE_HEIGHT 156
#define MADS_SCREEN_HEIGHT 200
#define MADS_Y_OFFSET ((MADS_SCREEN_HEIGHT - MADS_SURFACE_HEIGHT) / 2)
@@ -44,7 +45,7 @@ enum AbortTimerMode {ABORTMODE_0 = 0, ABORTMODE_1 = 1, ABORTMODE_2 = 2};
class MadsSpriteSlot {
public:
- int spriteId;
+ int spriteType;
int timerIndex;
int spriteListIndex;
int frameNumber;
@@ -58,16 +59,21 @@ public:
#define SPRITE_SLOTS_SIZE 50
+enum SpriteIdSpecial {
+ BACKGROUND_SPRITE = -4, FULL_SCREEN_REFRESH = -2, FOREGROUND_SPRITE = 1
+};
+
typedef Common::Array<Common::SharedPtr<SpriteAsset> > SpriteList;
class MadsSpriteSlots {
private:
+ MadsView &_owner;
Common::Array<MadsSpriteSlot> _entries;
SpriteList _sprites;
public:
int startIndex;
- MadsSpriteSlots();
+ MadsSpriteSlots(MadsView &owner);
MadsSpriteSlot &operator[](int idx) {
assert(idx < SPRITE_SLOTS_SIZE);
@@ -80,13 +86,12 @@ public:
int getIndex();
int addSprites(const char *resName);
- void clear() {
- startIndex = 0;
- _sprites.clear();
- }
+ void clear();
void deleteTimer(int timerIndex);
- void draw(View *view);
+ void drawBackground();
+ void drawForeground(View *view);
+ void setDirtyAreas();
void cleanUp();
};
@@ -108,9 +113,10 @@ public:
class MadsTextDisplay {
private:
+ MadsView &_owner;
Common::Array<MadsTextDisplayEntry> _entries;
public:
- MadsTextDisplay();
+ MadsTextDisplay(MadsView &owner);
MadsTextDisplayEntry &operator[](int idx) {
assert(idx < TEXT_DISPLAY_SIZE);
@@ -125,6 +131,8 @@ public:
int add(int xp, int yp, uint fontColour, int charSpacing, const char *msg, Font *font);
void clear();
void draw(View *view);
+ void setDirtyAreas();
+ void setDirtyAreas2();
void cleanUp();
};
@@ -204,7 +212,7 @@ public:
class DynamicHotspot {
public:
bool active;
- int timerIndex;
+ int seqIndex;
Common::Rect bounds;
Common::Point pos;
int facing;
@@ -229,13 +237,47 @@ public:
MadsDynamicHotspots(MadsView &owner);
DynamicHotspot &operator[](uint idx) { return _entries[idx]; }
- int add(int descId, int field14, int timerIndex, const Common::Rect &bounds);
+ int add(int descId, int field14, int seqIndex, const Common::Rect &bounds);
int setPosition(int index, int xp, int yp, int facing);
int set17(int index, int v);
void remove(int index);
void reset();
};
+class MadsDirtyArea {
+public:
+ Common::Rect bounds;
+ Common::Rect bounds2;
+ bool textActive;
+ bool active;
+
+ MadsDirtyArea() { active = false; }
+ void setArea(int width, int height, int maxWidth, int maxHeight);
+};
+
+#define DIRTY_AREAS_SIZE 90
+#define DIRTY_AREAS_TEXT_DISPLAY_IDX 50
+
+class MadsDirtyAreas {
+private:
+ MadsView &_owner;
+ Common::Array<MadsDirtyArea> _entries;
+public:
+ MadsDirtyAreas(MadsView &owner);
+
+ MadsDirtyArea &operator[](uint idx) {
+ assert(idx < _entries.size());
+ return _entries[idx];
+ }
+
+ void setSpriteSlot(int dirtyIdx, const MadsSpriteSlot &spriteSlot);
+ void setTextDisplay(int dirtyIdx, const MadsTextDisplayEntry &textDisplay);
+ void merge(int startIndex, int count);
+ bool intersects(int idx1, int idx2);
+ void mergeAreas(int idx1, int idx2);
+ void copy(M4Surface *dest, M4Surface *src);
+};
+
enum SpriteAnimType {ANIMTYPE_CYCLED = 1, ANIMTYPE_REVERSIBLE = 2};
enum SequenceSubEntryMode {SM_0 = 0, SM_1 = 1, SM_FRAME_INDEX = 2};
@@ -303,6 +345,7 @@ public:
bool loadSprites(int timerIndex);
void tick();
void delay(uint32 v1, uint32 v2);
+ void setAnimRange(int timerIndex, int startVal, int endVal);
};
class MadsView {
@@ -315,6 +358,7 @@ public:
ScreenObjects _screenObjects;
MadsDynamicHotspots _dynamicHotspots;
MadsSequenceList _sequenceList;
+ MadsDirtyAreas _dirtyAreas;
int _textSpacing;
int _ticksAmount;
@@ -323,6 +367,10 @@ public:
int8 _abortTimers2;
AbortTimerMode _abortTimersMode;
AbortTimerMode _abortTimersMode2;
+ Common::Point _posAdjust;
+
+ M4Surface *_depthSurface;
+ M4Surface *_bgSurface;
public:
MadsView(View *view);
diff --git a/engines/m4/sprite.h b/engines/m4/sprite.h
index 022f3bbc4f..49a96a6c4a 100644
--- a/engines/m4/sprite.h
+++ b/engines/m4/sprite.h
@@ -115,18 +115,6 @@ public:
void loadDeltaRle(Common::SeekableReadStream* rleData, int destX, int destY);
void loadMadsSprite(Common::SeekableReadStream* source);
- void draw1(M4Surface *destSurface, int scale, int depth, int xp, int yp) {
- // TODO: Properly implement drawing
- copyTo(destSurface, xp, yp, 0);
- }
- void draw2(M4Surface *destSurface, int depth, int xp, int yp) {
- // TODO: Properly implement drawing
- copyTo(destSurface, xp, yp, 0);
- }
- void draw3(M4Surface *destSurface, int xp, int yp) {
- // TODO: Properly implement drawing
- copyTo(destSurface, xp, yp, 0);
- }
byte getTransparentColor() const;
protected:
};