/* 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 "common/str.h" #include "common/stream.h" #include "gob/gob.h" #include "gob/dataio.h" #include "gob/draw.h" #include "gob/decfile.h" #include "gob/anifile.h" #include "gob/aniobject.h" #include "gob/pregob/seqfile.h" namespace Gob { SEQFile::SEQFile(GobEngine *vm, const Common::String &fileName) : _vm(vm) { for (uint i = 0; i < kObjectCount; i++) _objects[i].object = 0; Common::SeekableReadStream *seq = _vm->_dataIO->getFile(Util::setExtension(fileName, ".SEQ")); if (!seq) { warning("SEQFile::SEQFile(): No such file \"%s\"", fileName.c_str()); return; } load(*seq); delete seq; } SEQFile::~SEQFile() { for (uint i = 0; i < kObjectCount; i++) delete _objects[i].object; for (Backgrounds::iterator b = _backgrounds.begin(); b != _backgrounds.end(); ++b) delete *b; for (Animations::iterator a = _animations.begin(); a != _animations.end(); ++a) delete *a; } void SEQFile::load(Common::SeekableReadStream &seq) { const uint16 decCount = (uint16)seq.readByte() + 1; const uint16 aniCount = (uint16)seq.readByte() + 1; // Load backgrounds _backgrounds.reserve(decCount); for (uint i = 0; i < decCount; i++) { const Common::String dec = Util::readString(seq, 13); if (!_vm->_dataIO->hasFile(dec)) { warning("SEQFile::load(): No such background \"%s\"", dec.c_str()); return; } _backgrounds.push_back(new DECFile(_vm, dec, 320, 200)); } // Load animations _animations.reserve(aniCount); for (uint i = 0; i < aniCount; i++) { const Common::String ani = Util::readString(seq, 13); if (!_vm->_dataIO->hasFile(ani)) { warning("SEQFile::load(): No such animation \"%s\"", ani.c_str()); return; } _animations.push_back(new ANIFile(_vm, ani)); } _frameRate = seq.readUint16LE(); // Load background change keys const uint16 bgKeyCount = seq.readUint16LE(); _bgKeys.resize(bgKeyCount); for (uint16 i = 0; i < bgKeyCount; i++) { const uint16 frame = seq.readUint16LE(); const uint16 index = seq.readUint16LE(); _bgKeys[i].frame = frame; _bgKeys[i].background = index < _backgrounds.size() ? _backgrounds[index] : 0; } // Load animation keys for all 4 objects for (uint i = 0; i < kObjectCount; i++) { const uint16 animKeyCount = seq.readUint16LE(); _animKeys.reserve(_animKeys.size() + animKeyCount); for (uint16 j = 0; j < animKeyCount; j++) { _animKeys.push_back(AnimationKey()); const uint16 frame = seq.readUint16LE(); const uint16 index = seq.readUint16LE(); uint16 animation; const ANIFile *ani = findANI(index, animation); _animKeys.back().object = i; _animKeys.back().frame = frame; _animKeys.back().ani = ani; _animKeys.back().animation = animation; _animKeys.back().x = seq.readSint16LE(); _animKeys.back().y = seq.readSint16LE(); _animKeys.back().order = seq.readSint16LE(); } } } const ANIFile *SEQFile::findANI(uint16 index, uint16 &animation) { animation = 0xFFFF; // 0xFFFF = remove animation if (index == 0xFFFF) return 0; for (Animations::const_iterator a = _animations.begin(); a != _animations.end(); ++a) { if (index < (*a)->getAnimationCount()) { animation = index; return *a; } index -= (*a)->getAnimationCount(); } return 0; } void SEQFile::play(bool abortable, uint16 endFrame, uint16 frameRate) { if (_bgKeys.empty() && _animKeys.empty()) // Nothing to do return; // Init _frame = 0; _abortPlay = false; for (uint i = 0; i < kObjectCount; i++) { delete _objects[i].object; _objects[i].object = 0; _objects[i].order = 0; } for (Loops::iterator l = _loops.begin(); l != _loops.end(); ++l) l->currentLoop = 0; // Set the frame rate int16 frameRateBack = _vm->_util->getFrameRate(); if (frameRate == 0) frameRate = _frameRate; _vm->_util->setFrameRate(frameRate); _abortable = abortable; while (!_vm->shouldQuit() && !_abortPlay) { // Handle the frame contents playFrame(); // Handle extra frame events handleFrameEvent(); // Wait for the frame to end _vm->_draw->blitInvalidated(); _vm->_util->waitEndFrame(); // Handle input _vm->_util->processInput(); int16 key = _vm->_util->checkKey(); int16 mouseX, mouseY; MouseButtons mouseButtons; _vm->_util->getMouseState(&mouseX, &mouseY, &mouseButtons); _vm->_util->forceMouseUp(); handleInput(key, mouseX, mouseY, mouseButtons); // Loop bool looped = false; for (Loops::iterator l = _loops.begin(); l != _loops.end(); ++l) { if ((l->endFrame == _frame) && (l->currentLoop < l->loopCount)) { _frame = l->startFrame; l->currentLoop++; looped = true; } } // If we didn't loop, advance the frame and look if we should end here if (!looped) { _frame++; if (_frame >= endFrame) break; } } // Restore the frame rate _vm->_util->setFrameRate(frameRateBack); } void SEQFile::playFrame() { // Remove the current animation frames clearAnims(); // Handle background keys, directly updating the background for (BackgroundKeys::const_iterator b = _bgKeys.begin(); b != _bgKeys.end(); ++b) { if (!b->background || (b->frame != _frame)) continue; b->background->draw(*_vm->_draw->_backSurface); _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199); } // Handle the animation keys, updating the objects for (AnimationKeys::const_iterator a = _animKeys.begin(); a != _animKeys.end(); ++a) { if (a->frame != _frame) continue; Object &object = _objects[a->object]; delete object.object; object.object = 0; // No valid animation => remove if ((a->animation == 0xFFFF) || !a->ani) continue; // Change the animation object.object = new ANIObject(*a->ani); object.object->setAnimation(a->animation); object.object->setPosition(a->x, a->y); object.object->setVisible(true); object.object->setPause(false); object.order = a->order; } // Draw the animations drawAnims(); } // NOTE: This is really not at all efficient. However, since there's only a // small number of objects, it should matter. We really do need a stable // sort, though, so Common::sort() is out. SEQFile::Objects SEQFile::getOrderedObjects() { int16 minOrder = (int16)0x7FFF; int16 maxOrder = (int16)0x8000; Objects objects; // Find the span of order values for (uint i = 0; i < kObjectCount; i++) { if (!_objects[i].object) continue; minOrder = MIN(minOrder, _objects[i].order); maxOrder = MAX(maxOrder, _objects[i].order); } // Stably sort the objects by order value for (int16 o = minOrder; o <= maxOrder; o++) for (uint i = 0; i < kObjectCount; i++) if (_objects[i].object && (_objects[i].order == o)) objects.push_back(_objects[i]); return objects; } void SEQFile::clearAnims() { Objects objects = getOrderedObjects(); // Remove the animation frames, in reverse drawing order for (Objects::iterator o = objects.reverse_begin(); o != objects.end(); --o) { int16 left, top, right, bottom; if (o->object->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); } } void SEQFile::drawAnims() { Objects objects = getOrderedObjects(); // Draw the animation frames and advance the animation for (Objects::iterator o = objects.begin(); o != objects.end(); ++o) { int16 left, top, right, bottom; if (o->object->draw(*_vm->_draw->_backSurface, left, top, right, bottom)) _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); o->object->advance(); } } uint16 SEQFile::getFrame() const { return _frame; } void SEQFile::seekFrame(uint16 frame) { _frame = frame; } uint SEQFile::addLoop(uint16 startFrame, uint16 endFrame, uint16 loopCount) { _loops.resize(_loops.size() + 1); _loops.back().startFrame = startFrame; _loops.back().endFrame = endFrame; _loops.back().loopCount = loopCount; _loops.back().currentLoop = 0; _loops.back().empty = false; return _loops.size() - 1; } void SEQFile::skipLoop(uint loopID) { if (loopID >= _loops.size()) return; _loops[loopID].currentLoop = 0xFFFF; } void SEQFile::delLoop(uint loopID) { if (loopID >= _loops.size()) return; _loops[loopID].empty = true; cleanLoops(); } void SEQFile::cleanLoops() { while (!_loops.empty() && _loops.back().empty) _loops.pop_back(); } void SEQFile::abortPlay() { _abortPlay = true; } void SEQFile::handleFrameEvent() { } void SEQFile::handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons) { if (_abortable && ((key != 0) || (mouseButtons != kMouseButtonsNone))) abortPlay(); } } // End of namespace Gob