diff options
Diffstat (limited to 'engines/m4/animation.cpp')
-rw-r--r-- | engines/m4/animation.cpp | 546 |
1 files changed, 414 insertions, 132 deletions
diff --git a/engines/m4/animation.cpp b/engines/m4/animation.cpp index fe46e121f0..1142ba48d1 100644 --- a/engines/m4/animation.cpp +++ b/engines/m4/animation.cpp @@ -26,181 +26,463 @@ #include "m4/assets.h" #include "m4/animation.h" #include "m4/compression.h" +#include "m4/mads_scene.h" namespace M4 { // TODO: this code needs cleanup -Animation::Animation(MadsM4Engine *vm) { - _vm = vm; - _playing = false; +MadsAnimation::MadsAnimation(MadsM4Engine *vm, MadsView *view): Animation(vm), _view(view) { + _font = NULL; + _resetFlag = false; + _freeFlag = false; + _skipLoad = false; + _unkIndex = -1; + _messageCtr= 0; + _field12 = 0; + + _currentFrame = 0; + _oldFrameEntry = 0; + _nextFrameTimer = _madsVm->_currentTimer; } -void Animation::loadFullScreen(const char *filename) { - _vm->_palette->deleteAllRanges(); - load(filename); +MadsAnimation::~MadsAnimation() { + for (uint i = 0; i < _messages.size(); ++i) { + if (_messages[i].kernelMsgIndex >= 0) + _view->_kernelMessages.remove(_messages[i].kernelMsgIndex); + } + + // Further deletion logic + if (_field12) { + _view->_spriteSlots.deleteSprites(_spriteListIndexes[_spriteListIndex]); + } + + delete _font; } -void Animation::load(const char *filename) { - MadsPack anim(filename, _vm); +/** + * Initialises and loads the data of an animation + */ +void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4Surface *interfaceSurface, M4Surface *sceneSurface) { + MadsPack anim(filename.c_str(), _vm); + bool madsRes = filename[0] == '*'; char buffer[20]; + int streamIndex = 1; // Chunk 1: header // header - // TODO: there are some unknown fields here, plus we don't read - // the entire chunk + Common::SeekableReadStream *animStream = anim.getItemStream(0); - Common::SeekableReadStream *spriteSeriesStream; - //printf("Chunk 0, size %i\n", animStream->size()); - _seriesCount = animStream->readUint16LE(); - _frameCount = animStream->readUint16LE(); - _frameEntryCount = animStream->readUint16LE(); - // Unknown - for (int i = 0; i < 43; i++) - animStream->readByte(); + int spriteListCount = animStream->readUint16LE(); + int miscEntriesCount = animStream->readUint16LE(); + int frameEntryCount = animStream->readUint16LE(); + int messagesCount = animStream->readUint16LE(); + animStream->skip(1); + _flags = animStream->readByte(); + + animStream->skip(2); + _animMode = animStream->readUint16LE(); + _roomNumber = animStream->readUint16LE(); + animStream->skip(2); + _field12 = animStream->readUint16LE() != 0; + _spriteListIndex = animStream->readUint16LE(); + _scrollX = animStream->readUint16LE(); + _scrollY = animStream->readSint16LE(); + animStream->skip(10); + + animStream->read(buffer, 13); + _interfaceFile = Common::String(buffer, 13); + + for (int i = 0; i < 10; ++i) { + animStream->read(buffer, 13); + _spriteSetNames[i] = Common::String(buffer, 13); + } - _spriteSeriesNames = new Common::String[_seriesCount]; - printf("%i sprite series\n", _seriesCount); + animStream->skip(81); + animStream->read(buffer, 13); + _lbmFilename = Common::String(buffer, 13); + animStream->read(buffer, 13); + _spritesFilename = Common::String(buffer, 13); + animStream->skip(48); + animStream->read(buffer, 13); + _soundName = Common::String(buffer, 13); + animStream->skip(26); + animStream->read(buffer, 13); + Common::String fontResource(buffer, 13); + + if (_animMode == 4) + flags |= 0x4000; + if (flags & 0x100) + loadInterface(interfaceSurface, sceneSurface); + + // Initialise the reference list + for (int i = 0; i < spriteListCount; ++i) + _spriteListIndexes.push_back(-1); - // TODO: for now, we only load the first sprite series - if (_seriesCount > 1) - printf("TODO: Anim has %i sprite series, for now, we only load the first one\n", _seriesCount); - _seriesCount = 1; // TODO + delete animStream; - for (int i = 0; i < _seriesCount; i++) { - animStream->read(buffer, 13); - _spriteSeriesNames[i] = Common::String(buffer); - //printf("%03d: %s\n", i, _spriteSeriesNames[i].c_str()); - - spriteSeriesStream = _vm->res()->get(_spriteSeriesNames[i].c_str()); - _spriteSeries = new SpriteAsset(_vm, spriteSeriesStream, - spriteSeriesStream->size(), _spriteSeriesNames[i].c_str()); - _vm->res()->toss(_spriteSeriesNames[i].c_str()); - - // Adjust the palette of the sprites in the sprite series - // so that they can be displayed on screen correctly - RGBList *palData = new RGBList(_spriteSeries->getColorCount(), _spriteSeries->getPalette(), true); - _vm->_palette->addRange(palData); - - for (int k = 0; k < _spriteSeries->getCount(); k++) { - M4Sprite *spr = _spriteSeries->getFrame(k); - spr->translate(palData); // sprite pixel translation + if (messagesCount > 0) { + // Chunk 2 + // Following is a list of any messages for the animation + + animStream = anim.getItemStream(streamIndex++); + + for (int i = 0; i < messagesCount; ++i) { + AnimMessage rec; + animStream->read(rec.msg, 70); + rec.pos.x = animStream->readUint16LE(); + rec.pos.y = animStream->readUint16LE(); + animStream->readUint16LE(); + rec.rgb1.r = animStream->readByte(); + rec.rgb1.g = animStream->readByte(); + rec.rgb1.b = animStream->readByte(); + rec.rgb2.r = animStream->readByte(); + rec.rgb2.g = animStream->readByte(); + rec.rgb2.b = animStream->readByte(); + rec.kernelMsgIndex = animStream->readUint16LE(); + animStream->skip(6); + rec.startFrame = animStream->readUint16LE(); + rec.endFrame = animStream->readUint16LE(); + animStream->readUint16LE(); + + _messages.push_back(rec); } + + delete animStream; } - //printf("End pos: %i\n", animStream->pos()); + if (frameEntryCount > 0) { + // Chunk 3: animation frame info + animStream = anim.getItemStream(streamIndex++); + + for (int i = 0; i < frameEntryCount; i++) { + AnimFrameEntry rec; + rec.frameNumber = animStream->readUint16LE(); + rec.seqIndex = animStream->readByte(); + rec.spriteSlot.spriteListIndex = animStream->readByte(); + rec.spriteSlot.frameNumber = animStream->readUint16LE(); + rec.spriteSlot.xp = animStream->readUint16LE(); + rec.spriteSlot.yp = animStream->readUint16LE(); + rec.spriteSlot.depth = animStream->readByte(); + rec.spriteSlot.scale = (int8)animStream->readByte(); + + _frameEntries.push_back(rec); + } - delete animStream; + delete animStream; + } - // ------------------ - - // Chunk 2: anim info - AnimationFrame frame; - animStream = anim.getItemStream(1); - //printf("Chunk 1, size %i\n", animStream->size()); - - _frameEntries = new AnimationFrame[_frameEntryCount]; - - for (int i = 0; i < _frameEntryCount; i++) { - - frame.animFrameIndex = animStream->readUint16LE(); - frame.u = animStream->readByte(); - frame.seriesIndex = animStream->readByte(); - frame.seriesFrameIndex = animStream->readUint16LE(); - frame.x = animStream->readUint16LE(); - frame.y = animStream->readUint16LE(); - frame.v = animStream->readByte(); - frame.w = animStream->readByte(); - - _frameEntries[i] = frame; - - /* - printf( - "animFrameIndex = %4d, " - "u = %3d, " - "seriesIndex = %3d, " - "seriesFrameIndex = %6d, " - "x = %3d, " - "y = %3d, " - "v = %3d, " - "w = %3d\n", - - frame.animFrameIndex, - frame.u, - frame.seriesIndex, - frame.seriesFrameIndex, - frame.x, - frame.y, - frame.v, - frame.w - ); - */ - } - //printf("End pos: %i\n", animStream->pos()); + if (miscEntriesCount > 0) { + // Chunk 4: Misc Data + animStream = anim.getItemStream(streamIndex); - delete animStream; + for (int i = 0; i < miscEntriesCount; ++i) { + AnimMiscEntry rec; + rec.soundNum = animStream->readByte(); + animStream->skip(1); + rec.numTicks = animStream->readUint16LE(); + rec.posAdjust.x = animStream->readUint16LE(); + rec.posAdjust.y = animStream->readUint16LE(); + animStream->readUint16LE(); - // Chunk 3: unknown (seems to be sound data?) - // TODO -} + _miscEntries.push_back(rec); + } + + delete animStream; + } + + // If the animation specifies a font, then load it for access + if (_flags & ANIM_CUSTOM_FONT) { + Common::String fontName; + if (madsRes) + fontName += "*"; + fontName += fontResource; -Animation::~Animation() { - //delete[] _spriteSeriesNames; - //delete[] _spriteSeries; - //delete[] _frameEntries; + _font = _vm->_font->getFont(fontName); + } + + // Load all the sprite sets for the animation + for (int i = 0; i < spriteListCount; ++i) { + if (_field12 && (i == _spriteListIndex)) + // Skip over field, since it's manually loaded + continue; + + _spriteListIndexes[i] = _view->_spriteSlots.addSprites(_spriteSetNames[i].c_str()); + } + + + if (_field12) { + Common::String resName; + if (madsRes) + resName += "*"; + resName += _spriteSetNames[_spriteListIndex]; + + _spriteListIndexes[_spriteListIndex] = _view->_spriteSlots.addSprites(resName.c_str()); + } + + // TODO: Unknown section about handling sprite set list combined with messages size + + // TODO: The original has two separate loops for the loop below based on _animMode == 4. Is it + // perhaps that in that mode the sprite frames has a different format..? + + // Remap the sprite list index fields from the initial value to the indexes of the loaded + // sprite sets for the animation + for (uint i = 0; i < _frameEntries.size(); ++i) { + int idx = _frameEntries[i].spriteSlot.spriteListIndex; + _frameEntries[i].spriteSlot.spriteListIndex = _spriteListIndexes[idx]; + } } -void Animation::start() { - _curFrame = 0; - _curFrameEntry = 0; - //for (int i = 0; i < _seriesCount; i++) { - //_spriteSeries[i] = new SpriteSeries((char*)_spriteSeriesNames[i].c_str()); - //} - _playing = true; - updateAnim(); +/** + * Loads an animation file for display + */ +void MadsAnimation::load(const Common::String &filename, int abortTimers) { + initialise(filename, 0, NULL, NULL); + _messageCtr = 0; + _skipLoad = true; + +/* TODO: figure out extra stuff in this routine + if (_field12) { + _unkIndex = -1; + int listIndex = _spriteListIndexes[_spriteListIndex]; + SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(listIndex); + ..?.. + } +*/ + + // Initialise miscellaneous fields + _currentFrame = 0; + _oldFrameEntry = 0; + _nextFrameTimer = _madsVm->_currentTimer; + _abortTimers = abortTimers; + _abortMode = _madsVm->scene()->_abortTimersMode2; + + for (int i = 0; i < 3; ++i) + _actionNouns[i] = _madsVm->scene()->actionNouns[i]; + + // Initialise kernel message list + for (uint i = 0; i < _messages.size(); ++i) + _messages[i].kernelMsgIndex = -1; } -bool Animation::updateAnim() { - if (!_playing) - return true; +void MadsAnimation::update() { + if (_field12) { + int spriteListIndex = _spriteListIndexes[_spriteListIndex]; + int newIndex = -1; + + for (uint idx = _oldFrameEntry; idx < _frameEntries.size(); ++idx) { + if (_frameEntries[idx].frameNumber > _currentFrame) + break; + if (_frameEntries[idx].spriteSlot.spriteListIndex == spriteListIndex) + newIndex = _frameEntries[idx].spriteSlot.frameNumber; + } + + if (newIndex >= 0) + load1(newIndex); + } + + // If it's not time for the next frame, then exit + if (_madsVm->_currentTimer < _nextFrameTimer) + return; - // Get the scene background surface - M4Surface *bg = _vm->_scene->getBackgroundSurface(); + // Loop checks for any prior animation sprite slots to be expired + for (int slotIndex = 0; slotIndex < _view->_spriteSlots.startIndex; ++slotIndex) { + if (_view->_spriteSlots[slotIndex].seqIndex >= 0x80) { + // Flag the frame as animation sprite slot + _view->_spriteSlots[slotIndex].spriteType = EXPIRED_SPRITE; + } + } - while (_frameEntries[_curFrameEntry].animFrameIndex == _curFrame) { - AnimationFrame *frame = &_frameEntries[_curFrameEntry]; - int seriesFrameIndex = (frame->seriesFrameIndex & 0x7FFF) - 1; + // Validate the current frame + if (_currentFrame >= (int)_miscEntries.size()) { + // Is the animation allowed to be repeated? + if (_resetFlag) { + _currentFrame = 0; + _oldFrameEntry = 0; + } else { + _freeFlag = true; + return; + } + } - // Write the sprite onto the screen - M4Sprite *spr = _spriteSeries->getFrame(seriesFrameIndex); + // Handle starting any sound for this frame + AnimMiscEntry &misc = _miscEntries[_currentFrame]; + if (misc.soundNum) + _vm->_sound->playSound(misc.soundNum); - // FIXME: correct x, y - spr->copyTo(bg, frame->x, frame->y, (int)spr->getTransparentColor()); + bool screenChanged = false; - // HACK: wait a bit - g_system->delayMillis(100); + // Handle any scrolling of the screen surface + if ((_scrollX != 0) || (_scrollY != 0)) { + _view->_bgSurface->scrollX(_scrollX); + _view->_bgSurface->scrollY(_scrollY); - //printf("_curFrameEntry = %d\n", _curFrameEntry); - _curFrameEntry++; + screenChanged = true; } - //printf("_curFrame = %d\n", _curFrame); + // Handle any offset adjustment for sprites as of this frame + if (_view->_posAdjust.x != misc.posAdjust.x) { + misc.posAdjust.x = _view->_posAdjust.x; + screenChanged = true; + } + if (_view->_posAdjust.y != misc.posAdjust.y) { + misc.posAdjust.y = _view->_posAdjust.y; + screenChanged = true; + } + if (screenChanged) { + // Signal the entire screen needs refreshing + _view->_spriteSlots.fullRefresh(); + } - _curFrame++; - if (_curFrame >= _frameCount) // anim done - stop(); + int spriteSlotsMax = _view->_spriteSlots.startIndex; + + // Main frame animation loop - frames get animated by being placed, as necessary, into the + // main sprite slot array + while ((uint)_oldFrameEntry < _frameEntries.size()) { + if (_frameEntries[_oldFrameEntry].frameNumber > _currentFrame) + break; + else if (_frameEntries[_oldFrameEntry].frameNumber == _currentFrame) { + // Found the correct frame + int spriteSlotIndex = 0; + int index = 0; + + for (;;) { + if ((spriteSlotIndex == 0) && (index < spriteSlotsMax)) { + int seqIndex = _frameEntries[_oldFrameEntry].seqIndex - _view->_spriteSlots[index].seqIndex; + if (seqIndex == 0x80) { + if (_view->_spriteSlots[index] == _frameEntries[_oldFrameEntry].spriteSlot) { + _view->_spriteSlots[index].spriteType = SPRITE_ZERO; + spriteSlotIndex = -1; + } + } + ++index; + continue; + } + + if (spriteSlotIndex == 0) { + int slotIndex = _view->_spriteSlots.getIndex(); + MadsSpriteSlot &slot = _view->_spriteSlots[slotIndex]; + slot.copy(_frameEntries[_oldFrameEntry].spriteSlot); + slot.seqIndex = _frameEntries[_oldFrameEntry].seqIndex + 0x80; + + SpriteAsset &spriteSet = _view->_spriteSlots.getSprite( + _view->_spriteSlots[slotIndex].spriteListIndex); + slot.spriteType = spriteSet.isBackground() ? BACKGROUND_SPRITE : FOREGROUND_SPRITE; + } + break; + } + } + + ++_oldFrameEntry; + } + + // Handle the display of any messages + for (uint idx = 0; idx < _messages.size(); ++idx) { + if (_messages[idx].kernelMsgIndex >= 0) { + // Handle currently active message + if ((_currentFrame < _messages[idx].startFrame) || (_currentFrame > _messages[idx].endFrame)) { + _view->_kernelMessages.remove(_messages[idx].kernelMsgIndex); + _messages[idx].kernelMsgIndex = -1; + --_messageCtr; + } + } else if ((_currentFrame >= _messages[idx].startFrame) && (_currentFrame <= _messages[idx].endFrame)) { + // Start displaying the message + AnimMessage &me = _messages[idx]; + + // The colour index to use is dependant on how many messages are currently on-screen + uint8 colIndex; + switch (_messageCtr) { + case 1: + colIndex = 252; + break; + case 2: + colIndex = 16; + break; + default: + colIndex = 250; + break; + } + + _vm->_palette->setEntry(colIndex, me.rgb1.r, me.rgb1.g, me.rgb1.b); + _vm->_palette->setEntry(colIndex + 1, me.rgb2.r, me.rgb2.g, me.rgb2.b); + + // Add a kernel message to display the given text + me.kernelMsgIndex = _view->_kernelMessages.add(me.pos, colIndex * 101, 0, 0, INDEFINITE_TIMEOUT, me.msg); + ++_messageCtr; + } + } + + // Move to the next frame + _currentFrame++; + if (_currentFrame >= (int)_miscEntries.size()) { + // Animation is complete + if (_abortTimers != 0) { + _view->_abortTimers = _abortTimers; + _view->_abortTimersMode = _abortMode; + + if (_abortMode != ABORTMODE_1) { + // Copy the noun list + for (int i = 0; i < 3; ++i) + _madsVm->scene()->actionNouns[i] = _actionNouns[i]; + } + } + } - return _curFrame >= _frameCount; + int frameNum = MIN(_currentFrame, (int)_miscEntries.size() - 1); + _nextFrameTimer = _madsVm->_currentTimer + _miscEntries[frameNum].numTicks; } -void Animation::stop() { - _playing = false; +void MadsAnimation::setCurrentFrame(int frameNumber) { + _currentFrame = frameNumber; + _oldFrameEntry = 0; + _freeFlag = false; +} + +void MadsAnimation::load1(int frameNumber) { + if (_skipLoad) + return; + + Common::Point pt; + int listIndex = _spriteListIndexes[_spriteListIndex]; + SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(listIndex); + + if (_unkIndex < 0) { + M4Surface *frame = spriteSet.getFrame(0); + pt.x = frame->bounds().left; + pt.y = frame->bounds().top; + } else { + pt.x = _unkList[_unkIndex].x; + pt.y = _unkList[_unkIndex].y; + _unkIndex = 1 - _unkIndex; + } + + if (proc1(spriteSet, pt, frameNumber)) + error("proc1 failure"); +} + +bool MadsAnimation::proc1(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber) { + return 0; +} - for (int i = 0; i < _seriesCount; i++) { - // TODO: cleanup - //delete _spriteSeries[i]; - //_spriteSeries[i] = NULL; +void MadsAnimation::loadInterface(M4Surface *&interfaceSurface, M4Surface *&depthSurface) { + if (_animMode <= 2) { + MadsSceneResources sceneResources; + sceneResources.load(_roomNumber, _interfaceFile.c_str(), 0, depthSurface, interfaceSurface); + + } else if (_animMode == 4) { + // Load a scene interface + interfaceSurface->madsLoadInterface(_interfaceFile); + } else { + // This mode allocates two large surfaces for the animation + // TODO: Are these ever properly freed? +error("Anim mode %d - need to check free logic", _animMode); + assert(!interfaceSurface); + assert(!depthSurface); + depthSurface = new M4Surface(MADS_SURFACE_WIDTH, MADS_SCREEN_HEIGHT); + interfaceSurface = new M4Surface(MADS_SURFACE_WIDTH, MADS_SCREEN_HEIGHT); + depthSurface->clear(); + interfaceSurface->clear(); } } |