diff options
| -rw-r--r-- | engines/draci/draci.cpp | 1 | ||||
| -rw-r--r-- | engines/draci/font.h | 3 | ||||
| -rw-r--r-- | engines/draci/game.cpp | 258 | ||||
| -rw-r--r-- | engines/draci/game.h | 41 | ||||
| -rw-r--r-- | engines/draci/mouse.h | 2 | ||||
| -rw-r--r-- | engines/draci/script.cpp | 78 | ||||
| -rw-r--r-- | engines/draci/script.h | 11 | 
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> ¶ms) {  		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> ¶ms) {  	_vm->_game->setExitLoop(false);  } +void Script::dialogue(Common::Queue<int> ¶ms) { +	int dialogueID = params.pop() - 1; + +	_vm->_game->dialogueMenu(dialogueID); +} +  void Script::loadMap(Common::Queue<int> ¶ms) {  	int mapID = params.pop() - 1;  	_vm->_game->loadWalkingMap(mapID);  } +void Script::resetDialogue(Common::Queue<int> ¶ms) { +	 +	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> ¶ms) { + +	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> ¶ms) { +	int blockID = params.pop(); + +	_vm->_game->_dialogueVars[_vm->_game->_dialogueOffsets[_vm->_game->_currentDialogue]+blockID] = 0; +} + +void Script::exitDialogue(Common::Queue<int> ¶ms) { +	_vm->_game->_dialogueExit = true; +} +  void Script::roomMap(Common::Queue<int> ¶ms) {  	// 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> ¶ms);  	void loadMap(Common::Queue<int> ¶ms);  	void roomMap(Common::Queue<int> ¶ms); +	void dialogue(Common::Queue<int> ¶ms); +	void exitDialogue(Common::Queue<int> ¶ms); +	void resetDialogue(Common::Queue<int> ¶ms); +	void resetDialogueFrom(Common::Queue<int> ¶ms); +	void resetBlock(Common::Queue<int> ¶ms);  	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); | 
