1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 
24 #include "queen/command.h"
25 
26 #include "queen/display.h"
27 #include "queen/input.h"
28 #include "queen/graphics.h"
29 #include "queen/grid.h"
30 #include "queen/logic.h"
31 #include "queen/queen.h"
32 #include "queen/resource.h"
33 #include "queen/sound.h"
34 #include "queen/state.h"
35 #include "queen/walk.h"
36 
37 namespace Queen {
38 
CmdText(uint8 y,QueenEngine * vm)39 CmdText::CmdText(uint8 y, QueenEngine *vm)
40 	: _y(y), _vm(vm) {
41 	clear();
42 }
43 
clear()44 void CmdText::clear() {
45 	memset(_command, 0, sizeof(_command));
46 }
47 
display(InkColor color,const char * command,bool outlined)48 void CmdText::display(InkColor color, const char *command, bool outlined) {
49 	_vm->display()->textCurrentColor(_vm->display()->getInkColor(color));
50 	if (!command) {
51 		command = _command;
52 	}
53 	_vm->display()->setTextCentered(_y, command, outlined);
54 }
55 
displayTemp(InkColor color,Verb v)56 void CmdText::displayTemp(InkColor color, Verb v) {
57 	char temp[MAX_COMMAND_LEN];
58 	strcpy(temp, _vm->logic()->verbName(v));
59 	display(color, temp, false);
60 }
61 
displayTemp(InkColor color,const char * name,bool outlined)62 void CmdText::displayTemp(InkColor color, const char *name, bool outlined) {
63 	char temp[MAX_COMMAND_LEN + 2];
64 	snprintf(temp, MAX_COMMAND_LEN + 1, "%s %s", _command, name);
65 	display(color, temp, outlined);
66 }
67 
setVerb(Verb v)68 void CmdText::setVerb(Verb v) {
69 	strcpy(_command, _vm->logic()->verbName(v));
70 }
71 
addLinkWord(Verb v)72 void CmdText::addLinkWord(Verb v) {
73 	strcat(_command, " ");
74 	strcat(_command, _vm->logic()->verbName(v));
75 }
76 
addObject(const char * objName)77 void CmdText::addObject(const char *objName) {
78 	strcat(_command, " ");
79 	strcat(_command, objName);
80 }
81 
82 class CmdTextHebrew : public CmdText {
83 public:
84 
CmdTextHebrew(uint8 y,QueenEngine * vm)85 	CmdTextHebrew(uint8 y, QueenEngine *vm) : CmdText(y, vm) {}
86 
displayTemp(InkColor color,const char * name,bool outlined)87 	void displayTemp(InkColor color, const char *name, bool outlined) override {
88 		char temp[MAX_COMMAND_LEN + 2];
89 
90 		snprintf(temp, MAX_COMMAND_LEN + 1, "%s %s", name, _command);
91 		display(color, temp, outlined);
92 	}
93 
addLinkWord(Verb v)94 	void addLinkWord(Verb v) override {
95 		char temp[MAX_COMMAND_LEN];
96 
97 		strcpy(temp, _command);
98 		strcpy(_command, _vm->logic()->verbName(v));
99 		strcat(_command, " ");
100 		strcat(_command, temp);
101 	}
102 
addObject(const char * objName)103 	void addObject(const char *objName) override {
104 		char temp[MAX_COMMAND_LEN];
105 
106 		strcpy(temp, _command);
107 		strcpy(_command, objName);
108 		strcat(_command, " ");
109 		strcat(_command, temp);
110 	}
111 };
112 
113 class CmdTextGreek : public CmdText {
114 public:
115 
CmdTextGreek(uint8 y,QueenEngine * vm)116 	CmdTextGreek(uint8 y, QueenEngine *vm) : CmdText(y, vm) {}
117 
displayTemp(InkColor color,const char * name,bool outlined)118 	void displayTemp(InkColor color, const char *name, bool outlined) override {
119 		char temp[MAX_COMMAND_LEN + 2];
120 		// don't show a space after the goto and give commands in the Greek version
121 		if (_command[1] != (char)-34 && !(_command[1] == (char)-2 && strlen(_command) > 5))
122 			snprintf(temp, MAX_COMMAND_LEN + 1, "%s %s", _command, name);
123 		else
124 			snprintf(temp, MAX_COMMAND_LEN + 1, "%s%s", _command, name);
125 		display(color, temp, outlined);
126 	}
127 
addObject(const char * objName)128 	void addObject(const char *objName) override {
129 		// don't show a space after the goto and give commands in the Greek version
130 		if (_command[1] != (char)-34 && !(_command[1] == (char)-2 && strlen(_command) > 5))
131 			strcat(_command, " ");
132 		strcat(_command, objName);
133 	}
134 };
135 
makeCmdTextInstance(uint8 y,QueenEngine * vm)136 CmdText *CmdText::makeCmdTextInstance(uint8 y, QueenEngine *vm) {
137 	switch (vm->resource()->getLanguage()) {
138 	case Common::HE_ISR:
139 		return new CmdTextHebrew(y, vm);
140 	case Common::GR_GRE:
141 		return new CmdTextGreek(y, vm);
142 	default:
143 		return new CmdText(y, vm);
144 	}
145 }
146 
init()147 void CmdState::init() {
148 	commandLevel = 1;
149 	oldVerb = verb = action = VERB_NONE;
150 	oldNoun = noun = subject[0] = subject[1] = 0;
151 
152 	selAction = VERB_NONE;
153 	selNoun = 0;
154 }
155 
Command(QueenEngine * vm)156 Command::Command(QueenEngine *vm)
157 	: _cmdList(NULL), _cmdArea(NULL), _cmdObject(NULL), _cmdInventory(NULL), _cmdGameState(NULL), _vm(vm) {
158 	_cmdText = CmdText::makeCmdTextInstance(CmdText::COMMAND_Y_POS, vm);
159 }
160 
~Command()161 Command::~Command() {
162 	delete _cmdText;
163 	delete[] _cmdList;
164 	delete[] _cmdArea;
165 	delete[] _cmdObject;
166 	delete[] _cmdInventory;
167 	delete[] _cmdGameState;
168 }
169 
clear(bool clearTexts)170 void Command::clear(bool clearTexts) {
171 	debug(6, "Command::clear(%d)", clearTexts);
172 	_cmdText->clear();
173 	if (clearTexts) {
174 		_vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
175 	}
176 	_parse = false;
177 	_state.init();
178 }
179 
executeCurrentAction()180 void Command::executeCurrentAction() {
181 	_vm->logic()->entryObj(0);
182 
183 	if (_mouseKey == Input::MOUSE_RBUTTON && _state.subject[0] > 0) {
184 
185 		ObjectData *od = _vm->logic()->objectData(_state.subject[0]);
186 		if (od == NULL || od->name <= 0) {
187 			cleanupCurrentAction();
188 			return;
189 		}
190 
191 		_state.verb = State::findDefaultVerb(od->state);
192 		_state.selAction = (_state.verb == VERB_NONE) ? VERB_WALK_TO : _state.verb;
193 		_cmdText->setVerb(_state.selAction);
194 		_cmdText->addObject(_vm->logic()->objectName(od->name));
195 	}
196 
197 	// always highlight the current command when actioned
198 	_cmdText->display(INK_CMD_SELECT);
199 
200 	_state.selNoun = _state.noun;
201 	_state.commandLevel = 1;
202 
203 	if (handleWrongAction()) {
204 		cleanupCurrentAction();
205 		return;
206 	}
207 
208 	// get the commands associated with this object/item
209 	uint16 comMax = 0;
210 	uint16 matchingCmds[MAX_MATCHING_CMDS];
211 	CmdListData *cmdList = &_cmdList[1];
212 	uint16 i;
213 	for (i = 1; i <= _numCmdList; ++i, ++cmdList) {
214 		if (cmdList->match(_state.selAction, _state.subject[0], _state.subject[1])) {
215 			assert(comMax < MAX_MATCHING_CMDS);
216 			matchingCmds[comMax] = i;
217 			++comMax;
218 		}
219 	}
220 
221 	debug(6, "Command::executeCurrentAction() - comMax=%d subj1=%X subj2=%X", comMax, _state.subject[0], _state.subject[1]);
222 
223 	if (comMax == 0) {
224 		sayInvalidAction(_state.selAction, _state.subject[0], _state.subject[1]);
225 		clear(true);
226 		cleanupCurrentAction();
227 		return;
228 	}
229 
230 	// process each associated command for the Object, until all done
231 	// or one of the Gamestate tests fails...
232 	int16 cond = 0;
233 	CmdListData *com = &_cmdList[0];
234 	uint16 comId = 0;
235 	for (i = 1; i <= comMax; ++i) {
236 
237 		comId = matchingCmds[i - 1];
238 
239 		// WORKAROUND bug #2636: This command is triggered in room 56 (the
240 		// room with two waterfalls in the maze part of the game) if the user
241 		// tries to walk through the left waterfall (object 423).
242 		//
243 		// Normally, this would move Joe to room 101 on the upper level and
244 		// start a cutscene. Joe would notice that Yan has been trapped (on
245 		// the lower level of the same room). The problem would then appear :
246 		// Joe is stuck behind the waterfall due to a walkbox issue. We could
247 		// fix the walkbox issue, but then Joe would walk through the waterfall
248 		// which wouldn't look that nice, graphically.
249 		//
250 		// Since this command isn't necessary to complete the game and doesn't
251 		// really makes sense here, we just skip it for now. The same cutscene
252 		// is already played in command 648, so the user don't miss anything
253 		// from the story/experience pov.
254 		//
255 		// Note: this happens with the original engine, too.
256 
257 		if (comId == 649) {
258 			continue;
259 		}
260 
261 		com = &_cmdList[comId];
262 
263 		// check the Gamestates and set them if necessary
264 		cond = 0;
265 		if (com->setConditions) {
266 			cond = setConditions(comId, (i == comMax));
267 		}
268 
269 		if (cond == -1 && i == comMax) {
270 			// only exit on a condition fail if at last command
271 			// Joe hasnt spoken, so do normal LOOK command
272 			break;
273 		} else if (cond == -2 && i == comMax) {
274 			// only exit on a condition fail if at last command
275 			// Joe has spoken, so skip LOOK command
276 			cleanupCurrentAction();
277 			return;
278 		} else if (cond >= 0) {
279 			// we've had a successful Gamestate check, so we must now exit
280 			cond = executeCommand(comId, cond);
281 			break;
282 		}
283 	}
284 
285 	if (_state.selAction == VERB_USE_JOURNAL) {
286 		clear(true);
287 	} else {
288 		if (cond <= 0 && _state.selAction == VERB_LOOK_AT) {
289 			lookAtSelectedObject();
290 		} else {
291 			// only play song if it's a PLAY AFTER type
292 			if (com->song < 0) {
293 				_vm->sound()->playSong(-com->song);
294 			}
295 			clear(true);
296 		}
297 		cleanupCurrentAction();
298 	}
299 }
300 
updatePlayer()301 void Command::updatePlayer() {
302 	if (_vm->logic()->joeWalk() != JWM_MOVE) {
303 		Common::Point mouse = _vm->input()->getMousePos();
304 		lookForCurrentObject(mouse.x, mouse.y);
305 		lookForCurrentIcon(mouse.x, mouse.y);
306 	}
307 
308 	if (_vm->input()->keyVerb() != VERB_NONE) {
309 		if (_vm->input()->keyVerb() == VERB_USE_JOURNAL) {
310 			_vm->logic()->useJournal();
311 		} else if (_vm->input()->keyVerb() != VERB_SKIP_TEXT) {
312 			_state.verb = _vm->input()->keyVerb();
313 			if (isVerbInv(_state.verb)) {
314 				_state.noun = _state.selNoun = 0;
315 				_state.oldNoun = 0;
316 				_state.oldVerb = VERB_NONE;
317 				grabSelectedItem();
318 			} else {
319 				grabSelectedVerb();
320 			}
321 		}
322 		_vm->input()->clearKeyVerb();
323 	}
324 
325 	_mouseKey = _vm->input()->mouseButton();
326 	_vm->input()->clearMouseButton();
327 	if (_mouseKey > 0) {
328 		grabCurrentSelection();
329 	}
330 }
331 
readCommandsFrom(byte * & ptr)332 void Command::readCommandsFrom(byte *&ptr) {
333 	uint16 i;
334 
335 	_numCmdList = READ_BE_UINT16(ptr); ptr += 2;
336 	_cmdList = new CmdListData[_numCmdList + 1];
337 	if (_numCmdList == 0) {
338 		_cmdList[0].readFromBE(ptr);
339 	} else {
340 		memset(&_cmdList[0], 0, sizeof(CmdListData));
341 		for (i = 1; i <= _numCmdList; i++) {
342 			_cmdList[i].readFromBE(ptr);
343 		}
344 	}
345 
346 	_numCmdArea = READ_BE_UINT16(ptr); ptr += 2;
347 	_cmdArea = new CmdArea[_numCmdArea + 1];
348 	if (_numCmdArea == 0) {
349 		_cmdArea[0].readFromBE(ptr);
350 	} else {
351 		memset(&_cmdArea[0], 0, sizeof(CmdArea));
352 		for (i = 1; i <= _numCmdArea; i++) {
353 			_cmdArea[i].readFromBE(ptr);
354 		}
355 	}
356 
357 	_numCmdObject = READ_BE_UINT16(ptr); ptr += 2;
358 	_cmdObject = new CmdObject[_numCmdObject + 1];
359 	if (_numCmdObject == 0) {
360 		_cmdObject[0].readFromBE(ptr);
361 	} else {
362 		memset(&_cmdObject[0], 0, sizeof(CmdObject));
363 		for (i = 1; i <= _numCmdObject; i++) {
364 			_cmdObject[i].readFromBE(ptr);
365 
366 			// WORKAROUND bug #3536: Fix an off by one error in the object
367 			// command 175. Object 309 should be copied to 308 (disabled).
368 			//
369 			// _objectData[307].name = -195
370 			// _objectData[308].name = 50
371 			// _objectData[309].name = -50
372 
373 			if (i == 175 && _cmdObject[i].id == 320 && _cmdObject[i].dstObj == 307 && _cmdObject[i].srcObj == 309) {
374 				_cmdObject[i].dstObj = 308;
375 			}
376 		}
377 	}
378 
379 	_numCmdInventory = READ_BE_UINT16(ptr);	ptr += 2;
380 	_cmdInventory = new CmdInventory[_numCmdInventory + 1];
381 	if (_numCmdInventory == 0) {
382 		_cmdInventory[0].readFromBE(ptr);
383 	} else {
384 		memset(&_cmdInventory[0], 0, sizeof(CmdInventory));
385 		for (i = 1; i <= _numCmdInventory; i++) {
386 			_cmdInventory[i].readFromBE(ptr);
387 		}
388 	}
389 
390 	_numCmdGameState = READ_BE_UINT16(ptr);	ptr += 2;
391 	_cmdGameState = new CmdGameState[_numCmdGameState + 1];
392 	if (_numCmdGameState == 0) {
393 		_cmdGameState[0].readFromBE(ptr);
394 	} else {
395 		memset(&_cmdGameState[0], 0, sizeof(CmdGameState));
396 		for (i = 1; i <= _numCmdGameState; i++) {
397 			_cmdGameState[i].readFromBE(ptr);
398 		}
399 	}
400 }
401 
findObjectData(uint16 objRoomNum) const402 ObjectData *Command::findObjectData(uint16 objRoomNum) const {
403 	ObjectData *od = NULL;
404 	if (objRoomNum != 0) {
405 		objRoomNum += _vm->logic()->currentRoomData();
406 		od = _vm->logic()->objectData(objRoomNum);
407 	}
408 	return od;
409 }
410 
findItemData(Verb invNum) const411 ItemData *Command::findItemData(Verb invNum) const {
412 	ItemData *id = NULL;
413 	uint16 itNum = _vm->logic()->findInventoryItem(invNum - VERB_INV_FIRST);
414 	if (itNum != 0) {
415 		id = _vm->logic()->itemData(itNum);
416 	}
417 	return id;
418 }
419 
executeCommand(uint16 comId,int16 condResult)420 int16 Command::executeCommand(uint16 comId, int16 condResult) {
421 	// execute.c l.313-452
422 	debug(6, "Command::executeCommand() - cond = %X, com = %X", condResult, comId);
423 
424 	CmdListData *com = &_cmdList[comId];
425 
426 	if (com->setAreas) {
427 		setAreas(comId);
428 	}
429 
430 	// don't try to grab if action is TALK or WALK
431 	if (_state.selAction != VERB_TALK_TO && _state.selAction != VERB_WALK_TO) {
432 		int i;
433 		for  (i = 0; i < 2; ++i) {
434 			int16 obj = _state.subject[i];
435 			if (obj > 0) {
436 				_vm->logic()->joeGrab(State::findGrab(_vm->logic()->objectData(obj)->state));
437 			}
438 		}
439 	}
440 
441 	bool cutDone = false;
442 	if (condResult > 0) {
443 		// check for cutaway/dialogs before updating Objects
444 		const char *desc = _vm->logic()->objectTextualDescription(condResult);
445 		if (executeIfCutaway(desc)) {
446 			condResult = 0;
447 			cutDone = true;
448 		} else if (executeIfDialog(desc)) {
449 			condResult = 0;
450 		}
451 	}
452 
453 	int16 oldImage = 0;
454 	if (_state.subject[0] > 0) {
455 		// an object (not an item)
456 		oldImage = _vm->logic()->objectData(_state.subject[0])->image;
457 	}
458 
459 	if (com->setObjects) {
460 		setObjects(comId);
461 	}
462 
463 	if (com->setItems) {
464 		setItems(comId);
465 	}
466 
467 	if (com->imageOrder != 0 && _state.subject[0] > 0) {
468 		ObjectData *od = _vm->logic()->objectData(_state.subject[0]);
469 		// we must update the graphic image of the object
470 		if (com->imageOrder < 0) {
471 			// instead of setting to -1 or -2, flag as negative
472 			if (od->image > 0) {
473 				// make sure that object is not already updated
474 				od->image = -(od->image + 10);
475 			}
476 		} else {
477 			od->image = com->imageOrder;
478 		}
479 		_vm->graphics()->refreshObject(_state.subject[0]);
480 	} else {
481 		// this object is not being updated by command list, see if
482 		// it has another image copied to it
483 		if (_state.subject[0] > 0) {
484 			// an object (not an item)
485 			if (_vm->logic()->objectData(_state.subject[0])->image != oldImage) {
486 				_vm->graphics()->refreshObject(_state.subject[0]);
487 			}
488 		}
489 	}
490 
491 	// don't play music on an OPEN/CLOSE command - in case the command fails
492 	if (_state.selAction != VERB_NONE &&
493 		_state.selAction != VERB_OPEN &&
494 		_state.selAction != VERB_CLOSE) {
495 		// only play song if it's a PLAY BEFORE type
496 		if (com->song > 0) {
497 			_vm->sound()->playSong(com->song);
498 		}
499 	}
500 
501 	// do a special hardcoded section
502 	// l.419-452 execute.c
503 	switch (com->specialSection) {
504 	case 1:
505 		_vm->logic()->useJournal();
506 		_state.selAction = VERB_USE_JOURNAL;
507 		return condResult;
508 	case 2:
509 		_vm->logic()->joeUseDress(true);
510 		break;
511 	case 3:
512 		_vm->logic()->joeUseClothes(true);
513 		break;
514 	case 4:
515 		_vm->logic()->joeUseUnderwear();
516 		break;
517 	default:
518 		break;
519 	}
520 
521 	if (_state.subject[0] > 0)
522 		changeObjectState(_state.selAction, _state.subject[0], com->song, cutDone);
523 
524 	if (condResult > 0) {
525 		_vm->logic()->makeJoeSpeak(condResult, true);
526 	}
527 	return condResult;
528 }
529 
makeJoeWalkTo(int16 x,int16 y,int16 objNum,Verb v,bool mustWalk)530 int16 Command::makeJoeWalkTo(int16 x, int16 y, int16 objNum, Verb v, bool mustWalk) {
531 	// Check to see if object is actually an exit to another
532 	// room. If so, then set up new room
533 	ObjectData *objData = _vm->logic()->objectData(objNum);
534 	if (objData->x != 0 || objData->y != 0) {
535 		x = objData->x;
536 		y = objData->y;
537 	}
538 	if (v == VERB_WALK_TO) {
539 		_vm->logic()->entryObj(objData->entryObj);
540 		if (objData->entryObj > 0) {
541 			_vm->logic()->newRoom(_vm->logic()->objectData(objData->entryObj)->room);
542 			// because this is an exit object, see if there is
543 			// a walk off point and set (x,y) accordingly
544 			WalkOffData *wod = _vm->logic()->walkOffPointForObject(objNum);
545 			if (wod != NULL) {
546 				x = wod->x;
547 				y = wod->y;
548 			}
549 		}
550 	} else {
551 		_vm->logic()->entryObj(0);
552 		_vm->logic()->newRoom(0);
553 	}
554 
555 	debug(6, "Command::makeJoeWalkTo() - x=%d y=%d newRoom=%d", x, y, _vm->logic()->newRoom());
556 
557 	int16 p = 0;
558 	if (mustWalk) {
559 		// determine which way for Joe to face Object
560 		uint16 facing = State::findDirection(objData->state);
561 		BobSlot *bobJoe = _vm->graphics()->bob(0);
562 		if (x == bobJoe->x && y == bobJoe->y) {
563 			_vm->logic()->joeFacing(facing);
564 			_vm->logic()->joeFace();
565 		} else {
566 			p = _vm->walk()->moveJoe(facing, x, y, false);
567 			if (p != 0) {
568 				_vm->logic()->newRoom(0); // cancel makeJoeWalkTo, that should be equivalent to cr10 fix
569 			}
570 		}
571 	}
572 	return p;
573 }
574 
grabCurrentSelection()575 void Command::grabCurrentSelection() {
576 	Common::Point mouse = _vm->input()->getMousePos();
577 	_selPosX = mouse.x;
578 	_selPosY = mouse.y;
579 
580 	uint16 zone = _vm->grid()->findObjectUnderCursor(_selPosX, _selPosY);
581 	_state.noun = _vm->grid()->findObjectNumber(zone);
582 	_state.verb = _vm->grid()->findVerbUnderCursor(_selPosX, _selPosY);
583 
584 	_selPosX += _vm->display()->horizontalScroll();
585 
586 	if (isVerbAction(_state.verb) || isVerbInvScroll(_state.verb)) {
587 		grabSelectedVerb();
588 	} else if (isVerbInv(_state.verb)) {
589 		grabSelectedItem();
590 	} else if (_state.noun != 0) {
591 		grabSelectedNoun();
592 	} else if (_selPosY < ROOM_ZONE_HEIGHT && _state.verb == VERB_NONE) {
593 		// select without a command, do a WALK
594 		clear(true);
595 		_vm->logic()->joeWalk(JWM_EXECUTE);
596 	}
597 }
598 
grabSelectedObject(int16 objNum,uint16 objState,uint16 objName)599 void Command::grabSelectedObject(int16 objNum, uint16 objState, uint16 objName) {
600 	if (_state.action != VERB_NONE) {
601 		_cmdText->addObject(_vm->logic()->objectName(objName));
602 	}
603 
604 	_state.subject[_state.commandLevel - 1] = objNum;
605 
606 	// if first noun and it's a 2 level command then set up action word
607 	if (_state.action == VERB_USE && _state.commandLevel == 1) {
608 		if (State::findUse(objState) == STATE_USE_ON) {
609 			// object supports 2 levels, command not fully constructed
610 			_state.commandLevel = 2;
611 			_cmdText->addLinkWord(VERB_PREP_WITH);
612 			_cmdText->display(INK_CMD_NORMAL);
613 			_parse = false;
614 		} else {
615 			_parse = true;
616 		}
617 	} else if (_state.action == VERB_GIVE && _state.commandLevel == 1) {
618 		// command not fully constructed
619 		_state.commandLevel = 2;
620 		_cmdText->addLinkWord(VERB_PREP_TO);
621 		_cmdText->display(INK_CMD_NORMAL);
622 		_parse = false;
623 	} else {
624 		_parse = true;
625 	}
626 
627 	if (_parse) {
628 		_state.verb = VERB_NONE;
629 		_vm->logic()->joeWalk(JWM_EXECUTE);
630 		_state.selAction = _state.action;
631 		_state.action = VERB_NONE;
632 	}
633 }
634 
grabSelectedItem()635 void Command::grabSelectedItem() {
636 	ItemData *id = findItemData(_state.verb);
637 	if (id == NULL || id->name <= 0) {
638 		return;
639 	}
640 
641 	int16 item = _vm->logic()->findInventoryItem(_state.verb - VERB_INV_FIRST);
642 
643 	// If we've selected via keyboard, and there is no VERB then do
644 	// the ITEMs default, otherwise keep constructing!
645 
646 	if (_mouseKey == Input::MOUSE_LBUTTON ||
647 		(_vm->input()->keyVerb() != VERB_NONE && _state.verb != VERB_NONE)) {
648 		if (_state.action == VERB_NONE) {
649 			if (_vm->input()->keyVerb() != VERB_NONE) {
650 				// We've selected via the keyboard, no command is being
651 				// constructed, so we shall find the item's default
652 				_state.verb = State::findDefaultVerb(id->state);
653 				if (_state.verb == VERB_NONE) {
654 					// set to Look At
655 					_state.verb = VERB_LOOK_AT;
656 					_cmdText->setVerb(VERB_LOOK_AT);
657 				}
658 				_state.action = _state.verb;
659 			} else {
660 				// Action>0 ONLY if command has been constructed
661 				// Left Mouse Button pressed just do Look At
662 				_state.action = VERB_LOOK_AT;
663 				_cmdText->setVerb(VERB_LOOK_AT);
664 			}
665 		}
666 		_state.verb = VERB_NONE;
667 	} else {
668 		if (_cmdText->isEmpty()) {
669 			_state.verb = VERB_LOOK_AT;
670 			_state.action = VERB_LOOK_AT;
671 			_cmdText->setVerb(VERB_LOOK_AT);
672 		} else {
673 			if (_state.commandLevel == 2 && _parse)
674 				_state.verb = _state.action;
675 			else
676 				_state.verb = State::findDefaultVerb(id->state);
677 			if (_state.verb == VERB_NONE) {
678 				// No match made, so command not yet completed. Redefine as LOOK AT
679 				_state.action = VERB_LOOK_AT;
680 				_cmdText->setVerb(VERB_LOOK_AT);
681 			} else {
682 				_state.action = _state.verb;
683 			}
684 			_state.verb = VERB_NONE;
685 		}
686 	}
687 
688 	grabSelectedObject(-item, id->state, id->name);
689 }
690 
grabSelectedNoun()691 void Command::grabSelectedNoun() {
692 	ObjectData *od = findObjectData(_state.noun);
693 	if (od == NULL || od->name <= 0) {
694 		// selected a turned off object, so just walk
695 		clear(true);
696 		_state.noun = 0;
697 		_vm->logic()->joeWalk(JWM_EXECUTE);
698 		return;
699 	}
700 
701 	if (_state.verb == VERB_NONE) {
702 		if (_mouseKey == Input::MOUSE_LBUTTON) {
703 			if ((_state.commandLevel != 2 && _state.action == VERB_NONE) ||
704 				(_state.commandLevel == 2 && _parse)) {
705 					_state.verb = VERB_WALK_TO;
706 					_state.action = VERB_WALK_TO;
707 					_cmdText->setVerb(VERB_WALK_TO);
708 			}
709 		} else if (_mouseKey == Input::MOUSE_RBUTTON) {
710 			if (_cmdText->isEmpty()) {
711 				_state.verb = State::findDefaultVerb(od->state);
712 				_state.selAction = (_state.verb == VERB_NONE) ? VERB_WALK_TO : _state.verb;
713 				_cmdText->setVerb(_state.selAction);
714 				_cmdText->addObject(_vm->logic()->objectName(od->name));
715 			} else {
716 				if ((_state.commandLevel == 2 && !_parse) || _state.action != VERB_NONE) {
717 					_state.verb = _state.action;
718 				} else {
719 					_state.verb = State::findDefaultVerb(od->state);
720 				}
721 				_state.action = (_state.verb == VERB_NONE) ? VERB_WALK_TO : _state.verb;
722 				_state.verb = VERB_NONE;
723 			}
724 		}
725 	}
726 
727 	_state.selNoun = 0;
728 	int16 objNum = _vm->logic()->currentRoomData() + _state.noun;
729 	grabSelectedObject(objNum, od->state, od->name);
730 }
731 
grabSelectedVerb()732 void Command::grabSelectedVerb() {
733 	if (isVerbInvScroll(_state.verb)) {
734 		// move through inventory (by four if right mouse button)
735 		uint16 scroll = (_mouseKey == Input::MOUSE_RBUTTON) ? 4 : 1;
736 		_vm->logic()->inventoryScroll(scroll, _state.verb == VERB_SCROLL_UP);
737 	} else {
738 		_state.action = _state.verb;
739 		_state.subject[0] = 0;
740 		_state.subject[1] = 0;
741 
742 		if (_vm->logic()->joeWalk() == JWM_MOVE && _state.verb != VERB_NONE) {
743 			_vm->logic()->joeWalk(JWM_NORMAL);
744 		}
745 		_state.commandLevel = 1;
746 		_state.oldVerb = VERB_NONE;
747 		_state.oldNoun = 0;
748 		_cmdText->setVerb(_state.verb);
749 		_cmdText->display(INK_CMD_NORMAL);
750 	}
751 }
752 
executeIfCutaway(const char * description)753 bool Command::executeIfCutaway(const char *description) {
754 	if (strlen(description) > 4 &&
755 		scumm_stricmp(description + strlen(description) - 4, ".CUT") == 0) {
756 
757 		_vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
758 
759 		char nextCutaway[20];
760 		memset(nextCutaway, 0, sizeof(nextCutaway));
761 		_vm->logic()->playCutaway(description, nextCutaway);
762 		while (nextCutaway[0] != '\0') {
763 			_vm->logic()->playCutaway(nextCutaway, nextCutaway);
764 		}
765 		return true;
766 	}
767 	return false;
768 }
769 
executeIfDialog(const char * description)770 bool Command::executeIfDialog(const char *description) {
771 	if (strlen(description) > 4 &&
772 		scumm_stricmp(description + strlen(description) - 4, ".DOG") == 0) {
773 
774 		_vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
775 
776 		char cutaway[20];
777 		memset(cutaway, 0, sizeof(cutaway));
778 		_vm->logic()->startDialogue(description, _state.selNoun, cutaway);
779 
780 		while (cutaway[0] != '\0') {
781 			char currentCutaway[20];
782 			strcpy(currentCutaway, cutaway);
783 			_vm->logic()->playCutaway(currentCutaway, cutaway);
784 		}
785 		return true;
786 	}
787 	return false;
788 }
789 
handleWrongAction()790 bool Command::handleWrongAction() {
791 	// l.96-141 execute.c
792 	uint16 objMax = _vm->grid()->objMax(_vm->logic()->currentRoom());
793 	uint16 roomData = _vm->logic()->currentRoomData();
794 
795 	// select without a command or WALK TO ; do a WALK
796 	if ((_state.selAction == VERB_WALK_TO || _state.selAction == VERB_NONE) &&
797 		(_state.selNoun > objMax || _state.selNoun == 0)) {
798 		if (_state.selAction == VERB_NONE) {
799 			_vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
800 		}
801 		_vm->walk()->moveJoe(0, _selPosX, _selPosY, false);
802 		return true;
803 	}
804 
805 	// check to see if one of the objects is hidden
806 	int i;
807 	for (i = 0; i < 2; ++i) {
808 		int16 obj = _state.subject[i];
809 		if (obj > 0 && _vm->logic()->objectData(obj)->name <= 0) {
810 			return true;
811 		}
812 	}
813 
814 	// check for USE command on exists
815 	if (_state.selAction == VERB_USE &&
816 		_state.subject[0] > 0 && _vm->logic()->objectData(_state.subject[0])->entryObj > 0) {
817 		_state.selAction = VERB_WALK_TO;
818 	}
819 
820 	if (_state.selNoun > 0 && _state.selNoun <= objMax) {
821 		uint16 objNum = roomData + _state.selNoun;
822 		if (makeJoeWalkTo(_selPosX, _selPosY, objNum, _state.selAction, true) != 0) {
823 			return true;
824 		}
825 		if (_state.selAction == VERB_WALK_TO && _vm->logic()->objectData(objNum)->entryObj < 0) {
826 			return true;
827 		}
828 	}
829 	return false;
830 }
831 
sayInvalidAction(Verb action,int16 subj1,int16 subj2)832 void Command::sayInvalidAction(Verb action, int16 subj1, int16 subj2) {
833 	// l.158-272 execute.c
834 	switch (action) {
835 
836 	case VERB_LOOK_AT:
837 		lookAtSelectedObject();
838 		break;
839 
840 	case VERB_OPEN:
841 		// 'it doesn't seem to open'
842 		_vm->logic()->makeJoeSpeak(1);
843 		break;
844 
845 	case VERB_USE:
846 		if (subj1 < 0) {
847 			uint16 k = _vm->logic()->itemData(-subj1)->sfxDescription;
848 			if (k > 0) {
849 				_vm->logic()->makeJoeSpeak(k, true);
850 			} else {
851 				_vm->logic()->makeJoeSpeak(2);
852 			}
853 		} else {
854 			_vm->logic()->makeJoeSpeak(2);
855 		}
856 		break;
857 
858 	case VERB_TALK_TO:
859 		_vm->logic()->makeJoeSpeak(24 + _vm->randomizer.getRandomNumber(2));
860 		break;
861 
862 	case VERB_CLOSE:
863 		_vm->logic()->makeJoeSpeak(2);
864 		break;
865 
866 	case VERB_MOVE:
867 		// 'I can't move it'
868 		if (subj1 > 0) {
869 			int16 img = _vm->logic()->objectData(subj1)->image;
870 			if (img == -4 || img == -3) {
871 				_vm->logic()->makeJoeSpeak(18);
872 			} else {
873 				_vm->logic()->makeJoeSpeak(3);
874 			}
875 		} else {
876 			_vm->logic()->makeJoeSpeak(3);
877 		}
878 		break;
879 
880 	case VERB_GIVE:
881 		// 'I can't give the subj1 to subj2'
882 		if (subj1 < 0) {
883 			if (subj2 > 0) {
884 				int16 img = _vm->logic()->objectData(subj2)->image;
885 				if (img == -4 || img == -3) {
886 					_vm->logic()->makeJoeSpeak(27 + _vm->randomizer.getRandomNumber(2));
887 				}
888 			} else {
889 				_vm->logic()->makeJoeSpeak(11);
890 			}
891 		} else {
892 			_vm->logic()->makeJoeSpeak(12);
893 		}
894 		break;
895 
896 	case VERB_PICK_UP:
897 		if (subj1 < 0) {
898 			_vm->logic()->makeJoeSpeak(14);
899 		} else {
900 			int16 img = _vm->logic()->objectData(subj1)->image;
901 			if (img == -4 || img == -3) {
902 				// Trying to get a person
903 				_vm->logic()->makeJoeSpeak(20);
904 			} else {
905 				// 5 : 'I can't pick that up'
906 				// 6 : 'I don't think I need that'
907 				// 7 : 'I'd rather leave it here'
908 				// 8 : 'I don't think I'd have any use for that'
909 				_vm->logic()->makeJoeSpeak(5 + _vm->randomizer.getRandomNumber(3));
910 			}
911 		}
912 		break;
913 
914 	default:
915 		break;
916 	}
917 }
918 
changeObjectState(Verb action,int16 obj,int16 song,bool cutDone)919 void Command::changeObjectState(Verb action, int16 obj, int16 song, bool cutDone) {
920 	// l.456-533 execute.c
921 	ObjectData *objData = _vm->logic()->objectData(obj);
922 
923 	if (action == VERB_OPEN && !cutDone) {
924 		if (State::findOn(objData->state) == STATE_ON_ON) {
925 			State::alterOn(&objData->state, STATE_ON_OFF);
926 			State::alterDefaultVerb(&objData->state, VERB_NONE);
927 
928 			// play music if it exists... (or SFX for open/close door)
929 			if (song != 0) {
930 				_vm->sound()->playSong(ABS(song));
931 			}
932 
933 			if (objData->entryObj != 0) {
934 				// if it's a door, then update door that it links to
935 				openOrCloseAssociatedObject(action, ABS(objData->entryObj));
936 				objData->entryObj = ABS(objData->entryObj);
937 			}
938 		} else {
939 			// 'it's already open !'
940 			_vm->logic()->makeJoeSpeak(9);
941 		}
942 	} else if (action == VERB_CLOSE && !cutDone) {
943 		if (State::findOn(objData->state) == STATE_ON_OFF) {
944 			State::alterOn(&objData->state, STATE_ON_ON);
945 			State::alterDefaultVerb(&objData->state, VERB_OPEN);
946 
947 			// play music if it exists... (or SFX for open/close door)
948 			if (song != 0) {
949 				_vm->sound()->playSong(ABS(song));
950 			}
951 
952 			if (objData->entryObj != 0) {
953 				// if it's a door, then update door that it links to
954 				openOrCloseAssociatedObject(action, ABS(objData->entryObj));
955 				objData->entryObj = -ABS(objData->entryObj);
956 			}
957 		} else {
958 			// 'it's already closed !'
959 			_vm->logic()->makeJoeSpeak(10);
960 		}
961 	} else if (action == VERB_MOVE) {
962 		State::alterOn(&objData->state, STATE_ON_OFF);
963 	}
964 }
965 
cleanupCurrentAction()966 void Command::cleanupCurrentAction() {
967 	// l.595-597 execute.c
968 	_vm->logic()->joeFace();
969 	_state.oldNoun = 0;
970 	_state.oldVerb = VERB_NONE;
971 }
972 
openOrCloseAssociatedObject(Verb action,int16 otherObj)973 void Command::openOrCloseAssociatedObject(Verb action, int16 otherObj) {
974 	CmdListData *cmdList = &_cmdList[1];
975 	uint16 com = 0;
976 	uint16 i;
977 	for (i = 1; i <= _numCmdList && com == 0; ++i, ++cmdList) {
978 		if (cmdList->match(action, otherObj, 0)) {
979 			if (cmdList->setConditions) {
980 				CmdGameState *cmdGs = _cmdGameState;
981 				uint16 j;
982 				for (j = 1; j <= _numCmdGameState; ++j) {
983 					if (cmdGs[j].id == i && cmdGs[j].gameStateSlot > 0) {
984 						if (_vm->logic()->gameState(cmdGs[j].gameStateSlot) == cmdGs[j].gameStateValue) {
985 							com = i;
986 							break;
987 						}
988 					}
989 				}
990 			} else {
991 				com = i;
992 				break;
993 			}
994 		}
995 	}
996 
997 	if (com != 0) {
998 
999 		debug(6, "Command::openOrCloseAssociatedObject() com=%X", com);
1000 
1001 		cmdList = &_cmdList[com];
1002 		ObjectData *objData = _vm->logic()->objectData(otherObj);
1003 
1004 		if (cmdList->imageOrder != 0) {
1005 			objData->image = cmdList->imageOrder;
1006 		}
1007 
1008 		if (action == VERB_OPEN) {
1009 			if (State::findOn(objData->state) == STATE_ON_ON) {
1010 				State::alterOn(&objData->state, STATE_ON_OFF);
1011 				State::alterDefaultVerb(&objData->state, VERB_NONE);
1012 				objData->entryObj = ABS(objData->entryObj);
1013 			}
1014 		} else if (action == VERB_CLOSE) {
1015 			if (State::findOn(objData->state) == STATE_ON_OFF) {
1016 				State::alterOn(&objData->state, STATE_ON_ON);
1017 				State::alterDefaultVerb(&objData->state, VERB_OPEN);
1018 				objData->entryObj = -ABS(objData->entryObj);
1019 			}
1020 		}
1021 	}
1022 }
1023 
setConditions(uint16 command,bool lastCmd)1024 int16 Command::setConditions(uint16 command, bool lastCmd) {
1025 	debug(9, "Command::setConditions(%d, %d)", command, lastCmd);
1026 
1027 	int16 ret = 0;
1028 	uint16 cmdState[21];
1029 	memset(cmdState, 0, sizeof(cmdState));
1030 	uint16 cmdStateCount = 0;
1031 	uint16 i;
1032 	CmdGameState *cmdGs = &_cmdGameState[1];
1033 	for (i = 1; i <= _numCmdGameState; ++i, ++cmdGs) {
1034 		if (cmdGs->id == command) {
1035 			if (cmdGs->gameStateSlot > 0) {
1036 				if (_vm->logic()->gameState(cmdGs->gameStateSlot) != cmdGs->gameStateValue) {
1037 					debug(6, "Command::setConditions() - GS[%d] == %d (should be %d)", cmdGs->gameStateSlot, _vm->logic()->gameState(cmdGs->gameStateSlot), cmdGs->gameStateValue);
1038 					// failed test
1039 					ret = i;
1040 					break;
1041 				}
1042 			} else {
1043 				cmdState[cmdStateCount] = i;
1044 				++cmdStateCount;
1045 			}
1046 		}
1047 	}
1048 
1049 	if (ret > 0) {
1050 		// we've failed, so see if we need to make Joe speak
1051 		cmdGs = &_cmdGameState[ret];
1052 		if (cmdGs->speakValue > 0 && lastCmd) {
1053 			// check to see if fail state is in fact a cutaway
1054 			const char *objDesc = _vm->logic()->objectTextualDescription(cmdGs->speakValue);
1055 			if (!executeIfCutaway(objDesc) && !executeIfDialog(objDesc)) {
1056 				_vm->logic()->makeJoeSpeak(cmdGs->speakValue, true);
1057 			}
1058 			ret = -2;
1059 		} else {
1060 			// return -1 so Joe will be able to speak a normal description
1061 			ret = -1;
1062 		}
1063 	} else {
1064 		ret = 0;
1065 		// all tests were okay, now set gamestates
1066 		for (i = 0; i < cmdStateCount; ++i) {
1067 			cmdGs = &_cmdGameState[cmdState[i]];
1068 			_vm->logic()->gameState(ABS(cmdGs->gameStateSlot), cmdGs->gameStateValue);
1069 			// set return value for Joe to say something
1070 			ret = cmdGs->speakValue;
1071 		}
1072 	}
1073 	return ret;
1074 }
1075 
setAreas(uint16 command)1076 void Command::setAreas(uint16 command) {
1077 	debug(9, "Command::setAreas(%d)", command);
1078 
1079 	CmdArea *cmdArea = &_cmdArea[1];
1080 	for (uint16 i = 1; i <= _numCmdArea; ++i, ++cmdArea) {
1081 		if (cmdArea->id == command) {
1082 			uint16 areaNum = ABS(cmdArea->area);
1083 			Area *area = _vm->grid()->area(cmdArea->room, areaNum);
1084 			if (cmdArea->area > 0) {
1085 				// turn on area
1086 				area->mapNeighbors = ABS(area->mapNeighbors);
1087 			} else {
1088 				// turn off area
1089 				area->mapNeighbors = -ABS(area->mapNeighbors);
1090 			}
1091 		}
1092 	}
1093 }
1094 
setObjects(uint16 command)1095 void Command::setObjects(uint16 command) {
1096 	debug(9, "Command::setObjects(%d)", command);
1097 
1098 	CmdObject *cmdObj = &_cmdObject[1];
1099 	for (uint16 i = 1; i <= _numCmdObject; ++i, ++cmdObj) {
1100 		if (cmdObj->id == command) {
1101 
1102 			// found an object
1103 			uint16 dstObj = ABS(cmdObj->dstObj);
1104 			ObjectData *objData = _vm->logic()->objectData(dstObj);
1105 
1106 			debug(6, "Command::setObjects() - dstObj=%X srcObj=%X _state.subject[0]=%X", cmdObj->dstObj, cmdObj->srcObj, _state.subject[0]);
1107 
1108 			if (cmdObj->dstObj > 0) {
1109 				// show the object
1110 				objData->name = ABS(objData->name);
1111 				// test that the object has not already been deleted
1112 				// by checking if it is not equal to zero
1113 				if (cmdObj->srcObj == -1 && objData->name != 0) {
1114 					// delete object by setting its name to 0 and
1115 					// turning off graphic image
1116 					objData->name = 0;
1117 					if (objData->room == _vm->logic()->currentRoom()) {
1118 						if (dstObj != _state.subject[0]) {
1119 							// if the new object we have updated is on screen and is not the
1120 							// current object, then we can update. This is because we turn
1121 							// current object off ourselves by COM_LIST(com, 8)
1122 							if (objData->image != -3 && objData->image != -4) {
1123 								// it is a normal object (not a person)
1124 								// turn the graphic image off for the object
1125 								objData->image = -(objData->image + 10);
1126 							}
1127 						}
1128 						// invalidate object area
1129 						uint16 objZone = dstObj - _vm->logic()->currentRoomData();
1130 						_vm->grid()->setZone(GS_ROOM, objZone, 0, 0, 1, 1);
1131 					}
1132 				}
1133 
1134 				if (cmdObj->srcObj > 0) {
1135 					// copy data from dummy object to object
1136 					int16 image1 = objData->image;
1137 					int16 image2 = _vm->logic()->objectData(cmdObj->srcObj)->image;
1138 					_vm->logic()->objectCopy(cmdObj->srcObj, dstObj);
1139 					if (image1 != 0 && image2 == 0 && objData->room == _vm->logic()->currentRoom()) {
1140 						uint16 bobNum = _vm->logic()->findBob(dstObj);
1141 						if (bobNum != 0) {
1142 							_vm->graphics()->clearBob(bobNum);
1143 						}
1144 					}
1145 				}
1146 
1147 				if (dstObj != _state.subject[0]) {
1148 					// if the new object we have updated is on screen and
1149 					// is not current object then update it
1150 					_vm->graphics()->refreshObject(dstObj);
1151 				}
1152 			} else {
1153 				// hide the object
1154 				if (objData->name > 0) {
1155 					objData->name = -objData->name;
1156 					// may need to turn BOBs off for objects to be hidden on current
1157 					// screen ! if the new object we have updated is on screen and
1158 					// is not current object then update it
1159 					_vm->graphics()->refreshObject(dstObj);
1160 				}
1161 			}
1162 		}
1163 	}
1164 }
1165 
setItems(uint16 command)1166 void Command::setItems(uint16 command) {
1167 	debug(9, "Command::setItems(%d)", command);
1168 
1169 	ItemData *items = _vm->logic()->itemData(0);
1170 	CmdInventory *cmdInv = &_cmdInventory[1];
1171 	for (uint16 i = 1; i <= _numCmdInventory; ++i, ++cmdInv) {
1172 		if (cmdInv->id == command) {
1173 			uint16 dstItem = ABS(cmdInv->dstItem);
1174 			// found an item
1175 			if (cmdInv->dstItem > 0) {
1176 				// add item to inventory
1177 				if (cmdInv->srcItem > 0) {
1178 					// copy data from source item to item, then enable it
1179 					items[dstItem] = items[cmdInv->srcItem];
1180 					items[dstItem].name = ABS(items[dstItem].name);
1181 				}
1182 				_vm->logic()->inventoryInsertItem(cmdInv->dstItem);
1183 			} else {
1184 				// delete item
1185 				if (items[dstItem].name > 0) {
1186 					_vm->logic()->inventoryDeleteItem(dstItem);
1187 				}
1188 				if (cmdInv->srcItem > 0) {
1189 					// copy data from source item to item, then disable it
1190 					items[dstItem] = items[cmdInv->srcItem];
1191 					items[dstItem].name = -ABS(items[dstItem].name);
1192 				}
1193 			}
1194 		}
1195 	}
1196 }
1197 
nextObjectDescription(ObjectDescription * objDesc,uint16 firstDesc)1198 uint16 Command::nextObjectDescription(ObjectDescription* objDesc, uint16 firstDesc) {
1199 	// l.69-103 select.c
1200 	uint16 i;
1201 	uint16 diff = objDesc->lastDescription - firstDesc;
1202 	debug(6, "Command::nextObjectDescription() - diff = %d, type = %d", diff, objDesc->type);
1203 	switch (objDesc->type) {
1204 	case 0:
1205 		// random type, start with first description
1206 		if (objDesc->lastSeenNumber == 0) {
1207 			// first time look at called
1208 			objDesc->lastSeenNumber = firstDesc;
1209 			break;
1210 		}
1211 		// already displayed first, do a random
1212 		// fall through
1213 	case 1:
1214 		i = objDesc->lastSeenNumber;
1215 		while (i == objDesc->lastSeenNumber) {
1216 			i = firstDesc + _vm->randomizer.getRandomNumber(diff);
1217 		}
1218 		objDesc->lastSeenNumber = i;
1219 		break;
1220 	case 2:
1221 		// sequential, but loop
1222 		++objDesc->lastSeenNumber;
1223 		if (objDesc->lastSeenNumber > objDesc->lastDescription) {
1224 			objDesc->lastSeenNumber = firstDesc;
1225 		}
1226 		break;
1227 	case 3:
1228 		// sequential without looping
1229 		if (objDesc->lastSeenNumber != objDesc->lastDescription) {
1230 			++objDesc->lastSeenNumber;
1231 		}
1232 		break;
1233 	default:
1234 		break;
1235 	}
1236 	return objDesc->lastSeenNumber;
1237 }
1238 
lookAtSelectedObject()1239 void Command::lookAtSelectedObject() {
1240 	uint16 desc;
1241 	if (_state.subject[0] < 0) {
1242 		desc = _vm->logic()->itemData(-_state.subject[0])->description;
1243 	} else {
1244 		ObjectData *objData = _vm->logic()->objectData(_state.subject[0]);
1245 		if (objData->name <= 0) {
1246 			return;
1247 		}
1248 		desc = objData->description;
1249 	}
1250 
1251 	debug(6, "Command::lookAtSelectedObject() - desc = %X, _state.subject[0] = %X", desc, _state.subject[0]);
1252 
1253 	// check to see if the object/item has a series of description
1254 	ObjectDescription *objDesc = _vm->logic()->objectDescription(1);
1255 	uint16 i;
1256 	for (i = 1; i <= _vm->logic()->objectDescriptionCount(); ++i, ++objDesc) {
1257 		if (objDesc->object == _state.subject[0]) {
1258 			desc = nextObjectDescription(objDesc, desc);
1259 			break;
1260 		}
1261 	}
1262 	if (desc != 0) {
1263 		_vm->logic()->makeJoeSpeak(desc, true);
1264 	}
1265 	_vm->logic()->joeFace();
1266 }
1267 
lookForCurrentObject(int16 cx,int16 cy)1268 void Command::lookForCurrentObject(int16 cx, int16 cy) {
1269 	uint16 obj = _vm->grid()->findObjectUnderCursor(cx, cy);
1270 	_state.noun = _vm->grid()->findObjectNumber(obj);
1271 
1272 	if (_state.oldNoun == _state.noun) {
1273 		return;
1274 	}
1275 
1276 	ObjectData *od = findObjectData(_state.noun);
1277 	if (od == NULL || od->name <= 0) {
1278 		_state.oldNoun = _state.noun;
1279 		_vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
1280 		if (_state.action != VERB_NONE) {
1281 			_cmdText->display(INK_CMD_NORMAL);
1282 		}
1283 		return;
1284 	}
1285 
1286 	// if no command yet selected, then use DEFAULT command, if any
1287 	if (_state.action == VERB_NONE) {
1288 		Verb v = State::findDefaultVerb(od->state);
1289 		_cmdText->setVerb((v == VERB_NONE) ? VERB_WALK_TO : v);
1290 		if (_state.noun == 0) {
1291 			_cmdText->clear();
1292 		}
1293 	}
1294 	const char *name = _vm->logic()->objectName(od->name);
1295 	_cmdText->displayTemp(INK_CMD_NORMAL, name, false);
1296 	_state.oldNoun = _state.noun;
1297 }
1298 
lookForCurrentIcon(int16 cx,int16 cy)1299 void Command::lookForCurrentIcon(int16 cx, int16 cy) {
1300 	_state.verb = _vm->grid()->findVerbUnderCursor(cx, cy);
1301 	if (_state.oldVerb != _state.verb) {
1302 
1303 		if (_state.action == VERB_NONE) {
1304 			_cmdText->clear();
1305 		}
1306 		_vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
1307 
1308 		if (isVerbInv(_state.verb)) {
1309 			ItemData *id = findItemData(_state.verb);
1310 			if (id != NULL && id->name > 0) {
1311 				if (_state.action == VERB_NONE) {
1312 					Verb v = State::findDefaultVerb(id->state);
1313 					_cmdText->setVerb((v == VERB_NONE) ? VERB_LOOK_AT : v);
1314 				}
1315 				const char *name = _vm->logic()->objectName(id->name);
1316 				_cmdText->displayTemp(INK_CMD_NORMAL, name, false);
1317 			}
1318 		} else if (isVerbAction(_state.verb)) {
1319 			_cmdText->displayTemp(INK_CMD_NORMAL, _state.verb);
1320 		} else if (_state.verb == VERB_NONE) {
1321 			_cmdText->display(INK_CMD_NORMAL);
1322 		}
1323 		_state.oldVerb = _state.verb;
1324 	}
1325 }
1326 
1327 } // End of namespace Queen
1328