aboutsummaryrefslogtreecommitdiff
path: root/engines/sherlock
diff options
context:
space:
mode:
authorPaul Gilbert2015-05-30 22:33:40 -0400
committerPaul Gilbert2015-05-30 22:33:40 -0400
commit59d81c0aa1595fed6de169d89f44fbab8fcf068c (patch)
tree8a7d887b299394c4a42efe2d1ab651680deacd10 /engines/sherlock
parent6796444f0861d0a689a0076cb9d7b43ed976ca74 (diff)
downloadscummvm-rg350-59d81c0aa1595fed6de169d89f44fbab8fcf068c.tar.gz
scummvm-rg350-59d81c0aa1595fed6de169d89f44fbab8fcf068c.tar.bz2
scummvm-rg350-59d81c0aa1595fed6de169d89f44fbab8fcf068c.zip
SHERLOCK: Added lightweight TsAGE object for handling logo animations
Diffstat (limited to 'engines/sherlock')
-rw-r--r--engines/sherlock/scalpel/tsage/logo.cpp261
-rw-r--r--engines/sherlock/scalpel/tsage/logo.h98
2 files changed, 349 insertions, 10 deletions
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();