From dd9bf70761f2de9771912984209422f1f63bd3e8 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sun, 23 May 2010 07:20:40 +0000 Subject: Major work done on the animation and support classes to match the original svn-id: r49150 --- engines/m4/assets.cpp | 4 +- engines/m4/assets.h | 7 +- engines/m4/globals.h | 1 + engines/m4/graphics.cpp | 81 +++++++++++- engines/m4/graphics.h | 24 ++-- engines/m4/mads_logic.cpp | 41 +++++- engines/m4/mads_menus.cpp | 6 +- engines/m4/mads_scene.cpp | 5 +- engines/m4/mads_scene.h | 19 +-- engines/m4/mads_views.cpp | 320 ++++++++++++++++++++++++++++++++++++++++++---- engines/m4/mads_views.h | 68 ++++++++-- engines/m4/sprite.h | 12 -- 12 files changed, 502 insertions(+), 86 deletions(-) (limited to 'engines') 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 _frameOffsets; Common::Array _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 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 > SpriteList; class MadsSpriteSlots { private: + MadsView &_owner; Common::Array _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 _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 _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: }; -- cgit v1.2.3