scummvm/engines/sherlock/animation.cpp

325 lines
9.3 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "sherlock/animation.h"
#include "sherlock/sherlock.h"
#include "sherlock/scalpel/scalpel_screen.h"
#include "sherlock/scalpel/3do/scalpel_3do_screen.h"
#include "common/algorithm.h"
namespace Sherlock {
static const int NO_FRAMES = FRAMES_END;
Animation::Animation(SherlockEngine *vm) : _vm(vm) {
}
bool Animation::play(const Common::String &filename, bool intro, int minDelay, int fade,
bool setPalette, int speed) {
Events &events = *_vm->_events;
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
int soundNumber = 0;
// Check for any any sound frames for the given animation
const int *soundFrames = checkForSoundFrames(filename, intro);
// Add on the VDX extension
Common::String vdxName = filename + ".vdx";
// Load the animation
Common::SeekableReadStream *stream;
if (!_gfxLibraryFilename.empty())
stream = _vm->_res->load(vdxName, _gfxLibraryFilename);
else if (_vm->_useEpilogue2)
stream = _vm->_res->load(vdxName, "epilog2.lib");
else
stream = _vm->_res->load(vdxName, "epilogue.lib");
// Load initial image
Common::String vdaName = filename + ".vda";
ImageFile images(vdaName, true, true);
events.wait(minDelay);
if (fade != 0 && fade != 255)
screen.fadeToBlack();
if (setPalette) {
if (fade != 255)
screen.setPalette(images._palette);
}
int frameNumber = 0;
Common::Point pt;
bool skipped = false;
while (!_vm->shouldQuit()) {
// Get the next sprite to display
int imageFrame = stream->readSint16LE();
if (imageFrame == -2) {
// End of animation reached
break;
} else if (imageFrame != -1) {
// Read position from either animation stream or the sprite frame itself
if (imageFrame < 0) {
imageFrame += 32768;
pt.x = stream->readUint16LE();
pt.y = stream->readUint16LE();
} else {
pt = images[imageFrame]._offset;
}
// Draw the sprite. Note that we explicitly use the raw frame below, rather than the ImageFrame,
// since we don't want the offsets in the image file to be used, just the explicit position we specify
screen.SHtransBlitFrom(images[imageFrame]._frame, pt);
} else {
// At this point, either the sprites for the frame has been complete, or there weren't any sprites
// at all to draw for the frame
if (fade == 255) {
// Gradual fade in
if (screen.equalizePalette(images._palette) == 0)
fade = 0;
}
// Check if we've reached a frame with sound
if (frameNumber++ == *soundFrames) {
++soundNumber;
++soundFrames;
Common::String sampleFilename;
if (!intro) {
// regular animation, append 1-digit number
sampleFilename = Common::String::format("%s%01d", filename.c_str(), soundNumber);
} else {
// intro animation, append 2-digit number
sampleFilename = Common::String::format("%s%02d", filename.c_str(), soundNumber);
}
if (sound._voices)
sound.playSound(sampleFilename, WAIT_RETURN_IMMEDIATELY, 100, _soundLibraryFilename.c_str());
}
events.wait(speed * 3);
}
if (events.kbHit()) {
Common::KeyState keyState = events.getKey();
if (keyState.keycode == Common::KEYCODE_ESCAPE ||
keyState.keycode == Common::KEYCODE_SPACE) {
skipped = true;
break;
}
} else if (events._pressed) {
skipped = true;
break;
}
}
events.clearEvents();
sound.stopSound();
delete stream;
return !skipped && !_vm->shouldQuit();
}
bool Animation::play3DO(const Common::String &filename, bool intro, int minDelay, bool fadeFromGrey,
int speed) {
Events &events = *_vm->_events;
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
int soundNumber = 0;
bool fadeActive = false;
uint16 fadeLimitColor = 0;
uint16 fadeLimitColorRed = 0;
uint16 fadeLimitColorGreen = 0;
uint16 fadeLimitColorBlue = 0;
// Check for any any sound frames for the given animation
const int *soundFrames = checkForSoundFrames(filename, intro);
// Add the VDX extension
Common::String indexName = "prologue/" + filename + ".3dx";
// Load the animation
Common::File *indexStream = new Common::File();
if (!indexStream->open(indexName)) {
warning("unable to open %s\n", indexName.c_str());
return false;
}
// Load initial image
Common::String graphicsName = "prologue/" + filename + ".3da";
ImageFile3DO images(graphicsName, kImageFile3DOType_Animation);
events.wait(minDelay);
if (fadeFromGrey) {
fadeActive = true;
fadeLimitColor = 0xCE59; // RGB565: 25, 50, 25 -> "grey"
}
int frameNumber = 0;
Common::Point pt;
bool skipped = false;
while (!_vm->shouldQuit()) {
// Get the next sprite to display
int imageFrame = indexStream->readSint16BE();
if (imageFrame == -2) {
// End of animation reached
break;
} else if (imageFrame != -1) {
// Read position from either animation stream or the sprite frame itself
if (imageFrame < 0) {
imageFrame += 32768;
pt.x = indexStream->readUint16BE();
pt.y = indexStream->readUint16BE();
} else {
pt = images[imageFrame]._offset;
}
// Draw the sprite. Note that we explicitly use the raw frame below, rather than the ImageFrame,
// since we don't want the offsets in the image file to be used, just the explicit position we specify
screen._backBuffer1.SHtransBlitFrom(images[imageFrame]._frame, pt);
if (!fadeActive)
screen.slamArea(pt.x, pt.y, images[imageFrame]._frame.w, images[imageFrame]._frame.h);
} else {
// At this point, either the sprites for the frame has been complete, or there weren't any sprites
// at all to draw for the frame
if (fadeActive) {
// process fading
static_cast<Scalpel::Scalpel3DOScreen *>(_vm->_screen)->blitFrom3DOcolorLimit(fadeLimitColor);
if (!fadeLimitColor) {
// we are at the end, so stop
fadeActive = false;
} else {
// decrease limit color
fadeLimitColorRed = fadeLimitColor & 0xF800;
fadeLimitColorGreen = fadeLimitColor & 0x07E0;
fadeLimitColorBlue = fadeLimitColor & 0x001F;
if (fadeLimitColorRed)
fadeLimitColor -= 0x0800;
if (fadeLimitColorGreen)
fadeLimitColor -= 0x0040; // -2 because we are using RGB565, sherlock uses RGB555
if (fadeLimitColorBlue)
fadeLimitColor -= 0x0001;
}
}
// Check if we've reached a frame with sound
if (frameNumber++ == *soundFrames) {
++soundNumber;
++soundFrames;
Common::String sampleFilename;
// append 1-digit number
sampleFilename = Common::String::format("prologue/sounds/%s%01d", filename.c_str(), soundNumber);
if (sound._voices)
sound.playSound(sampleFilename, WAIT_RETURN_IMMEDIATELY, 100); // no sound library
}
events.wait(speed * 3);
}
if (events.kbHit()) {
Common::KeyState keyState = events.getKey();
if (keyState.keycode == Common::KEYCODE_ESCAPE ||
keyState.keycode == Common::KEYCODE_SPACE) {
skipped = true;
break;
}
} else if (events._pressed) {
skipped = true;
break;
}
}
events.clearEvents();
sound.stopSound();
delete indexStream;
return !skipped && !_vm->shouldQuit();
}
void Animation::setPrologueNames(const char *const *names, int count) {
for (int idx = 0; idx < count; ++idx, ++names) {
_prologueNames.push_back(*names);
}
}
void Animation::setPrologueFrames(const int *frames, int count, int maxFrames) {
_prologueFrames.resize(count);
for (int idx = 0; idx < count; ++idx, frames += maxFrames) {
_prologueFrames[idx].resize(maxFrames);
Common::copy(frames, frames + maxFrames, &_prologueFrames[idx][0]);
}
}
void Animation::setTitleNames(const char *const *names, int count) {
for (int idx = 0; idx < count; ++idx, ++names) {
_titleNames.push_back(*names);
}
}
void Animation::setTitleFrames(const int *frames, int count, int maxFrames) {
_titleFrames.resize(count);
for (int idx = 0; idx < count; ++idx, frames += maxFrames) {
_titleFrames[idx].resize(maxFrames);
Common::copy(frames, frames + maxFrames, &_titleFrames[idx][0]);
}
}
const int *Animation::checkForSoundFrames(const Common::String &filename, bool intro) {
const int *frames = &NO_FRAMES;
if (!intro) {
// regular animation is playing
for (uint idx = 0; idx < _prologueNames.size(); ++idx) {
if (filename.equalsIgnoreCase(_prologueNames[idx])) {
frames = &_prologueFrames[idx][0];
break;
}
}
} else {
// intro-animation is playing
for (uint idx = 0; idx < _titleNames.size(); ++idx) {
if (filename.equalsIgnoreCase(_titleNames[idx])) {
frames = &_titleFrames[idx][0];
break;
}
}
}
return frames;
}
} // End of namespace Sherlock