From 59d81c0aa1595fed6de169d89f44fbab8fcf068c Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sat, 30 May 2015 22:33:40 -0400 Subject: SHERLOCK: Added lightweight TsAGE object for handling logo animations --- engines/sherlock/scalpel/tsage/logo.cpp | 261 ++++++++++++++++++++++++++++++-- engines/sherlock/scalpel/tsage/logo.h | 98 +++++++++++- 2 files changed, 349 insertions(+), 10 deletions(-) (limited to 'engines/sherlock/scalpel/tsage') diff --git a/engines/sherlock/scalpel/tsage/logo.cpp b/engines/sherlock/scalpel/tsage/logo.cpp index fb8566229e..bf1c0f522a 100644 --- a/engines/sherlock/scalpel/tsage/logo.cpp +++ b/engines/sherlock/scalpel/tsage/logo.cpp @@ -28,6 +28,216 @@ namespace Sherlock { namespace Scalpel { namespace TsAGE { +TLib *Visage::_tLib; + +Visage::Visage() { + _resNum = -1; + _rlbNum = -1; + _stream = nullptr; +} + +void Visage::setVisage(int resNum, int rlbNum) { + if ((_resNum != resNum) || (_rlbNum != rlbNum)) { + _resNum = resNum; + _rlbNum = rlbNum; + delete _stream; + + // Games after Ringworld have an extra indirection via the visage index file + Common::SeekableReadStream *stream = _tLib->getResource(RES_VISAGE, resNum, 9999); + if (rlbNum == 0) + rlbNum = 1; + + // Check how many slots there are + uint16 count = stream->readUint16LE(); + if (rlbNum > count) + rlbNum = count; + + // Get the flags/rlbNum to use + stream->seek((rlbNum - 1) * 4 + 2); + uint32 v = stream->readUint32LE(); + int flags = v >> 30; + + if (flags & 3) { + rlbNum = (int)(v & 0xff); + } + assert((flags & 3) == 0); + delete stream; + + _stream = _tLib->getResource(RES_VISAGE, resNum, rlbNum); + } +} + +Visage::~Visage() { + delete _stream; +} + +void Visage::getFrame(ObjectSurface &s, int frameNum) { + _stream->seek(0); + int numFrames = _stream->readUint16LE(); + if (frameNum > numFrames) + frameNum = numFrames; + if (frameNum > 0) + --frameNum; + + _stream->seek(frameNum * 4 + 2); + int offset = _stream->readUint32LE(); + _stream->seek(offset); + + surfaceFromRes(s); +} + +int Visage::getFrameCount() const { + _stream->seek(0); + return _stream->readUint16LE(); +} + +bool Visage::isLoaded() const { + return _stream != nullptr; +} + +void Visage::surfaceFromRes(ObjectSurface &s) { + int frameWidth = _stream->readUint16LE(); + int frameHeight = _stream->readUint16LE(); + Common::Rect r(0, 0, frameWidth, frameHeight); + s.create(r.width(), r.height()); + + s._centroid.x = _stream->readSint16LE(); + s._centroid.y = _stream->readSint16LE(); + + _stream->skip(1); + byte flags = _stream->readByte(); + bool rleEncoded = (flags & 2) != 0; + + byte *destP = (byte *)s.getPixels(); + + if (!rleEncoded) { + _stream->read(destP, r.width() * r.height()); + } else { + Common::fill(destP, destP + (r.width() * r.height()), 0xff); + + for (int yp = 0; yp < r.height(); ++yp) { + int width = r.width(); + destP = (byte *)s.getBasePtr(0, yp); + + while (width > 0) { + uint8 controlVal = _stream->readByte(); + if ((controlVal & 0x80) == 0) { + // Copy specified number of bytes + _stream->read(destP, controlVal); + width -= controlVal; + destP += controlVal; + } else if ((controlVal & 0x40) == 0) { + // Skip a specified number of output pixels + destP += controlVal & 0x3f; + width -= controlVal & 0x3f; + } else { + // Copy a specified pixel a given number of times + controlVal &= 0x3f; + int pixel = _stream->readByte(); + + Common::fill(destP, destP + controlVal, pixel); + destP += controlVal; + width -= controlVal; + } + } + assert(width == 0); + } + } +} + +/*--------------------------------------------------------------------------*/ + +ScalpelEngine *Object::_vm; + +Object::Object() { + _vm = nullptr; + _animMode = ANIM_MODE_NONE; + _frame = 0; + _numFrames = 0; + _frameChange = 0; +} + +void Object::setVisage(int visage, int strip) { + _visage.setVisage(visage, strip); +} + +void Object::setAnimMode(AnimationMode mode) { + _animMode = mode; + _finished = false; + + _updateStartFrame = _vm->_events->getFrameCounter(); + if (_numFrames) + _updateStartFrame += 60 / _numFrames; + _frameChange = 1; +} + +void Object::update() { + Screen &screen = *_vm->_screen; + + if (_visage.isLoaded()) { + switch (_animMode) { + case ANIM_MODE_5: + if (_frame < _visage.getFrameCount()) + _frame = changeFrame(); + else + _finished = true; + break; + + default: + break; + } + + // Erase previous frame, if any + if (!_oldBounds.isEmpty()) + screen.blitFrom(screen._backBuffer1, Common::Point(_oldBounds.left, _oldBounds.top), _oldBounds); + + // Get the new frame + ObjectSurface s; + _visage.getFrame(s, _frame); + + // Display the frame + _oldBounds = Common::Rect(_position.x, _position.y, _position.x + s.w(), _position.y + s.h()); + _oldBounds.translate(-s._centroid.x, -s._centroid.y); + screen.transBlitFrom(s, Common::Point(_oldBounds.left, _oldBounds.top)); + } +} + +int Object::changeFrame() { + int frameNum = _frame; + uint32 currentFrame = _vm->_events->getFrameCounter(); + + if (_updateStartFrame <= currentFrame) { + if (_numFrames > 0) { + int v = 60 / _numFrames; + _updateStartFrame = currentFrame + v; + + frameNum = getNewFrame(); + } + } + + return frameNum; +} + +int Object::getNewFrame() { + int frameNum = _frame + _frameChange; + + if (_frameChange > 0) { + if (frameNum > _visage.getFrameCount()) { + frameNum = 1; + } + } else if (frameNum < 1) { + frameNum = _visage.getFrameCount(); + } + + return frameNum; +} + +bool Object::isAnimEnded() const { + return _finished; +} + +/*----------------------------------------------------------------*/ + bool Logo::show(ScalpelEngine *vm) { Events &events = *vm->_events; Logo *logo = new Logo(vm); @@ -39,6 +249,9 @@ bool Logo::show(ScalpelEngine *vm) { events.wait(2); events.setButtonState(); + for (int idx = 0; idx < 4; ++idx) + logo->_objects[idx].update(); + interrupted = vm->shouldQuit() || events.kbHit() || events._pressed; if (interrupted) { events.clearEvents(); @@ -50,10 +263,16 @@ bool Logo::show(ScalpelEngine *vm) { return !interrupted; } -Logo::Logo(ScalpelEngine *vm) : _vm(vm), _lib("sf3.rlb"), _surface(vm->_screen->w(), vm->_screen->h()) { - // Initialize frame counter +Logo::Logo(ScalpelEngine *vm) : _vm(vm), _lib("sf3.rlb") { + Object::_vm = vm; + Visage::_tLib = &_lib; + + // Initialize counter _counter = 0; + // Save a copy of the original palette + _vm->_screen->getPalette(_originalPalette); + // Set up the palettes Common::fill(&_palette1[0], &_palette1[PALETTE_SIZE], 0); Common::fill(&_palette1[0], &_palette2[PALETTE_SIZE], 0); @@ -67,18 +286,42 @@ Logo::Logo(ScalpelEngine *vm) : _vm(vm), _lib("sf3.rlb"), _surface(vm->_screen-> _lib.getPalette(_palette3, 14); } +Logo::~Logo() { + // Restore the original palette + _vm->_screen->setPalette(_originalPalette); +} + bool Logo::finished() const { - return false; + return _counter >= 4; } void Logo::nextFrame() { - switch (_counter++) { case 0: + // Load the background and fade it in loadBackground(); fade(_palette1); break; + case 1: + // First half of square, circle, and triangle arranging themselves + _objects[0].setVisage(16, 1); + _objects[0]._frame = 1; + _objects[0]._position = Common::Point(169, 107); + _objects[0]._numFrames = 7; + _objects[0].setAnimMode(ANIM_MODE_5); + break; + + case 2: + // Keep waiting until first animation ends + if (!_objects[0].isAnimEnded()) + --_counter; + break; + + case 3: + // Keep waiting until second animation of shapes ordering themselves ends + return; + default: break; } @@ -92,10 +335,10 @@ void Logo::loadBackground() { Common::SeekableReadStream *stream = _lib.getResource(RES_BITMAP, 10, idx); // Load it onto the surface - Common::Point pt((idx / 2) * (_surface.w() / 2), (idx % 2) * (_surface.h() / 2)); - for (int y = 0; y < (_surface.h() / 2); ++y, ++pt.y) { - byte *pDest = (byte *)_surface.getBasePtr(pt.x, pt.y); - stream->read(pDest, _surface.w() / 2); + Common::Point pt((idx / 2) * (SHERLOCK_SCREEN_WIDTH / 2), (idx % 2) * (SHERLOCK_SCREEN_HEIGHT / 2)); + for (int y = 0; y < (SHERLOCK_SCREEN_HEIGHT / 2); ++y, ++pt.y) { + byte *pDest = (byte *)screen._backBuffer1.getBasePtr(pt.x, pt.y); + stream->read(pDest, SHERLOCK_SCREEN_WIDTH / 2); } // _backgroundBounds = Rect(0, 0, READ_LE_UINT16(data), READ_LE_UINT16(data + 2)); @@ -108,7 +351,7 @@ void Logo::loadBackground() { screen.setPalette(palette); // Copy the surface to the screen - screen.blitFrom(_surface); + screen.blitFrom(screen._backBuffer1); } void Logo::fade(const byte palette[PALETTE_SIZE]) { diff --git a/engines/sherlock/scalpel/tsage/logo.h b/engines/sherlock/scalpel/tsage/logo.h index 4adbe95ff4..0b1f0a0500 100644 --- a/engines/sherlock/scalpel/tsage/logo.h +++ b/engines/sherlock/scalpel/tsage/logo.h @@ -41,17 +41,113 @@ class ScalpelEngine; namespace TsAGE { +enum AnimationMode { ANIM_MODE_NONE = 0, ANIM_MODE_4 = 4, ANIM_MODE_5 = 5 }; + +class ObjectSurface : public Surface { +public: + Common::Point _centroid; +public: + ObjectSurface() : Surface() {} + virtual ~ObjectSurface() {} +}; + +class Visage { +private: + Common::SeekableReadStream *_stream; + + /** + * Translates a raw image resource into a graphics surface + */ + void surfaceFromRes(ObjectSurface &s); +public: + static TLib *_tLib; + int _resNum; + int _rlbNum; +public: + Visage(); + ~Visage(); + + /** + * Set the visage number + */ + void setVisage(int resNum, int rlbNum = 9999); + + /** + * Get a frame from the visage + */ + void getFrame(ObjectSurface &s, int frameNum); + + /** + * Return the number of frames + */ + int getFrameCount() const; + + /** + * Returns whether the visage is loaded + */ + bool isLoaded() const; +}; + +class Object { +private: + Visage _visage; + uint32 _updateStartFrame; + int _animMode; + bool _finished; + + /** + * Return the next frame when the object is animating + */ + int changeFrame(); + + /** + * Gets the next frame in the sequence + */ + int getNewFrame(); +public: + static ScalpelEngine *_vm; + Common::Point _position; + Common::Rect _oldBounds; + int _frame; + int _numFrames; + int _frameChange; +public: + Object(); + + /** + * Load the data for the object + */ + void setVisage(int visage, int strip); + + /** + * Sets the animation mode + */ + void setAnimMode(AnimationMode mode); + + /** + * Returns true if an animation is ended + */ + bool isAnimEnded() const; + + /** + * Update the frame + */ + void update(); +}; + class Logo { private: ScalpelEngine *_vm; TLib _lib; - Surface _surface; int _counter; + byte _originalPalette[PALETTE_SIZE]; byte _palette1[PALETTE_SIZE]; byte _palette2[PALETTE_SIZE]; byte _palette3[PALETTE_SIZE]; + Object _objects[4]; Logo(ScalpelEngine *vm); + ~Logo(); void nextFrame(); -- cgit v1.2.3