aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/draci/draci.cpp1
-rw-r--r--engines/draci/font.h3
-rw-r--r--engines/draci/game.cpp258
-rw-r--r--engines/draci/game.h41
-rw-r--r--engines/draci/mouse.h2
-rw-r--r--engines/draci/script.cpp78
-rw-r--r--engines/draci/script.h11
7 files changed, 356 insertions, 38 deletions
diff --git a/engines/draci/draci.cpp b/engines/draci/draci.cpp
index 4060b678ad..a09f5488a9 100644
--- a/engines/draci/draci.cpp
+++ b/engines/draci/draci.cpp
@@ -211,6 +211,7 @@ bool DraciEngine::handleEvents() {
_game->setRoomNum(_game->getEscRoom());
_game->setGateNum(0);
_game->_roomChange = true;
+ _game->setExitLoop(true);
// End any currently running GPL programs
_script->endCurrentProgram();
diff --git a/engines/draci/font.h b/engines/draci/font.h
index e566a5ad54..e81b344af1 100644
--- a/engines/draci/font.h
+++ b/engines/draci/font.h
@@ -42,7 +42,8 @@ extern const Common::String kFontBig;
enum {
kFontColour1 = 2, kFontColour2 = 0,
kFontColour3 = 3, kFontColour4 = 4,
- kOverFontColour = 255, kTitleColour = 255
+ kOverFontColour = 255, kTitleColour = 255,
+ kLineActiveColour = 254, kLineInactiveColour = 255
};
/**
diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp
index 6c3ee96e98..2b861c46f8 100644
--- a/engines/draci/game.cpp
+++ b/engines/draci/game.cpp
@@ -35,6 +35,8 @@
namespace Draci {
+const Common::String dialoguePath("ROZH");
+
static double real_to_double(byte real[6]);
Game::Game(DraciEngine *vm) : _vm(vm) {
@@ -61,21 +63,24 @@ Game::Game(DraciEngine *vm) : _vm(vm) {
// Close persons file
file->close();
- // Read in dialog offsets
+ // Read in dialogue offsets
file = initArchive->getFile(4);
- Common::MemoryReadStream dialogData(file->_data, file->_length);
+ Common::MemoryReadStream dialogueData(file->_data, file->_length);
- unsigned int numDialogs = file->_length / sizeof(uint16);
- _dialogOffsets = new uint[numDialogs];
+ unsigned int numDialogues = file->_length / sizeof(uint16);
+ _dialogueOffsets = new uint[numDialogues];
unsigned int curOffset;
- for (i = 0, curOffset = 0; i < numDialogs; ++i) {
- _dialogOffsets[i] = curOffset;
- curOffset += dialogData.readUint16LE();
+ for (i = 0, curOffset = 0; i < numDialogues; ++i) {
+ _dialogueOffsets[i] = curOffset;
+ curOffset += dialogueData.readUint16LE();
}
- // Close dialogs file
+ _dialogueVars = new int[curOffset];
+ memset(_dialogueVars, 0, sizeof (int) * curOffset);
+
+ // Close dialogues file
file->close();
// Read in game info
@@ -89,7 +94,7 @@ Game::Game(DraciEngine *vm) : _vm(vm) {
_info._numIcons = gameData.readUint16LE();
_info._numVariables = gameData.readByte();
_info._numPersons = gameData.readByte();
- _info._numDialogs = gameData.readByte();
+ _info._numDialogues = gameData.readByte();
_info._maxIconWidth = gameData.readUint16LE();
_info._maxIconHeight = gameData.readUint16LE();
_info._musicLength = gameData.readUint16LE();
@@ -98,7 +103,7 @@ Game::Game(DraciEngine *vm) : _vm(vm) {
_info._crc[2] = gameData.readUint16LE();
_info._crc[3] = gameData.readUint16LE();
- _info._numDialogBlocks = curOffset;
+ _info._numDialogueBlocks = curOffset;
// Close game info file
file->close();
@@ -145,7 +150,7 @@ Game::Game(DraciEngine *vm) : _vm(vm) {
// Close object status file
file->close();
- assert(numDialogs == _info._numDialogs);
+ assert(numDialogues == _info._numDialogues);
assert(numPersons == _info._numPersons);
assert(numVariables == _info._numVariables);
assert(numObjects == _info._numObjects);
@@ -227,6 +232,19 @@ void Game::init() {
Text *speech = new Text("", _vm->_bigFont, kFontColour1, 0, 0);
speechAnim->addFrame(speech);
+ for (uint i = 0; i < kDialogueLines; ++i) {
+ _dialogueAnims[i] = _vm->_anims->addText(-10 - i, true);
+ Text *dialogueLine0 = new Text("", _vm->_smallFont, kLineInactiveColour, 0, 0);
+ _dialogueAnims[i]->addFrame(dialogueLine0);
+
+ _dialogueAnims[i]->setZ(254);
+ _dialogueAnims[i]->setRelative(1,
+ kScreenHeight - (i + 1) * _vm->_smallFont->getFontHeight());
+
+ Text *text = reinterpret_cast<Text *>(_dialogueAnims[i]->getFrame());
+ text->setText("");
+ }
+
loadObject(kDragonObject);
GameObject *dragon = getObject(kDragonObject);
@@ -254,11 +272,23 @@ void Game::loop() {
_vm->handleEvents();
- if (_currentRoom._mouseOn) {
+ // Fetch mouse coordinates
+ int x = _vm->_mouse->getPosX();
+ int y = _vm->_mouse->getPosY();
- // Fetch mouse coordinates
- int x = _vm->_mouse->getPosX();
- int y = _vm->_mouse->getPosY();
+ if (_loopStatus == kStatusDialogue && _loopSubstatus == kStatusOrdinary) {
+
+ // Find animation under cursor
+ _animUnderCursor = _vm->_anims->getTopAnimationID(x, y);
+
+ if (_vm->_mouse->lButtonPressed() || _vm->_mouse->rButtonPressed()) {
+ _shouldExitLoop = true;
+ _vm->_mouse->lButtonSet(false);
+ _vm->_mouse->rButtonSet(false);
+ }
+ }
+
+ if (_currentRoom._mouseOn) {
// Fetch the dedicated objects' title animation / current frame
Animation *titleAnim = _vm->_anims->getAnimation(kTitleText);
@@ -268,8 +298,8 @@ void Game::loop() {
if(_vm->_mouse->isCursorOn()) {
// Find the game object under the cursor
// (to be more precise, one that corresponds to the animation under the cursor)
- int animUnderCursor = _vm->_anims->getTopAnimationID(x, y);
- int curObject = getObjectWithAnimation(animUnderCursor);
+ _animUnderCursor = _vm->_anims->getTopAnimationID(x, y);
+ int curObject = getObjectWithAnimation(_animUnderCursor);
updateTitle();
@@ -342,11 +372,12 @@ void Game::loop() {
}
}
}
- debugC(2, kDraciAnimationDebugLevel, "Anim under cursor: %d", animUnderCursor);
}
}
}
+ debug(8, "Anim under cursor: %d", _animUnderCursor);
+
// Handle character talking (if there is any)
if (_loopSubstatus == kStatusTalk) {
Animation *speechAnim = _vm->_anims->getAnimation(kSpeechText);
@@ -382,7 +413,8 @@ void Game::loop() {
_vm->_system->delayMillis(20);
// HACK: Won't be needed once the game loop is implemented properly
- _shouldExitLoop = _shouldExitLoop || _roomChange;
+ _shouldExitLoop = _shouldExitLoop || (_roomChange &&
+ (_loopStatus == kStatusOrdinary || _loopStatus == kStatusGate));
} while (!shouldExitLoop());
}
@@ -469,7 +501,193 @@ int Game::getObjectWithAnimation(int animID) {
return kObjectNotFound;
}
+
+void Game::dialogueMenu(int dialogueID) {
+
+ int oldLines, hit;
+
+ char tmp[5];
+ sprintf(tmp, "%d", dialogueID+1);
+ Common::String ext(tmp);
+ _dialogueArchive = new BArchive(dialoguePath + ext + ".dfw");
+
+ debugC(4, kDraciLogicDebugLevel, "Starting dialogue (ID: %d, Archive: %s)",
+ dialogueID, (dialoguePath + ext + ".dfw").c_str());
+
+ _currentDialogue = dialogueID;
+ oldLines = 255;
+ dialogueInit(dialogueID);
+
+ do {
+ _dialogueExit = false;
+ hit = dialogueDraw();
+
+ debug(2, "Hit: %d, _lines[hit]: %d", hit, _lines[hit]);
+
+ if ((!_dialogueExit) && (hit != -1) && (_lines[hit] != -1)) {
+ if ((oldLines == 1) && (_dialogueLines == 1) && (_lines[hit] == _lastBlock)) {
+ break;
+ }
+ _currentBlock = _lines[hit];
+ runDialogueProg(_dialogueBlocks[_lines[hit]]._program, 1);
+ } else {
+ break;
+ }
+ _lastBlock = _lines[hit];
+ _dialogueVars[_dialogueOffsets[dialogueID] + _lastBlock + 1] += 1;
+ _dialogueBegin = false;
+ oldLines = _dialogueLines;
+
+ } while(!_dialogueExit);
+
+ dialogueDone();
+ _currentDialogue = kNoDialogue;
+}
+
+int Game::dialogueDraw() {
+ _dialogueLines = 0;
+ int i = 0;
+ int ret = 0;
+ Animation *anim;
+ Text *dialogueLine;
+
+ while ((_dialogueLines < 4) && (i < _blockNum)) {
+
+ GPL2Program blockTest;
+ blockTest._bytecode = _dialogueBlocks[i]._canBlock;
+ blockTest._length = _dialogueBlocks[i]._canLen;
+
+ debugC(3, kDraciLogicDebugLevel, "Testing dialogue block %d", i);
+ if (_vm->_script->testExpression(blockTest, 1)) {
+ anim = _dialogueAnims[_dialogueLines];
+ dialogueLine = reinterpret_cast<Text *>(anim->getFrame());
+ dialogueLine->setText(_dialogueBlocks[i]._title);
+
+ dialogueLine->setColour(kLineInactiveColour);
+ _lines[_dialogueLines] = i;
+ _dialogueLines++;
+ }
+ ++i;
+ }
+
+ for (i = _dialogueLines; i < kDialogueLines; ++i) {
+ _lines[i] = 0;
+ anim = _dialogueAnims[i];
+ dialogueLine = reinterpret_cast<Text *>(anim->getFrame());
+ dialogueLine->setText("");
+ }
+
+ _oldObjUnderCursor = kObjectNotFound;
+
+ if (_dialogueLines > 1) {
+ _vm->_mouse->cursorOn();
+ _shouldExitLoop = false;
+ loop();
+ _vm->_mouse->cursorOff();
+
+ bool notDialogueAnim = true;
+ for (uint j = 0; j < kDialogueLines; ++j) {
+ if (_dialogueAnims[j]->getID() == _animUnderCursor) {
+ notDialogueAnim = false;
+ break;
+ }
+ }
+
+ if (notDialogueAnim) {
+ ret = -1;
+ } else {
+ ret = _dialogueAnims[0]->getID() - _animUnderCursor;
+ }
+ } else {
+ ret = _dialogueLines - 1;
+ }
+
+ for (i = 0; i < kDialogueLines; ++i) {
+ dialogueLine = reinterpret_cast<Text *>(_dialogueAnims[i]->getFrame());
+ _dialogueAnims[i]->markDirtyRect(_vm->_screen->getSurface());
+ dialogueLine->setText("");
+ }
+
+ return ret;
+}
+
+void Game::dialogueInit(int dialogID) {
+ _vm->_mouse->setCursorType(kDialogueCursor);
+
+ _blockNum = _dialogueArchive->size() / 3;
+ _dialogueBlocks = new Dialogue[_blockNum];
+
+ BAFile *f;
+
+ for (uint i = 0; i < kDialogueLines; ++i) {
+ _lines[i] = 0;
+ }
+
+ for (int i = 0; i < _blockNum; ++i) {
+ f = _dialogueArchive->getFile(i * 3);
+ _dialogueBlocks[i]._canLen = f->_length;
+ _dialogueBlocks[i]._canBlock = f->_data;
+
+ f = _dialogueArchive->getFile(i * 3 + 1);
+
+ // The first byte of the file is the length of the string (without the length)
+ assert(f->_length - 1 == f->_data[0]);
+
+ _dialogueBlocks[i]._title = Common::String((char *)(f->_data+1), f->_length-1);
+
+ f = _dialogueArchive->getFile(i * 3 + 2);
+ _dialogueBlocks[i]._program._bytecode = f->_data;
+ _dialogueBlocks[i]._program._length = f->_length;
+ }
+
+ for (uint i = 0; i < kDialogueLines; ++i) {
+ _vm->_anims->play(_dialogueAnims[i]->getID());
+ }
+
+ _loopStatus = kStatusDialogue;
+ _lastBlock = 0;
+ _dialogueBegin = true;
+}
+
+void Game::dialogueDone() {
+ for (uint i = 0; i < kDialogueLines; ++i) {
+ _vm->_anims->stop(_dialogueAnims[i]->getID());
+ }
+
+ _dialogueArchive->closeArchive();
+
+ delete[] _dialogueBlocks;
+
+ _loopStatus = kStatusOrdinary;
+ _vm->_mouse->setCursorType(kNormalCursor);
+}
+
+void Game::runDialogueProg(GPL2Program prog, int offset) {
+
+ // Mark last animation
+ int lastAnimIndex = _vm->_anims->getLastIndex();
+
+ // Run gate program
+ _vm->_script->run(prog, offset);
+
+ // Delete all animations loaded after the marked one
+ // (from objects and from the AnimationManager)
+ for (uint i = 0; i < getNumObjects(); ++i) {
+ GameObject *obj = &_objects[i];
+
+ for (uint j = 0; j < obj->_anims.size(); ++j) {
+ Animation *anim;
+
+ anim = _vm->_anims->getAnimation(obj->_anims[j]);
+ if (anim != NULL && anim->getIndex() > lastAnimIndex)
+ obj->_anims.remove_at(j);
+ }
+ }
+
+ _vm->_anims->deleteAfterIndex(lastAnimIndex);
+}
+
void Game::walkHero(int x, int y) {
Surface *surface = _vm->_screen->getSurface();
@@ -937,7 +1155,7 @@ void Game::setMarkedAnimationIndex(int index) {
Game::~Game() {
delete[] _persons;
delete[] _variables;
- delete[] _dialogOffsets;
+ delete[] _dialogueOffsets;
delete[] _objects;
}
diff --git a/engines/draci/game.h b/engines/draci/game.h
index 47b940ce7e..a712542e9e 100644
--- a/engines/draci/game.h
+++ b/engines/draci/game.h
@@ -27,6 +27,7 @@
#define DRACI_GAME_H
#include "common/str.h"
+#include "draci/barchive.h"
#include "draci/script.h"
#include "draci/animation.h"
#include "draci/sprite.h"
@@ -68,6 +69,10 @@ enum {
kDefaultRoomMap = -1
};
+enum {
+ kNoDialogue = -1, kDialogueLines = 4
+};
+
enum SpeechConstants {
kBaseSpeechDuration = 200,
kSpeechTimeUnit = 400
@@ -134,11 +139,11 @@ struct GameInfo {
uint _numIcons;
byte _numVariables;
byte _numPersons;
- byte _numDialogs;
+ byte _numDialogues;
uint _maxIconWidth, _maxIconHeight;
uint _musicLength;
uint _crc[4];
- uint _numDialogBlocks;
+ uint _numDialogueBlocks;
};
struct Person {
@@ -146,6 +151,14 @@ struct Person {
byte _fontColour;
};
+struct Dialogue {
+ int _canLen;
+ byte *_canBlock;
+ Common::String _title;
+ GPL2Program _program;
+};
+
+
struct Room {
int _roomNum;
byte _music;
@@ -266,13 +279,18 @@ public:
void updateTitle();
void updateCursor();
+ void dialogueMenu(int dialogueID);
+ int dialogueDraw();
+ void dialogueInit(int dialogID);
+ void dialogueDone();
+ void runDialogueProg(GPL2Program, int offset);
+
bool _roomChange;
private:
DraciEngine *_vm;
GameInfo _info;
- uint *_dialogOffsets;
int *_variables;
byte *_iconStatus;
@@ -286,6 +304,22 @@ private:
int _currentIcon;
+// HACK: remove public when tested and add getters instead
+public:
+ uint *_dialogueOffsets;
+ int _currentDialogue;
+ int *_dialogueVars;
+ BArchive *_dialogueArchive;
+ Dialogue *_dialogueBlocks;
+ bool _dialogueBegin;
+ bool _dialogueExit;
+ int _currentBlock;
+ int _lastBlock;
+ int _dialogueLines;
+ int _blockNum;
+ int _lines[4];
+ Animation *_dialogueAnims[4];
+
LoopStatus _loopStatus;
LoopStatus _loopSubstatus;
@@ -296,6 +330,7 @@ private:
int _objUnderCursor;
int _oldObjUnderCursor;
+ int _animUnderCursor;
int _markedAnimationIndex; //!< Used by the Mark GPL command
};
diff --git a/engines/draci/mouse.h b/engines/draci/mouse.h
index f3e0123f59..d6df7f312d 100644
--- a/engines/draci/mouse.h
+++ b/engines/draci/mouse.h
@@ -34,7 +34,7 @@ namespace Draci {
enum CursorType {
kNormalCursor, kArrowCursor1,
kArrowCursor2, kArrowCursor3,
- kArrowCursor4, kDialogCursor,
+ kArrowCursor4, kDialogueCursor,
kHighlightedCursor, kMainMenuCursor
};
diff --git a/engines/draci/script.cpp b/engines/draci/script.cpp
index af64204f42..e7e4861e62 100644
--- a/engines/draci/script.cpp
+++ b/engines/draci/script.cpp
@@ -53,11 +53,11 @@ void Script::setupCommandList() {
{ 7, 1, "ObjStat", 2, { 3, 3 }, &Script::objStat },
{ 7, 2, "ObjStat_On", 2, { 3, 3 }, &Script::objStatOn },
{ 8, 1, "IcoStat", 2, { 3, 3 }, NULL },
- { 9, 1, "Dialogue", 1, { 2 }, NULL },
- { 9, 2, "ExitDialogue", 0, { 0 }, NULL },
- { 9, 3, "ResetDialogue", 0, { 0 }, NULL },
- { 9, 4, "ResetDialogueFrom", 0, { 0 }, NULL },
- { 9, 5, "ResetBlock", 1, { 3 }, NULL },
+ { 9, 1, "Dialogue", 1, { 2 }, &Script::dialogue },
+ { 9, 2, "ExitDialogue", 0, { 0 }, &Script::exitDialogue },
+ { 9, 3, "ResetDialogue", 0, { 0 }, &Script::resetDialogue },
+ { 9, 4, "ResetDialogueFrom", 0, { 0 }, &Script::resetDialogueFrom },
+ { 9, 5, "ResetBlock", 1, { 3 }, &Script::resetBlock },
{ 10, 1, "WalkOn", 3, { 1, 1, 3 }, &Script::walkOn },
{ 10, 2, "StayOn", 3, { 1, 1, 3 }, &Script::walkOn }, // HACK: not a proper implementation
{ 10, 3, "WalkOnPlay", 3, { 1, 1, 3 }, &Script::walkOn }, // HACK: not a proper implementation
@@ -126,11 +126,11 @@ void Script::setupCommandList() {
{ "IsObjOff", &Script::funcIsObjOff },
{ "IsObjAway", &Script::funcIsObjAway },
{ "ObjStat", &Script::funcObjStat },
- { "LastBlock", NULL },
- { "AtBegin", NULL },
- { "BlockVar", NULL },
- { "HasBeen", NULL },
- { "MaxLine", NULL },
+ { "LastBlock", &Script::funcLastBlock },
+ { "AtBegin", &Script::funcAtBegin },
+ { "BlockVar", &Script::funcBlockVar },
+ { "HasBeen", &Script::funcHasBeen },
+ { "MaxLine", &Script::funcMaxLine },
{ "ActPhase", &Script::funcActPhase },
{ "Cheat", NULL },
};
@@ -218,6 +218,27 @@ int Script::funcRandom(int n) {
return _vm->_rnd.getRandomNumber(n);
}
+int Script::funcAtBegin(int yesno) {
+ return _vm->_game->_dialogueBegin == yesno;
+}
+
+int Script::funcLastBlock(int blockID) {
+
+ return _vm->_game->_lastBlock == blockID;
+}
+
+int Script::funcBlockVar(int blockID) {
+ return _vm->_game->_dialogueVars[_vm->_game->_dialogueOffsets[_vm->_game->_currentDialogue] + blockID];
+}
+
+int Script::funcHasBeen(int blockID) {
+ return _vm->_game->_dialogueVars[_vm->_game->_dialogueOffsets[_vm->_game->_currentDialogue] + blockID] > 0;
+}
+
+int Script::funcMaxLine(int lines) {
+ return _vm->_game->_dialogueLines < lines;
+}
+
int Script::funcNot(int n) {
return !n;
}
@@ -373,8 +394,11 @@ void Script::start(Common::Queue<int> &params) {
return;
}
- int objID = params.pop() - 1;
- int animID = params.pop() - 1;
+ int objID = params.pop();
+ int animID = params.pop();
+
+ objID -= 1;
+ animID -= 1;
GameObject *obj = _vm->_game->getObject(objID);
@@ -623,12 +647,42 @@ void Script::talk(Common::Queue<int> &params) {
_vm->_game->setExitLoop(false);
}
+void Script::dialogue(Common::Queue<int> &params) {
+ int dialogueID = params.pop() - 1;
+
+ _vm->_game->dialogueMenu(dialogueID);
+}
+
void Script::loadMap(Common::Queue<int> &params) {
int mapID = params.pop() - 1;
_vm->_game->loadWalkingMap(mapID);
}
+void Script::resetDialogue(Common::Queue<int> &params) {
+
+ for (int i = 0; i < _vm->_game->_blockNum; ++i) {
+ _vm->_game->_dialogueVars[_vm->_game->_dialogueOffsets[_vm->_game->_currentDialogue]+i] = 0;
+ }
+}
+
+void Script::resetDialogueFrom(Common::Queue<int> &params) {
+
+ for (int i = _vm->_game->_currentBlock; i < _vm->_game->_blockNum; ++i) {
+ _vm->_game->_dialogueVars[_vm->_game->_dialogueOffsets[_vm->_game->_currentDialogue]+i] = 0;
+ }
+}
+
+void Script::resetBlock(Common::Queue<int> &params) {
+ int blockID = params.pop();
+
+ _vm->_game->_dialogueVars[_vm->_game->_dialogueOffsets[_vm->_game->_currentDialogue]+blockID] = 0;
+}
+
+void Script::exitDialogue(Common::Queue<int> &params) {
+ _vm->_game->_dialogueExit = true;
+}
+
void Script::roomMap(Common::Queue<int> &params) {
// Load the default walking map for the room
diff --git a/engines/draci/script.h b/engines/draci/script.h
index 6c85a7cc5b..b1f9ed9bbd 100644
--- a/engines/draci/script.h
+++ b/engines/draci/script.h
@@ -121,6 +121,11 @@ private:
void talk(Common::Queue<int> &params);
void loadMap(Common::Queue<int> &params);
void roomMap(Common::Queue<int> &params);
+ void dialogue(Common::Queue<int> &params);
+ void exitDialogue(Common::Queue<int> &params);
+ void resetDialogue(Common::Queue<int> &params);
+ void resetDialogueFrom(Common::Queue<int> &params);
+ void resetBlock(Common::Queue<int> &params);
int operAnd(int op1, int op2);
int operOr(int op1, int op2);
@@ -148,7 +153,11 @@ private:
int funcIsObjAway(int objID);
int funcActPhase(int objID);
int funcObjStat(int objID);
-
+ int funcLastBlock(int blockID);
+ int funcAtBegin(int yesno);
+ int funcBlockVar(int blockID);
+ int funcHasBeen(int blockID);
+ int funcMaxLine(int lines);
void setupCommandList();
const GPL2Command *findCommand(byte num, byte subnum);