[Scummvm-git-logs] scummvm master -> 29900c627a167aeda632beaaab036da48650350f
mduggan
noreply at scummvm.org
Sun Jan 12 06:02:15 UTC 2025
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:
29900c627a DGDS: Refactor and fix conversation TTM script execution
Commit: 29900c627a167aeda632beaaab036da48650350f
https://github.com/scummvm/scummvm/commit/29900c627a167aeda632beaaab036da48650350f
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2025-01-12T16:56:07+11:00
Commit Message:
DGDS: Refactor and fix conversation TTM script execution
Willy Beamish CD voice acting is now *mostly* working.
Changed paths:
engines/dgds/dgds.cpp
engines/dgds/dgds.h
engines/dgds/head.cpp
engines/dgds/head.h
engines/dgds/minigames/dragon_arcade_ttm.cpp
engines/dgds/scene.cpp
engines/dgds/scene.h
engines/dgds/ttm.cpp
engines/dgds/ttm.h
diff --git a/engines/dgds/dgds.cpp b/engines/dgds/dgds.cpp
index c15fb2c2210..bd6bb9ee383 100644
--- a/engines/dgds/dgds.cpp
+++ b/engines/dgds/dgds.cpp
@@ -499,7 +499,8 @@ void DgdsEngine::loadRestartFile() {
_gdsScene->loadRestart(_rstFileName, _resource, _decompressor);
}
-static void _dumpFrame(const Graphics::ManagedSurface &surf, const char *name) {
+/*static*/ void
+DgdsEngine::dumpFrame(const Graphics::ManagedSurface &surf, const char *name) {
#ifdef DUMP_FRAME_DATA
/* For debugging, dump the frame contents.. */
Common::DumpFile outf;
@@ -596,6 +597,28 @@ void DgdsEngine::pumpMessages() {
}
}
+void DgdsEngine::dimPalForWillyDialog(bool force) {
+ WillyGlobals *globals = static_cast<WillyGlobals *>(_gameGlobals);
+ int16 fade = globals->getPalFade();
+ fade = CLIP(fade, (int16)0, (int16)255);
+
+ // TODO: Same constants are in globals.cpp
+ static const int FADE_STARTCOL = 0x40;
+ static const int FADE_NUMCOLS = 0xC0;
+
+ if (force || _scene->hasVisibleHead() ) {
+ fade = 0x80;
+ } else {
+ fade = 0;
+ }
+
+ if (_lastGlobalFade != fade || _lastGlobalFadedPal != _gamePals->getCurPalNum()) {
+ _gamePals->setFade(FADE_STARTCOL, FADE_NUMCOLS, 0, fade);
+ _lastGlobalFade = fade;
+ _lastGlobalFadedPal = _gamePals->getCurPalNum();
+ }
+}
+
Common::Error DgdsEngine::run() {
syncSoundSettings();
_isLoading = true;
@@ -678,7 +701,7 @@ Common::Error DgdsEngine::run() {
//
//_scene->drawActiveDialogBgs(&_compositionBuffer);
- _dumpFrame(_compositionBuffer, "comp-before-ads");
+ dumpFrame(_compositionBuffer, "comp-before-ads");
if (!_inventory->isOpen() || (_inventory->isZoomVisible() && getGameId() != GID_WILLY))
_adsInterp->run();
@@ -729,9 +752,9 @@ Common::Error DgdsEngine::run() {
_scene->runPostTickOps();
_scene->checkTriggers();
- _dumpFrame(_backgroundBuffer, "back");
- _dumpFrame(_storedAreaBuffer, "stor");
- _dumpFrame(_compositionBuffer, "comp");
+ dumpFrame(_backgroundBuffer, "back");
+ dumpFrame(_storedAreaBuffer, "stor");
+ dumpFrame(_compositionBuffer, "comp");
if (!_inventory->isOpen()) {
_gdsScene->drawItems(_compositionBuffer);
@@ -747,15 +770,17 @@ Common::Error DgdsEngine::run() {
_scene->drawDebugHotAreas(&_compositionBuffer);
if (getGameId() == GID_WILLY) {
- _scene->drawVisibleHeads(&_compositionBuffer);
+ if (!justChangedScene1())
+ _scene->drawVisibleHeads(&_compositionBuffer);
_scene->drawAndUpdateDialogs(&_compositionBuffer);
_scene->updateHotAreasFromDynamicRects();
} else {
_scene->drawAndUpdateDialogs(&_compositionBuffer);
- _scene->drawVisibleHeads(&_compositionBuffer);
+ if (!justChangedScene1())
+ _scene->drawVisibleHeads(&_compositionBuffer);
}
- _dumpFrame(_compositionBuffer, "comp-with-dlg");
+ dumpFrame(_compositionBuffer, "comp-with-dlg");
bool gameRunning = (!haveActiveDialog && _gameGlobals->getGlobal(0x57) /* TODO: && _dragItem == nullptr*/);
_clock.update(gameRunning);
@@ -769,29 +794,12 @@ Common::Error DgdsEngine::run() {
// Mouse event is now handled.
_lastMouseEvent = Common::EVENT_INVALID;
- // Willy Beamish dims the palette of the screen while dialogs are active
if (getGameId() == GID_WILLY) {
- WillyGlobals *globals = static_cast<WillyGlobals *>(_gameGlobals);
- int16 fade = globals->getPalFade();
- fade = CLIP(fade, (int16)0, (int16)255);
-
- // TODO: Same constants are in globals.cpp
- static const int FADE_STARTCOL = 0x40;
- static const int FADE_NUMCOLS = 0xC0;
-
- if (_scene->hasVisibleHead()) {
- fade = 0x80;
- } else {
- fade = 0;
- }
-
- if (_lastGlobalFade != fade || _lastGlobalFadedPal != _gamePals->getCurPalNum()) {
- _gamePals->setFade(FADE_STARTCOL, FADE_NUMCOLS, 0, fade);
- _lastGlobalFade = fade;
- _lastGlobalFadedPal = _gamePals->getCurPalNum();
- }
+ // Willy Beamish dims the palette of the screen while dialogs are active
+ dimPalForWillyDialog(false);
// TODO: When should we show the cursor again?
+ WillyGlobals *globals = static_cast<WillyGlobals *>(_gameGlobals);
if (globals->isHideMouseCursor() && !_menu->menuShown())
CursorMan.showMouse(false);
}
diff --git a/engines/dgds/dgds.h b/engines/dgds/dgds.h
index 75031f9ce04..a715611e11a 100644
--- a/engines/dgds/dgds.h
+++ b/engines/dgds/dgds.h
@@ -283,6 +283,10 @@ public:
void setDebugShowHotAreas(bool enable) { _debugShowHotAreas = enable; }
bool getDebugShowHotAreas() const { return _debugShowHotAreas; }
+ static void dumpFrame(const Graphics::ManagedSurface &surf, const char *name);
+
+ void dimPalForWillyDialog(bool force);
+
private:
Common::Error syncGame(Common::Serializer &s);
diff --git a/engines/dgds/head.cpp b/engines/dgds/head.cpp
index fd09f809047..81c39f39f23 100644
--- a/engines/dgds/head.cpp
+++ b/engines/dgds/head.cpp
@@ -22,7 +22,13 @@
#include "dgds/head.h"
#include "dgds/dgds.h"
#include "dgds/image.h"
+#include "dgds/includes.h"
#include "dgds/sound_raw.h"
+#include "dgds/drawing.h"
+#include "dgds/scene.h"
+#include "dgds/dialog.h"
+
+#include "graphics/cursorman.h"
namespace Dgds {
@@ -162,26 +168,154 @@ bool TalkData::hasVisibleHead() const {
return false;
}
-//////
+CDSTTMInterpreter::CDSTTMInterpreter(DgdsEngine *vm) : TTMInterpreter(vm) {
+ _storedAreaBuffer.create(SCREEN_WIDTH, SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
+}
+
+
+void CDSTTMInterpreter::handleOperation(TTMEnviro &env_, TTMSeq &seq, uint16 op, byte count, const int16 *ivals, const Common::String &sval, const Common::Array<Common::Point> &pts) {
+ CDSTTMEnviro &env = static_cast<CDSTTMEnviro &>(env_);
+
+ switch (op) {
+ case 0x0080: // FREE SHAPE
+ env._scriptShapes[0].reset();
+ break;
+ case 0x0110: // PURGE void
+ break;
+ case 0x0FF0: // REFRESH
+ break;
+ case 0x1020: { // SET DELAY: i:int [0..n]
+ // TODO: Probably should do this accounting (as well as timeCut and dialogs)
+ // in game frames, not millis.
+ int delayMillis = (int)round(ivals[0] * MS_PER_FRAME);
+ env._cdsDelay = delayMillis;
+ break;
+ }
+ case 0x1050: // SELECT BMP: id:int [0]
+ seq._currentBmpId = ivals[0];
+ break;
+ case 0x1060: // SELECT PAL: id:int [0]
+ seq._currentPalId = ivals[0];
+ break;
+ case 0x1100: // SET_SCENE: i:int [1..n]
+ case 0x1110: // SET_SCENE: i:int [1..n]
+ break;
+ case 0x2000: // SET (DRAW) COLORS: fgcol,bgcol:int [0..255]
+ seq._drawColFG = static_cast<byte>(ivals[0]); // aka Line Color
+ seq._drawColBG = static_cast<byte>(ivals[1]); // aka Fill Color
+ break;
+ case 0x3200: // CDS FIND GOTO TARGET frameno
+ seq._gotoFrame = findGOTOTarget(env, seq, ivals[0]);
+ break;
+ case 0x3300: // CDS GOSUB - first 2 args are ignored by original
+ if (!env._cdsJumped && seq._gotoFrame + ivals[2] != seq._currentFrame && seq._gotoFrame >= 0) {
+ env._cdsJumped = true;
+ int64 prevPos = env.scr->pos();
+ int16 currentFrame = seq._currentFrame;
+ env.scr->seek(env._frameOffsets[seq._gotoFrame + ivals[2]]);
+ seq._currentFrame = seq._gotoFrame;
+ run(env, seq);
+ seq._currentFrame = currentFrame;
+ env.scr->seek(prevPos);
+ env._cdsJumped = false;
+ }
+ break;
+ case 0x4200: { // STORE AREA: x,y,w,h:int [0..n] ; makes this area of foreground persist in the next frames.
+ if (env._cdsDidStoreArea) // this is a one-shot op
+ break;
+
+ if (env._storedAreaRect.width * 2 < (int)ivals[2]) {
+ env._storedAreaRect = DgdsRect(ivals[0] + env._xOff, ivals[1] + env._yOff, ivals[2], ivals[3]);
+ }
+ const Common::Rect rect = env._storedAreaRect.toCommonRect();
+ _storedAreaBuffer.blitFrom(_vm->_compositionBuffer, rect, rect);
+ env._cdsDidStoreArea = true;
+ break;
+ }
+ case 0xa500: // DRAW SPRITE: x,y,frameno,bmpno:int [-n,+n]
+ case 0xa510: // DRAW SPRITE FLIP V x,y:int
+ case 0xa520: // DRAW SPRITE FLIP H: x,y:int
+ case 0xa530: // DRAW SPRITE FLIP HV: x,y,frameno,bmpno:int [-n,+n]
+ doDrawSpriteOp(env, seq, op, count, ivals, env._xOff, env._yOff);
+ break;
+ case 0xc220: // PLAY RAW SFX
+ if (env._cdsPlayedSound) // this is a one-shot op
+ break;
+ if (!env._soundRaw) {
+ warning("TODO: Trying to play raw SFX but nothing loaded");
+ } else {
+ env._soundRaw->play();
+ env._cdsPlayedSound = true;
+ }
+ break;
+ case 0xc250: { // SYNC RAW SFX
+ uint16 hi = (uint16)ivals[1];
+ uint16 lo = (uint16)ivals[0];
+ uint32 offset = ((uint32)hi << 16) + lo;
+ debug("TODO: 0xC250 Sync raw sfx?? offset %d", offset);
+ /*if (env._soundRaw->playedOffset() < offset) {
+ // Not played to this point yet.
+ env.scr->seek(-6, SEEK_CUR);
+ return false;
+ }*/
+ break;
+ }
+ case 0xa100: // DRAW FILLED RECT
+ case 0xa110: // DRAW EMPTY RECT x1,y1,x2,y2:int
+ // Appear in the scripts but not implemented in the original.
+ break;
+ case 0xc200: // ??? SFX: ??,?? - not implemented in willy, ignore?
+ case 0xc210: // LOAD RAW SFX: filename:str
+ case 0xf010: // LOAD SCR: filename:str
+ case 0xf020: // LOAD BMP: filename:str
+ case 0xf050: // LOAD PAL: filename:str
+ // Ignore all these in CDS scripts. The original implements them but only to load
+ // patch data outside of the CDS files. That data isn't present in the final
+ // games so we can just ignore the opcode.
+ break;
+ default:
+ if (count < 15)
+ warning("Unimplemented CDS TTM opcode: 0x%04X (%s, %d args) (ivals: %d %d %d %d)",
+ op, ttmOpName(op), count, ivals[0], ivals[1], ivals[2], ivals[3]);
+ else
+ warning("Unimplemented CDS TTM opcode: 0x%04X (%s, sval: %s)", op,
+ ttmOpName(op), sval.c_str());
+ break;
+ }
+}
+
Conversation::~Conversation() {
- unload();
+ unloadData();
}
-void Conversation::unload() {
- if (_sound) {
- _sound->stop();
- _sound.reset();
- }
- _img.reset();
+void Conversation::unloadData() {
+ debug(10, "CDS: Unloading data (%d,%d)", _dlgNum, _dlgFileNum);
_ttmScript.reset();
+
+ _ttmEnv._scriptShapes[0].reset();
if (_ttmEnv._soundRaw)
_ttmEnv._soundRaw->stop();
- _ttmEnv = TTMEnviro();
+ _ttmEnv = CDSTTMEnviro();
+ _loadState = 0;
+}
+
+void Conversation::clear() {
+ _dlgNum = -1;
+ _dlgFileNum = -1;
+ _subNum = -1;
+ _finished = false;
+ _haveHeadData = false;
+ _stopScript = false;
+}
+
+bool Conversation::isForDlg(const Dialog *dlg) const {
+ return dlg && dlg->_num == _dlgNum && dlg->_fileNum == _dlgFileNum;
}
-void Conversation::loadData(uint16 dlgFileNum, uint16 dlgNum, int16 sub) {
- unload();
+void Conversation::loadData(uint16 dlgFileNum, uint16 dlgNum, int16 sub, bool haveHeadData) {
+ unloadData();
+ clear();
DgdsEngine *engine = DgdsEngine::getInstance();
@@ -189,6 +323,19 @@ void Conversation::loadData(uint16 dlgFileNum, uint16 dlgNum, int16 sub) {
if (engine->getGameId() != GID_WILLY)
return;
+ _dlgNum = dlgNum;
+ _dlgFileNum = dlgFileNum;
+ _subNum = sub;
+ _nextExecMs = 0;
+ _runTempFrame = 0;
+ _tempFrameNum = 0;
+ _thisFrameMs = 0;
+ _stopScript = false;
+ _haveHeadData = haveHeadData;
+
+ if (!haveHeadData)
+ _drawRect = DgdsRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+
ResourceManager *resourceManager = engine->getResourceManager();
Decompressor *decompressor = engine->getDecompressor();
@@ -205,51 +352,205 @@ void Conversation::loadData(uint16 dlgFileNum, uint16 dlgNum, int16 sub) {
debug(10, "CDS: Load CDS resource %s", fname.c_str());
- _sound.reset(new SoundRaw(resourceManager, decompressor));
- _sound->load(fname);
- _img.reset(new Image(resourceManager, decompressor));
- _img->loadBitmap(fname);
- _ttmScript.reset(new TTMInterpreter(engine));
+ // The scripts are designed so the resources are patchable, but by default
+ // they use the sound and image data from the CDS file.
+ _ttmEnv._soundRaw.reset(new SoundRaw(resourceManager, decompressor));
+ _ttmEnv._soundRaw->load(fname);
+ _ttmEnv._scriptShapes[0].reset(new Image(resourceManager, decompressor));
+ _ttmEnv._scriptShapes[0]->loadBitmap(fname);
+ _ttmScript.reset(new CDSTTMInterpreter(engine));
_ttmScript->load(fname, _ttmEnv);
_ttmScript->findAndAddSequences(_ttmEnv, _ttmSeqs);
- // The scripts are designed so the resources are patchable, but by default
- // they use the sound and image data from the CDS file.
- _ttmEnv._soundRaw = _sound;
- _ttmEnv._scriptShapes[0] = _img;
+ _loadState = 1;
- // Always run seq 1 on init.
- _ttmEnv._cdsSeqNum = 1;
- runScript();
+ // Then run START
+ for (const auto &tag : _ttmEnv._tags) {
+ if (tag._value.equalsIgnoreCase("START"))
+ _ttmEnv._cdsFrame = tag._key;
+ }
+ _tempFrameNum = _ttmScript->findGOTOTarget(_ttmEnv, *_ttmSeqs[0], _ttmEnv._cdsFrame);
+ _ttmEnv._cdsFrame = _tempFrameNum;
+
+ // Always run frame 1 on init.
+ runScriptFrame(1);
}
-void Conversation::runScript() {
+bool Conversation::runScriptFrame(int16 frameNum) {
+ if (!_ttmScript || frameNum >= _ttmEnv._totalFrames)
+ return false;
+
+ _ttmEnv.scr->seek(_ttmEnv._frameOffsets[frameNum]);
+ TTMSeq *seq = _ttmSeqs[0].get();
+ seq->_currentFrame = frameNum;
+
+ debug(10, "CDS: Running TTM sequence %d frame %d", seq->_seqNum, seq->_currentFrame);
+
+ seq->_drawWin = _drawRect.toCommonRect();
+ return _ttmScript->run(_ttmEnv, *seq);
+}
+
+void Conversation::checkAndRunScript() {
+ if (!_ttmScript || _finished)
+ return;
+
+ DgdsEngine *engine = DgdsEngine::getInstance();
+
+ // Always add the stored buffer first. Copy the whole thing to overwrite other state.
+ engine->_compositionBuffer.transBlitFrom(_ttmScript->getStoredAreaBuffer());
+
+ if (_runTempFrame) {
+ runScriptFrame(_tempFrameNum);
+ }
+ runScriptFrame(_ttmEnv._cdsFrame);
+ if (_ttmEnv._cdsDelay > 0) {
+ _nextExecMs = _thisFrameMs + _ttmEnv._cdsDelay;
+ _ttmEnv._cdsDelay = -1;
+ } else {
+ _nextExecMs = 0;
+ }
+}
+
+void Conversation::incrementFrame() {
if (!_ttmScript)
return;
+ // TODO: check load type 2 here?
+
+ if (_loadState == 1 && _ttmEnv._scriptShapes[0] && _ttmEnv._scriptShapes[0]->loadedFrameCount() > 0) {
+ _ttmEnv._storedAreaRect.x = _drawRect.x;
+ _ttmEnv._storedAreaRect.y = _drawRect.y;
+ _ttmEnv._storedAreaRect.width = _ttmEnv._scriptShapes[0]->getFrames()[0]->w + 8;
+ _ttmEnv._storedAreaRect.height = _ttmEnv._scriptShapes[0]->getFrames()[0]->h;
+ _loadState = 2;
+ }
+
+ if (_runTempFrame)
+ _runTempFrame--;
+
+ if (!_nextExecMs || _thisFrameMs >= _nextExecMs) {
+ debug(10, "CDS: Increment frame %d -> %d", _ttmEnv._cdsFrame, _ttmEnv._cdsFrame + 1);
+ _ttmEnv._cdsFrame++;
+ }
+}
+
+bool Conversation::isScriptRunning() {
+ return (_ttmScript &&
+ ((_ttmEnv._soundRaw && _ttmEnv._soundRaw->isPlaying())
+ ||
+ (_ttmEnv._cdsFrame < _ttmEnv._totalFrames)
+ ));
+}
+
+void Conversation::pumpMessages() {
+ Common::Event ev;
DgdsEngine *engine = DgdsEngine::getInstance();
- if (_nextExec && engine->getThisFrameMs() < _nextExec)
+
+ while (engine->getEventManager()->pollEvent(ev)) {
+ if (ev.type == Common::EVENT_LBUTTONDOWN ||
+ ev.type == Common::EVENT_RBUTTONDOWN ||
+ ev.type == Common::EVENT_KEYDOWN) {
+ _stopScript = true;
+ engine->getScene()->setIgnoreMouseUp();
+ }
+ }
+}
+
+void Conversation::runScript() {
+ if (!_ttmScript)
return;
- _nextExec = 0;
+ DgdsEngine *engine = DgdsEngine::getInstance();
+ engine->disableKeymapper();
+
+ _nextExecMs = 0;
_ttmEnv._xOff = _drawRect.x;
_ttmEnv._yOff = _drawRect.y;
+ _runTempFrame = 2;
+
+ if (_drawRect.width == 0) {
+ _drawRect.width = SCREEN_WIDTH;
+ _drawRect.height = SCREEN_HEIGHT;
+ }
+
+ int frameCount = 0;
+ uint32 startMillis = g_system->getMillis();
+
+ DgdsEngine::dumpFrame(engine->_compositionBuffer, "cds-comp-before-script");
+
+ engine->getScene()->checkDialogActive();
+ // update here to make sure the dialog gets drawn.
+ engine->getScene()->drawAndUpdateDialogs(&engine->_compositionBuffer);
+
+ DgdsEngine::dumpFrame(engine->_compositionBuffer, "cds-comp-before-script-with-dlg");
+
+ _ttmScript->getStoredAreaBuffer().blitFrom(engine->_compositionBuffer);
+
+ CursorMan.showMouse(false);
+
+ g_system->copyRectToScreen(engine->_compositionBuffer.getPixels(), SCREEN_WIDTH, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+ g_system->updateScreen();
+
+ // If we have head data we should dim the palette
+ engine->dimPalForWillyDialog(_haveHeadData);
- for (auto seq : _ttmSeqs) {
- if (seq->_seqNum == _ttmEnv._cdsSeqNum && seq->_currentFrame < (int)_ttmEnv._frameOffsets.size()) {
- debug(10, "CDS: Running TTM sequence %d frame %d", seq->_seqNum, seq->_currentFrame);
- _ttmEnv.scr->seek(_ttmEnv._frameOffsets[seq->_currentFrame]);
-
- seq->_drawWin = _drawRect.toCommonRect();
- _ttmScript->run(_ttmEnv, *seq);
- if (_ttmEnv._cdsDelay) {
- _nextExec = engine->getThisFrameMs() + _ttmEnv._cdsDelay;
- _ttmEnv._cdsDelay = 0;
- } else {
- seq->_currentFrame++;
+ debug(10, "CDS: Start execution of (%d,%d) - frame %d maxFrame %d", _dlgNum, _dlgFileNum,
+ _ttmEnv._cdsFrame, _ttmEnv._totalFrames);
+
+ while (isScriptRunning() && !engine->shouldQuit()) {
+ _thisFrameMs = g_system->getMillis();
+ pumpMessages();
+ if (_stopScript)
+ break;
+
+ if (!_nextExecMs || _nextExecMs <= _thisFrameMs) {
+ incrementFrame();
+
+ const Common::Rect r = _drawRect.toCommonRect();
+
+ checkAndRunScript();
+
+ const byte *srcPtr = (const byte *)engine->_compositionBuffer.getPixels() + r.top * SCREEN_WIDTH + r.left;
+ g_system->copyRectToScreen(srcPtr, SCREEN_WIDTH, r.left, r.top, r.width(), r.height());
+ }
+
+ g_system->updateScreen();
+
+ frameCount++;
+ // Limit to 15 FPS
+ static const int framesPerSecond = 15;
+ uint32 thisFrameEndMillis = g_system->getMillis();
+ uint32 elapsedMillis = thisFrameEndMillis - startMillis;
+ const uint32 targetMillis = (frameCount * 1000 / framesPerSecond);
+ if (targetMillis > elapsedMillis) {
+ while (targetMillis > elapsedMillis) {
+ pumpMessages();
+ g_system->updateScreen();
+ g_system->delayMillis(5);
+ elapsedMillis = g_system->getMillis() - startMillis;
}
+ } else if (targetMillis < elapsedMillis) {
+ startMillis = thisFrameEndMillis;
+ frameCount = 0;
}
}
+
+ debug(10, "CDS: Finished execution of (%d,%d) - stop %s frame %d maxFrame %d", _dlgNum, _dlgFileNum,
+ _stopScript ? "true" : "false", _ttmEnv._cdsFrame, _ttmEnv._totalFrames);
+
+ CursorMan.showMouse(true);
+ engine->enableKeymapper();
+ if (_ttmEnv._soundRaw)
+ _ttmEnv._soundRaw->stop();
+
+ // If the dialog was cleared, set the force-clear flag in the scene. Otherwise, just set
+ // a flag the scene can check when updating dialogs.
+ if (_stopScript)
+ engine->getScene()->setShouldClearDlg();
+ else
+ _finished = true;
+
+ unloadData();
}
} // end namespace Dgds
diff --git a/engines/dgds/head.h b/engines/dgds/head.h
index 916db07923e..25476a473b4 100644
--- a/engines/dgds/head.h
+++ b/engines/dgds/head.h
@@ -38,7 +38,7 @@ namespace Dgds {
class Image;
class SoundRaw;
-
+class Dialog;
class TalkDataHeadFrame {
public:
@@ -105,23 +105,75 @@ public:
bool hasVisibleHead() const;
};
-/** CDS data from Willy Beamish talkie */
+
+/**
+ * A TTM interpreter with customized opcode handling for the TTM bit of
+ * CDS files (Willy Beamish CD version conversation data)
+ */
+class CDSTTMInterpreter : public TTMInterpreter {
+public:
+ CDSTTMInterpreter(DgdsEngine *vm);
+
+ Graphics::ManagedSurface &getStoredAreaBuffer() { return _storedAreaBuffer; }
+
+protected:
+ virtual void handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byte count, const int16 *ivals, const Common::String &sval, const Common::Array<Common::Point> &pts) override;
+
+ Graphics::ManagedSurface _storedAreaBuffer;
+};
+
+class CDSTTMEnviro : public TTMEnviro {
+public:
+ CDSTTMEnviro() : _cdsPlayedSound(false), _cdsFrame(-1), _cdsJumped(false), _cdsDelay(0),
+ _cdsDidStoreArea(false), TTMEnviro()
+ {}
+
+ bool _cdsPlayedSound;
+ int16 _cdsFrame; // The GOTO target to use in the CDS script (Willy Beamish talkie)
+ int16 _cdsDelay;
+ bool _cdsJumped;
+ bool _cdsDidStoreArea;
+ DgdsRect _storedAreaRect;
+};
+
+/** CDS data from Willy Beamish CD version talkie */
class Conversation {
public:
- Conversation() : _nextExec(0) {}
+ Conversation() : _nextExecMs(0), _runTempFrame(0), _tempFrameNum(0), _stopScript(false), _loadState(0), _dlgNum(-1), _dlgFileNum(-1), _subNum(-1), _finished(false), _haveHeadData(false) {}
~Conversation();
- void unload();
+ void unloadData();
void runScript();
- void loadData(uint16 num, uint16 num2, int16 sub);
+ void loadData(uint16 num, uint16 num2, int16 sub, bool haveHeadData);
+ bool isForDlg(const Dialog *dlg) const;
+ bool isFinished() const { return _finished; }
+ void clear();
- Common::SharedPtr<SoundRaw> _sound;
- Common::SharedPtr<Image> _img;
- Common::SharedPtr<TTMInterpreter> _ttmScript;
- Common::Array<Common::SharedPtr<TTMSeq>> _ttmSeqs;
- TTMEnviro _ttmEnv;
- uint32 _nextExec;
DgdsRect _drawRect;
+
+private:
+ Common::SharedPtr<CDSTTMInterpreter> _ttmScript;
+ Common::Array<Common::SharedPtr<TTMSeq>> _ttmSeqs;
+ CDSTTMEnviro _ttmEnv;
+ uint32 _nextExecMs;
+
+ bool runScriptFrame(int16 frameNum);
+ void checkAndRunScript();
+ void incrementFrame();
+ bool isScriptRunning();
+ void pumpMessages();
+
+ int16 _runTempFrame;
+ int16 _tempFrameNum;
+
+ uint32 _thisFrameMs;
+ bool _stopScript;
+ int16 _loadState;
+ int16 _dlgNum;
+ int16 _dlgFileNum;
+ int16 _subNum;
+ bool _finished;
+ bool _haveHeadData;
};
diff --git a/engines/dgds/minigames/dragon_arcade_ttm.cpp b/engines/dgds/minigames/dragon_arcade_ttm.cpp
index f2a2e640dab..ab8859fed9d 100644
--- a/engines/dgds/minigames/dragon_arcade_ttm.cpp
+++ b/engines/dgds/minigames/dragon_arcade_ttm.cpp
@@ -220,11 +220,9 @@ int16 DragonArcadeTTM::handleOperation(TTMEnviro &env, int16 page, uint16 op, by
debug(1, "Floor: %s", data.dump().c_str());
_floorData.push_back(data);
} else {
- const Common::Rect r(Common::Point(ivals[0], ivals[1]), ivals[2] - 1, ivals[3] - 1);
- compBuffer.drawLine(r.left, r.top, r.right, r.top, _drawColFG);
- compBuffer.drawLine(r.left, r.bottom, r.right, r.bottom, _drawColFG);
- compBuffer.drawLine(r.left, r.top, r.left, r.bottom, _drawColFG);
- compBuffer.drawLine(r.right, r.top, r.right, r.bottom, _drawColFG);
+ const Common::Rect r(Common::Point(ivals[0], ivals[1]), ivals[2], ivals[3]);
+ const Common::Rect drawWin(SCREEN_WIDTH, SCREEN_HEIGHT);
+ Drawing::rectClipped(r, drawWin, &compBuffer, _drawColFG);
}
break;
case 0xA404: { // DRAW FILLED CIRCLE
diff --git a/engines/dgds/scene.cpp b/engines/dgds/scene.cpp
index 548fe3b3c0c..d828fd9f41d 100644
--- a/engines/dgds/scene.cpp
+++ b/engines/dgds/scene.cpp
@@ -592,7 +592,7 @@ void SDSScene::unload() {
_triggers.clear();
_talkData.clear();
_dynamicRects.clear();
- _conversation.unload();
+ _conversation.unloadData();
_conditionalOps.clear();
_sceneDialogFlags = kDlgFlagNone;
}
@@ -856,13 +856,16 @@ bool SDSScene::loadTalkData(uint16 num) {
}
-void SDSScene::freeTalkData(uint16 num) {
+bool SDSScene::freeTalkData(uint16 num) {
+ bool result = false;
for (int i = 0; i < (int)_talkData.size(); i++) {
if (_talkData[i]._num == num) {
_talkData.remove_at(i);
i--;
+ result = true;
}
}
+ return result;
}
void SDSScene::updateVisibleTalkers() {
@@ -875,7 +878,10 @@ void SDSScene::drawVisibleHeads(Graphics::ManagedSurface *dst) {
for (const auto &tds : _talkData) {
tds.drawVisibleHeads(dst);
}
- _conversation.runScript();
+
+ if (_conversation.isForDlg(getVisibleDialog())) {
+ _conversation.runScript();
+ }
}
bool SDSScene::hasVisibleHead() const {
@@ -887,8 +893,10 @@ bool SDSScene::hasVisibleHead() const {
}
-void SDSScene::loadTalkDataAndSetFlags(uint16 talknum, uint16 headnum) {
+bool SDSScene::loadTalkDataAndSetFlags(uint16 talknum, uint16 headnum) {
updateVisibleTalkers();
+
+ _conversation._drawRect = DgdsRect();
if (loadTalkData(talknum)) {
for (auto &data : _talkData) {
if (data._num != talknum)
@@ -905,7 +913,9 @@ void SDSScene::loadTalkDataAndSetFlags(uint16 talknum, uint16 headnum) {
}
break;
}
+ return true;
}
+ return false;
}
@@ -964,11 +974,12 @@ void SDSScene::showDialog(uint16 fileNum, uint16 dlgNum) {
dialog.setFlag(kDlgFlagOpening);
// For beamish
+ bool haveHeadData = false;
if (dialog._talkDataHeadNum) {
- loadTalkDataAndSetFlags(dialog._talkDataNum, dialog._talkDataHeadNum);
+ haveHeadData = loadTalkDataAndSetFlags(dialog._talkDataNum, dialog._talkDataHeadNum);
}
- _conversation.loadData(fileNum, dlgNum, -1);
+ _conversation.loadData(fileNum, dlgNum, -1, haveHeadData);
// hide time gets set the first time it's drawn.
if (_dlgWithFlagLo8IsClosing && dialog.hasFlag(kDlgFlagLo8)) {
@@ -1008,6 +1019,13 @@ bool SDSScene::checkDialogActive() {
if ((dlg._state->_hideTime == 0) && dlg._action.size() < 2)
no_options = true;
+ // If voice acting in Willy Beamish is finished, clear the dialog
+ // unless we are waiting for a choice.
+ if (dlg._action.size() < 2 && (_conversation.isForDlg(&dlg) && _conversation.isFinished())) {
+ finished = true;
+ _conversation.clear();
+ }
+
if ((!finished && !no_options) || dlg.hasFlag(kDlgFlagHi20) || dlg.hasFlag(kDlgFlagHi40)) {
if (!finished && dlg._action.size() > 1 && !dlg.hasFlag(kDlgFlagHiFinished)) {
DialogAction *action = dlg.pickAction(false, clearDlgFlag);
@@ -1022,19 +1040,18 @@ bool SDSScene::checkDialogActive() {
_dlgWithFlagLo8IsClosing = dlg.hasFlag(kDlgFlagLo8);
// For Willy Beamish
+ bool haveHeadData = false;
if (dlg._talkDataNum) {
- freeTalkData(dlg._talkDataNum);
+ haveHeadData = freeTalkData(dlg._talkDataNum);
}
DialogAction *action = dlg.pickAction(true, clearDlgFlag);
if (action || dlg._action.empty()) {
dlg.setFlag(kDlgFlagHiFinished);
if (action) {
- // TODO: We can load selected item voice acting here, but it generally
- // immediately starts another dialog or changes scene, so the sound
- // doesn't end up playing.
- // Need to work out how to correctly delay until the sound finishes?
- _conversation.loadData(dlg._fileNum, dlg._num, action->num);
+ // Play the response voice acting script.
+ _conversation.loadData(dlg._fileNum, dlg._num, action->num, haveHeadData);
+ _conversation.runScript();
// Take a copy of the dialog because the actions might change the scene
Dialog dlgCopy = dlg;
@@ -1061,8 +1078,8 @@ bool SDSScene::checkDialogActive() {
dlg.setFlag(kDlgFlagHiFinished);
showDialog(dlg._nextDialogFileNum, dlg._nextDialogDlgNum);
} else {
- // No next dialog clear CDS data
- _conversation.unload();
+ // No next dialog .. clear CDS data?
+ //_conversation.unloadData();
}
}
if (dlg.hasFlag(kDlgFlagVisible)) {
@@ -1218,6 +1235,8 @@ void SDSScene::mouseLDown(const Common::Point &pt) {
return;
}
+ _ignoreMouseUp = false;
+
// Don't start drag in look/target mode.
if (_lookMode)
return;
diff --git a/engines/dgds/scene.h b/engines/dgds/scene.h
index 05d0c7790f1..ec84a3bbcd1 100644
--- a/engines/dgds/scene.h
+++ b/engines/dgds/scene.h
@@ -313,9 +313,9 @@ public:
Dialog *loadDialogData(uint16 num);
void freeDialogData(uint16 num);
bool loadTalkData(uint16 num);
- void freeTalkData(uint16 num);
+ bool freeTalkData(uint16 num);
void updateVisibleTalkers();
- void loadTalkDataAndSetFlags(uint16 talknum, uint16 headnum);
+ bool loadTalkDataAndSetFlags(uint16 talknum, uint16 headnum);
void drawVisibleHeads(Graphics::ManagedSurface *dst);
bool hasVisibleHead() const;
@@ -334,6 +334,8 @@ public:
void setDynamicSceneRect(int16 num, int16 x, int16 y, int16 width, int16 height);
void setSceneNum(int16 num) { _num = num; }
void drawDebugHotAreas(Graphics::ManagedSurface *dst) const;
+ void setIgnoreMouseUp() { _ignoreMouseUp = true; }
+ void setShouldClearDlg() { _shouldClearDlg = true; }
protected:
HotArea *findAreaUnderMouse(const Common::Point &pt);
diff --git a/engines/dgds/ttm.cpp b/engines/dgds/ttm.cpp
index 17915de6262..0145480c5b8 100644
--- a/engines/dgds/ttm.cpp
+++ b/engines/dgds/ttm.cpp
@@ -132,7 +132,8 @@ bool TTMInterpreter::load(const Common::String &filename, TTMEnviro &scriptData)
void TTMInterpreter::unload() {
}
-static const char *ttmOpName(uint16 op) {
+/*static*/
+const char *TTMInterpreter::ttmOpName(uint16 op) {
switch (op) {
case 0x0000: return "FINISH";
case 0x0020: return "SAVE(free?) BACKGROUND";
@@ -493,7 +494,7 @@ void TTMInterpreter::doWipeOp(uint16 code, const TTMEnviro &env, const TTMSeq &s
}
-int16 TTMInterpreter::doOpInitCreditScroll(const Image *img) {
+int16 TTMInterpreter::doInitCreditScrollOp(const Image *img) {
assert(img);
int16 maxWidth = 0;
for (int i = 0; i < img->loadedFrameCount(); i++)
@@ -501,7 +502,7 @@ int16 TTMInterpreter::doOpInitCreditScroll(const Image *img) {
return maxWidth;
}
- bool TTMInterpreter::doOpCreditsScroll(const Image *img, int16 ygap, int16 ymax, int16 xoff, int16 measuredWidth, const Common::Rect &clipRect) {
+ bool TTMInterpreter::doCreditsScrollOp(const Image *img, int16 ygap, int16 ymax, int16 xoff, int16 measuredWidth, const Common::Rect &clipRect) {
int nframes = img->loadedFrameCount();
bool scrollFinished = true;
int y = SCREEN_HEIGHT - ymax;
@@ -573,6 +574,99 @@ void TTMInterpreter::doDrawDialogForStrings(const TTMEnviro &env, const TTMSeq &
}
}
+/// Handle 0xa5xx draw ops
+void TTMInterpreter::doDrawSpriteOp(const TTMEnviro &env, const TTMSeq &seq, uint16 op, byte count, const int16 *ivals, int16 xoff, int16 yoff) {
+ int frameno;
+ int bmpNo;
+ int dstWidth = 0;
+ int dstHeight = 0;
+ if (count == 6) {
+ frameno = ivals[2];
+ bmpNo = ivals[3];
+ dstWidth = ivals[4];
+ dstHeight = ivals[5];
+ } else if (count == 4) {
+ frameno = ivals[2];
+ bmpNo = ivals[3];
+ } else {
+ frameno = seq._brushNum;
+ bmpNo = seq._currentBmpId;
+ }
+
+ ImageFlipMode flipMode = kImageFlipNone;
+ if (op == 0xa510)
+ flipMode = kImageFlipV;
+ else if (op == 0xa520)
+ flipMode = kImageFlipH;
+ else if (op == 0xa530)
+ flipMode = kImageFlipHV;
+
+ Common::SharedPtr<Image> img = env._scriptShapes[bmpNo];
+ if (img) {
+ int x = ivals[0] + xoff;
+ int y = ivals[1] + yoff;
+ // Use env offset if we are in gosub
+ if (_stackDepth > 0) {
+ x += env._xOff;
+ y += env._yOff;
+ }
+ img->drawBitmap(frameno, x, y, seq._drawWin, _vm->_compositionBuffer, flipMode, dstWidth, dstHeight);
+ } else {
+ warning("Trying to draw image %d in env %d which is not loaded", bmpNo, env._enviro);
+ }
+}
+
+void TTMInterpreter::doFadeOutOp(int16 colorno, int16 ncolors, int16 targetcol, int16 speed) {
+ if (speed == 0) {
+ _vm->getGamePals()->clearPalette();
+ } else {
+ // The original tight-loops here with 640 steps and i/10 as the fade level..
+ // bring that down a bit to use less cpu.
+ // Speed 4 should complete fade in 2 seconds (eg, Dynamix logo fade)
+
+ // TODO: this is a pretty bad way to do it - should pump messages in this loop?
+ for (int i = 0; i < 320; i += speed) {
+ int fade = MIN(i / 5, 63);
+ _vm->getGamePals()->setFade(colorno, ncolors, targetcol, fade * 4);
+ g_system->updateScreen();
+ g_system->delayMillis(5);
+ }
+ }
+
+ // Logic here is different in Dragon + HOC. They clear all buffers after fade
+ if (_vm->getGameId() == GID_DRAGON || _vm->getGameId() == GID_HOC) {
+ _vm->_compositionBuffer.fillRect(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT), 0);
+ _vm->getBackgroundBuffer().fillRect(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT), 0);
+ } else {
+ // In Willy Beamish, copy comp->screen and comp->back
+ g_system->copyRectToScreen(_vm->_compositionBuffer.getPixels(), SCREEN_WIDTH, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+ g_system->updateScreen();
+ _vm->getBackgroundBuffer().blitFrom(_vm->_compositionBuffer);
+ }
+ // Stored area is cleared in all games.
+ _vm->getStoredAreaBuffer().fillRect(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT), 0);
+
+ // Reset to previous palette.
+ _vm->getGamePals()->setPalette();
+}
+
+void TTMInterpreter::doFadeInOp(int16 colorno, int16 ncolors, int16 targetcol, int16 speed) {
+ if (speed == 0) {
+ _vm->getGamePals()->setPalette();
+ } else {
+ for (int i = 320; i > 0; i -= speed) {
+ int fade = MAX(0, MIN(i / 5, 63));
+ _vm->getGamePals()->setFade(colorno, ncolors, targetcol, fade * 4);
+ if (i == 320) {
+ // update screen first to make the initial fade-in work
+ g_system->copyRectToScreen(_vm->_compositionBuffer.getPixels(), SCREEN_WIDTH, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+ }
+ g_system->updateScreen();
+ g_system->delayMillis(5);
+ }
+ }
+}
+
void TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byte count, const int16 *ivals, const Common::String &sval, const Common::Array<Common::Point> &pts) {
switch (op) {
@@ -634,10 +728,7 @@ void TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
break;
case 0x0110: // PURGE void
// only set if not running from CDS script
- if (env._cdsSeqNum < 0)
- _vm->adsInterpreter()->setHitTTMOp0110();
- else
- env._cdsSeqNum++;
+ _vm->adsInterpreter()->setHitTTMOp0110();
break;
case 0x0220: // STOP CURRENT MUSIC
if (seq._executed) // this is a one-shot op
@@ -661,12 +752,7 @@ void TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
// TODO: Probably should do this accounting (as well as timeCut and dialogs)
// in game frames, not millis.
int delayMillis = (int)round(ivals[0] * MS_PER_FRAME);
- // Slight HACK - if we are running from CDS (Willy Beamish conversation) script,
- // set that delay, otherwise set ADS interpreter delay.
- if (env._cdsSeqNum >= 0)
- env._cdsDelay = delayMillis;
- else
- _vm->adsInterpreter()->setScriptDelay(delayMillis);
+ _vm->adsInterpreter()->setScriptDelay(delayMillis);
break;
}
case 0x1030: // SET BRUSH: id:int [-1:n]
@@ -680,8 +766,6 @@ void TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
seq._currentPalId = ivals[0];
if (seq._executed) // this is a mostly on-shot op.
break;
- if (env._cdsSeqNum >= 0) // don't change global pal for CDS scripts.
- break;
_vm->getGamePals()->selectPalNum(env._scriptPals[ivals[0]]);
break;
case 0x1070: // SELECT FONT i:int
@@ -794,23 +878,6 @@ void TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
_vm->_compositionBuffer.blitFrom(_vm->getBackgroundBuffer());
break;
}
- case 0x3200: // CDS FIND GOTO TARGET frameno
- env._cdsSeqNum = findGOTOTarget(env, seq, ivals[0]);
- break;
- case 0x3300: // CDS GOSUB
- if (!env._cdsJumped && env._frameOffsets[env._cdsSeqNum] != seq._currentFrame) {
- env._cdsJumped = true;
- int64 prevPos = env.scr->pos();
- env._xOff += ivals[0];
- env._yOff += ivals[1];
- env.scr->seek(env._frameOffsets[env._cdsSeqNum]);
- run(env, seq);
- env._xOff -= ivals[0];
- env._yOff -= ivals[1];
- env.scr->seek(prevPos);
- env._cdsJumped = false;
- }
- break;
case 0x4000: // SET CLIP WINDOW x,y,x2,y2:int [0..320,0..200]
// NOTE: params are xmax/ymax, NOT w/h
seq._drawWin = Common::Rect(ivals[0], ivals[1], ivals[2] + 1, ivals[3] + 1);
@@ -818,65 +885,18 @@ void TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
case 0x4110: // FADE OUT: colorno,ncolors,targetcol,speed:byte
if (seq._executed) // this is a one-shot op.
break;
- if (ivals[3] == 0) {
- _vm->getGamePals()->clearPalette();
- } else {
- // The original tight-loops here with 640 steps and i/10 as the fade level..
- // bring that down a bit to use less cpu.
- // Speed 4 should complete fade in 2 seconds (eg, Dynamix logo fade)
-
- // TODO: this is a pretty bad way to do it - should pump messages in this loop?
- for (int i = 0; i < 320; i += ivals[3]) {
- int fade = MIN(i / 5, 63);
- _vm->getGamePals()->setFade(ivals[0], ivals[1], ivals[2], fade * 4);
- g_system->updateScreen();
- g_system->delayMillis(5);
- }
- }
-
- // Logic here is different in Dragon + HOC. They clear all buffers after fade
- if (_vm->getGameId() == GID_DRAGON || _vm->getGameId() == GID_HOC) {
- _vm->_compositionBuffer.fillRect(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT), 0);
- _vm->getBackgroundBuffer().fillRect(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT), 0);
- } else {
- // In Willy Beamish, copy comp->screen and comp->back
- g_system->copyRectToScreen(_vm->_compositionBuffer.getPixels(), SCREEN_WIDTH, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
- g_system->updateScreen();
- _vm->getBackgroundBuffer().blitFrom(_vm->_compositionBuffer);
- }
- // Stored area is cleared in all games.
- _vm->getStoredAreaBuffer().fillRect(Common::Rect(SCREEN_WIDTH, SCREEN_HEIGHT), 0);
-
- // Reset to previous palette.
- _vm->getGamePals()->setPalette();
-
+ doFadeOutOp(ivals[0], ivals[1], ivals[2], ivals[3]);
break;
case 0x4120: { // FADE IN: colorno,ncolors,targetcol,speed:byte
if (seq._executed) // this is a one-shot op.
break;
-
- if (ivals[3] == 0) {
- _vm->getGamePals()->setPalette();
- } else {
- for (int i = 320; i > 0; i -= ivals[3]) {
- int fade = MAX(0, MIN(i / 5, 63));
- _vm->getGamePals()->setFade(ivals[0], ivals[1], ivals[2], fade * 4);
- if (i == 320) {
- // update screen first to make the initial fade-in work
- g_system->copyRectToScreen(_vm->_compositionBuffer.getPixels(), SCREEN_WIDTH, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
- }
- g_system->updateScreen();
- g_system->delayMillis(5);
- }
- }
+ doFadeInOp(ivals[0], ivals[1], ivals[2], ivals[3]);
break;
}
case 0x4200: { // STORE AREA: x,y,w,h:int [0..n] ; makes this area of foreground persist in the next frames.
if (seq._executed) // this is a one-shot op
break;
Common::Rect rect(Common::Point(ivals[0], ivals[1]), ivals[2], ivals[3]);
- if (env._cdsSeqNum >= 0)
- rect.translate(env._xOff, env._yOff);
_vm->getStoredAreaBuffer().blitFrom(_vm->_compositionBuffer, rect, rect);
break;
}
@@ -979,47 +999,9 @@ void TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
case 0xa500: // DRAW SPRITE: x,y,frameno,bmpno:int [-n,+n]
case 0xa510: // DRAW SPRITE FLIP V x,y:int
case 0xa520: // DRAW SPRITE FLIP H: x,y:int
- case 0xa530: { // DRAW SPRITE FLIP HV: x,y,frameno,bmpno:int [-n,+n] (CHINA+)
- int frameno;
- int bmpNo;
- int dstWidth = 0;
- int dstHeight = 0;
- if (count == 6) {
- frameno = ivals[2];
- bmpNo = ivals[3];
- dstWidth = ivals[4];
- dstHeight = ivals[5];
- } else if (count == 4) {
- frameno = ivals[2];
- bmpNo = ivals[3];
- } else {
- frameno = seq._brushNum;
- bmpNo = seq._currentBmpId;
- }
-
- ImageFlipMode flipMode = kImageFlipNone;
- if (op == 0xa510)
- flipMode = kImageFlipV;
- else if (op == 0xa520)
- flipMode = kImageFlipH;
- else if (op == 0xa530)
- flipMode = kImageFlipHV;
-
- Common::SharedPtr<Image> img = env._scriptShapes[bmpNo];
- if (img) {
- int x = ivals[0];
- int y = ivals[1];
- // Use env offset if we are in gosub *or* running from CDS
- if (_stackDepth > 0 || env._cdsSeqNum >= 0) {
- x += env._xOff;
- y += env._yOff;
- }
- img->drawBitmap(frameno, x, y, seq._drawWin, _vm->_compositionBuffer, flipMode, dstWidth, dstHeight);
- } else {
- warning("Trying to draw image %d in env %d which is not loaded", bmpNo, env._enviro);
- }
+ case 0xa530: // DRAW SPRITE FLIP HV: x,y,frameno,bmpno:int [-n,+n] (CHINA+)
+ doDrawSpriteOp(env, seq, op, count, ivals);
break;
- }
case 0xa600: { // DRAW GETPUT: i:int
if (seq._executed) // this is a one-shot op.
break;
@@ -1078,13 +1060,13 @@ void TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
case 0xb000: // INIT CREDITS SCRLL
if (seq._executed) // this is a one-shot op
break;
- env._creditScrollMeasure = doOpInitCreditScroll(env._scriptShapes[seq._currentBmpId].get());
+ env._creditScrollMeasure = doInitCreditScrollOp(env._scriptShapes[seq._currentBmpId].get());
env._creditScrollYOffset = 0;
break;
case 0xb010: { // DRAW CREDITS SCROLL ygap,ystep
const Image *img = env._scriptShapes[seq._currentBmpId].get();
if (img && img->isLoaded()) {
- bool finished = doOpCreditsScroll(env._scriptShapes[seq._currentBmpId].get(), ivals[0], env._creditScrollYOffset,
+ bool finished = doCreditsScrollOp(env._scriptShapes[seq._currentBmpId].get(), ivals[0], env._creditScrollYOffset,
ivals[2], env._creditScrollMeasure, seq._drawWin);
env._creditScrollYOffset += ivals[1];
if (finished)
@@ -1140,8 +1122,7 @@ void TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
snd->load(sval);
env._soundRaw.reset(snd);
} else {
- // This happens in Willy Beamish talkie CDS files.
- debug("TTM 0xC210: Skip loading RAW %s, not found.", sval.c_str());
+ warning("TTM 0xC210: Skip loading RAW %s, not found.", sval.c_str());
}
break;
}
@@ -1162,23 +1143,9 @@ void TTMInterpreter::handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byt
}
break;
}
- case 0xc250: { // SYNC RAW SFX
- uint16 hi = (uint16)ivals[1];
- uint16 lo = (uint16)ivals[0];
- uint32 offset = ((uint32)hi << 16) + lo;
- debug("TODO: 0xC250 Sync raw sfx?? offset %d", offset);
- /*if (env._soundRaw->playedOffset() < offset) {
- // Not played to this point yet.
- env.scr->seek(-6, SEEK_CUR);
- return false;
- }*/
- break;
- }
case 0xf010: { // LOAD SCR: filename:str
if (seq._executed) // this is a one-shot op
break;
- if (env._cdsSeqNum >= 0) // don't run from CDS scripts?
- break;
Image tmp(_vm->getResourceManager(), _vm->getDecompressor());
tmp.drawScreen(sval, _vm->getBackgroundBuffer());
_vm->_compositionBuffer.blitFrom(_vm->getBackgroundBuffer());
diff --git a/engines/dgds/ttm.h b/engines/dgds/ttm.h
index 4436a297490..8d7f0ba0165 100644
--- a/engines/dgds/ttm.h
+++ b/engines/dgds/ttm.h
@@ -41,7 +41,7 @@ class TTMEnviro : public ScriptParserData {
public:
TTMEnviro() : _totalFrames(330), _enviro(0), _creditScrollMeasure(0),
_creditScrollYOffset(0), _xOff(0), _yOff(0), _xScroll(0), _yScroll(0),
- _cdsSeqNum(-1), _cdsJumped(false), _cdsDelay(0), ScriptParserData() {
+ ScriptParserData() {
ARRAYCLEAR(_scriptPals);
}
@@ -65,9 +65,6 @@ public:
int16 _xScroll;
int16 _yScroll;
Common::SharedPtr<SoundRaw> _soundRaw;
- int16 _cdsSeqNum; // The GOTO target to use in the CDS script (Willy Beamish talkie)
- int16 _cdsDelay;
- bool _cdsJumped;
};
enum TTMRunType {
@@ -128,14 +125,19 @@ public:
void findAndAddSequences(TTMEnviro &scriptData, Common::Array<Common::SharedPtr<TTMSeq>> &seqArray);
static Common::String readTTMStringVal(Common::SeekableReadStream *scr);
+ int32 findGOTOTarget(const TTMEnviro &env, const TTMSeq &seq, int16 frame);
protected:
- void handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byte count, const int16 *ivals, const Common::String &sval, const Common::Array<Common::Point> &pts);
- int32 findGOTOTarget(const TTMEnviro &env, const TTMSeq &seq, int16 frame);
+ virtual void handleOperation(TTMEnviro &env, TTMSeq &seq, uint16 op, byte count, const int16 *ivals, const Common::String &sval, const Common::Array<Common::Point> &pts);
void doWipeOp(uint16 code, const TTMEnviro &env, const TTMSeq &seq, const Common::Rect &r);
- int16 doOpInitCreditScroll(const Image *img);
- bool doOpCreditsScroll(const Image *img, int16 ygap, int16 ymax, int16 xoff, int16 measuredWidth, const Common::Rect &clipRect);
+ int16 doInitCreditScrollOp(const Image *img);
+ bool doCreditsScrollOp(const Image *img, int16 ygap, int16 ymax, int16 xoff, int16 measuredWidth, const Common::Rect &clipRect);
void doDrawDialogForStrings(const TTMEnviro &env, const TTMSeq &seq, int16 x, int16 y, int16 width, int16 height);
+ void doDrawSpriteOp(const TTMEnviro &env, const TTMSeq &seq, uint16 op, byte count, const int16 *ivals, int16 xoff = 0, int16 yoff = 0);
+ void doFadeOutOp(int16 colorno, int16 ncolors, int16 targetcol, int16 speed);
+ void doFadeInOp(int16 colorno, int16 ncolors, int16 targetcol, int16 speed);
+
+ static const char *ttmOpName(uint16 op);
DgdsEngine *_vm;
int _stackDepth;
More information about the Scummvm-git-logs
mailing list