diff options
| -rw-r--r-- | engines/hugo/hugo.cpp | 7 | ||||
| -rw-r--r-- | engines/hugo/hugo.h | 2 | ||||
| -rw-r--r-- | engines/hugo/parser.cpp | 764 | ||||
| -rw-r--r-- | engines/hugo/parser.h | 54 | 
4 files changed, 625 insertions, 202 deletions
| diff --git a/engines/hugo/hugo.cpp b/engines/hugo/hugo.cpp index 58629b5189..acc047b68d 100644 --- a/engines/hugo/hugo.cpp +++ b/engines/hugo/hugo.cpp @@ -154,7 +154,6 @@ Common::Error HugoEngine::run() {  	_mouseHandler = new MouseHandler(*this);  	_inventoryHandler = new InventoryHandler(*this); -	_parser = new Parser(*this);  	_route = new Route(*this);  	_soundHandler = new SoundHandler(*this); @@ -164,36 +163,42 @@ Common::Error HugoEngine::run() {  		_scheduler = new Scheduler_v3d(*this);  		_introHandler = new intro_v1w(*this);  		_screen = new Screen_v1w(*this); +		_parser = new Parser_v1w(*this);  		break;  	case 1:  		_fileManager = new FileManager_v2d(*this);  		_scheduler = new Scheduler_v3d(*this);  		_introHandler = new intro_v2w(*this);  		_screen = new Screen_v1w(*this); +		_parser = new Parser_v1w(*this);  		break;  	case 2:  		_fileManager = new FileManager_v2d(*this);  		_scheduler = new Scheduler_v3d(*this);  		_introHandler = new intro_v3w(*this);  		_screen = new Screen_v1w(*this); +		_parser = new Parser_v1w(*this);  		break;  	case 3: // H1 DOS  		_fileManager = new FileManager_v1d(*this);  		_scheduler = new Scheduler_v1d(*this);  		_introHandler = new intro_v1d(*this);  		_screen = new Screen_v1d(*this); +		_parser = new Parser_v1d(*this);  		break;  	case 4:  		_fileManager = new FileManager_v2d(*this);  		_scheduler = new Scheduler_v1d(*this);  		_introHandler = new intro_v2d(*this);  		_screen = new Screen_v1d(*this); +		_parser = new Parser_v2d(*this);  		break;  	case 5:  		_fileManager = new FileManager_v3d(*this);  		_scheduler = new Scheduler_v3d(*this);  		_introHandler = new intro_v3d(*this);  		_screen = new Screen_v1d(*this); +		_parser = new Parser_v1w(*this);  		break;  	} diff --git a/engines/hugo/hugo.h b/engines/hugo/hugo.h index 9ba7b47b22..9801546f34 100644 --- a/engines/hugo/hugo.h +++ b/engines/hugo/hugo.h @@ -33,7 +33,7 @@  #include "hugo/game.h"  #define HUGO_DAT_VER_MAJ 0  // 1 byte -#define HUGO_DAT_VER_MIN 23 // 1 byte +#define HUGO_DAT_VER_MIN 24 // 1 byte  #define DATAALIGNMENT 4  namespace Common { diff --git a/engines/hugo/parser.cpp b/engines/hugo/parser.cpp index 59fe023788..a232e9df96 100644 --- a/engines/hugo/parser.cpp +++ b/engines/hugo/parser.cpp @@ -56,6 +56,9 @@ Parser::Parser(HugoEngine &vm) :  	_vm(vm), _putIndex(0), _getIndex(0), _checkDoubleF1Fl(false) {  } +Parser::~Parser() { +} +  void Parser::keyHandler(uint16 nChar, uint16 nFlags) {  	debugC(1, kDebugParser, "keyHandler(%d, %d)", nChar, nFlags); @@ -202,8 +205,218 @@ void Parser::command(const char *format, ...) {  	lineHandler();  } +Parser_v1w::Parser_v1w(HugoEngine &vm) : Parser(vm) { +} + +Parser_v1w::~Parser_v1w() { +} + +// Test whether command line contains a verb allowed by this object. +// If it does, and the object is near and passes the tests in the command +// list then carry out the actions in the action list and return TRUE +bool Parser_v1w::isObjectVerb(object_t *obj, char *comment) { +	debugC(1, kDebugParser, "isObjectVerb(object_t *obj, %s)", comment); + +	// First, find matching verb in cmd list +	uint16 cmdIndex = obj->cmdIndex;                // ptr to list of commands +	if (cmdIndex == 0)                              // No commands for this obj +		return false; + +	int i; +	for (i = 0; _vm._cmdList[cmdIndex][i].verbIndex != 0; i++) {                 // For each cmd +		if (isWordPresent(_vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex]))        // Was this verb used? +			break; +	} + +	if (_vm._cmdList[cmdIndex][i].verbIndex == 0)   // No verbs used. +		return false; + +	// Verb match found.  Check if object is Near +	char *verb = *_vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex]; +	if (!isNear(obj, verb, comment)) +		return false; + +	// Check all required objects are being carried +	cmd *cmnd = &_vm._cmdList[cmdIndex][i];         // ptr to struct cmd +	if (cmnd->reqIndex) {                           // At least 1 thing in list +		uint16 *reqs = _vm._arrayReqs[cmnd->reqIndex];      // ptr to list of required objects +		for (i = 0; reqs[i]; i++) {                 // for each obj +			if (!isCarrying(reqs[i])) { +				Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataNoCarryIndex]); +				return true; +			} +		} +	} + +	// Required objects are present, now check state is correct +	if ((obj->state != cmnd->reqState) && (cmnd->reqState != DONT_CARE)) { +		Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataWrongIndex]); +		return true; +	} + +	// Everything checked.  Change the state and carry out any actions +	if (cmnd->reqState != DONT_CARE)                // Don't change new state if required state didn't care +		obj->state = cmnd->newState; +	Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataDoneIndex]); +	_vm.scheduler().insertActionList(cmnd->actIndex); + +	// See if any additional generic actions +	if ((verb == _vm._arrayVerbs[_vm._look][0]) || (verb == _vm._arrayVerbs[_vm._take][0]) || (verb == _vm._arrayVerbs[_vm._drop][0])) +		isGenericVerb(obj, comment); +	return true; +} + +// Test whether command line contains one of the generic actions +bool Parser_v1w::isGenericVerb(object_t *obj, char *comment) { +	debugC(1, kDebugParser, "isGenericVerb(object_t *obj, %s)", comment); + +	if (!obj->genericCmd) +		return false; + +	// Following is equivalent to switch, but couldn't do one +	if (isWordPresent(_vm._arrayVerbs[_vm._look]) && isNear(obj, _vm._arrayVerbs[_vm._look][0], comment)) { +		// Test state-dependent look before general look +		if ((obj->genericCmd & LOOK_S) == LOOK_S) { +			Utils::Box(BOX_ANY, "%s", _vm._textData[obj->stateDataIndex[obj->state]]); +			warning("isGenericVerb: use of state dependant look - To be validated"); +		} else { +			if ((LOOK & obj->genericCmd) == LOOK) { +				if (_vm._textData[obj->dataIndex]) +					Utils::Box(BOX_ANY, "%s", _vm._textData[obj->dataIndex]); +				else +					return false; +			} else { +				Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBUnusual]); +			} +		} +	} else if (isWordPresent(_vm._arrayVerbs[_vm._take]) && isNear(obj, _vm._arrayVerbs[_vm._take][0], comment)) { +		if (obj->carriedFl) +			Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBHave]); +		else if ((TAKE & obj->genericCmd) == TAKE) +			takeObject(obj); +		else if (obj->cmdIndex != 0)                // No comment if possible commands +			return false; +		else if (!obj->verbOnlyFl && (TAKE & obj->genericCmd) == TAKE)  // Make sure not taking object in context! +			Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoUse]); +		else +			return false; +	} else if (isWordPresent(_vm._arrayVerbs[_vm._drop])) { +		if (!obj->carriedFl && ((DROP & obj->genericCmd) == DROP)) +			Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBDontHave]); +		else if (obj->carriedFl && ((DROP & obj->genericCmd) == DROP)) +			dropObject(obj); +		else if (obj->cmdIndex == 0) +			Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNeed]); +		else +			return false; +	} else {                                        // It was not a generic cmd +		return false; +	} + +	return true; +} + +// Test whether hero is close to object.  Return TRUE or FALSE +// If object not near, return suitable comment; may be another object close +// If radius is -1, treat radius as infinity +// Verb is included to determine correct comment if not near +bool Parser_v1w::isNear(object_t *obj, char *verb, char *comment) { +	debugC(1, kDebugParser, "isNear(object_t *obj, %s, %s)", verb, comment); + +	if (obj->carriedFl)                             // Object is being carried +		return true; + +	if (obj->screenIndex != *_vm._screen_p) { +		// Not in same screen +		if (obj->objValue) +			strcpy(comment, _vm._textParser[kCmtAny1]); +		else +			strcpy(comment, _vm._textParser[kCmtAny2]); +		return false; +	} + +	if (obj->cycling == INVISIBLE) { +		if (obj->seqNumb) { +			// There is an image +			strcpy(comment, _vm._textParser[kCmtAny3]); +			return false; +		} else { +			// No image, assume visible +			if ((obj->radius < 0) || +			        ((abs(obj->x - _vm._hero->x) <= obj->radius) && +					(abs(obj->y - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) { +				return true; +			} else { +				// User is not close enough +				if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0])) +					strcpy(comment, _vm._textParser[kCmtAny1]); +				else +					strcpy(comment, _vm._textParser[kCmtClose]); +				return false; +			} +		} +	} + +	if ((obj->radius < 0) || +	    ((abs(obj->x - _vm._hero->x) <= obj->radius) && +	     (abs(obj->y + obj->currImagePtr->y2 - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) { +		return true; +	} else { +		// User is not close enough +		if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0])) +			strcpy(comment, _vm._textParser[kCmtAny1]); +		else +			strcpy(comment, _vm._textParser[kCmtClose]); +		return false; +	} +	return true; +} + +// Search for matching verbs in background command list. +// Noun is not required.  Return TRUE if match found +// Note that if the background command list has match set TRUE then do not +// print text if there are any recognizable nouns in the command line +bool Parser_v1w::isCatchallVerb(objectList_t obj) { +	debugC(1, kDebugParser, "isCatchallVerb(object_list_t obj)"); + +	for (int i = 0; obj[i].verbIndex != 0; i++) { +		if (isWordPresent(_vm._arrayVerbs[obj[i].verbIndex]) && obj[i].nounIndex == 0 && +		   (!obj[i].matchFl || !findNoun()) && +		   ((obj[i].roomState == DONT_CARE) || +		    (obj[i].roomState == _vm._screenStates[*_vm._screen_p]))) { +			Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex)); +			_vm.scheduler().processBonus(obj[i].bonusIndex); + +			// If this is LOOK (without a noun), show any takeable objects +			if (*(_vm._arrayVerbs[obj[i].verbIndex]) == _vm._arrayVerbs[_vm._look][0]) +				showTakeables(); + +			return true; +		} +	} +	return false; +} + +// Search for matching verb/noun pairs in background command list +// Print text for possible background object.  Return TRUE if match found +bool Parser_v1w::isBackgroundWord(objectList_t obj) { +	debugC(1, kDebugParser, "isBackgroundWord(object_list_t obj)"); + +	for (int i = 0; obj[i].verbIndex != 0; i++) { +		if (isWordPresent(_vm._arrayVerbs[obj[i].verbIndex]) && +		    isWordPresent(_vm._arrayNouns[obj[i].nounIndex]) && +		    ((obj[i].roomState == DONT_CARE) || +		     (obj[i].roomState == _vm._screenStates[*_vm._screen_p]))) { +			Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex)); +			_vm.scheduler().processBonus(obj[i].bonusIndex); +			return true; +		} +	} +	return false; +} +  // Parse the user's line of text input.  Generate events as necessary -void Parser::lineHandler() { +void Parser_v1w::lineHandler() {  	debugC(1, kDebugParser, "lineHandler");  	status_t &gameStatus = _vm.getGameStatus(); @@ -349,105 +562,6 @@ void Parser::lineHandler() {  	}  } -// Search for matching verb/noun pairs in background command list -// Print text for possible background object.  Return TRUE if match found -bool Parser::isBackgroundWord(objectList_t obj) { -	debugC(1, kDebugParser, "isBackgroundWord(object_list_t obj)"); - -	for (int i = 0; obj[i].verbIndex != 0; i++) { -		if (isWordPresent(_vm._arrayVerbs[obj[i].verbIndex]) && -		    isWordPresent(_vm._arrayNouns[obj[i].nounIndex]) && -		    ((obj[i].roomState == DONT_CARE) || -		     (obj[i].roomState == _vm._screenStates[*_vm._screen_p]))) { -			Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex)); -			_vm.scheduler().processBonus(obj[i].bonusIndex); -			return true; -		} -	} -	return false; -} - -// Search for matching verbs in background command list. -// Noun is not required.  Return TRUE if match found -// Note that if the background command list has match set TRUE then do not -// print text if there are any recognizable nouns in the command line -bool Parser::isCatchallVerb(objectList_t obj) { -	debugC(1, kDebugParser, "isCatchallVerb(object_list_t obj)"); - -	for (int i = 0; obj[i].verbIndex != 0; i++) { -		if (isWordPresent(_vm._arrayVerbs[obj[i].verbIndex]) && obj[i].nounIndex == 0 && -		        (!obj[i].matchFl || !findNoun()) && -		        ((obj[i].roomState == DONT_CARE) || -		         (obj[i].roomState == _vm._screenStates[*_vm._screen_p]))) { -			Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex)); -			_vm.scheduler().processBonus(obj[i].bonusIndex); - -			// If this is LOOK (without a noun), show any takeable objects -			if (*(_vm._arrayVerbs[obj[i].verbIndex]) == _vm._arrayVerbs[_vm._look][0]) -				showTakeables(); - -			return true; -		} -	} -	return false; -} - -// Test whether hero is close to object.  Return TRUE or FALSE -// If object not near, return suitable comment; may be another object close -// If radius is -1, treat radius as infinity -// Verb is included to determine correct comment if not near -bool Parser::isNear(object_t *obj, char *verb, char *comment) { -	debugC(1, kDebugParser, "isNear(object_t *obj, %s, %s)", verb, comment); - -	if (obj->carriedFl)                             // Object is being carried -		return true; - -	if (obj->screenIndex != *_vm._screen_p) { -		// Not in same screen -		if (obj->objValue) -			strcpy(comment, _vm._textParser[kCmtAny1]); -		else -			strcpy(comment, _vm._textParser[kCmtAny2]); -		return false; -	} - -	if (obj->cycling == INVISIBLE) { -		if (obj->seqNumb) { -			// There is an image -			strcpy(comment, _vm._textParser[kCmtAny3]); -			return false; -		} else { -			// No image, assume visible -			if ((obj->radius < 0) || -			        ((abs(obj->x - _vm._hero->x) <= obj->radius) && -					(abs(obj->y - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) { -				return true; -			} else { -				// User is not close enough -				if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0])) -					strcpy(comment, _vm._textParser[kCmtAny1]); -				else -					strcpy(comment, _vm._textParser[kCmtClose]); -				return false; -			} -		} -	} - -	if ((obj->radius < 0) || -	        ((abs(obj->x - _vm._hero->x) <= obj->radius) && -			(abs(obj->y + obj->currImagePtr->y2 - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) { -		return true; -	} else { -		// User is not close enough -		if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0])) -			strcpy(comment, _vm._textParser[kCmtAny1]); -		else -			strcpy(comment, _vm._textParser[kCmtClose]); -		return false; -	} -	return true; -} -  // Locate any member of object name list appearing in command line  bool Parser::isWordPresent(char **wordArr) {  	debugC(1, kDebugParser, "isWordPresent(%s)", wordArr[0]); @@ -494,8 +608,8 @@ void Parser::showTakeables() {  	for (int j = 0; j < _vm._numObj; j++) {  		object_t *obj = &_vm._objects[j];  		if ((obj->cycling != INVISIBLE) && -		        (obj->screenIndex == *_vm._screen_p) && -		        (((TAKE & obj->genericCmd) == TAKE) || obj->objValue)) { +		    (obj->screenIndex == *_vm._screen_p) && +		    (((TAKE & obj->genericCmd) == TAKE) || obj->objValue)) {  			Utils::Box(BOX_ANY, "You can also see:\n%s.", _vm._arrayNouns[obj->nounIndex][LOOK_NAME]);  		}  	} @@ -535,49 +649,168 @@ void Parser::dropObject(object_t *obj) {  	Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBOk]);  } -// Test whether command line contains one of the generic actions -bool Parser::isGenericVerb(object_t *obj, char *comment) { -	debugC(1, kDebugParser, "isGenericVerb(object_t *obj, %s)", comment); +// Return TRUE if object being carried by hero +bool Parser::isCarrying(uint16 wordIndex) { +	debugC(1, kDebugParser, "isCarrying(%d)", wordIndex); -	if (!obj->genericCmd) +	for (int i = 0; i < _vm._numObj; i++) { +		if ((wordIndex == _vm._objects[i].nounIndex) && _vm._objects[i].carriedFl) +			return true; +	} +	return false; +} + +void Parser::showDosInventory() { +// Show user all objects being carried in a variable width 2 column format +	static const char *blanks = "                                        "; +	uint16 index = 0, len1 = 0, len2 = 0; + +	for (int i = 0; i < _vm._numObj; i++) {         // Find widths of 2 columns +		if (_vm._objects[i].carriedFl) { +			uint16 len = strlen(_vm._arrayNouns[_vm._objects[i].nounIndex][1]); +			if (index++ & 1)                        // Right hand column +				len2 = (len > len2) ? len : len2; +			else +				len1 = (len > len1) ? len : len1; +		} +	} +	len1 += 1;                                      // For gap between columns + +	if (len1 + len2 < (uint16)strlen(_vm._textParser[kTBOutro])) +		len1 = strlen(_vm._textParser[kTBOutro]); + +	char buffer[XBYTES *NUM_ROWS] = "\0"; +	strncat(buffer, blanks, (len1 + len2 - strlen(_vm._textParser[kTBIntro])) / 2); +	strcat(strcat(buffer, _vm._textParser[kTBIntro]), "\n"); +	index = 0; +	for (int i = 0; i < _vm._numObj; i++) {         // Assign strings +		if (_vm._objects[i].carriedFl) { +			if (index++ & 1) +				strcat(strcat(buffer, _vm._arrayNouns[_vm._objects[i].nounIndex][1]), "\n"); +			else +				strncat(strcat(buffer, _vm._arrayNouns[_vm._objects[i].nounIndex][1]), blanks, len1 - strlen(_vm._arrayNouns[_vm._objects[i].nounIndex][1])); +		} +	} +	if (index & 1) +		strcat(buffer, "\n"); +	strcat(buffer, _vm._textParser[kTBOutro]); + +	Utils::Box(BOX_ANY, "%s", buffer); +} + +Parser_v1d::Parser_v1d(HugoEngine &vm) : Parser(vm) { +} + +Parser_v1d::~Parser_v1d() { +} + +// Locate word in list of nouns and return ptr to string in noun list +// If n is NULL, start at beginning of list, else with n +char *Parser_v1d::findNextNoun(char *n) { +	int k = -1; +	if (n) {                                        // If n not NULL, find index +		for (k = 0; _vm._arrayNouns[k]; k++) { +			if (n == _vm._arrayNouns[k][0]) +				break; +		} +	} +	for (int i = k + 1; _vm._arrayNouns[i]; i++) { +		for (int j = 0; strlen(_vm._arrayNouns[i][j]); j++) { +			if (strstr(_line, _vm._arrayNouns[i][j])) +				return _vm._arrayNouns[i][0]; +		} +	} +	return 0; +} + +// Test whether hero is close to object.  Return TRUE or FALSE +// If no noun specified, check context flag in object before other tests. +// If object not near, return suitable string; may be similar object closer +// If radius is -1, treat radius as infinity +bool Parser_v1d::isNear(char *verb, char *noun, object_t *obj, char *comment) { +	if (!noun && !obj->verbOnlyFl) {                // No noun specified & object not context senesitive +		return false; +	} else if (noun && (noun != _vm._arrayNouns[obj->nounIndex][0])) { // Noun specified & not same as object +		return false; +	} else if (obj->carriedFl) {                    // Object is being carried +		return true; +	} else if (obj->screenIndex != *_vm._screen_p) { // Not in same screen +		if (obj->objValue) +			strcpy (comment, "You don't have it!");  		return false; +	} -	// Following is equivalent to switch, but couldn't do one -	if (isWordPresent(_vm._arrayVerbs[_vm._look]) && isNear(obj, _vm._arrayVerbs[_vm._look][0], comment)) { -		// Test state-dependent look before general look -		if ((obj->genericCmd & LOOK_S) == LOOK_S) { -			Utils::Box(BOX_ANY, "%s", _vm._textData[obj->stateDataIndex[obj->state]]); -			warning("isGenericVerb: use of state dependant look - To be validated"); -		} else { -			if ((LOOK & obj->genericCmd) == LOOK) { -				if (_vm._textData[obj->dataIndex]) -					Utils::Box(BOX_ANY, "%s", _vm._textData[obj->dataIndex]); -				else -					return false; +	if (obj->cycling == INVISIBLE) { +		if (obj->seqNumb) {                         // There is an image +			strcpy(comment, "I don't see it anywhere"); +			return false; +		} else {                                    // No image, assume visible +			if ((obj->radius < 0) || +			   ((abs(obj->x - _vm._hero->x) <= obj->radius) && +			   (abs(obj->y - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) { +			   return true;  			} else { -				Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBUnusual]); +				// User is either not close enough (stationary, valueless objects) +				// or is not carrying it (small, portable objects of value) +				if (noun) {                         // Don't say unless object specified +					if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0])) +						strcpy(comment, "You don't have it!"); +					else +						strcpy(comment, "You're not close enough!"); +				} +				return false;  			}  		} -	} else if (isWordPresent(_vm._arrayVerbs[_vm._take]) && isNear(obj, _vm._arrayVerbs[_vm._take][0], comment)) { +	} + +	if ((obj->radius < 0) || +	    ((abs(obj->x - _vm._hero->x) <= obj->radius) && +	    (abs(obj->y + obj->currImagePtr->y2 - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) { +	   return true; +	} else { +		// User is either not close enough (stationary, valueless objects) +		// or is not carrying it (small, portable objects of value) +		if (noun) {                                 // Don't say unless object specified +			if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0])) +				strcpy(comment, "You don't have it!"); +			else +				strcpy(comment, "You're not close enough!"); +		} +		return false; +	} + +	return true; +} + +// Test whether supplied verb is one of the common variety for this object +// say_ok needed for special case of take/drop which may be handled not only +// here but also in a cmd_list with a donestr string simultaneously +bool Parser_v1d::isGenericVerb(char *word, object_t *obj) { +	if (!obj->genericCmd) +		return false; + +	// Following is equivalent to switch, but couldn't do one +	if (word == _vm._arrayVerbs[_vm._look][0]) { +		if ((LOOK & obj->genericCmd) == LOOK) +			Utils::Box(BOX_ANY, "%s", _vm._textData[obj->dataIndex]); +		else +			Utils::Box(BOX_ANY, "I see nothing special about it"); +	} else if (word == _vm._arrayVerbs[_vm._take][0]) {  		if (obj->carriedFl) -			Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBHave]); +			Utils::Box(BOX_ANY, "You already have it");  		else if ((TAKE & obj->genericCmd) == TAKE)  			takeObject(obj); -		else if (obj->cmdIndex != 0)                // No comment if possible commands -			return false; -		else if (!obj->verbOnlyFl && (TAKE & obj->genericCmd) == TAKE)  // Make sure not taking object in context! -			Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoUse]); +		else if (!obj->verbOnlyFl)                  // Make sure not taking object in context! +			Utils::Box(BOX_ANY, "It is of no use to you");  		else  			return false; -	} else if (isWordPresent(_vm._arrayVerbs[_vm._drop])) { -		if (!obj->carriedFl && ((DROP & obj->genericCmd) == DROP)) -			Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBDontHave]); -		else if (obj->carriedFl && ((DROP & obj->genericCmd) == DROP)) +	} else if (word == _vm._arrayVerbs[_vm._drop][0]) { +		if (!obj->carriedFl) +			Utils::Box(BOX_ANY, "You don't have it"); +		else if ((DROP & obj->genericCmd) == DROP)  			dropObject(obj); -		else if (obj->cmdIndex == 0) -			Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNeed]);  		else -			return false; +			Utils::Box(BOX_ANY, "No! You'll be needing it");  	} else {                                        // It was not a generic cmd  		return false;  	} @@ -585,46 +818,31 @@ bool Parser::isGenericVerb(object_t *obj, char *comment) {  	return true;  } -// Return TRUE if object being carried by hero -bool Parser::isCarrying(uint16 wordIndex) { -	debugC(1, kDebugParser, "isCarrying(%d)", wordIndex); - -	for (int i = 0; i < _vm._numObj; i++) { -		if ((wordIndex == _vm._objects[i].nounIndex) && _vm._objects[i].carriedFl) -			return true; -	} -	return false; -} - -// Test whether command line contains a verb allowed by this object. -// If it does, and the object is near and passes the tests in the command -// list then carry out the actions in the action list and return TRUE -bool Parser::isObjectVerb(object_t *obj, char *comment) { -	debugC(1, kDebugParser, "isObjectVerb(object_t *obj, %s)", comment); +// Test whether supplied verb is included in the list of allowed verbs for +// this object.  If it is, then perform the tests on it from the cmd list +// and if it passes, perform the actions in the action list.  If the verb +// is catered for, return TRUE +bool Parser_v1d::isObjectVerb(char *word, object_t *obj) { +//actlist  *actions;  	// First, find matching verb in cmd list  	uint16 cmdIndex = obj->cmdIndex;                // ptr to list of commands -	if (cmdIndex == 0)                              // No commands for this obj +	if (!cmdIndex)                                  // No commands for this obj  		return false;  	int i; -	for (i = 0; _vm._cmdList[cmdIndex][i].verbIndex != 0; i++) {                 // For each cmd -		if (isWordPresent(_vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex]))        // Was this verb used? +	for (i = 0; _vm._cmdList[cmdIndex][i].verbIndex != 0; i++) { // For each cmd +		if (!strcmp(word, _vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex][0])) // Is this verb catered for?  			break;  	} -	if (_vm._cmdList[cmdIndex][i].verbIndex == 0)   // No verbs used. -		return false; - -	// Verb match found.  Check if object is Near -	char *verb = *_vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex]; -	if (!isNear(obj, verb, comment)) +	if (_vm._cmdList[cmdIndex][i].verbIndex == 0)   // No  		return false; -	// Check all required objects are being carried +	// Verb match found, check all required objects are being carried  	cmd *cmnd = &_vm._cmdList[cmdIndex][i];         // ptr to struct cmd  	if (cmnd->reqIndex) {                           // At least 1 thing in list -		uint16 *reqs = _vm._arrayReqs[cmnd->reqIndex];      // ptr to list of required objects +		uint16 *reqs = _vm._arrayReqs[cmnd->reqIndex]; // ptr to list of required objects  		for (i = 0; reqs[i]; i++) {                 // for each obj  			if (!isCarrying(reqs[i])) {  				Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataNoCarryIndex]); @@ -634,7 +852,7 @@ bool Parser::isObjectVerb(object_t *obj, char *comment) {  	}  	// Required objects are present, now check state is correct -	if ((obj->state != cmnd->reqState) && (cmnd->reqState != DONT_CARE)) { +	if ((obj->state != cmnd->reqState) && (cmnd->reqState != DONT_CARE)){  		Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataWrongIndex]);  		return true;  	} @@ -644,49 +862,211 @@ bool Parser::isObjectVerb(object_t *obj, char *comment) {  		obj->state = cmnd->newState;  	Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataDoneIndex]);  	_vm.scheduler().insertActionList(cmnd->actIndex); - -	// See if any additional generic actions -	if ((verb == _vm._arrayVerbs[_vm._look][0]) || (verb == _vm._arrayVerbs[_vm._take][0]) || (verb == _vm._arrayVerbs[_vm._drop][0])) -		isGenericVerb(obj, comment); +	// Special case if verb is Take or Drop.  Assume additional generic actions +	if ((word == _vm._arrayVerbs[_vm._take][0]) || (word == _vm._arrayVerbs[_vm._drop][0])) +		isGenericVerb(word, obj);  	return true;  } -void Parser::showDosInventory() { -// Show user all objects being carried in a variable width 2 column format -	static const char *blanks = "                                        "; -	uint16 index = 0, len1 = 0, len2 = 0; +// Print text for possible background object.  Return TRUE if match found +// Only match if both verb and noun found.  Test_ca will match verb-only +bool Parser_v1d::isBackgroundWord(char *noun, char *verb, objectList_t obj) { +	if (!noun) +		return false; -	for (int i = 0; i < _vm._numObj; i++) {         // Find widths of 2 columns -		if (_vm._objects[i].carriedFl) { -			uint16 len = strlen(_vm._arrayNouns[_vm._objects[i].nounIndex][1]); -			if (index++ & 1)                        // Right hand column -				len2 = (len > len2) ? len : len2; -			else -				len1 = (len > len1) ? len : len1; +	for (int i = 0; obj[i].verbIndex; i++) { +		if ((verb == _vm._arrayVerbs[obj[i].verbIndex][0]) && (noun == _vm._arrayNouns[obj[i].nounIndex][0])) { +			Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex)); +			return true;  		}  	} -	len1 += 1;                                      // For gap between columns +	return false; +} -	if (len1 + len2 < (uint16)strlen(_vm._textParser[kTBOutro])) -		len1 = strlen(_vm._textParser[kTBOutro]); +// Print text for possible background object.  Return TRUE if match found +// If test_noun TRUE, must have a noun given +bool Parser_v1d::isCatchallVerb(bool test_noun, char *noun, char *verb, objectList_t obj) { +	if (test_noun && !noun) +		return false; -	char buffer[XBYTES *NUM_ROWS] = "\0"; -	strncat(buffer, blanks, (len1 + len2 - strlen(_vm._textParser[kTBIntro])) / 2); -	strcat(strcat(buffer, _vm._textParser[kTBIntro]), "\n"); -	index = 0; -	for (int i = 0; i < _vm._numObj; i++) {         // Assign strings -		if (_vm._objects[i].carriedFl) { -			if (index++ & 1) -				strcat(strcat(buffer, _vm._arrayNouns[_vm._objects[i].nounIndex][1]), "\n"); -			else -				strncat(strcat(buffer, _vm._arrayNouns[_vm._objects[i].nounIndex][1]), blanks, len1 - strlen(_vm._arrayNouns[_vm._objects[i].nounIndex][1])); +	for (int i = 0; obj[i].verbIndex; i++) { +		if ((verb == _vm._arrayVerbs[obj[i].verbIndex][0]) && ((noun == _vm._arrayNouns[obj[i].nounIndex][0]) || (obj[i].nounIndex == 0))) { +			Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex)); +			return true;  		}  	} -	if (index & 1) -		strcat(buffer, "\n"); -	strcat(buffer, _vm._textParser[kTBOutro]); +	return false; +} -	Utils::Box(BOX_ANY, "%s", buffer); +// Parse the user's line of text input.  Generate events as necessary +void Parser_v1d::lineHandler() { +	object_t    *obj; +	status_t &gameStatus = _vm.getGameStatus(); +	char        farComment[XBYTES * 5] = "";        // hold 5 line comment if object not nearby + +//	Reset_prompt_line (); +	Utils::strlwr(_line);                           // Convert to lower case + +	if (!strcmp("exit", _line) || strstr(_line, "quit")) { +		if (Utils::Box(BOX_YESNO, "Are you sure you want to QUIT?") != 0) +			_vm.endGame(); +		else +			return; +	} + +	// SAVE/RESTORE +	if (!strcmp("save", _line)) { +		_config.soundFl = false; +		if (gameStatus.gameOverFl) +			Utils::gameOverMsg(); +		else +//			_vm.file().saveOrRestore(true); +			warning("STUB: saveOrRestore()"); +		return; +	} + +	if (!strcmp("restore", _line)) { +		_config.soundFl = false; +//		_vm.file().saveOrRestore(false); +		warning("STUB: saveOrRestore()"); +		return; +	} + +	if (*_line == '\0')                             // Empty line +		return; + +	if (strspn(_line, " ") == strlen(_line))        // Nothing but spaces! +		return; + +	if (gameStatus.gameOverFl) {                    // No commands allowed! +		Utils::gameOverMsg(); +		return; +	} + +	// Find the first verb in the line +	char *verb = findVerb(); +	char *noun = 0;                                 // Noun not found yet + +	if (verb) {                                     // OK, verb found.  Try to match with object +		do { +			noun = findNextNoun(noun);              // Find a noun in the line +			// Must try at least once for objects allowing verb-context +			for (int i = 0; i < _vm._numObj; i++) { +				obj = &_vm._objects[i]; +				if (isNear(verb, noun, obj, farComment)) { +					if (isObjectVerb(verb, obj)     // Foreground object +					 || isGenericVerb(verb, obj))   // Common action type +						return; +				} +			} +			if ((*farComment == '\0') && isBackgroundWord(noun, verb, _vm._backgroundObjects[*_vm._screen_p])) +				return; +		} while (noun); +	} +	noun = findNextNoun(noun); +	if (*farComment != '\0')                        // An object matched but not near enough +		Utils::Box(BOX_ANY, "%s", farComment); +	else if (!isCatchallVerb(true, noun, verb, _vm._catchallList) && +		     !isCatchallVerb(false, noun, verb, _vm._backgroundObjects[*_vm._screen_p])  && +			 !isCatchallVerb(false, noun, verb, _vm._catchallList)) +		Utils::Box(BOX_ANY, "Apparently our hero either doesn't\n" +				   "understand what you mean or doesn't\n" +				   "think that would be very useful!"); +} + +Parser_v2d::Parser_v2d(HugoEngine &vm) : Parser_v1d(vm) { +} + +Parser_v2d::~Parser_v2d() { +} + +// Parse the user's line of text input.  Generate events as necessary +void Parser_v2d::lineHandler() { +	object_t    *obj; +	status_t &gameStatus = _vm.getGameStatus(); +	char        farComment[XBYTES * 5] = "";        // hold 5 line comment if object not nearby + +//	Reset_prompt_line (); +	Utils::strlwr(_line);                           // Convert to lower case + +	if (!strcmp("exit", _line) || strstr(_line, "quit")) { +		if (Utils::Box(BOX_YESNO, "Are you sure you want to QUIT?") != 0) +			_vm.endGame(); +		else +			return; +	} + +	// SAVE/RESTORE +	if (!strcmp("save", _line)) { +		_config.soundFl = false; +		if (gameStatus.gameOverFl) +			Utils::gameOverMsg(); +		else +//			_vm.file().saveOrRestore(true); +			warning("STUB: saveOrRestore()"); +		return; +	} + +	if (!strcmp("restore", _line)) { +		_config.soundFl = false; +//		_vm.file().saveOrRestore(false); +		warning("STUB: saveOrRestore()"); +		return; +	} + +	if (!strlen(_line))                             // Empty line +		return; + +	if (strspn(_line, " ") == strlen(_line))        // Nothing but spaces! +		return; + +	if (gameStatus.gameOverFl) {                    // No commands allowed! +		Utils::gameOverMsg(); +		return; +	} + +	// Find the first verb in the line +	char *verb = findVerb(); +	char *noun = 0;                                 // Noun not found yet + +	if (verb) {                                     // OK, verb found.  Try to match with object +		do { +			noun = findNextNoun(noun);              // Find a noun in the line +			// Must try at least once for objects allowing verb-context +			for (int i = 0; i < _vm._numObj; i++) { +				obj = &_vm._objects[i]; +				if (isNear(verb, noun, obj, farComment)) { +					if (isObjectVerb(verb, obj)     // Foreground object +					 || isGenericVerb(verb, obj))   // Common action type +						return; +				} +			} +			if ((*farComment != '\0') && isBackgroundWord(noun, verb, _vm._backgroundObjects[*_vm._screen_p])) +				return; +		} while (noun); +	} + +	noun = findNextNoun(noun); +	if (   !isCatchallVerb(true, noun, verb, _vm._backgroundObjects[*_vm._screen_p]) +		&& !isCatchallVerb(true, noun, verb, _vm._catchallList) +		&& !isCatchallVerb(false, noun, verb, _vm._backgroundObjects[*_vm._screen_p]) +		&& !isCatchallVerb(false, noun, verb, _vm._catchallList)) { +		if (*farComment != '\0') {                  // An object matched but not near enough +			Utils::Box(BOX_ANY, "%s", farComment); +		} else if (_maze.enabledFl && (verb == _vm._arrayVerbs[_vm._look][0])) { +			Utils::Box(BOX_ANY, "You are in a maze of\n" +					   "twisty little paths,\n" +					   "which are all alike!"); +			showTakeables(); +		} else if (verb && noun) {                  // A combination I didn't think of +			Utils::Box(BOX_ANY, "I don't think that would\n" +					   "accomplish much, somehow!"); +		} else if (verb || noun) { +			Utils::Box(BOX_ANY, "I don't fully understand!"); +		} else { +			Utils::Box(BOX_ANY, "I find that befuddling!"); +		} +	}  }  } // End of namespace Hugo diff --git a/engines/hugo/parser.h b/engines/hugo/parser.h index 8d2b326d5d..96e5085050 100644 --- a/engines/hugo/parser.h +++ b/engines/hugo/parser.h @@ -44,39 +44,77 @@ enum seqTextParser {  class Parser {  public:  	Parser(HugoEngine &vm); +	virtual ~Parser();  	bool  isWordPresent(char **wordArr);  	void  charHandler();  	void  command(const char *format, ...);  	void  keyHandler(uint16 nChar, uint16 nFlags); -	void  lineHandler(); +	virtual void lineHandler() = 0;  protected:  	HugoEngine &_vm; +protected: +	bool  isCarrying(uint16 wordIndex); + +	char *findNoun(); +	char *findVerb(); + +	void  dropObject(object_t *obj); +	void  takeObject(object_t *obj); +	void  showTakeables(); +  private:  	char   _ringBuffer[32];                         // Ring buffer  	uint16 _putIndex;  	uint16 _getIndex;                               // Index into ring buffer  	bool   _checkDoubleF1Fl;                        // Flag used to display user help or instructions +	void  showDosInventory(); +}; + +class Parser_v1w : public Parser { +public: +	Parser_v1w(HugoEngine &vm); +	~Parser_v1w(); + +	virtual void  lineHandler(); + +private:  	bool  isBackgroundWord(objectList_t obj); -	bool  isCarrying(uint16 wordIndex);  	bool  isCatchallVerb(objectList_t obj);  	bool  isGenericVerb(object_t *obj, char *comment);  	bool  isNear(object_t *obj, char *verb, char *comment);  	bool  isObjectVerb(object_t *obj, char *comment); +}; -	char *findNoun(); -	char *findVerb(); +class Parser_v1d : public Parser { +public: +	Parser_v1d(HugoEngine &vm); +	~Parser_v1d(); + +	void lineHandler(); + +protected: +	char *findNextNoun(char *noun); +	bool isNear(char *verb, char *noun, object_t *obj, char *comment); +	bool isGenericVerb(char *word, object_t *obj); +	bool isObjectVerb(char *word, object_t *obj); +	bool isBackgroundWord(char *noun, char *verb, objectList_t obj); +	bool isCatchallVerb(bool test_noun, char *noun, char *verb, objectList_t obj); -	void  dropObject(object_t *obj); -	void  showDosInventory(); -	void  showTakeables(); -	void  takeObject(object_t *obj);  }; +class Parser_v2d : public Parser_v1d { +public: +	Parser_v2d(HugoEngine &vm); +	~Parser_v2d(); + +	void lineHandler(); +}; +	  } // End of namespace Hugo  #endif //HUGO_PARSER_H | 
