[Scummvm-git-logs] scummvm master -> 32fb690aae8d7beaa2440065f1029e28c89a137a
bluegr
noreply at scummvm.org
Sun Jan 23 22:24:50 UTC 2022
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
32fb690aae TRECISION: Add support for audio in Amiga video
Commit: 32fb690aae8d7beaa2440065f1029e28c89a137a
https://github.com/scummvm/scummvm/commit/32fb690aae8d7beaa2440065f1029e28c89a137a
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2022-01-24T00:24:28+02:00
Commit Message:
TRECISION: Add support for audio in Amiga video
The audio is in raw format, like the rest of the sounds in the Amiga
port.
Overall:
- A new class has been created, NightlongVideoDecoder, which
encapsulates the PC and Amiga video playing functionality
- The AnimManager class has been moved into animmanager.*
- The AnimType class has been moved from anim.* to animtype.*
Changed paths:
A engines/trecision/animmanager.cpp
A engines/trecision/animmanager.h
A engines/trecision/animtype.cpp
A engines/trecision/animtype.h
R engines/trecision/anim.cpp
R engines/trecision/anim.h
engines/trecision/dialog.cpp
engines/trecision/graphics.cpp
engines/trecision/inventory.cpp
engines/trecision/logic.cpp
engines/trecision/module.mk
engines/trecision/pathfinding3d.cpp
engines/trecision/renderer3d.cpp
engines/trecision/resource.cpp
engines/trecision/saveload.cpp
engines/trecision/script.cpp
engines/trecision/text.cpp
engines/trecision/trecision.cpp
engines/trecision/video.cpp
engines/trecision/video.h
diff --git a/engines/trecision/animmanager.cpp b/engines/trecision/animmanager.cpp
new file mode 100644
index 00000000000..6007d492b6e
--- /dev/null
+++ b/engines/trecision/animmanager.cpp
@@ -0,0 +1,548 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/config-manager.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "common/scummsys.h"
+#include "common/system.h"
+
+#include "audio/decoders/raw.h"
+
+#include "trecision/actor.h"
+#include "trecision/animmanager.h"
+#include "trecision/animtype.h"
+#include "trecision/defines.h"
+#include "trecision/dialog.h"
+#include "trecision/graphics.h"
+#include "trecision/sound.h"
+#include "trecision/text.h"
+#include "trecision/trecision.h"
+#include "trecision/video.h"
+
+namespace Trecision {
+
+AnimManager::AnimManager(TrecisionEngine *vm) : _vm(vm) {
+ for (int i = 0; i < MAXACTIVEANIM; ++i) {
+ _animations[i] = nullptr;
+ _playingAnims[i] = 0;
+ }
+
+ for (int i = 0; i < MAXANIM; ++i) {
+ _animTab[i]._flag = 0;
+ _animTab[i]._name[0] = '\0';
+ }
+
+ _curCD = 1;
+ swapCD(_curCD);
+
+ _bgAnimRestarted = false;
+}
+
+AnimManager::~AnimManager() {
+ for (int i = 0; i < MAXACTIVEANIM; ++i) {
+ delete _animations[i];
+ _animations[i] = nullptr;
+ _animFile[i].close();
+ }
+}
+
+void AnimManager::playMovie(const Common::String &filename, int startFrame, int endFrame, bool singleChoice) {
+ NightlongVideoDecoder *smkDecoder = new NightlongVideoDecoder(_vm->isAmiga());
+
+ if (!smkDecoder->loadFile(filename)) {
+ warning("playMovie: File %s not found", filename.c_str());
+ delete smkDecoder;
+ _vm->_dialogMgr->afterChoice();
+ return;
+ }
+
+ Common::Event event;
+ bool skipVideo = false;
+ uint16 x = (g_system->getWidth() - smkDecoder->getWidth()) / 2;
+ uint16 y = (g_system->getHeight() - smkDecoder->getHeight()) / 2;
+ _vm->_drawText._text.clear();
+
+ smkDecoder->start();
+
+ // WORKAROUND: If the video has a single choice, and it starts from
+ // the beginning, ignore the calculated end frame and play all of it
+ if (singleChoice && startFrame < 10 && endFrame < (int)smkDecoder->getFrameCount() - 1)
+ endFrame = smkDecoder->getFrameCount() - 1;
+
+ setVideoRange(smkDecoder, startFrame, endFrame);
+
+ while (!_vm->shouldQuit() && startFrame != endFrame && !smkDecoder->endOfVideo() && !skipVideo) {
+ if (smkDecoder->needsUpdate()) {
+ drawFrame(smkDecoder, x, y, true);
+ }
+
+ while (_vm->getEventManager()->pollEvent(event)) {
+ if (event.type == Common::EVENT_KEYUP && event.kbd.keycode == Common::KEYCODE_ESCAPE)
+ skipVideo = true;
+ }
+
+ g_system->delayMillis(10);
+ }
+
+ delete smkDecoder;
+
+ _vm->_mouseLeftBtn = _vm->_mouseRightBtn = false;
+ _vm->freeKey();
+ _vm->_dialogMgr->afterChoice();
+}
+
+void AnimManager::setVideoRange(NightlongVideoDecoder *smkDecoder, int &startFrame, int &endFrame) {
+ // Trecision starts at 1 but ScummVM starts at 0
+ startFrame = CLIP<int32>(startFrame - 1, 0, smkDecoder->getFrameCount() - 1);
+ endFrame = CLIP<int32>(endFrame - 1, 0, smkDecoder->getFrameCount() - 1);
+
+ // If choices are attached
+ if (startFrame > 0 && startFrame > smkDecoder->getCurFrame()) {
+ smkDecoder->forceSeekToFrame(startFrame - 1);
+ }
+ smkDecoder->setEndFrame(endFrame);
+}
+
+void AnimManager::drawFrame(NightlongVideoDecoder *smkDecoder, uint16 x, uint16 y, bool updateScreen) {
+ const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
+ if (frame) {
+ Graphics::Surface *frame16 = frame->convertTo(g_system->getScreenFormat(), smkDecoder->getPalette());
+ drawFrameSubtitles(frame16, smkDecoder->getCurFrame());
+ g_system->copyRectToScreen(frame16->getPixels(), frame16->pitch, x, y, frame16->w, frame16->h);
+ frame16->free();
+ delete frame16;
+
+ if (updateScreen)
+ _vm->_system->updateScreen();
+ }
+}
+
+void AnimManager::drawFrameSubtitles(Graphics::Surface *surface, int frameNum) {
+ if (!ConfMan.getBool("subtitles"))
+ return;
+
+ _vm->_dialogMgr->dialogHandler(frameNum);
+ if (_vm->_drawText._text.empty())
+ return;
+
+ // Subtitles can be placed in different coordinates in the video,
+ // which are set inside dialogHandler(), but are then reset to
+ // fixed coordinates
+ _vm->_drawText._rect.left = 20;
+ _vm->_drawText._rect.top = 380 - TOP;
+ _vm->_drawText._rect.setWidth(MAXX - 40);
+ _vm->_drawText._rect.setHeight(_vm->_drawText.calcHeight(_vm));
+ _vm->_drawText._subtitleRect = Common::Rect(MAXX, MAXY);
+ _vm->_drawText.draw(_vm, false, surface);
+}
+
+void AnimManager::openSmkAnim(int slot, const Common::String &name) {
+ for (int i = 0; i < 3; i++) {
+ // Open the animation, or swap the 3 CDs to find it
+ if (_animFile[slot].hasFile(name)) {
+ openSmk(slot, _animFile[slot].createReadStreamForMember(name));
+ return;
+ }
+
+ _curCD = _curCD < 3 ? _curCD + 1 : 1;
+ swapCD(_curCD);
+ }
+
+ error("openSmkAnim(): File %s not found", name.c_str());
+}
+
+void AnimManager::openSmk(int slot, Common::SeekableReadStream *stream) {
+ _animations[slot] = new NightlongVideoDecoder(_vm->isAmiga());
+
+ if (!_animations[slot]->loadStream(stream)) {
+ warning("Invalid SMK file");
+ closeSmk(slot);
+ } else {
+ _animations[slot]->start();
+ }
+}
+
+void AnimManager::closeSmk(int slot) {
+ delete _animations[slot];
+ _animations[slot] = nullptr;
+}
+
+void AnimManager::smkGoto(int slot, int frame) {
+ if (_animations[slot] == nullptr)
+ return;
+
+ _animations[slot]->forceSeekToFrame(frame);
+}
+
+void AnimManager::smkToggleTrackAudio(int slot, int track, bool on) {
+ if (_animations[slot] == nullptr)
+ return;
+
+ _animations[slot]->muteTrack(track, !on);
+}
+
+void AnimManager::smkToggleAudio(int slot, bool on) {
+ if (_animations[slot] == nullptr)
+ return;
+
+ _animations[slot]->setMute(!on);
+}
+
+int16 AnimManager::smkCurFrame(int slot) {
+ if (_animations[slot] == nullptr)
+ return -1;
+
+ return _animations[slot]->getCurFrame();
+}
+
+void AnimManager::startSmkAnim(uint16 animation) {
+ int slot;
+ uint16 animFlag = _animTab[animation]._flag;
+
+ // choose the buffer to use
+ if (animFlag & SMKANIM_BKG)
+ slot = kSmackerBackground;
+ else if (animFlag & SMKANIM_ICON)
+ slot = kSmackerIcon;
+ else
+ slot = kSmackerAction;
+
+ smkStop(slot);
+
+ _playingAnims[slot] = animation;
+
+ // choose how to open
+ if (slot == kSmackerBackground) {
+ openSmkAnim(kSmackerBackground, _animTab[animation]._name);
+ _bgAnimRestarted = false;
+
+ toggleMuteBgAnim(animation);
+ } else if (slot == kSmackerIcon) {
+ openSmkAnim(kSmackerIcon, _animTab[animation]._name);
+ } else {
+ uint32 st = _vm->readTime();
+ openSmkAnim(kSmackerAction, _animTab[animation]._name);
+ _vm->_nextRefresh += _vm->readTime() - st; // fixup opening time
+ }
+}
+
+void AnimManager::toggleMuteBgAnim(uint16 animation) {
+ const bool area1Shown = _animTab[animation].isAnimAreaShown(1);
+ const bool area2Shown = _animTab[animation].isAnimAreaShown(2);
+ const bool area4Shown = _animTab[animation].isAnimAreaShown(4);
+ NightlongVideoDecoder *decoder = _animations[kSmackerBackground];
+ if (decoder == nullptr)
+ return;
+
+ // Turns off when not needed
+ if (animation == aBKG11 && !area1Shown)
+ decoder->muteTrack(1, true);
+ else if (animation == aBKG14 && !area1Shown)
+ decoder->muteTrack(1, true);
+ else if (animation == aBKG1C && _vm->_obj[oFAX17].isFlagExtra()) {
+ _animTab[animation].toggleAnimArea(1, false);
+ decoder->muteTrack(1, true);
+ } else if (animation == aBKG1D && !area1Shown)
+ decoder->muteTrack(1, true);
+ else if (animation == aBKG22 && !area1Shown)
+ decoder->muteTrack(1, true);
+ else if (animation == aBKG48 && !area1Shown)
+ decoder->muteTrack(1, true);
+ else if (animation == aBKG4P && !area1Shown)
+ decoder->muteTrack(1, true);
+ else if (animation == aBKG28 && area4Shown)
+ decoder->muteTrack(1, true);
+ else if (animation == aBKG37 && !_vm->_room[_vm->_curRoom].hasExtra())
+ decoder->muteTrack(1, true);
+ else if (animation == aBKG2E && area2Shown)
+ decoder->muteTrack(2, true);
+ else if (animation == aBKG2G && _vm->_dialogMgr->isDialogFinished(556))
+ decoder->muteTrack(2, true);
+ else if (animation == aBKG34 && // If it's BKG 34 and
+ (_vm->_dialogMgr->isDialogFinished(616) || // if the FMV is already done or
+ _vm->isObjectVisible(oTUBOT34) || // if the whole tube is available or
+ _vm->isObjectVisible(oTUBOFT34) || // if the outside of the tube is available or
+ _vm->isObjectVisible(oVALVOLAC34))) // if the valve is closed
+ decoder->muteTrack(2, true);
+}
+
+void AnimManager::smkStop(uint16 slot) {
+ _playingAnims[slot] = 0;
+
+ closeSmk(slot);
+
+ _vm->_lightIcon = 0xFF;
+}
+
+void AnimManager::stopAllSmkAnims() {
+ for (int slot = 0; slot < MAXACTIVEANIM; slot++) {
+ if (_playingAnims[slot])
+ smkStop(slot);
+ }
+}
+
+void AnimManager::startFullMotion() {
+ stopAllSmkAnims();
+
+ _vm->_flagDialogActive = true;
+ _vm->_flagShowCharacter = false;
+
+ _vm->_textStatus = TEXT_OFF;
+ _vm->_inventoryStatus = INV_OFF;
+ _vm->_inventoryCounter = INVENTORY_HIDE;
+
+ _vm->_textMgr->clearTextStack();
+ _vm->_graphicsMgr->clearScreen();
+
+ _vm->_scheduler->resetQueues();
+ _vm->_actor->actorStop();
+ _vm->_graphicsMgr->hideCursor();
+}
+
+void AnimManager::stopFullMotion() {
+ const uint16 curDialog = _vm->_dialogMgr->getCurDialog();
+
+ _vm->_flagDialogActive = false;
+ _vm->_flagDialogMenuActive = false;
+ _vm->_flagSomeoneSpeaks = false;
+ _vm->_lightIcon = 0xFF;
+ _vm->_graphicsMgr->showCursor();
+
+ if (curDialog == dFCRED) {
+ _vm->quitGame();
+ return;
+ }
+
+ if (!((curDialog == dSHOPKEEPER1A) && _vm->_dialogMgr->getCurChoice() == 185)) {
+ if ((curDialog == dF582) || (curDialog == dFLOG) || (curDialog == dINTRO) || (curDialog == dF362) || (curDialog == dC381) || (curDialog == dF381) ||
+ (curDialog == dF491) || ((curDialog == dC581) && !_vm->_dialogMgr->isDialogFinished(886) && _vm->_dialogMgr->isDialogFinished(258)) ||
+ ((curDialog == dC5A1) && _vm->_room[kRoom5A].hasExtra()))
+ _vm->_flagShowCharacter = false;
+ else
+ _vm->redrawRoom();
+
+ if (curDialog == dF582)
+ _vm->_soundMgr->stopAllExceptMusic();
+ }
+}
+
+void AnimManager::refreshAnim(int box) {
+ for (int i = 0; i < MAXACTIVEANIM; i++) {
+ if (_playingAnims[i] != 0 && box == BOX_BACKGROUND && i != kSmackerAction) {
+ refreshSmkAnim(_playingAnims[i]);
+ }
+ }
+}
+
+void AnimManager::refreshSmkAnim(uint16 animation) {
+ if (animation == 0)
+ return;
+
+ if (_animTab[animation]._flag & SMKANIM_ICON) {
+ drawSmkIconFrame(_vm->_inventoryRefreshStartIcon, animation);
+ } else if (_animTab[animation]._flag & SMKANIM_BKG) {
+ drawSmkBackgroundFrame(animation);
+ handleEndOfVideo(animation, kSmackerBackground);
+ } else {
+ drawSmkActionFrame();
+ handleEndOfVideo(animation, kSmackerAction);
+ }
+
+ for (int32 i = 0; i < MAXAREA; i++) {
+ if (_animTab[animation].isAnimAreaShown(i + 1) && _animTab[animation]._lim[i].bottom != 0) {
+ _vm->_graphicsMgr->addDirtyRect(_animTab[animation]._lim[i], true);
+ }
+ }
+}
+
+void AnimManager::handleEndOfVideo(int animation, int slot) {
+ const bool isLoopingOrBackground = (_animTab[animation]._flag & SMKANIM_LOOP) || (_animTab[animation]._flag & SMKANIM_BKG);
+
+ if (_animations[slot] == nullptr) {
+ smkStop(slot);
+ return;
+ }
+ if (!_animations[slot]->endOfFrames())
+ return;
+
+ if (!isLoopingOrBackground) {
+ smkStop(slot);
+ _vm->_flagPaintCharacter = true;
+ } else {
+ _animations[slot]->rewind();
+ _vm->_animTypeMgr->init(animation, 0);
+ _bgAnimRestarted = true;
+ }
+}
+
+static bool rectsIntersect(Common::Rect r1, Common::Rect r2) {
+ return (r1.left <= r2.right) && (r1.right >= r2.left) && (r1.top <= r2.bottom) && (r1.bottom >= r2.top);
+}
+
+bool AnimManager::shouldShowAnim(int animation, Common::Rect curRect) {
+ for (int32 i = 0; i < MAXAREA; i++) {
+ const bool intersect = rectsIntersect(_animTab[animation]._lim[i], curRect);
+ if (intersect && !_animTab[animation].isAnimAreaShown(i + 1))
+ return false;
+ }
+
+ return true;
+}
+
+void AnimManager::drawSmkBackgroundFrame(int animation) {
+ NightlongVideoDecoder *smkDecoder = _animations[kSmackerBackground];
+ if (smkDecoder == nullptr)
+ return;
+ const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
+ if (!frame)
+ return;
+
+ const Common::Rect *lastRect = smkDecoder->getNextDirtyRect();
+ const byte *palette = smkDecoder->getPalette();
+
+ if (smkDecoder->getCurFrame() == 0 && shouldShowAnim(animation, *lastRect) && !_bgAnimRestarted) {
+ _vm->_graphicsMgr->blitToScreenBuffer(frame, 0, TOP, palette, true);
+ } else {
+ while (lastRect) {
+ if (smkDecoder->getCurFrame() > 0 && shouldShowAnim(animation, *lastRect)) {
+ Graphics::Surface anim = frame->getSubArea(*lastRect);
+ _vm->_graphicsMgr->blitToScreenBuffer(&anim, lastRect->left, lastRect->top + TOP, palette, true);
+ }
+
+ lastRect = smkDecoder->getNextDirtyRect();
+ }
+ }
+}
+
+void AnimManager::drawSmkIconFrame(uint16 startIcon, uint16 iconNum) {
+ NightlongVideoDecoder *smkDecoder = _animations[kSmackerIcon];
+ if (smkDecoder == nullptr)
+ return;
+
+ int stx = ICONMARGSX;
+ uint a;
+ for (a = 0; a < ICONSHOWN; ++a) {
+ if (a + startIcon >= _vm->_inventory.size())
+ break;
+
+ if (_vm->_inventory[a + startIcon] == iconNum - FIRST_INV_ITEM + 1) {
+ stx = a * ICONDX + ICONMARGSX;
+ break;
+ }
+ }
+
+ if (a == ICONSHOWN)
+ return;
+
+ const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
+ _vm->_graphicsMgr->copyToScreenBuffer(frame, stx, FIRSTLINE, smkDecoder->getPalette());
+
+ if (smkDecoder->endOfVideo())
+ smkDecoder->rewind();
+}
+
+void AnimManager::drawSmkActionFrame() {
+ NightlongVideoDecoder *smkDecoder = _animations[kSmackerAction];
+ if (smkDecoder == nullptr)
+ return;
+ const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
+ if (!frame)
+ return;
+
+ const byte *palette = smkDecoder->getPalette();
+
+ if (smkDecoder->getCurFrame() == 0) {
+ _animRect = *smkDecoder->getNextDirtyRect();
+ }
+
+ if (_animRect.width() > 0 && _animRect.height() > 0) {
+ Graphics::Surface anim = frame->getSubArea(_animRect);
+ _vm->_graphicsMgr->blitToScreenBuffer(&anim, _animRect.left, _animRect.top + TOP, palette, false);
+ _vm->_graphicsMgr->addDirtyRect(_animRect, true);
+ }
+}
+
+void AnimManager::swapCD(int cd) {
+ Common::String animFileName = Common::String::format("nlanim.cd%d", cd);
+ for (uint8 i = 0; i < MAXACTIVEANIM; ++i) {
+ _animFile[i].close();
+ _animFile[i].open(_vm, animFileName);
+ }
+}
+
+void AnimManager::syncGameStream(Common::Serializer &ser) {
+ for (int i = 0; i < MAXANIM; i++) {
+ SAnim *cur = &_animTab[i];
+ ser.syncBytes((byte *)cur->_name, 14);
+ ser.syncAsUint16LE(cur->_flag);
+ for (uint8 j = 0; j < MAXAREA; ++j) {
+ ser.syncAsUint16LE(cur->_lim[j].left);
+ ser.syncAsUint16LE(cur->_lim[j].top);
+ ser.syncAsUint16LE(cur->_lim[j].right);
+ ser.syncAsUint16LE(cur->_lim[j].bottom);
+ }
+ ser.syncAsByte(cur->_nbox);
+ ser.skip(1, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX);
+ for (uint8 j = 0; j < MAXATFRAME; ++j) {
+ ser.syncAsByte(cur->_atFrame[j]._type);
+ ser.syncAsByte(cur->_atFrame[j]._area);
+ ser.syncAsUint16LE(cur->_atFrame[j]._numFrame);
+ ser.syncAsUint16LE(cur->_atFrame[j]._index);
+ }
+ }
+
+ patchAnimTab();
+}
+
+void AnimManager::loadAnimTab(Common::SeekableReadStreamEndian *stream) {
+ for (uint16 i = 0; i < MAXANIM; ++i) {
+ stream->read(&_animTab[i]._name, 14);
+
+ _animTab[i]._flag = stream->readUint16();
+
+ for (uint8 j = 0; j < MAXAREA; ++j) {
+ _animTab[i]._lim[j].left = stream->readUint16();
+ _animTab[i]._lim[j].top = stream->readUint16();
+ _animTab[i]._lim[j].right = stream->readUint16();
+ _animTab[i]._lim[j].bottom = stream->readUint16();
+ }
+
+ _animTab[i]._nbox = stream->readByte();
+ stream->readByte(); // Padding
+
+ for (uint8 j = 0; j < MAXATFRAME; ++j) {
+ _animTab[i]._atFrame[j]._type = stream->readByte();
+ _animTab[i]._atFrame[j]._area = stream->readByte();
+ _animTab[i]._atFrame[j]._numFrame = stream->readUint16();
+ _animTab[i]._atFrame[j]._index = stream->readUint16();
+ }
+ }
+
+ patchAnimTab();
+}
+
+void AnimManager::patchAnimTab() {
+ _animTab[aBKG28]._lim[3].left = 308; // Patch the brazier animation rect in kRoom28 - bug #12628
+ _animTab[aBKG35]._lim[0].right = 200; // Patch the terrorist animation rect in kRoom35
+}
+
+} // End of namespace Trecision
diff --git a/engines/trecision/animmanager.h b/engines/trecision/animmanager.h
new file mode 100644
index 00000000000..d6bf04108b7
--- /dev/null
+++ b/engines/trecision/animmanager.h
@@ -0,0 +1,106 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef TRECISION_ANIMMANAGER_H
+#define TRECISION_ANIMMANAGER_H
+
+#include "trecision/fastfile.h"
+#include "trecision/struct.h"
+
+namespace Trecision {
+
+#define MAXANIM 750
+#define MAXACTIVEANIM 3
+
+// SMACKER ANIMATION FLAGS
+#define SMKANIM_BKG 1
+#define SMKANIM_ICON 2
+#define SMKANIM_LOOP 4
+#define SMKANIM_OLD 8
+#define SMKANIM_ON 16
+
+enum SmackerType {
+ kSmackerBackground = 0, // Scene background animations
+ kSmackerAction = 1, // Main character action animations
+ kSmackerIcon = 2 // Smacker inventory animations
+};
+
+class TrecisionEngine;
+class NightlongVideoDecoder;
+
+class AnimManager {
+public:
+ AnimManager(TrecisionEngine *vm);
+ ~AnimManager();
+
+private:
+ TrecisionEngine *_vm;
+
+ NightlongVideoDecoder *_animations[MAXACTIVEANIM];
+ uint16 _playingAnims[MAXACTIVEANIM];
+
+ FastFile _animFile[MAXACTIVEANIM]; // nlanim.cd1 / nlanim.cd2 / nlanim.cd3
+ int _curCD;
+ bool _bgAnimRestarted;
+
+ void openSmk(int slot, Common::SeekableReadStream *stream);
+ void openSmkAnim(int slot, const Common::String &name);
+ void toggleMuteBgAnim(uint16 animation);
+ void closeSmk(int slot);
+ void drawFrame(NightlongVideoDecoder *smkDecoder, uint16 x, uint16 y, bool updateScreen);
+ void drawFrameSubtitles(Graphics::Surface *surface, int frameNum);
+ void setVideoRange(NightlongVideoDecoder *smkDecoder, int &startFrame, int &endFrame);
+ void refreshSmkAnim(uint16 animation);
+ void handleEndOfVideo(int animation, int slot);
+ bool shouldShowAnim(int animation, Common::Rect curRect);
+
+ void drawSmkBackgroundFrame(int animation);
+ void drawSmkIconFrame(uint16 startIcon, uint16 iconNum);
+ void drawSmkActionFrame();
+ void swapCD(int cd);
+ void patchAnimTab();
+
+public:
+ Common::Rect _animRect;
+ SAnim _animTab[MAXANIM];
+
+ void smkGoto(int slot, int frame);
+ void smkToggleAudio(int slot, bool on);
+ void smkToggleTrackAudio(int slot, int track, bool on);
+ int16 smkCurFrame(int slot);
+ void smkStop(uint16 slot);
+ void refreshActionAnimation() { refreshSmkAnim(_playingAnims[kSmackerAction]); }
+ bool isActionActive() const { return _playingAnims[kSmackerAction] != 0; }
+ void playMovie(const Common::String &filename, int startFrame = 0, int endFrame = -1, bool singleChoice = false);
+ void startFullMotion();
+ void stopFullMotion();
+
+ void refreshAnim(int box);
+ void startSmkAnim(uint16 animation);
+ void stopAllSmkAnims();
+
+ void syncGameStream(Common::Serializer &ser);
+ void loadAnimTab(Common::SeekableReadStreamEndian *stream);
+};
+
+} // End of namespace Trecision
+#endif
+
diff --git a/engines/trecision/anim.cpp b/engines/trecision/animtype.cpp
similarity index 99%
rename from engines/trecision/anim.cpp
rename to engines/trecision/animtype.cpp
index c15a56d1e9e..3e5a094335f 100644
--- a/engines/trecision/anim.cpp
+++ b/engines/trecision/animtype.cpp
@@ -19,7 +19,8 @@
*
*/
-#include "trecision/anim.h"
+#include "trecision/animmanager.h"
+#include "trecision/animtype.h"
#include "trecision/dialog.h"
#include "trecision/logic.h"
#include "trecision/pathfinding3d.h"
diff --git a/engines/trecision/anim.h b/engines/trecision/animtype.h
similarity index 96%
rename from engines/trecision/anim.h
rename to engines/trecision/animtype.h
index 099e2518d89..f99ce70161b 100644
--- a/engines/trecision/anim.h
+++ b/engines/trecision/animtype.h
@@ -19,8 +19,8 @@
*
*/
-#ifndef TRECISION_ANIM_H
-#define TRECISION_ANIM_H
+#ifndef TRECISION_ANIMTYPE_H
+#define TRECISION_ANIMTYPE_H
#include "trecision/struct.h"
diff --git a/engines/trecision/dialog.cpp b/engines/trecision/dialog.cpp
index 3507c02b486..d548e9d062e 100644
--- a/engines/trecision/dialog.cpp
+++ b/engines/trecision/dialog.cpp
@@ -20,6 +20,7 @@
*/
#include "trecision/actor.h"
+#include "trecision/animmanager.h"
#include "trecision/defines.h"
#include "trecision/dialog.h"
#include "trecision/graphics.h"
diff --git a/engines/trecision/graphics.cpp b/engines/trecision/graphics.cpp
index 3a476223dad..2e454c3a2bf 100644
--- a/engines/trecision/graphics.cpp
+++ b/engines/trecision/graphics.cpp
@@ -27,7 +27,8 @@
#include "graphics/surface.h"
#include "trecision/actor.h"
-#include "trecision/anim.h"
+#include "trecision/animmanager.h"
+#include "trecision/animtype.h"
#include "trecision/defines.h"
#include "trecision/graphics.h"
#include "trecision/pathfinding3d.h"
diff --git a/engines/trecision/inventory.cpp b/engines/trecision/inventory.cpp
index e57123f24cd..e663950cd6b 100644
--- a/engines/trecision/inventory.cpp
+++ b/engines/trecision/inventory.cpp
@@ -20,6 +20,7 @@
*/
#include "trecision/actor.h"
+#include "trecision/animmanager.h"
#include "trecision/defines.h"
#include "trecision/graphics.h"
#include "trecision/logic.h"
diff --git a/engines/trecision/logic.cpp b/engines/trecision/logic.cpp
index 00ed0be7ee3..e2f87ae6971 100644
--- a/engines/trecision/logic.cpp
+++ b/engines/trecision/logic.cpp
@@ -22,6 +22,7 @@
#include "common/config-manager.h"
#include "trecision/actor.h"
+#include "trecision/animmanager.h"
#include "trecision/defines.h"
#include "trecision/dialog.h"
#include "trecision/graphics.h"
diff --git a/engines/trecision/module.mk b/engines/trecision/module.mk
index e149ef97277..95bccab4adf 100644
--- a/engines/trecision/module.mk
+++ b/engines/trecision/module.mk
@@ -3,7 +3,8 @@ MODULE := engines/trecision
MODULE_OBJS = \
console.o \
actor.o \
- anim.o \
+ animmanager.o \
+ animtype.o \
dialog.o \
fastfile.o \
graphics.o \
diff --git a/engines/trecision/pathfinding3d.cpp b/engines/trecision/pathfinding3d.cpp
index 4e740376c6e..4f291183b2c 100644
--- a/engines/trecision/pathfinding3d.cpp
+++ b/engines/trecision/pathfinding3d.cpp
@@ -20,7 +20,7 @@
*/
#include "trecision/actor.h"
-#include "trecision/anim.h"
+#include "trecision/animtype.h"
#include "trecision/pathfinding3d.h"
#include "trecision/sound.h"
#include "trecision/trecision.h"
diff --git a/engines/trecision/renderer3d.cpp b/engines/trecision/renderer3d.cpp
index 4b8095b829d..fcff0f594e6 100644
--- a/engines/trecision/renderer3d.cpp
+++ b/engines/trecision/renderer3d.cpp
@@ -20,7 +20,7 @@
*/
#include "trecision/actor.h"
-#include "trecision/anim.h"
+#include "trecision/animtype.h"
#include "trecision/graphics.h"
#include "trecision/renderer3d.h"
#include "trecision/trecision.h"
diff --git a/engines/trecision/resource.cpp b/engines/trecision/resource.cpp
index d9f46a1e8d2..2a88873bb18 100644
--- a/engines/trecision/resource.cpp
+++ b/engines/trecision/resource.cpp
@@ -29,6 +29,7 @@
#include "gui/saveload.h"
#include "trecision/actor.h"
+#include "trecision/animmanager.h"
#include "trecision/defines.h"
#include "trecision/dialog.h"
#include "trecision/graphics.h"
diff --git a/engines/trecision/saveload.cpp b/engines/trecision/saveload.cpp
index 56e52d2c994..fbcc662c3ac 100644
--- a/engines/trecision/saveload.cpp
+++ b/engines/trecision/saveload.cpp
@@ -25,6 +25,7 @@
#include "common/translation.h"
#include "trecision/actor.h"
+#include "trecision/animmanager.h"
#include "trecision/dialog.h"
#include "trecision/graphics.h"
#include "trecision/logic.h"
diff --git a/engines/trecision/script.cpp b/engines/trecision/script.cpp
index 599902cf16f..cc700fc0b63 100644
--- a/engines/trecision/script.cpp
+++ b/engines/trecision/script.cpp
@@ -23,7 +23,8 @@
#include "graphics/scaler.h"
#include "trecision/actor.h"
-#include "trecision/anim.h"
+#include "trecision/animmanager.h"
+#include "trecision/animtype.h"
#include "trecision/defines.h"
#include "trecision/dialog.h"
#include "trecision/graphics.h"
diff --git a/engines/trecision/text.cpp b/engines/trecision/text.cpp
index f7643399c31..6682cc04824 100644
--- a/engines/trecision/text.cpp
+++ b/engines/trecision/text.cpp
@@ -23,6 +23,7 @@
#include "common/str.h"
#include "trecision/actor.h"
+#include "trecision/animmanager.h"
#include "trecision/graphics.h"
#include "trecision/sound.h"
#include "trecision/trecision.h"
diff --git a/engines/trecision/trecision.cpp b/engines/trecision/trecision.cpp
index 63f2ac825e6..9594710a9b1 100644
--- a/engines/trecision/trecision.cpp
+++ b/engines/trecision/trecision.cpp
@@ -28,7 +28,8 @@
#include "common/fs.h"
#include "common/str.h"
-#include "trecision/anim.h"
+#include "trecision/animmanager.h"
+#include "trecision/animtype.h"
#include "trecision/actor.h"
#include "trecision/console.h"
#include "trecision/defines.h"
diff --git a/engines/trecision/video.cpp b/engines/trecision/video.cpp
index 14bd91d72c3..727d0f72339 100644
--- a/engines/trecision/video.cpp
+++ b/engines/trecision/video.cpp
@@ -25,8 +25,10 @@
#include "common/scummsys.h"
#include "common/system.h"
+#include "audio/decoders/raw.h"
+
#include "trecision/actor.h"
-#include "trecision/anim.h"
+#include "trecision/animtype.h"
#include "trecision/defines.h"
#include "trecision/dialog.h"
#include "trecision/graphics.h"
@@ -37,6 +39,160 @@
namespace Trecision {
+NightlongVideoDecoder::NightlongVideoDecoder(bool isAmiga) {
+ _isAmiga = isAmiga;
+ _smkDecoder = !isAmiga ? new NightlongSmackerDecoder() : nullptr;
+ _mixer = g_system->getMixer();
+}
+
+NightlongVideoDecoder::~NightlongVideoDecoder() {
+ delete _smkDecoder;
+
+ if (_mixer->isSoundHandleActive(_amigaSoundHandle))
+ _mixer->stopHandle(_amigaSoundHandle);
+}
+
+bool NightlongVideoDecoder::loadFile(const Common::Path &filename) {
+ if (!_isAmiga)
+ return _smkDecoder->loadFile(filename);
+ else {
+ // TODO: Amiga video format
+
+ // Load the video's audio track
+ Common::File *stream = new Common::File();
+ Common::String file = filename.toString();
+ stream->open("a" + file);
+
+ if (stream->isOpen()) {
+ Audio::SeekableAudioStream *sound = Audio::makeRawStream(stream, 11025, 0, DisposeAfterUse::YES);
+
+ _mixer->playStream(
+ Audio::Mixer::kSFXSoundType,
+ &_amigaSoundHandle,
+ sound);
+
+ return true;
+ } else {
+ delete stream;
+ return false;
+ }
+ }
+}
+
+bool NightlongVideoDecoder::loadStream(Common::SeekableReadStream *stream) {
+ if (!_isAmiga)
+ return _smkDecoder->loadStream(stream);
+ else
+ return false; // TODO: Amiga videos
+}
+
+void NightlongVideoDecoder::muteTrack(uint track, bool mute) {
+ if (!_isAmiga)
+ _smkDecoder->muteTrack(track, mute);
+ // TODO: Amiga videos
+}
+
+void NightlongVideoDecoder::setMute(bool mute) {
+ if (!_isAmiga)
+ _smkDecoder->setMute(mute);
+ // TODO: Amiga videos
+}
+
+bool NightlongVideoDecoder::forceSeekToFrame(uint frame) {
+ if (!_isAmiga)
+ return _smkDecoder->forceSeekToFrame(frame);
+ else
+ return false; // TODO: Amiga videos
+}
+
+bool NightlongVideoDecoder::endOfFrames() const {
+ if (!_isAmiga)
+ return _smkDecoder->endOfFrames();
+ else
+ return !_mixer->isSoundHandleActive(_amigaSoundHandle); // HACK, since we only play the audio for now
+}
+
+int NightlongVideoDecoder::getCurFrame() const {
+ if (!_isAmiga)
+ return _smkDecoder->getCurFrame();
+ else
+ return 0; // TODO: Amiga videos
+}
+
+uint16 NightlongVideoDecoder::getWidth() const {
+ if (!_isAmiga)
+ return _smkDecoder->getWidth();
+ else
+ return 0; // TODO: Amiga videos
+}
+
+uint16 NightlongVideoDecoder::getHeight() const {
+ if (!_isAmiga)
+ return _smkDecoder->getHeight();
+ else
+ return 0; // TODO: Amiga videos
+}
+
+const Graphics::Surface *NightlongVideoDecoder::decodeNextFrame() {
+ if (!_isAmiga)
+ return _smkDecoder->decodeNextFrame();
+ else
+ return nullptr; // TODO: Amiga videos
+}
+
+uint32 NightlongVideoDecoder::getFrameCount() const {
+ if (!_isAmiga)
+ return _smkDecoder->getFrameCount();
+ else
+ return 10; // TODO: Amiga videos. Anything > 1 to keep playing till the audio is done
+}
+
+const byte *NightlongVideoDecoder::getPalette() {
+ if (!_isAmiga)
+ return _smkDecoder->getPalette();
+ else
+ return nullptr; // TODO: Amiga videos
+}
+
+void NightlongVideoDecoder::start() {
+ if (!_isAmiga)
+ _smkDecoder->start();
+ // TODO: Amiga videos
+}
+
+void NightlongVideoDecoder::rewind() {
+ if (!_isAmiga)
+ _smkDecoder->rewind();
+ // TODO: Amiga videos
+}
+
+bool NightlongVideoDecoder::needsUpdate() const {
+ if (!_isAmiga)
+ return _smkDecoder->needsUpdate();
+ else
+ return false; // TODO: Amiga videos
+}
+
+void NightlongVideoDecoder::setEndFrame(uint frame) {
+ if (!_isAmiga)
+ _smkDecoder->setEndFrame(frame);
+ // TODO: Amiga videos
+}
+
+bool NightlongVideoDecoder::endOfVideo() const {
+ if (!_isAmiga)
+ return _smkDecoder->endOfVideo();
+ else
+ return false; // TODO: Amiga videos
+}
+
+const Common::Rect *NightlongVideoDecoder::getNextDirtyRect() {
+ if (!_isAmiga)
+ return _smkDecoder->getNextDirtyRect();
+ else
+ return nullptr; // TODO: Amiga videos
+}
+
bool NightlongSmackerDecoder::loadStream(Common::SeekableReadStream *stream) {
if (!SmackerDecoder::loadStream(stream))
return false;
@@ -115,509 +271,4 @@ bool NightlongSmackerDecoder::endOfFrames() const {
return getCurFrame() >= (int32)getFrameCount() - 1;
}
-AnimManager::AnimManager(TrecisionEngine *vm) : _vm(vm) {
- for (int i = 0; i < MAXSMACK; ++i) {
- _smkAnims[i] = nullptr;
- _playingAnims[i] = 0;
- }
-
- for (int i = 0; i < MAXANIM; ++i) {
- _animTab[i]._flag = 0;
- _animTab[i]._name[0] = '\0';
- }
-
- _curCD = 1;
- swapCD(_curCD);
-
- _bgAnimRestarted = false;
-}
-
-AnimManager::~AnimManager() {
- for (int i = 0; i < MAXSMACK; ++i) {
- delete _smkAnims[i];
- _smkAnims[i] = nullptr;
- _animFile[i].close();
- }
-}
-
-void AnimManager::playMovie(const Common::String &filename, int startFrame, int endFrame, bool singleChoice) {
- NightlongSmackerDecoder *smkDecoder = new NightlongSmackerDecoder;
-
- if (!smkDecoder->loadFile(filename)) {
- warning("playMovie: File %s not found", filename.c_str());
- delete smkDecoder;
- _vm->_dialogMgr->afterChoice();
- return;
- }
-
- Common::Event event;
- bool skipVideo = false;
- uint16 x = (g_system->getWidth() - smkDecoder->getWidth()) / 2;
- uint16 y = (g_system->getHeight() - smkDecoder->getHeight()) / 2;
- _vm->_drawText._text.clear();
-
- smkDecoder->start();
-
- // WORKAROUND: If the video has a single choice, and it starts from
- // the beginning, ignore the calculated end frame and play all of it
- if (singleChoice && startFrame < 10 && endFrame < (int)smkDecoder->getFrameCount() - 1)
- endFrame = smkDecoder->getFrameCount() - 1;
-
- setVideoRange(smkDecoder, startFrame, endFrame);
-
- while (!_vm->shouldQuit() && startFrame != endFrame && !smkDecoder->endOfVideo() && !skipVideo) {
- if (smkDecoder->needsUpdate()) {
- drawFrame(smkDecoder, x, y, true);
- }
-
- while (_vm->getEventManager()->pollEvent(event)) {
- if (event.type == Common::EVENT_KEYUP && event.kbd.keycode == Common::KEYCODE_ESCAPE)
- skipVideo = true;
- }
-
- g_system->delayMillis(10);
- }
-
- delete smkDecoder;
-
- _vm->_mouseLeftBtn = _vm->_mouseRightBtn = false;
- _vm->freeKey();
- _vm->_dialogMgr->afterChoice();
-}
-
-void AnimManager::setVideoRange(NightlongSmackerDecoder *smkDecoder, int &startFrame, int &endFrame) {
- // Trecision starts at 1 but ScummVM starts at 0
- startFrame = CLIP<int32>(startFrame - 1, 0, smkDecoder->getFrameCount() - 1);
- endFrame = CLIP<int32>(endFrame - 1, 0, smkDecoder->getFrameCount() - 1);
-
- // If choices are attached
- if (startFrame > 0 && startFrame > smkDecoder->getCurFrame()) {
- smkDecoder->forceSeekToFrame(startFrame - 1);
- }
- smkDecoder->setEndFrame(endFrame);
-}
-
-void AnimManager::drawFrame(NightlongSmackerDecoder *smkDecoder, uint16 x, uint16 y, bool updateScreen) {
- const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
- if (frame) {
- Graphics::Surface *frame16 = frame->convertTo(g_system->getScreenFormat(), smkDecoder->getPalette());
- drawFrameSubtitles(frame16, smkDecoder->getCurFrame());
- g_system->copyRectToScreen(frame16->getPixels(), frame16->pitch, x, y, frame16->w, frame16->h);
- frame16->free();
- delete frame16;
-
- if (updateScreen)
- _vm->_system->updateScreen();
- }
-}
-
-void AnimManager::drawFrameSubtitles(Graphics::Surface *surface, int frameNum) {
- if (!ConfMan.getBool("subtitles"))
- return;
-
- _vm->_dialogMgr->dialogHandler(frameNum);
- if (_vm->_drawText._text.empty())
- return;
-
- // Subtitles can be placed in different coordinates in the video,
- // which are set inside dialogHandler(), but are then reset to
- // fixed coordinates
- _vm->_drawText._rect.left = 20;
- _vm->_drawText._rect.top = 380 - TOP;
- _vm->_drawText._rect.setWidth(MAXX - 40);
- _vm->_drawText._rect.setHeight(_vm->_drawText.calcHeight(_vm));
- _vm->_drawText._subtitleRect = Common::Rect(MAXX, MAXY);
- _vm->_drawText.draw(_vm, false, surface);
-}
-
-void AnimManager::openSmkAnim(int slot, const Common::String &name) {
- for (int i = 0; i < 3; i++) {
- // Open the animation, or swap the 3 CDs to find it
- if (_animFile[slot].hasFile(name)) {
- openSmk(slot, _animFile[slot].createReadStreamForMember(name));
- return;
- }
-
- _curCD = _curCD < 3 ? _curCD + 1 : 1;
- swapCD(_curCD);
- }
-
- error("openSmkAnim(): File %s not found", name.c_str());
-}
-
-void AnimManager::openSmk(int slot, Common::SeekableReadStream *stream) {
- _smkAnims[slot] = new NightlongSmackerDecoder();
-
- if (!_smkAnims[slot]->loadStream(stream)) {
- warning("Invalid SMK file");
- closeSmk(slot);
- } else {
- _smkAnims[slot]->start();
- }
-}
-
-void AnimManager::closeSmk(int slot) {
- delete _smkAnims[slot];
- _smkAnims[slot] = nullptr;
-}
-
-void AnimManager::smkGoto(int slot, int frame) {
- if (_smkAnims[slot] == nullptr)
- return;
-
- _smkAnims[slot]->forceSeekToFrame(frame);
-}
-
-void AnimManager::smkToggleTrackAudio(int slot, int track, bool on) {
- if (_smkAnims[slot] == nullptr)
- return;
-
- _smkAnims[slot]->muteTrack(track, !on);
-}
-
-void AnimManager::smkToggleAudio(int slot, bool on) {
- if (_smkAnims[slot] == nullptr)
- return;
-
- _smkAnims[slot]->setMute(!on);
-}
-
-int16 AnimManager::smkCurFrame(int slot) {
- if (_smkAnims[slot] == nullptr)
- return -1;
-
- return _smkAnims[slot]->getCurFrame();
-}
-
-void AnimManager::startSmkAnim(uint16 animation) {
- int slot;
- uint16 animFlag = _animTab[animation]._flag;
-
- // choose the buffer to use
- if (animFlag & SMKANIM_BKG)
- slot = kSmackerBackground;
- else if (animFlag & SMKANIM_ICON)
- slot = kSmackerIcon;
- else
- slot = kSmackerAction;
-
- smkStop(slot);
-
- _playingAnims[slot] = animation;
-
- // choose how to open
- if (slot == kSmackerBackground) {
- openSmkAnim(kSmackerBackground, _animTab[animation]._name);
- _bgAnimRestarted = false;
-
- toggleMuteBgAnim(animation);
- } else if (slot == kSmackerIcon) {
- openSmkAnim(kSmackerIcon, _animTab[animation]._name);
- } else {
- uint32 st = _vm->readTime();
- openSmkAnim(kSmackerAction, _animTab[animation]._name);
- _vm->_nextRefresh += _vm->readTime() - st; // fixup opening time
- }
-}
-
-void AnimManager::toggleMuteBgAnim(uint16 animation) {
- const bool area1Shown = _animTab[animation].isAnimAreaShown(1);
- const bool area2Shown = _animTab[animation].isAnimAreaShown(2);
- const bool area4Shown = _animTab[animation].isAnimAreaShown(4);
- NightlongSmackerDecoder *decoder = _smkAnims[kSmackerBackground];
- if (decoder == nullptr)
- return;
-
- // Turns off when not needed
- if (animation == aBKG11 && !area1Shown)
- decoder->muteTrack(1, true);
- else if (animation == aBKG14 && !area1Shown)
- decoder->muteTrack(1, true);
- else if (animation == aBKG1C && _vm->_obj[oFAX17].isFlagExtra()) {
- _animTab[animation].toggleAnimArea(1, false);
- decoder->muteTrack(1, true);
- } else if (animation == aBKG1D && !area1Shown)
- decoder->muteTrack(1, true);
- else if (animation == aBKG22 && !area1Shown)
- decoder->muteTrack(1, true);
- else if (animation == aBKG48 && !area1Shown)
- decoder->muteTrack(1, true);
- else if (animation == aBKG4P && !area1Shown)
- decoder->muteTrack(1, true);
- else if (animation == aBKG28 && area4Shown)
- decoder->muteTrack(1, true);
- else if (animation == aBKG37 && !_vm->_room[_vm->_curRoom].hasExtra())
- decoder->muteTrack(1, true);
- else if (animation == aBKG2E && area2Shown)
- decoder->muteTrack(2, true);
- else if (animation == aBKG2G && _vm->_dialogMgr->isDialogFinished(556))
- decoder->muteTrack(2, true);
- else if (animation == aBKG34 && // If it's BKG 34 and
- (_vm->_dialogMgr->isDialogFinished(616) || // if the FMV is already done or
- _vm->isObjectVisible(oTUBOT34) || // if the whole tube is available or
- _vm->isObjectVisible(oTUBOFT34) || // if the outside of the tube is available or
- _vm->isObjectVisible(oVALVOLAC34))) // if the valve is closed
- decoder->muteTrack(2, true);
-}
-
-void AnimManager::smkStop(uint16 slot) {
- _playingAnims[slot] = 0;
-
- closeSmk(slot);
-
- _vm->_lightIcon = 0xFF;
-}
-
-void AnimManager::stopAllSmkAnims() {
- for (int slot = 0; slot < MAXSMACK; slot++) {
- if (_playingAnims[slot])
- smkStop(slot);
- }
-}
-
-void AnimManager::startFullMotion() {
- stopAllSmkAnims();
-
- _vm->_flagDialogActive = true;
- _vm->_flagShowCharacter = false;
-
- _vm->_textStatus = TEXT_OFF;
- _vm->_inventoryStatus = INV_OFF;
- _vm->_inventoryCounter = INVENTORY_HIDE;
-
- _vm->_textMgr->clearTextStack();
- _vm->_graphicsMgr->clearScreen();
-
- _vm->_scheduler->resetQueues();
- _vm->_actor->actorStop();
- _vm->_graphicsMgr->hideCursor();
-}
-
-void AnimManager::stopFullMotion() {
- const uint16 curDialog = _vm->_dialogMgr->getCurDialog();
-
- _vm->_flagDialogActive = false;
- _vm->_flagDialogMenuActive = false;
- _vm->_flagSomeoneSpeaks = false;
- _vm->_lightIcon = 0xFF;
- _vm->_graphicsMgr->showCursor();
-
- if (curDialog == dFCRED) {
- _vm->quitGame();
- return;
- }
-
- if (!((curDialog == dSHOPKEEPER1A) && _vm->_dialogMgr->getCurChoice() == 185)) {
- if ((curDialog == dF582) || (curDialog == dFLOG) || (curDialog == dINTRO) || (curDialog == dF362) || (curDialog == dC381) || (curDialog == dF381) ||
- (curDialog == dF491) || ((curDialog == dC581) && !_vm->_dialogMgr->isDialogFinished(886) && _vm->_dialogMgr->isDialogFinished(258)) ||
- ((curDialog == dC5A1) && _vm->_room[kRoom5A].hasExtra()))
- _vm->_flagShowCharacter = false;
- else
- _vm->redrawRoom();
-
- if (curDialog == dF582)
- _vm->_soundMgr->stopAllExceptMusic();
- }
-}
-
-void AnimManager::refreshAnim(int box) {
- for (int i = 0; i < MAXSMACK; i++) {
- if (_playingAnims[i] != 0 && box == BOX_BACKGROUND && i != kSmackerAction) {
- refreshSmkAnim(_playingAnims[i]);
- }
- }
-}
-
-void AnimManager::refreshSmkAnim(uint16 animation) {
- if (animation == 0)
- return;
-
- if (_animTab[animation]._flag & SMKANIM_ICON) {
- drawSmkIconFrame(_vm->_inventoryRefreshStartIcon, animation);
- } else if (_animTab[animation]._flag & SMKANIM_BKG) {
- drawSmkBackgroundFrame(animation);
- handleEndOfVideo(animation, kSmackerBackground);
- } else {
- drawSmkActionFrame();
- handleEndOfVideo(animation, kSmackerAction);
- }
-
- for (int32 i = 0; i < MAXAREA; i++) {
- if (_animTab[animation].isAnimAreaShown(i + 1) && _animTab[animation]._lim[i].bottom != 0) {
- _vm->_graphicsMgr->addDirtyRect(_animTab[animation]._lim[i], true);
- }
- }
-}
-
-void AnimManager::handleEndOfVideo(int animation, int slot) {
- const bool isLoopingOrBackground = (_animTab[animation]._flag & SMKANIM_LOOP) || (_animTab[animation]._flag & SMKANIM_BKG);
-
- if (_smkAnims[slot] == nullptr) {
- smkStop(slot);
- return;
- }
- if (!_smkAnims[slot]->endOfFrames())
- return;
-
- if (!isLoopingOrBackground) {
- smkStop(slot);
- _vm->_flagPaintCharacter = true;
- } else {
- _smkAnims[slot]->rewind();
- _vm->_animTypeMgr->init(animation, 0);
- _bgAnimRestarted = true;
- }
-}
-
-static bool rectsIntersect(Common::Rect r1, Common::Rect r2) {
- return (r1.left <= r2.right) && (r1.right >= r2.left) && (r1.top <= r2.bottom) && (r1.bottom >= r2.top);
-}
-
-bool AnimManager::shouldShowAnim(int animation, Common::Rect curRect) {
- for (int32 i = 0; i < MAXAREA; i++) {
- const bool intersect = rectsIntersect(_animTab[animation]._lim[i], curRect);
- if (intersect && !_animTab[animation].isAnimAreaShown(i + 1))
- return false;
- }
-
- return true;
-}
-
-void AnimManager::drawSmkBackgroundFrame(int animation) {
- NightlongSmackerDecoder *smkDecoder = _smkAnims[kSmackerBackground];
- if (smkDecoder == nullptr)
- return;
- const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
- if (!frame)
- return;
-
- const Common::Rect *lastRect = smkDecoder->getNextDirtyRect();
- const byte *palette = smkDecoder->getPalette();
-
- if (smkDecoder->getCurFrame() == 0 && shouldShowAnim(animation, *lastRect) && !_bgAnimRestarted) {
- _vm->_graphicsMgr->blitToScreenBuffer(frame, 0, TOP, palette, true);
- } else {
- while (lastRect) {
- if (smkDecoder->getCurFrame() > 0 && shouldShowAnim(animation, *lastRect)) {
- Graphics::Surface anim = frame->getSubArea(*lastRect);
- _vm->_graphicsMgr->blitToScreenBuffer(&anim, lastRect->left, lastRect->top + TOP, palette, true);
- }
-
- lastRect = smkDecoder->getNextDirtyRect();
- }
- }
-}
-
-void AnimManager::drawSmkIconFrame(uint16 startIcon, uint16 iconNum) {
- NightlongSmackerDecoder *smkDecoder = _smkAnims[kSmackerIcon];
- if (smkDecoder == nullptr)
- return;
-
- int stx = ICONMARGSX;
- uint a;
- for (a = 0; a < ICONSHOWN; ++a) {
- if (a + startIcon >= _vm->_inventory.size())
- break;
-
- if (_vm->_inventory[a + startIcon] == iconNum - FIRST_INV_ITEM + 1) {
- stx = a * ICONDX + ICONMARGSX;
- break;
- }
- }
-
- if (a == ICONSHOWN)
- return;
-
- const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
- _vm->_graphicsMgr->copyToScreenBuffer(frame, stx, FIRSTLINE, smkDecoder->getPalette());
-
- if (smkDecoder->endOfVideo())
- smkDecoder->rewind();
-}
-
-void AnimManager::drawSmkActionFrame() {
- NightlongSmackerDecoder *smkDecoder = _smkAnims[kSmackerAction];
- if (smkDecoder == nullptr)
- return;
- const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
- if (!frame)
- return;
-
- const byte *palette = smkDecoder->getPalette();
-
- if (smkDecoder->getCurFrame() == 0) {
- _animRect = *smkDecoder->getNextDirtyRect();
- }
-
- if (_animRect.width() > 0 && _animRect.height() > 0) {
- Graphics::Surface anim = frame->getSubArea(_animRect);
- _vm->_graphicsMgr->blitToScreenBuffer(&anim, _animRect.left, _animRect.top + TOP, palette, false);
- _vm->_graphicsMgr->addDirtyRect(_animRect, true);
- }
-}
-
-void AnimManager::swapCD(int cd) {
- Common::String animFileName = Common::String::format("nlanim.cd%d", cd);
- for (uint8 i = 0; i < MAXSMACK; ++i) {
- _animFile[i].close();
- _animFile[i].open(_vm, animFileName);
- }
-}
-
-void AnimManager::syncGameStream(Common::Serializer &ser) {
- for (int i = 0; i < MAXANIM; i++) {
- SAnim *cur = &_animTab[i];
- ser.syncBytes((byte *)cur->_name, 14);
- ser.syncAsUint16LE(cur->_flag);
- for (uint8 j = 0; j < MAXAREA; ++j) {
- ser.syncAsUint16LE(cur->_lim[j].left);
- ser.syncAsUint16LE(cur->_lim[j].top);
- ser.syncAsUint16LE(cur->_lim[j].right);
- ser.syncAsUint16LE(cur->_lim[j].bottom);
- }
- ser.syncAsByte(cur->_nbox);
- ser.skip(1, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX);
- for (uint8 j = 0; j < MAXATFRAME; ++j) {
- ser.syncAsByte(cur->_atFrame[j]._type);
- ser.syncAsByte(cur->_atFrame[j]._area);
- ser.syncAsUint16LE(cur->_atFrame[j]._numFrame);
- ser.syncAsUint16LE(cur->_atFrame[j]._index);
- }
- }
-
- patchAnimTab();
-}
-
-void AnimManager::loadAnimTab(Common::SeekableReadStreamEndian *stream) {
- for (uint16 i = 0; i < MAXANIM; ++i) {
- stream->read(&_animTab[i]._name, 14);
-
- _animTab[i]._flag = stream->readUint16();
-
- for (uint8 j = 0; j < MAXAREA; ++j) {
- _animTab[i]._lim[j].left = stream->readUint16();
- _animTab[i]._lim[j].top = stream->readUint16();
- _animTab[i]._lim[j].right = stream->readUint16();
- _animTab[i]._lim[j].bottom = stream->readUint16();
- }
-
- _animTab[i]._nbox = stream->readByte();
- stream->readByte(); // Padding
-
- for (uint8 j = 0; j < MAXATFRAME; ++j) {
- _animTab[i]._atFrame[j]._type = stream->readByte();
- _animTab[i]._atFrame[j]._area = stream->readByte();
- _animTab[i]._atFrame[j]._numFrame = stream->readUint16();
- _animTab[i]._atFrame[j]._index = stream->readUint16();
- }
- }
-
- patchAnimTab();
-}
-
-void AnimManager::patchAnimTab() {
- _animTab[aBKG28]._lim[3].left = 308; // Patch the brazier animation rect in kRoom28 - bug #12628
- _animTab[aBKG35]._lim[0].right = 200; // Patch the terrorist animation rect in kRoom35
-}
-
} // namespace Trecision
diff --git a/engines/trecision/video.h b/engines/trecision/video.h
index c9aabf2a9cd..19c9b1bdd5f 100644
--- a/engines/trecision/video.h
+++ b/engines/trecision/video.h
@@ -30,22 +30,6 @@
namespace Trecision {
-#define MAXANIM 750
-#define MAXSMACK 3
-
-// SMACKER ANIMATION FLAGS
-#define SMKANIM_BKG 1
-#define SMKANIM_ICON 2
-#define SMKANIM_LOOP 4
-#define SMKANIM_OLD 8
-#define SMKANIM_ON 16
-
-enum SmackerType {
- kSmackerBackground = 0, // Scene background animations
- kSmackerAction = 1, // Main character action animations
- kSmackerIcon = 2 // Smacker inventory animations
-};
-
class TrecisionEngine;
class NightlongSmackerDecoder : public Video::SmackerDecoder {
@@ -57,59 +41,38 @@ public:
bool endOfFrames() const;
};
-class AnimManager {
+class NightlongVideoDecoder {
public:
- AnimManager(TrecisionEngine *vm);
- ~AnimManager();
-
-private:
- TrecisionEngine *_vm;
-
- NightlongSmackerDecoder *_smkAnims[MAXSMACK];
- uint16 _playingAnims[MAXSMACK];
-
- FastFile _animFile[MAXSMACK]; // nlanim.cd1 / nlanim.cd2 / nlanim.cd3
- int _curCD;
- bool _bgAnimRestarted;
-
- void openSmk(int slot, Common::SeekableReadStream *stream);
- void openSmkAnim(int slot, const Common::String &name);
- void toggleMuteBgAnim(uint16 animation);
- void closeSmk(int slot);
- void drawFrame(NightlongSmackerDecoder *smkDecoder, uint16 x, uint16 y, bool updateScreen);
- void drawFrameSubtitles(Graphics::Surface *surface, int frameNum);
- void setVideoRange(NightlongSmackerDecoder *smkDecoder, int &startFrame, int &endFrame);
- void refreshSmkAnim(uint16 animation);
- void handleEndOfVideo(int animation, int slot);
- bool shouldShowAnim(int animation, Common::Rect curRect);
-
- void drawSmkBackgroundFrame(int animation);
- void drawSmkIconFrame(uint16 startIcon, uint16 iconNum);
- void drawSmkActionFrame();
- void swapCD(int cd);
- void patchAnimTab();
-
-public:
- Common::Rect _animRect;
- SAnim _animTab[MAXANIM];
-
- void smkGoto(int slot, int frame);
- void smkToggleAudio(int slot, bool on);
- void smkToggleTrackAudio(int slot, int track, bool on);
- int16 smkCurFrame(int slot);
- void smkStop(uint16 slot);
- void refreshActionAnimation() { refreshSmkAnim(_playingAnims[kSmackerAction]); }
- bool isActionActive() const { return _playingAnims[kSmackerAction] != 0; }
- void playMovie(const Common::String &filename, int startFrame = 0, int endFrame = -1, bool singleChoice = false);
- void startFullMotion();
- void stopFullMotion();
+ NightlongVideoDecoder(bool isAmiga);
+ ~NightlongVideoDecoder();
+ bool loadStream(Common::SeekableReadStream *stream);
+ void muteTrack(uint track, bool mute);
+ void setMute(bool mute);
+ bool forceSeekToFrame(uint frame);
+ bool endOfFrames() const;
- void refreshAnim(int box);
- void startSmkAnim(uint16 animation);
- void stopAllSmkAnims();
+ // VideoDecoder functions
+ int getCurFrame() const;
+ uint16 getWidth() const;
+ uint16 getHeight() const;
+ const Graphics::Surface *decodeNextFrame();
+ uint32 getFrameCount() const;
+ const byte *getPalette();
+ void start();
+ void rewind();
+ bool needsUpdate() const;
+ void setEndFrame(uint frame);
+ bool endOfVideo() const;
+
+ bool loadFile(const Common::Path &filename);
+ const Common::Rect *getNextDirtyRect();
- void syncGameStream(Common::Serializer &ser);
- void loadAnimTab(Common::SeekableReadStreamEndian *stream);
+private:
+ bool _isAmiga;
+ NightlongSmackerDecoder *_smkDecoder;
+ Audio::SoundHandle _amigaSoundHandle;
+ Audio::Mixer *_mixer;
};
+
} // End of namespace Trecision
#endif
More information about the Scummvm-git-logs
mailing list