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 "common/rect.h"
25 #include "common/textconsole.h"
26 
27 #include "queen/talk.h"
28 
29 #include "queen/bankman.h"
30 #include "queen/display.h"
31 #include "queen/graphics.h"
32 #include "queen/grid.h"
33 #include "queen/input.h"
34 #include "queen/logic.h"
35 #include "queen/queen.h"
36 #include "queen/resource.h"
37 #include "queen/sound.h"
38 #include "queen/state.h"
39 #include "queen/walk.h"
40 
41 #include "common/file.h"
42 
43 namespace Queen {
44 
talk(const char * filename,int personInRoom,char * cutawayFilename,QueenEngine * vm)45 void Talk::talk(
46 		const char *filename,
47 		int personInRoom,
48 		char *cutawayFilename,
49 		QueenEngine *vm) {
50 	Talk *talk = new Talk(vm);
51 	talk->talk(filename, personInRoom, cutawayFilename);
52 	delete talk;
53 }
54 
speak(const char * sentence,Person * person,const char * voiceFilePrefix,QueenEngine * vm)55 bool Talk::speak(
56 		const char *sentence,
57 		Person *person,
58 		const char *voiceFilePrefix,
59 		QueenEngine *vm) {
60 	Talk *talk = new Talk(vm);
61 	bool result;
62 	if (sentence)
63 		result = talk->speak(sentence, person, voiceFilePrefix);
64 	else
65 		result = false;
66 	delete talk;
67 	return result;
68 }
69 
Talk(QueenEngine * vm)70 Talk::Talk(QueenEngine *vm)
71 	: _vm(vm), _fileData(NULL) {
72 	_vm->input()->talkQuitReset();
73 }
74 
~Talk()75 Talk::~Talk() {
76 	delete[] _fileData;
77 }
78 
talk(const char * filename,int personInRoom,char * cutawayFilename)79 void Talk::talk(const char *filename, int personInRoom, char *cutawayFilename) {
80 	int i;
81 	_oldSelectedSentenceIndex = 0;
82 	_oldSelectedSentenceValue = 0;
83 
84 	debug(6, "----- talk(\"%s\") -----", filename);
85 
86 	cutawayFilename[0] = '\0';
87 
88 	load(filename);
89 
90 	Person person;
91 	memset(&person, 0, sizeof(Person));
92 	_vm->logic()->initPerson(personInRoom, "", false, &person);
93 
94 	if (NULL == person.name) {
95 		error("Invalid person object");
96 	}
97 
98 	int16 oldLevel = 0;
99 
100 	// Lines 828-846 in talk.c
101 	for (i = 1; i <= 4; i++) {
102 		if (selectedValue(i) > 0) {
103 			// This option has been redefined so display new dialogue option
104 			_dialogueTree[1][i].head = selectedValue(i);
105 		} else if (selectedValue(i) == -1) {
106 			// Already selected so don't redisplay
107 			if (_dialogueTree[1][i].gameStateIndex >= 0) {
108 				_dialogueTree[1][i].head = -1;
109 				_dialogueTree[1][i].dialogueNodeValue1 = -1;
110 				_dialogueTree[1][i].gameStateIndex = -1;
111 				_dialogueTree[1][i].gameStateValue = -1;
112 			}
113 		}
114 	}
115 
116 	initialTalk();
117 
118 	// Lines 906-? in talk.c
119 	_vm->display()->showMouseCursor(true);
120 
121 	int16 level=1, retval=0;
122 	int16 head = _dialogueTree[level][0].head;
123 
124 	// TODO: split this loop in several functions
125 	while (retval != -1) {
126 		char otherVoiceFilePrefix[MAX_STRING_SIZE];
127 
128 		_talkString[0][0] = '\0';
129 
130 		if (hasTalkedTo() && head == 1)
131 			strcpy(_talkString[0], _person2String);
132 		else
133 			findDialogueString(_person1PtrOff, head, _pMax, _talkString[0]);
134 
135 		if (hasTalkedTo() && head == 1)
136 			sprintf(otherVoiceFilePrefix, "%2dXXXXP", _talkKey);
137 		else
138 			sprintf(otherVoiceFilePrefix, "%2d%4xP", _talkKey, head);
139 
140 		if (_talkString[0][0] == '\0' && retval > 1) {
141 			findDialogueString(_person1PtrOff, retval, _pMax, _talkString[0]);
142 			sprintf(otherVoiceFilePrefix,"%2d%4xP", _talkKey, retval);
143 		}
144 
145 		// Joe dialogue
146 
147 		for (i = 1; i <= 4; i++) {
148 			findDialogueString(_joePtrOff, _dialogueTree[level][i].head, _jMax, _talkString[i]);
149 
150 			int16 index = _dialogueTree[level][i].gameStateIndex;
151 
152 			if (index < 0 && _vm->logic()->gameState(ABS(index)) != _dialogueTree[level][i].gameStateValue)
153 				_talkString[i][0] = '\0';
154 
155 			sprintf(_joeVoiceFilePrefix[i], "%2d%4xJ", _talkKey, _dialogueTree[level][i].head);
156 		}
157 
158 		// Check to see if (all the dialogue options have been selected.
159 		// if this is the case, and the last one left is the exit option,
160 		// then automatically set S to that and exit.
161 
162 		int choicesLeft = 0;
163 		int selectedSentence = 0;
164 
165 		for (i = 1; i <= 4; i++) {
166 			if (_talkString[i][0] != '\0') {
167 				choicesLeft++;
168 				selectedSentence = i;
169 			}
170 		}
171 
172 		debug(6, "choicesLeft = %i", choicesLeft);
173 
174 		if (1 == choicesLeft) {
175 			// Automatically run the final dialogue option
176 			speak(_talkString[0], &person, otherVoiceFilePrefix);
177 
178 			if (_vm->input()->talkQuit())
179 				break;
180 
181 			speak(_talkString[selectedSentence], NULL, _joeVoiceFilePrefix[selectedSentence]);
182 		} else {
183 			if (person.actor->bobNum > 0) {
184 				speak(_talkString[0], &person, otherVoiceFilePrefix);
185 				selectedSentence = selectSentence();
186 			} else {
187 				warning("bobBum is %i", person.actor->bobNum);
188 				selectedSentence = 0;
189 			}
190 		}
191 
192 		if (_vm->input()->talkQuit() || _vm->shouldQuit())
193 			break;
194 
195 		retval   = _dialogueTree[level][selectedSentence].dialogueNodeValue1;
196 		head     = _dialogueTree[level][selectedSentence].head;
197 		oldLevel = level;
198 		level    = 0;
199 
200 		// Set LEVEL to the selected child in dialogue tree
201 
202 		for (i = 1; i <= _levelMax; i++)
203 			if (_dialogueTree[i][0].head == head)
204 				level = i;
205 
206 		if (0 == level) {
207 			// No new level has been selected, so lets set LEVEL to the
208 			// tree path pointed to by the RETVAL
209 
210 			for (i = 1; i <= _levelMax; i++)
211 				for (int j = 0; j <= 5; j++)
212 					if (_dialogueTree[i][j].head == retval)
213 						level = i;
214 
215 			disableSentence(oldLevel, selectedSentence);
216 		} else { // 0 != level
217 			// Check to see if Person Return value is positive, if it is, then
218 			// change the selected dialogue option to the Return value
219 
220 			if (_dialogueTree[level][0].dialogueNodeValue1 > 0) {
221 				if (1 == oldLevel) {
222 						_oldSelectedSentenceIndex = selectedSentence;
223 						_oldSelectedSentenceValue = selectedValue(selectedSentence);
224 						selectedValue(selectedSentence, _dialogueTree[level][0].dialogueNodeValue1);
225 				}
226 
227 				_dialogueTree[oldLevel][selectedSentence].head = _dialogueTree[level][0].dialogueNodeValue1;
228 				_dialogueTree[level][0].dialogueNodeValue1 = -1;
229 			} else {
230 				disableSentence(oldLevel, selectedSentence);
231 			}
232 		}
233 
234 		// Check selected person to see if any Gamestates need setting
235 
236 		int16 index = _dialogueTree[level][0].gameStateIndex;
237 		if (index > 0)
238 			_vm->logic()->gameState(index, _dialogueTree[level][0].gameStateValue);
239 
240 		// if the selected dialogue line has a POSITIVE game state value
241 		// then set gamestate to Value = TALK(OLDLEVEL,S,3)
242 
243 		index = _dialogueTree[oldLevel][selectedSentence].gameStateIndex;
244 		if (index > 0)
245 			_vm->logic()->gameState(index, _dialogueTree[oldLevel][selectedSentence].gameStateValue);
246 
247 		// check to see if person has something final to say
248 		if (-1 == retval) {
249 			findDialogueString(_person1PtrOff, head, _pMax, _talkString[0]);
250 			if (_talkString[0][0] != '\0') {
251 				sprintf(otherVoiceFilePrefix, "%2d%4xP", _talkKey, head);
252 				speak(_talkString[0], &person, otherVoiceFilePrefix);
253 			}
254 		}
255 	}
256 
257 	cutawayFilename[0] = '\0';
258 
259 	for (i = 0; i < 2; i++) {
260 		if (_gameState[i] > 0) {
261 			if (_vm->logic()->gameState(_gameState[i]) == _testValue[i]) {
262 				if (_itemNumber[i] > 0)
263 					_vm->logic()->inventoryInsertItem(_itemNumber[i]);
264 				else
265 					_vm->logic()->inventoryDeleteItem(ABS(_itemNumber[i]));
266 			}
267 		}
268 	}
269 
270 	_vm->grid()->setupPanel();
271 
272 	uint16 offset = _cutawayPtrOff;
273 
274 	int16 cutawayGameState = (int16)READ_BE_INT16(_fileData + offset); offset += 2;
275 	int16 cutawayTestValue = (int16)READ_BE_INT16(_fileData + offset); offset += 2;
276 
277 	if (_vm->logic()->gameState(cutawayGameState) == cutawayTestValue) {
278 		getString(_fileData, offset, cutawayFilename, 20);
279 		if (cutawayFilename[0]) {
280 			//CR 2 - 7/3/95, If we're executing a cutaway scene, then make sure
281 			// Joe can talk, so set TALKQUIT to 0 just in case we exit on the
282 			// line that set's the cutaway game states.
283 			_vm->input()->talkQuitReset();
284 		}
285 	}
286 	if (_vm->input()->talkQuit()) {
287 		if (_oldSelectedSentenceIndex > 0)
288 			selectedValue(_oldSelectedSentenceIndex, _oldSelectedSentenceValue);
289 		_vm->input()->talkQuitReset();
290 		_vm->display()->clearTexts(0, 198);
291 		_vm->logic()->makeJoeSpeak(15, false);
292 	} else {
293 		setHasTalkedTo();
294 	}
295 
296 	_vm->logic()->joeFace();
297 
298 	if (cutawayFilename[0] == '\0') {
299 		BobSlot *pbs = _vm->graphics()->bob(person.actor->bobNum);
300 
301 		pbs->x = person.actor->x;
302 		pbs->y = person.actor->y;
303 
304 		// Better kick start the persons anim sequence
305 		_vm->graphics()->resetPersonAnim(person.actor->bobNum);
306 	}
307 
308 	_vm->logic()->joeWalk(JWM_NORMAL);
309 }
310 
disableSentence(int oldLevel,int selectedSentence)311 void Talk::disableSentence(int oldLevel, int selectedSentence) {
312 	// Mark off selected option
313 
314 	if (1 == oldLevel) {
315 		if (_dialogueTree[oldLevel][selectedSentence].dialogueNodeValue1 != -1) {
316 			// Make sure choice is not exit option
317 			_oldSelectedSentenceIndex = selectedSentence;
318 			_oldSelectedSentenceValue = selectedValue(selectedSentence);
319 			selectedValue(selectedSentence, -1);
320 		}
321 	}
322 
323 	// Cancel selected dialogue line, so that its no longer displayed
324 	_dialogueTree[oldLevel][selectedSentence].head = -1;
325 	_dialogueTree[oldLevel][selectedSentence].dialogueNodeValue1 = -1;
326 }
327 
findDialogueString(uint16 offset,int16 id,int16 max,char * str)328 void Talk::findDialogueString(uint16 offset, int16 id, int16 max, char *str) {
329 	str[0] = '\0';
330 	for (int i = 1; i <= max; i++) {
331 		offset += 2;
332 		int16 currentId = (int16)READ_BE_INT16(_fileData + offset);
333 		offset += 2;
334 		if (id == currentId) {
335 			getString(_fileData, offset, str, MAX_STRING_LENGTH, 4);
336 			break;
337 		} else {
338 			getString(_fileData, offset, NULL, MAX_STRING_LENGTH, 4);
339 		}
340 	}
341 }
342 
loadDialogFile(const char * filename)343 byte *Talk::loadDialogFile(const char *filename) {
344 	static const struct {
345 		const char *filename;
346 		Common::Language language;
347 	} dogFiles[] = {
348 		{ "CHIEF1.DOG", Common::FR_FRA },
349 		{ "CHIEF2.DOG", Common::FR_FRA },
350 		{ "BUD1.DOG",   Common::IT_ITA }
351 	};
352 	for (int i = 0; i < ARRAYSIZE(dogFiles); ++i) {
353 		if (!scumm_stricmp(filename, dogFiles[i].filename) &&
354 			_vm->resource()->getLanguage() == dogFiles[i].language) {
355 			Common::File fdog;
356 			fdog.open(filename);
357 			if (fdog.isOpen()) {
358 				debug(6, "Loading dog file '%s' from game data path", filename);
359 				uint32 size = fdog.size() - DOG_HEADER_SIZE;
360 				byte *buf = new byte[size];
361 				fdog.seek(DOG_HEADER_SIZE);
362 				fdog.read(buf, size);
363 				return buf;
364 			}
365 		}
366 	}
367 	return _vm->resource()->loadFile(filename, DOG_HEADER_SIZE);
368 }
369 
load(const char * filename)370 void Talk::load(const char *filename) {
371 	int i;
372 	byte *ptr = _fileData = loadDialogFile(filename);
373 
374 	// Load talk header
375 
376 	_levelMax = (int16)READ_BE_INT16(ptr); ptr += 2;
377 
378 	if (_levelMax < 0) {
379 		_levelMax = -_levelMax;
380 		_vm->input()->canQuit(false);
381 	} else {
382 		_vm->input()->canQuit(true);
383 	}
384 
385 	_uniqueKey      = (int16)READ_BE_INT16(ptr); ptr += 2;
386 	_talkKey        = (int16)READ_BE_INT16(ptr); ptr += 2;
387 	_jMax           = (int16)READ_BE_INT16(ptr); ptr += 2;
388 	_pMax           = (int16)READ_BE_INT16(ptr); ptr += 2;
389 
390 	for (i = 0; i < 2; i++) {
391 		_gameState [i] = (int16)READ_BE_INT16(ptr); ptr += 2;
392 		_testValue [i] = (int16)READ_BE_INT16(ptr); ptr += 2;
393 		_itemNumber[i] = (int16)READ_BE_INT16(ptr); ptr += 2;
394 	}
395 
396 	_person1PtrOff = READ_BE_UINT16(ptr); ptr += 2;
397 	_cutawayPtrOff = READ_BE_UINT16(ptr); ptr += 2;
398 	_person2PtrOff = READ_BE_UINT16(ptr); ptr += 2;
399 	_joePtrOff     = 32 + _levelMax * 96;
400 
401 	// Load dialogue tree
402 	ptr = _fileData + 32;
403 	memset(&_dialogueTree[0], 0, sizeof(_dialogueTree[0]));
404 	for (i = 1; i <= _levelMax; i++)
405 		for (int j = 0; j <= 5; j++) {
406 			ptr += 2;
407 			_dialogueTree[i][j].head = (int16)READ_BE_INT16(ptr); ptr += 2;
408 			ptr += 2;
409 			_dialogueTree[i][j].dialogueNodeValue1 = (int16)READ_BE_INT16(ptr); ptr += 2;
410 			ptr += 2;
411 			_dialogueTree[i][j].gameStateIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
412 			ptr += 2;
413 			_dialogueTree[i][j].gameStateValue = (int16)READ_BE_INT16(ptr); ptr += 2;
414 		}
415 }
416 
initialTalk()417 void Talk::initialTalk() {
418 	// Lines 848-903 in talk.c
419 
420 	uint16 offset = _joePtrOff + 2;
421 	uint16 hasNotString = READ_BE_UINT16(_fileData + offset); offset += 2;
422 
423 	char joeString[MAX_STRING_SIZE];
424 	if (!hasNotString) {
425 		getString(_fileData, offset, joeString, MAX_STRING_LENGTH);
426 	} else {
427 		joeString[0] = '\0';
428 	}
429 
430 	offset = _person2PtrOff;
431 	char joe2String[MAX_STRING_SIZE];
432 	getString(_fileData, offset, _person2String, MAX_STRING_LENGTH);
433 	getString(_fileData, offset, joe2String, MAX_STRING_LENGTH);
434 
435 	if (!hasTalkedTo()) {
436 		// Not yet talked to this person
437 		if (joeString[0] != '0') {
438 			char voiceFilePrefix[MAX_STRING_SIZE];
439 			sprintf(voiceFilePrefix, "%2dSSSSJ", _talkKey);
440 			speak(joeString, NULL, voiceFilePrefix);
441 		}
442 	} else {
443 		// Already spoken to them, choose second response
444 		if (joe2String[0] != '0') {
445 			char voiceFilePrefix[MAX_STRING_SIZE];
446 			sprintf(voiceFilePrefix, "%2dXXXXJ", _talkKey);
447 			speak(joe2String, NULL, voiceFilePrefix);
448 		}
449 	}
450 }
451 
getSpeakCommand(const Person * person,const char * sentence,unsigned & index)452 int Talk::getSpeakCommand(const Person *person, const char *sentence, unsigned &index) {
453 	// Lines 1299-1362 in talk.c
454 	int commandCode = SPEAK_DEFAULT;
455 	uint16 id = (sentence[index] << 8) | sentence[index + 1];
456 	switch (id) {
457 	case 'AO':
458 		commandCode = SPEAK_AMAL_ON;
459 		break;
460 	case 'FL':
461 		commandCode = SPEAK_FACE_LEFT;
462 		break;
463 	case 'FF':
464 		commandCode = SPEAK_FACE_FRONT;
465 		break;
466 	case 'FB':
467 		commandCode = SPEAK_FACE_BACK;
468 		break;
469 	case 'FR':
470 		commandCode = SPEAK_FACE_RIGHT;
471 		break;
472 	case 'GD':
473 		_vm->logic()->joeGrab(STATE_GRAB_DOWN);
474 		commandCode = SPEAK_NONE;
475 		break;
476 	case 'GM':
477 		_vm->logic()->joeGrab(STATE_GRAB_MID);
478 		commandCode = SPEAK_NONE;
479 		break;
480 	case 'WT':
481 		commandCode = SPEAK_PAUSE;
482 		break;
483 	case 'XY':
484 		// For example *XY00(237,112)
485 		{
486 			commandCode = atoi(sentence + index + 2);
487 			int x = atoi(sentence + index + 5);
488 			int y = atoi(sentence + index + 9);
489 			if (0 == strcmp(person->name, "JOE"))
490 				_vm->walk()->moveJoe(0, x, y, _vm->input()->cutawayRunning());
491 			else
492 				_vm->walk()->movePerson(person, x, y, _vm->graphics()->numFrames(), 0);
493 			index += 11;
494 			// if (JOEWALK==3) CUTQUIT=0;
495 			// XXX personWalking = true;
496 		}
497 		break;
498 	default:
499 		if (sentence[index + 0] >= '0' && sentence[index + 0] <= '9' &&
500 				sentence[index + 1] >= '0' && sentence[index + 1] <= '9') {
501 			commandCode = (sentence[index] - '0') * 10 + (sentence[index + 1] - '0');
502 		} else
503 			warning("Unknown command string: '%2s'", sentence + index);
504 	}
505 
506 	index += 2;
507 
508 	return commandCode;
509 }
510 
511 
speak(const char * sentence,Person * person,const char * voiceFilePrefix)512 bool Talk::speak(const char *sentence, Person *person, const char *voiceFilePrefix) {
513 	// Function SPEAK, lines 1266-1384 in talk.c
514 	bool personWalking = false;
515 	unsigned segmentIndex = 0;
516 	unsigned segmentStart = 0;
517 	unsigned i;
518 
519 	Person joe_person;
520 	ActorData joe_actor;
521 
522 	_vm->logic()->joeWalk(JWM_SPEAK);
523 
524 	if (!person) {
525 		// Fill in values for use by speakSegment() etc.
526 		memset(&joe_person, 0, sizeof(Person));
527 		memset(&joe_actor, 0, sizeof(ActorData));
528 
529 		joe_actor.bobNum = 0;
530 		joe_actor.color = 14;
531 		joe_actor.bankNum = 7;
532 
533 		joe_person.actor = &joe_actor;
534 		joe_person.name = "JOE";
535 
536 		person = &joe_person;
537 	}
538 
539 	debug(6, "Sentence '%s' is said by person '%s' and voice files with prefix '%s' played",
540 			sentence, person->name, voiceFilePrefix);
541 
542 	if (sentence[0] == '\0') {
543 		return personWalking;
544 	}
545 
546 	if (0 == strcmp(person->name, "FAYE-H") ||
547 		0 == strcmp(person->name, "FRANK-H") ||
548 		0 == strcmp(person->name, "AZURA-H") ||
549 		0 == strcmp(person->name, "X3_RITA") ||
550 		(0 == strcmp(person->name, "JOE") && _vm->logic()->currentRoom() == FAYE_HEAD) ||
551 		(0 == strcmp(person->name, "JOE") && _vm->logic()->currentRoom() == AZURA_HEAD) ||
552 		(0 == strcmp(person->name, "JOE") && _vm->logic()->currentRoom() == FRANK_HEAD))
553 		_talkHead = true;
554 	else
555 		_talkHead = false;
556 
557 	for (i = 0; i < strlen(sentence); ) {
558 		if (sentence[i] == '*') {
559 			int segmentLength = i - segmentStart;
560 
561 			i++;
562 			int command = getSpeakCommand(person, sentence, i);
563 
564 			if (SPEAK_NONE != command) {
565 				speakSegment(
566 						sentence + segmentStart,
567 						segmentLength,
568 						person,
569 						command,
570 						voiceFilePrefix,
571 						segmentIndex);
572 				// XXX if (JOEWALK == 2) break
573 			}
574 
575 			segmentIndex++;
576 			segmentStart = i;
577 		} else
578 			i++;
579 
580 		if (_vm->input()->cutawayQuit() || _vm->input()->talkQuit())
581 			return personWalking;
582 	}
583 
584 	if (segmentStart != i) {
585 		speakSegment(
586 				sentence + segmentStart,
587 				i - segmentStart,
588 				person,
589 				0,
590 				voiceFilePrefix,
591 				segmentIndex);
592 	}
593 
594 	return personWalking;
595 }
596 
countSpaces(const char * segment)597 int Talk::countSpaces(const char *segment) {
598 	int tmp = 0;
599 
600 	while (*segment++)
601 		tmp++;
602 
603 	if (tmp < 10)
604 		tmp = 10;
605 
606 	return (tmp * 2) / (_vm->talkSpeed() / 3);
607 }
608 
headStringAnimation(const SpeechParameters * parameters,int bobNum,int bankNum)609 void Talk::headStringAnimation(const SpeechParameters *parameters, int bobNum, int bankNum) {
610 	// talk.c lines 1612-1635
611 	BobSlot *bob2 = _vm->graphics()->bob(2);
612 
613 	if (parameters->animation[0] == 'E') {
614 		int offset = 1;
615 
616 		BobSlot *bob  = _vm->graphics()->bob(bobNum);
617 		int16 x = bob->x;
618 		int16 y = bob->y;
619 
620 		for (;;) {
621 			uint16 frame;
622 
623 			frame = atoi(parameters->animation + offset);
624 			if (!frame)
625 				break;
626 
627 			offset += 4;
628 
629 			_vm->bankMan()->unpack(frame, _vm->graphics()->numFrames(), bankNum);
630 
631 			bob2->frameNum = _vm->graphics()->numFrames();
632 			bob2->scale = 100;
633 			bob2->active = true;
634 			bob2->x = x;
635 			bob2->y = y;
636 
637 			_vm->update();
638 		}
639 	} else
640 		bob2->active = false;
641 }
642 
stringAnimation(const SpeechParameters * parameters,int startFrame,int bankNum)643 void Talk::stringAnimation(const SpeechParameters *parameters, int startFrame, int bankNum) {
644 	// lines 1639-1690 in talk.c
645 
646 	int offset = 0;
647 	bool torso;
648 
649 	if (parameters->animation[0] == 'T') {
650 		// Torso animation
651 		torso = true;
652 		_vm->bankMan()->overpack(parameters->body, startFrame, bankNum);
653 		offset++;
654 	} else if (parameters->animation[0] == 'E') {
655 		// Talking head animation
656 		return;
657 	} else if (!Common::isDigit(parameters->animation[0])) {
658 		debug(6, "Error in speak string animation: '%s'", parameters->animation);
659 		return;
660 	} else
661 		torso = false;
662 
663 	for (;;) {
664 		uint16 frame;
665 
666 		frame = atoi(parameters->animation + offset);
667 		if (!frame)
668 			break;
669 
670 		offset += 4;
671 
672 		if (frame > 500) {
673 			frame -= 500;
674 			_vm->sound()->playSfx(_vm->logic()->currentRoomSfx());
675 		}
676 
677 		if (torso) {
678 			_vm->bankMan()->overpack(frame, startFrame, bankNum);
679 		} else {
680 			_vm->bankMan()->unpack(frame, startFrame, bankNum);
681 			// XXX bobs[BNUM].scale=SF;
682 		}
683 
684 		_vm->update();
685 	}
686 }
687 
defaultAnimation(const char * segment,bool isJoe,const SpeechParameters * parameters,int startFrame,int bankNum)688 void Talk::defaultAnimation(
689 		const char *segment,
690 		bool isJoe,
691 		const SpeechParameters *parameters,
692 		int startFrame,
693 		int bankNum) {
694 	// lines 1730-1823 in talk.c
695 
696 	if (segment[0] != 0)  {
697 
698 		// Why on earth would someone name a variable qzx?
699 		short qzx = 0;
700 
701 		int len = countSpaces(segment);
702 		while (1) {
703 			if (parameters != NULL) {
704 
705 				int bf;
706 				if (segment[0] == ' ')
707 					bf = 0;
708 				else
709 					bf = parameters->bf;
710 
711 				int head;
712 				if (parameters->rf > 0)
713 					head = bf + _vm->randomizer.getRandomNumber(parameters->rf);
714 				else
715 					head = bf;
716 
717 				if (bf > 0) {
718 					// Make the head move
719 					qzx ^= 1;
720 					if (parameters->af && qzx)
721 						_vm->bankMan()->overpack(parameters->af + head, startFrame, bankNum);
722 					else {
723 						_vm->bankMan()->overpack(head, startFrame, bankNum);
724 					}
725 				} else {
726 					debug(6, "[Talk::defaultAnimation] Body action");
727 					// Just do a body action
728 					_vm->bankMan()->overpack(parameters->body, startFrame, bankNum);
729 				}
730 
731 				if (!_talkHead)
732 					_vm->update();
733 			} else { // (_talkHead && isJoe)
734 				_vm->update();
735 			}
736 
737 			if (_vm->input()->talkQuit()) {
738 				_vm->sound()->stopSpeech();
739 				break;
740 			}
741 
742 			if (_vm->logic()->joeWalk() == JWM_SPEAK) {
743 				_vm->update();
744 			} else {
745 				_vm->update(true);
746 				if (_vm->logic()->joeWalk() == JWM_EXECUTE)
747 					// Selected a command, so exit
748 					break;
749 			}
750 
751 			// Skip through text more quickly
752 			if (_vm->input()->keyVerb() == VERB_SKIP_TEXT) {
753 				_vm->input()->clearKeyVerb();
754 				_vm->sound()->stopSpeech();
755 				break;
756 			}
757 
758 			if (_vm->sound()->speechOn() && _vm->sound()->speechSfxExists()) {
759 				// sfx is finished, stop the speak animation
760 				if (!_vm->sound()->isSpeechActive()) {
761 					break;
762 				}
763 			} else {
764 				// no sfx, stop the animation when speak segment 'length' is 0
765 				--len;
766 				if (len <= 0) {
767 					break;
768 				}
769 			}
770 		}
771 	}
772 
773 	// Make sure that Person closes their mouth
774 	if (!isJoe && parameters && parameters->ff > 0)
775 		_vm->bankMan()->overpack(parameters->ff, startFrame, bankNum);
776 }
777 
778 
speakSegment(const char * segmentStart,int length,Person * person,int command,const char * voiceFilePrefix,int index)779 void Talk::speakSegment(
780 		const char *segmentStart,
781 		int length,
782 		Person *person,
783 		int command,
784 		const char *voiceFilePrefix,
785 		int index)
786 {
787 	int i;
788 	char segment[MAX_STRING_SIZE];
789 	memcpy(segment, segmentStart, length);
790 	segment[length] = '\0';
791 
792 	char voiceFileName[MAX_STRING_SIZE];
793 	sprintf(voiceFileName, "%s%1x", voiceFilePrefix, index + 1);
794 
795 	// French talkie version has a useless voice file ;	c30e_102 file is the same as c30e_101,
796 	// so there is no need to play it. This voice was used in room 30 (N8) when talking to Klunk.
797 	if (!(_vm->resource()->getLanguage() == Common::FR_FRA && !strcmp(voiceFileName, "c30e_102")))
798 		_vm->sound()->playSpeech(voiceFileName);
799 
800 	int faceDirectionCommand = 0;
801 
802 	switch (command) {
803 	case SPEAK_PAUSE:
804 		for (i = 0; i < 10 && !_vm->input()->talkQuit() && !_vm->shouldQuit(); i++) {
805 			_vm->update();
806 		}
807 		return;
808 
809 	case SPEAK_FACE_LEFT:
810 	case SPEAK_FACE_RIGHT:
811 	case SPEAK_FACE_FRONT:
812 	case SPEAK_FACE_BACK:
813 		faceDirectionCommand = command;
814 		command = 0;
815 		break;
816 	}
817 
818 	bool isJoe = (0 == person->actor->bobNum);
819 
820 	int16  bobNum  = person->actor->bobNum;
821 	uint16 color   = person->actor->color;
822 	uint16 bankNum = person->actor->bankNum;
823 
824 	BobSlot *bob = _vm->graphics()->bob(bobNum);
825 
826 	bool oracle = false;
827 	int textX = 0;
828 	int textY = 0;
829 
830 	if (!isJoe) {
831 		if (SPEAK_AMAL_ON == command) {
832 			// It's the oracle!
833 			// Don't turn AMAL animation off, and don't manually anim person
834 			command = SPEAK_ORACLE;
835 			oracle = true;
836 			uint16 frameNum = _vm->graphics()->personFrames(bobNum);
837 			for (i = 5; i <= 8; ++i) {
838 				_vm->bankMan()->unpack(i, frameNum, bankNum);
839 				++frameNum;
840 			}
841 		} else {
842 			bob->animating = false;
843 			bob->frameNum = 31 + bobNum;
844 		}
845 	}
846 
847 	if (_talkHead) {
848 		// talk.c lines 1491-1533
849 		switch (_vm->logic()->currentRoom()) {
850 		case FAYE_HEAD:
851 			textX = 15;
852 			if (_vm->resource()->getPlatform() == Common::kPlatformAmiga) {
853 				color = isJoe ? 15 : 29;
854 			}
855 			break;
856 		case AZURA_HEAD:
857 			textX = 15;
858 			if (_vm->resource()->getPlatform() == Common::kPlatformAmiga) {
859 				color = isJoe ? 6 : 30;
860 			}
861 			break;
862 		default: // FRANK_HEAD
863 			textX = 150;
864 			if (_vm->resource()->getPlatform() == Common::kPlatformAmiga) {
865 				color = 17;
866 			}
867 			break;
868 		}
869 		textY = isJoe ? 30 : 60;
870 	} else {
871 		textX = bob->x;
872 		textY = bob->y;
873 	}
874 
875 	// Set the focus rectangle
876 	// FIXME: This may not be correct!
877 	BobFrame *pbf = _vm->bankMan()->fetchFrame(bob->frameNum);
878 
879 	int height = (pbf->height * bob->scale) / 100;
880 
881 	Common::Rect focus(textX - 96, textY - height - 64, textX + 96, textY + height + 64);
882 	_vm->display()->setFocusRect(focus);
883 
884 
885 	//int SF = _vm->grid()->findScale(textX, textY);
886 
887 	const SpeechParameters *parameters = NULL;
888 	int startFrame = 0;
889 
890 	if (_talkHead && isJoe) {
891 		if (_vm->subtitles())
892 			_vm->graphics()->setBobText(bob, segment, textX, textY, color, true);
893 		defaultAnimation(segment, isJoe, parameters, startFrame, bankNum);
894 	} else {
895 		if (SPEAK_UNKNOWN_6 == command)
896 			return;
897 
898 		if (isJoe) {
899 			if (_vm->logic()->currentRoom() == 108)
900 				parameters = findSpeechParameters("JOE-E", command, 0);
901 			else
902 				parameters = findSpeechParameters("JOE", command, _vm->logic()->joeFacing());
903 		}
904 		else
905 			parameters = findSpeechParameters(person->name, command, 0);
906 
907 		startFrame = 31 + bobNum;
908 		int faceDirection = 0;
909 
910 		if (isJoe && _vm->logic()->joeFacing() == DIR_LEFT)
911 			faceDirection = DIR_LEFT;
912 		else if (!isJoe) {
913 			ObjectData *data = _vm->logic()->objectData(_vm->logic()->objectForPerson(bobNum));
914 
915 			if (data->image == -3)
916 				faceDirection = DIR_LEFT;
917 
918 			if (faceDirectionCommand == SPEAK_FACE_LEFT)
919 				data->image = -3;
920 			else if (faceDirectionCommand == SPEAK_FACE_RIGHT)
921 				data->image = -4;
922 		}
923 
924 		if (faceDirectionCommand) {
925 			switch (faceDirectionCommand) {
926 			case SPEAK_FACE_LEFT:
927 				faceDirection = DIR_LEFT;
928 				break;
929 			case SPEAK_FACE_RIGHT:
930 				faceDirection = DIR_RIGHT;
931 				break;
932 			case SPEAK_FACE_FRONT:
933 				faceDirection = DIR_FRONT;
934 				break;
935 			case SPEAK_FACE_BACK:
936 				faceDirection = DIR_BACK;
937 				break;
938 			}
939 			if (isJoe)
940 				_vm->logic()->joeFacing(faceDirection);
941 		}
942 
943 		if (!isJoe) {
944 			bob->xflip = (faceDirection == DIR_LEFT);
945 		}
946 
947 		// Run animated sequence if SANIMstr is primed
948 
949 		if (_talkHead) {
950 			// talk.c lines 1612-1635
951 			headStringAnimation(parameters, bobNum, bankNum);
952 		}
953 
954 		if (_vm->subtitles())
955 			_vm->graphics()->setBobText(bob, segment, textX, textY, color, _talkHead);
956 
957 		if (parameters->animation[0] != '\0' && parameters->animation[0] != 'E') {
958 			stringAnimation(parameters, startFrame, bankNum);
959 		} else {
960 			_vm->bankMan()->unpack(parameters->body, startFrame, bankNum);
961 
962 			if (length == 0 && !isJoe && parameters->bf > 0) {
963 				_vm->bankMan()->overpack(parameters->bf, startFrame, bankNum);
964 				_vm->update();
965 			}
966 
967 			if (-1 == parameters->rf) {
968 				// Setup the Torso frames
969 				_vm->bankMan()->overpack(parameters->bf, startFrame, bankNum);
970 				if (isJoe)
971 					parameters = findSpeechParameters(person->name, 0, _vm->logic()->joeFacing());
972 				else
973 					parameters = findSpeechParameters(person->name, 0, 0);
974 			}
975 
976 			if (-2 == parameters->rf) {
977 				// Setup the Torso frames
978 				_vm->bankMan()->overpack(parameters->bf, startFrame, bankNum);
979 				if (isJoe)
980 					parameters = findSpeechParameters(person->name, 14, _vm->logic()->joeFacing());
981 				else
982 					parameters = findSpeechParameters(person->name, 14, 0);
983 			}
984 
985 			defaultAnimation(segment, isJoe, parameters, startFrame, bankNum);
986 		}
987 	}
988 
989 	// Moved here so that Text is cleared when a Torso command done!
990 	_vm->display()->clearTexts(0,198);
991 
992 	if (oracle) {
993 		uint16 frameNum = _vm->graphics()->personFrames(bobNum);
994 		for (i = 1; i <= 4; ++i) {
995 			_vm->bankMan()->unpack(i, frameNum, bankNum);
996 			++frameNum;
997 		}
998 	}
999 
1000 	// Ensure that the correct buffer frame is selected
1001 
1002 	if (isJoe && !_talkHead) {
1003 		if (_vm->logic()->joeFacing() == DIR_FRONT ||
1004 				_vm->logic()->joeFacing() == DIR_BACK) {
1005 			// Joe is facing either Front or Back!
1006 			//  - Don't FACE_JOE in room 69, because Joe is probably
1007 			//       holding the Dino Ray gun.
1008 			if (_vm->logic()->currentRoom() != 69)
1009 				_vm->logic()->joeFace();
1010 		} else {
1011 			if (command == SPEAK_DEFAULT ||
1012 					command == 6 ||
1013 					command == 7) {
1014 				_vm->logic()->joeFace();
1015 			} else if (command != 5) {
1016 				// 7/11/94, Ensure that correct mouth closed frame is used!
1017 				if (parameters->rf != -1)
1018 					// XXX should really be just "bf", but it is not always calculated... :-(
1019 					_vm->bankMan()->overpack(parameters->bf, startFrame, bankNum);
1020 
1021 				if (parameters->ff == 0)
1022 					_vm->bankMan()->overpack(10, startFrame, bankNum);
1023 				else
1024 					_vm->bankMan()->overpack(parameters->ff, startFrame, bankNum);
1025 			}
1026 		}
1027 	}
1028 
1029 	_vm->update();
1030 }
1031 
findSpeechParameters(const char * name,int state,int faceDirection)1032 const Talk::SpeechParameters *Talk::findSpeechParameters(
1033 		const char *name,
1034 		int state,
1035 		int faceDirection) {
1036 	const SpeechParameters *iterator = _speechParameters;
1037 	if (faceDirection == DIR_RIGHT)
1038 		faceDirection = DIR_LEFT;
1039 	while (iterator->name[0] != '*') {
1040 		if (0 == scumm_stricmp(iterator->name, name) &&
1041 				iterator->state == state &&
1042 				iterator->faceDirection == faceDirection)
1043 			break;
1044 		iterator++;
1045 	}
1046 	return iterator;
1047 }
1048 
getString(const byte * ptr,uint16 & offset,char * str,int maxLength,int align)1049 void Talk::getString(const byte *ptr, uint16 &offset, char *str, int maxLength, int align) {
1050 	assert((align & 1) == 0);
1051 	int length = *(ptr + offset);
1052 	++offset;
1053 
1054 	if (length > maxLength) {
1055 		error("String too long. Length = %i, maxLength = %i", length, maxLength);
1056 	} else if (length) {
1057 		if (str) {
1058 			memcpy(str, ptr + offset, length);
1059 			str[length] = '\0';
1060 		}
1061 		offset = (offset + length + (align - 1)) & ~(align - 1);
1062 	} else {
1063 		if (str) {
1064 			str[0] = '\0';
1065 		}
1066 	}
1067 }
1068 
talkSelected()1069 TalkSelected *Talk::talkSelected() {
1070 	return _vm->logic()->talkSelected(_uniqueKey);
1071 }
1072 
splitOption(const char * str,char optionText[5][MAX_STRING_SIZE])1073 int Talk::splitOption(const char *str, char optionText[5][MAX_STRING_SIZE]) {
1074 	char option[MAX_STRING_SIZE];
1075 	strcpy(option, str);
1076 	// option text ends at '*' char
1077 	char *p = strchr(option, '*');
1078 	if (p) {
1079 		*p = '\0';
1080 	}
1081 	int lines;
1082 	memset(optionText, 0, 5 * MAX_STRING_SIZE);
1083 	if (_vm->resource()->getLanguage() == Common::EN_ANY || _vm->display()->textWidth(option) <= MAX_TEXT_WIDTH) {
1084 		strcpy(optionText[0], option);
1085 		lines = 1;
1086 	} else if (_vm->resource()->getLanguage() == Common::HE_ISR) {
1087 		lines = splitOptionHebrew(option, optionText);
1088 	} else {
1089 		lines = splitOptionDefault(option, optionText);
1090 	}
1091 	return lines;
1092 }
1093 
splitOptionHebrew(const char * str,char optionText[5][MAX_STRING_SIZE])1094 int Talk::splitOptionHebrew(const char *str, char optionText[5][MAX_STRING_SIZE]) {
1095 	char tmpString[MAX_STRING_SIZE] = "";
1096 	uint16 len = 0;
1097 	uint16 spaceCharWidth = _vm->display()->textWidth(" ");
1098 	uint16 width = 0;
1099 	uint16 optionLines = 0;
1100 	uint16 maxTextLen = MAX_TEXT_WIDTH;
1101 	const char *p = strchr(str, '\0');
1102 	while (p != str - 1) {
1103 		while (*p != ' ' && p != str - 1) {
1104 			--p;
1105 			++len;
1106 		}
1107 		if (p != str - 1) {
1108 			uint16 wordWidth = _vm->display()->textWidth(p, len);
1109 			width += wordWidth;
1110 			if (width > maxTextLen) {
1111 				++optionLines;
1112 				strncpy(optionText[optionLines], p, len);
1113 				optionText[optionLines][len] = '\0';
1114 				width = wordWidth;
1115 				maxTextLen = MAX_TEXT_WIDTH - OPTION_TEXT_MARGIN;
1116 			} else {
1117 				strcpy(tmpString, optionText[optionLines]);
1118 				strncpy(optionText[optionLines], p, len);
1119 				optionText[optionLines][len] = '\0';
1120 				strcat(optionText[optionLines], tmpString);
1121 			}
1122 			--p;
1123 			len = 1;
1124 			width += spaceCharWidth;
1125 		} else {
1126 				if (len > 1) {
1127 				if (width + _vm->display()->textWidth(p + 1, len) > maxTextLen) {
1128 					++optionLines;
1129 				}
1130 				strcpy(tmpString, optionText[optionLines]);
1131 				strncpy(optionText[optionLines], p + 1, len);
1132 				optionText[optionLines][len] = '\0';
1133 				strcat(optionText[optionLines], tmpString);
1134 			}
1135 			++optionLines;
1136 		}
1137 	}
1138 	return optionLines;
1139 }
1140 
splitOptionDefault(const char * str,char optionText[5][MAX_STRING_SIZE])1141 int Talk::splitOptionDefault(const char *str, char optionText[5][MAX_STRING_SIZE]) {
1142 	// Split up multiple line option at closest space character
1143 	uint16 spaceCharWidth = _vm->display()->textWidth(" ");
1144 	uint16 width = 0;
1145 	uint16 optionLines = 0;
1146 	uint16 maxTextLen = MAX_TEXT_WIDTH;
1147 	const char *p = str;
1148 	while (p) {
1149 		p = strchr(str, ' ');
1150 		if (p) {
1151 			uint16 len = p - str;
1152 			uint16 wordWidth = _vm->display()->textWidth(str, len);
1153 			width += wordWidth;
1154 			if (width > maxTextLen) {
1155 				++optionLines;
1156 				strncpy(optionText[optionLines], str, len + 1);
1157 				width = wordWidth;
1158 				maxTextLen = MAX_TEXT_WIDTH - OPTION_TEXT_MARGIN;
1159 			} else {
1160 				strncat(optionText[optionLines], str, len + 1);
1161 			}
1162 			width += spaceCharWidth;
1163 			str = p + 1;
1164 		} else {
1165 			if (str[0]) {
1166 				if (width + _vm->display()->textWidth(str) > maxTextLen) {
1167 					++optionLines;
1168 				}
1169 				strcat(optionText[optionLines], str);
1170 			}
1171 			++optionLines;
1172 		}
1173 	}
1174 	return optionLines;
1175 }
1176 
selectSentence()1177 int16 Talk::selectSentence() {
1178 	int selectedSentence = 0;
1179 
1180 	int startOption = 1;
1181 	int optionLines = 0;
1182 	char optionText[5][MAX_STRING_SIZE];
1183 	int talkZone[5];
1184 	int i;
1185 
1186 	_vm->display()->textCurrentColor(_vm->display()->getInkColor(INK_TALK_NORMAL));
1187 
1188 	_vm->graphics()->setupArrows();
1189 	BobSlot *arrowBobUp   = _vm->graphics()->bob(Graphics::ARROW_BOB_UP);
1190 	arrowBobUp->active    = false;
1191 	BobSlot *arrowBobDown = _vm->graphics()->bob(Graphics::ARROW_BOB_DOWN);
1192 	arrowBobDown->active  = false;
1193 
1194 	bool rezone = true;
1195 
1196 	while (rezone) {
1197 		rezone = false;
1198 
1199 		// Set zones for UP/DOWN text arrows when not English version
1200 
1201 		_vm->grid()->clear(GS_PANEL);
1202 
1203 		if (_vm->resource()->getLanguage() != Common::EN_ANY) {
1204 			_vm->grid()->setZone(GS_PANEL, ARROW_ZONE_UP,   MAX_TEXT_WIDTH + 1, 0,  319, 24);
1205 			_vm->grid()->setZone(GS_PANEL, ARROW_ZONE_DOWN, MAX_TEXT_WIDTH + 1, 25, 319, 49);
1206 		}
1207 
1208 		_vm->display()->clearTexts(151, 199);
1209 
1210 		int sentenceCount = 0;
1211 		int yOffset = 1;
1212 
1213 		for (i = startOption; i <= 4; i++) {
1214 			talkZone[i] = 0;
1215 
1216 			if (_talkString[i][0] != '\0') {
1217 				sentenceCount++;
1218 				optionLines = splitOption(_talkString[i], optionText);
1219 
1220 				if (yOffset < 5) {
1221 					_vm->grid()->setZone(
1222 							GS_PANEL,
1223 							i,
1224 							0,
1225 							yOffset * LINE_HEIGHT - PUSHUP,
1226 							(_vm->resource()->getLanguage() == Common::EN_ANY) ? 319 : MAX_TEXT_WIDTH,
1227 							(yOffset + optionLines) * LINE_HEIGHT - PUSHUP);
1228 				}
1229 
1230 				int j;
1231 				for (j = 0; j < optionLines; j++) {
1232 					if (yOffset < 5) {
1233 						_vm->display()->setText(
1234 								(j == 0) ? 0 : OPTION_TEXT_MARGIN,
1235 								150 - PUSHUP + yOffset * LINE_HEIGHT,
1236 								optionText[j]);
1237 					}
1238 					yOffset++;
1239 				}
1240 
1241 				talkZone[i] = sentenceCount;
1242 			}
1243 		}
1244 
1245 		yOffset--;
1246 
1247 		// Up and down dialogue arrows
1248 
1249 		if (_vm->resource()->getLanguage() != Common::EN_ANY) {
1250 			arrowBobUp->active    = (startOption > 1);
1251 			arrowBobDown->active  = (yOffset > 4);
1252 		}
1253 
1254 		_vm->input()->clearKeyVerb();
1255 		_vm->input()->clearMouseButton();
1256 
1257 		if (sentenceCount > 0) {
1258 			int oldZone = 0;
1259 
1260 			while (0 == selectedSentence && !_vm->input()->talkQuit() && !_vm->shouldQuit()) {
1261 				_vm->update();
1262 
1263 				Common::Point mouse = _vm->input()->getMousePos();
1264 				int zone = _vm->grid()->findZoneForPos(GS_PANEL, mouse.x, mouse.y);
1265 
1266 				int mouseButton = _vm->input()->mouseButton();
1267 				_vm->input()->clearMouseButton();
1268 
1269 				if (ARROW_ZONE_UP == zone || ARROW_ZONE_DOWN == zone) {
1270 					if (oldZone > 0) {
1271 						int16 y;
1272 						const Box *b = _vm->grid()->zone(GS_PANEL, oldZone);
1273 						for (y = b->y1; y < b->y2; y += 10)
1274 							_vm->display()->textColor(150 + y, _vm->display()->getInkColor(INK_TALK_NORMAL));
1275 						oldZone = 0;
1276 					}
1277 					if (mouseButton != 0) {
1278 						if (zone == ARROW_ZONE_UP && arrowBobUp->active) {
1279 							startOption--;
1280 						} else if (zone == ARROW_ZONE_DOWN && arrowBobDown->active) {
1281 							startOption++;
1282 						}
1283 					}
1284 					rezone = true;
1285 					break;
1286 				} else {
1287 					if (oldZone != zone) {
1288 						// Changed zone, change text colors
1289 						int y;
1290 
1291 						debug(6, "Changed zone. oldZone = %i, zone = %i",
1292 								oldZone, zone);
1293 
1294 						if (zone > 0) {
1295 							const Box *b = _vm->grid()->zone(GS_PANEL, zone);
1296 							for (y = b->y1; y < b->y2; y += 10)
1297 								_vm->display()->textColor(150 + y, _vm->display()->getInkColor(INK_JOE));
1298 						}
1299 
1300 						if (oldZone > 0) {
1301 							const Box *b = _vm->grid()->zone(GS_PANEL, oldZone);
1302 							for (y = b->y1; y < b->y2; y += 10)
1303 								_vm->display()->textColor(150 + y, _vm->display()->getInkColor(INK_TALK_NORMAL));
1304 						}
1305 
1306 						oldZone = zone;
1307 					}
1308 
1309 				}
1310 
1311 				Verb v = _vm->input()->keyVerb();
1312 				if (v >= VERB_DIGIT_FIRST && v <= VERB_DIGIT_LAST) {
1313 					int n = v - VERB_DIGIT_FIRST + 1;
1314 					for (i = 1; i <= 4; i++) {
1315 						if (talkZone[i] == n) {
1316 							selectedSentence = i;
1317 							break;
1318 						}
1319 					}
1320 
1321 					_vm->input()->clearKeyVerb();
1322 				} else if (mouseButton) {
1323 					selectedSentence = zone;
1324 				}
1325 
1326 			} // while ()
1327 		}
1328 	}
1329 
1330 	_vm->input()->clearKeyVerb();
1331 	_vm->input()->clearMouseButton();
1332 
1333 	debug(6, "Selected sentence %i", selectedSentence);
1334 
1335 	arrowBobUp->active    = false;
1336 	arrowBobDown->active  = false;
1337 
1338 	if (selectedSentence > 0) {
1339 		_vm->display()->clearTexts(0, 198);
1340 
1341 		speak(_talkString[selectedSentence], NULL, _joeVoiceFilePrefix[selectedSentence]);
1342 	}
1343 
1344 	_vm->display()->clearTexts(151, 151);
1345 
1346 	return selectedSentence;
1347 }
1348 
1349 const Talk::SpeechParameters Talk::_speechParameters[] = {
1350 	{ "JOE", 0, 1, 1, 10, 2, 3, "", 0 },
1351 	{ "JOE", 0, 3, 3, 28, 2, 3, "", 0 },
1352 	{ "JOE", 0, 4, 5, 38, 1, 0, "", 0 },
1353 
1354 	{ "JOE", 1, 1, 1, 45, -1, 0, "", 0 },
1355 	{ "JOE", 1, 3, 3, 28,  2, 3, "", 0 },
1356 	{ "JOE", 1, 4, 5, 38,  1, 0, "", 0 },
1357 
1358 	{ "JOE", 2, 1, 1, 46, -1, 0, "", 0 },
1359 	{ "JOE", 2, 3, 3, 28,  2, 3, "", 0 },
1360 	{ "JOE", 2, 4, 5, 38,  1, 0, "", 0 },
1361 
1362 	{ "JOE", 3, 1, 1, 47, -1, 0, "", 0 },
1363 	{ "JOE", 3, 3, 3, 28,  2, 3, "", 0 },
1364 	{ "JOE", 3, 4, 5, 38,  1, 0, "", 0 },
1365 
1366 	{ "JOE", 4, 1, 1, 50, -1, 0, "", 0 },
1367 	{ "JOE", 4, 3, 3, 28,  2, 3, "", 0 },
1368 	{ "JOE", 4, 4, 5, 38,  1, 0, "", 0 },
1369 
1370 	{ "JOE", 5, 1, 2, 0, 0, 0, "", 0 },
1371 	{ "JOE", 5, 3, 4, 0, 0, 0, "", 0 },
1372 	{ "JOE", 5, 4, 6, 0, 0, 0, "", 0 },
1373 
1374 	{ "JOE", 6, 1, 1, 48, 0, 1, "", 0 },
1375 	{ "JOE", 6, 3, 3, 28, 2, 3, "", 0 },
1376 	{ "JOE", 6, 4, 5, 38, 1, 0, "", 0 },
1377 
1378 	{ "JOE", 7, 1, 1, 51, 0, 1, "", 0 },
1379 	{ "JOE", 7, 3, 3, 28, 2, 3, "", 0 },
1380 	{ "JOE", 7, 4, 5, 38, 1, 0, "", 0 },
1381 
1382 	{ "JOE", 8, 1, 1, 26, 0, 0, "", 0 },
1383 	{ "JOE", 8, 3, 3, 28, 2, 3, "", 0 },
1384 	{ "JOE", 8, 4, 5, 38, 1, 0, "", 0 },
1385 
1386 	{ "JOE", 9, 1, 1, 29, 0, 0, "", 0 },
1387 	{ "JOE", 9, 3, 3, 28, 0, 0, "", 0 },
1388 	{ "JOE", 9, 4, 5, 38, 0, 0, "", 0 },
1389 
1390 	{ "JOE", 10, 1, 1, 12, 0, 0, "T046,010,010,010,012,012,012,012,012,012,012,012,012,012,012,012,012,012,010,000", 0 },
1391 	{ "JOE", 10, 3, 3, 18, 0, 0, "", 0 },
1392 	{ "JOE", 10, 4, 5, 44, 0, 0, "", 0 },
1393 
1394 	{ "JOE", 11, 1, 1, 53, -1, 0, "", 0 },
1395 	{ "JOE", 11, 3, 3, 28,  2, 3, "", 0 },
1396 	{ "JOE", 11, 4, 5, 38,  1, 0, "", 0 },
1397 
1398 	{ "JOE", 12, 1, 1, 10, 2, 3, "", 0 },
1399 	{ "JOE", 12, 3, 3, 28, 2, 0, "", 0 },
1400 	{ "JOE", 12, 4, 5, 38, 1, 0, "", 0 },
1401 
1402 	{ "JOE", 13, 1, 1, 10, 2, 3, "T012,013,019,019,019,019,019,019,019,019,019,019,013,010,000", 0 },
1403 	{ "JOE", 13, 3, 3, 28, 2, 3, "", 0 },
1404 	{ "JOE", 13, 4, 5, 38, 1, 0, "", 0 },
1405 
1406 	{ "JOE", 14, 1, 1, 16, 2, 3, "", 16 },
1407 	{ "JOE", 14, 3, 3, 28, 2, 3, "", 0 },
1408 	{ "JOE", 14, 4, 5, 38, 1, 0, "", 0 },
1409 
1410 	{ "JOE", 15, 1, 1, 58, -1, 0, "", 0 },
1411 	{ "JOE", 15, 3, 3, 28,  2, 3, "", 0 },
1412 	{ "JOE", 15, 4, 5, 38,  1, 0, "", 0 },
1413 
1414 	{ "JOE", 16, 1, 1, 59, -1, 0, "", 0 },
1415 	{ "JOE", 16, 3, 3, 28,  2, 3, "", 0 },
1416 	{ "JOE", 16, 4, 5, 38,  1, 0, "", 0 },
1417 
1418 	{ "JOE", 17, 1, 1, 56, -1, 0, "", 0 },
1419 	{ "JOE", 17, 3, 3, 28,  2, 3, "", 0 },
1420 	{ "JOE", 17, 4, 5, 38,  1, 0, "", 0 },
1421 
1422 	{ "JOE", 18, 1, 56, 16, 2, 3, "T056,057,057,057,056,056,000", 0 },
1423 	{ "JOE", 18, 3,  3, 28, 2, 3, "", 0 },
1424 	{ "JOE", 18, 4,  5, 38, 1, 0, "", 0 },
1425 
1426 	{ "JOE", 19, 1, 54, 16, 2, 3, "T054,055,057,056,000", 0 },
1427 	{ "JOE", 19, 3,  3, 28, 2, 3, "", 0 },
1428 	{ "JOE", 19, 4,  5, 38, 1, 0, "", 0 },
1429 
1430 	{ "JOE", 20, 1, 56, 16, 2, 3, "T056,057,055,054,001,000", 0 },
1431 	{ "JOE", 20, 3,  3, 28, 2, 3, "", 0 },
1432 	{ "JOE", 20, 4,  5, 38, 1, 0, "", 0 },
1433 
1434 	{ "JOE", 21, 1,  1, 60, -1, 0, "", 0 },
1435 	{ "JOE", 21, 3,  3, 28,  2, 3, "", 0 },
1436 	{ "JOE", 21, 4, 61, 38,  1, 0, "", 0 },
1437 
1438 	{ "JOE", 22, 1, 1, 16, 2, 3, "T063,060,000", 0 },
1439 	{ "JOE", 22, 3, 3, 28, 2, 3, "", 0 },
1440 	{ "JOE", 22, 4, 5, 38, 1, 0, "", 0 },
1441 
1442 	{ "JOE", 23, 1, 1, 16, 2, 3, "T060,063,001,000", 0 },
1443 	{ "JOE", 23, 3, 3, 28, 2, 3, "", 0 },
1444 	{ "JOE", 23, 4, 5, 38, 1, 0, "", 0 },
1445 
1446 	{ "JOE", 24, 1, 1, 47, -2, 0, "", 0 },
1447 	{ "JOE", 24, 3, 3, 28,  2, 3, "", 0 },
1448 	{ "JOE", 24, 4, 5, 38,  1, 0, "", 0 },
1449 
1450 	{ "RICO", 0, 0, 1, 7,  1, 3, "", 7 },
1451 	{ "RICO", 1, 0, 1, 5, -1, 0, "", 7 },
1452 	{ "RICO", 2, 0, 1, 9,  0, 3, "", 7 },
1453 	{ "RICO", 3, 0, 1, 4, -1, 0, "", 7 },
1454 
1455 	{ "EDDY", 0, 0, 14, 18, 1, 3, "", 18 },
1456 	{ "EDDY", 1, 0, 14,  0, 0, 0, "T016,017,017,016,016,017,017,016,016,017,017,000", 18 },
1457 
1458 	{ "MIKE", 0, 0, 1, 2, 2, 3, "", 2 },
1459 
1460 	{ "LOLA", 0, 0, 30, 33, 2, 3, "", 33 },
1461 	{ "LOLA", 1, 0,  9, 10, 2, 3, "", 33 },
1462 	{ "LOLA", 2, 0, 30, 33, 2, 3, "", 33 },
1463 	{ "LOLA", 3, 0, 32, 33, 2, 3, "", 33 },
1464 	{ "LOLA", 4, 0,  8,  0, 0, 0, "", 33 },
1465 	{ "LOLA", 5, 0, 31,  0, 0, 0, "", 0 },
1466 	{ "LOLA", 6, 0, 31,  0, 0, 0, "047,048,049,050,000", 33 },
1467 
1468 	{ "LOLA_SHOWER", 0, 0,  7, 10, 2, 3, "", 10 },
1469 	{ "LOLA_SHOWER", 1, 0,  9, 10, 2, 3, "", 10 },
1470 	{ "LOLA_SHOWER", 2, 0, 30, 33, 2, 3, "", 10 },
1471 	{ "LOLA_SHOWER", 3, 0, 32, 33, 2, 3, "", 10 },
1472 	{ "LOLA_SHOWER", 4, 0,  8,  0, 0, 0, "", 0 },
1473 	{ "LOLA_SHOWER", 5, 0, 61,  0, 0, 0, "", 0 },
1474 	{ "LOLA_SHOWER", 6, 0, 64, 10, 2, 3, "", 0 },
1475 	{ "LOLA_SHOWER", 7, 0, 31,  0, 0, 0, "062,063,064,000", 0 },
1476 
1477 	{ "SECRETARY", 0, 0, 1, 12, 2, 3, "", 12 },
1478 	{ "SECRETARY", 1, 0, 1, 12, 2, 0, "", 12 },
1479 	{ "SECRETARY", 2, 0, 1, 12, 2, 0, "", 12 },
1480 
1481 	{ "SPARKY",  0, 0, 21, 23, 2, 3, "", 23 },
1482 	{ "SPARKY",  1, 0, 21, 22, 0, 0, "", 0 },
1483 	{ "SPARKY",  2, 0, 21, 22, 0, 0, "021,042,043,000", 0 },
1484 	{ "SPARKY",  3, 0, 21, 22, 0, 0, "043,042,021,000", 0 },
1485 	{ "SPARKY",  4, 0, 43, 43, 1, 0, "", 0 },
1486 	{ "SPARKY", 14, 0, 21, 29, 5, 0, "", 29 },
1487 
1488 	{ "SPARKY-F",  0, 0, 45, 23, 5, 0, "", 23 },
1489 	{ "SPARKY-F",  1, 0, 45, 47, 0, 0, "", 0 },
1490 	{ "SPARKY-F",  2, 0, 45, 23, 5, 0, "045,046,046,045,000", 23 },
1491 	{ "SPARKY-F", 14, 0, 45, 29, 5, 0, "", 29 },
1492 
1493 	{ "FAYE", 0, 0, 19, 35,  2, 3, "", 35 },
1494 	{ "FAYE", 1, 0, 19, 41,  2, 3, "", 35 },
1495 	{ "FAYE", 2, 0, 19, 28, -1, 0, "", 35 },
1496 	{ "FAYE", 3, 0, 19, 20,  0, 0, "", 0 },
1497 	{ "FAYE", 4, 0, 19, 27, -1, 0, "", 35 },
1498 	{ "FAYE", 5, 0, 19, 29, -1, 0, "", 35 },
1499 	{ "FAYE", 6, 0, 59, 35,  2, 3, "", 35 },
1500 	{ "FAYE", 7, 0, 19, 30, -1, 0, "", 35 },
1501 	{ "FAYE", 8, 0, 19, 31, -1, 0, "", 35 },
1502 
1503 	{ "BOB", 0, 0, 27, 34,  2, 3, "", 34 },
1504 	{ "BOB", 1, 0, 27, 28, -1, 0, "", 34 },
1505 	{ "BOB", 2, 0, 30,  0,  0, 0, "", 0 },
1506 	{ "BOB", 3, 0, 31,  0,  0, 0, "", 0 },
1507 	{ "BOB", 4, 0, 27, 61, -1, 0, "", 34 },
1508 	{ "BOB", 5, 0, 27, 42,  1, 0, "", 42 },
1509 
1510 	{ "PYGMY", 0, 0, 20, 21, 2, 6, "", 0 },
1511 	{ "PYGMY", 1, 0, 20, 21, 2, 6, "020,068,068,068,068,068,068,068,068,020,000", 0 },
1512 	{ "PYGMY", 2, 0, 20, 21, 2, 6, "T028,029,030,031,031,031,031,030,029,028,035,000", 0 },
1513 	{ "PYGMY", 3, 0, 20, 21, 2, 6, "T035,036,037,038,037,038,037,038,036,035,000", 0 },
1514 	{ "PYGMY", 4, 0, 20, 21, 2, 6, "T032,033,032,033,032,033,035,000", 0 },
1515 	{ "PYGMY", 5, 0, 20, 21, 2, 6, "T023,022,021,022,023,024,025,026,027,026,025,024,023,000", 0 },
1516 	{ "PYGMY", 6, 0, 20, 21, 2, 6, "T034,034,034,035,000", 0 },
1517 
1518 	{ "WITCH", 0, 0, 39, 40,  2, 6, "", 40 },
1519 	{ "WITCH", 1, 0, 39, 40,  2, 6, "073,074,000", 40 },
1520 	{ "WITCH", 2, 0, 39, 40,  2, 6, "074,073,000", 40 },
1521 	{ "WITCH", 3, 0, 39, 40,  2, 6, "T047,048,049,050,051,000", 40 },
1522 	{ "WITCH", 4, 0, 39, 40,  2, 6, "T052,053,054,055,056,057,058,057,056,056,056,055,054,053,052,000", 40 },
1523 	{ "WITCH", 5, 0, 39, 40,  2, 6, "069,070,071,072,073,074,000", 40 },
1524 	{ "WITCH", 6, 0, 39, 40,  2, 6, "074,073,072,071,070,069,070,000", 40 },
1525 	{ "WITCH", 7, 0, 39, 51, -1, 0, "", 40 },
1526 	{ "WITCH", 8, 0, 39, 40,  2, 6, "T051,050,049,048,047,000", 40 },
1527 
1528 	{ "CHIEF", 0, 0, 1,  7,  1, 7, "", 3 },
1529 	{ "CHIEF", 1, 0, 1,  2,  2, 6, "062,063,064,065,066,000", 0 },
1530 	{ "CHIEF", 2, 0, 1,  2,  2, 6, "066,065,064,063,062,000", 0 },
1531 	{ "CHIEF", 3, 0, 1, 17, -1, 0, "", 3 },
1532 	{ "CHIEF", 4, 0, 1, 18, -1, 0, "", 3 },
1533 	{ "CHIEF", 5, 0, 1, 19, -1, 0, "", 3 },
1534 
1535 	{ "NAOMI", 0, 0, 1,  2,  2, 3, "", 2 },
1536 	{ "NAOMI", 1, 0, 1,  2,  2, 6, "048,049,050,051,052,053,054,055,000", 0 },
1537 	{ "NAOMI", 2, 0, 1,  2,  2, 6, "055,054,053,052,051,050,049,048,000", 0 },
1538 	{ "NAOMI", 3, 0, 1, 13, -1, 0, "", 2 },
1539 	{ "NAOMI", 4, 0, 1, 14, -1, 0, "", 2 },
1540 	{ "NAOMI", 5, 0, 1, 10, -1, 0, "", 2 },
1541 	{ "NAOMI", 6, 0, 1, 12, -1, 0, "", 2 },
1542 	{ "NAOMI", 7, 0, 1, 12, -1, 0, "T008,008,008,002,000", 2 },
1543 
1544 	{ "WEDGEWOOD", 0, 0, 8, 1, 2, 0, "", 8 },
1545 	{ "WEDGEWOOD", 1, 0, 1, 1, 3, 0, "", 1 },
1546 
1547 	{ "BUD", 0, 0, 1,  2,  3, 2, "", 2 },
1548 	{ "BUD", 1, 0, 1,  2,  4, 2, "T017,018,000", 2 },
1549 	{ "BUD", 2, 0, 1, 21, -1, 0, "", 2 },
1550 	{ "BUD", 3, 0, 1, 14, -1, 0, "", 2 },
1551 	{ "BUD", 4, 0, 1, 15, -1, 0, "", 2 },
1552 	{ "BUD", 5, 0, 1, 20, -1, 0, "", 2 },
1553 	{ "BUD", 6, 0, 1, 16, -1, 0, "", 2 },
1554 	{ "BUD", 7, 0, 1, 19, -1, 0, "", 2 },
1555 	{ "BUD", 8, 0, 1, 17, -1, 0, "", 2 },
1556 	{ "BUD", 9, 0, 1, 14, -1, 0, "T014,008,008,003,003,008,008,003,003,010,010,012,012,000", 2 },
1557 
1558 	{ "LOU", 0, 0, 1, 2, 2, 3, "", 2 },
1559 	{ "LOU", 1, 0, 1, 2, 4, 2, "013,014,015,016,017,018,000", 2 },
1560 	{ "LOU", 2, 0, 1, 2, 4, 2, "018,017,016,015,014,013,000", 2 },
1561 
1562 	{ "JIMMY", 0, 0, 16, 17,  2, 3, "", 17 },
1563 	{ "JIMMY", 1, 0, 16, 25, -1, 0, "", 17 },
1564 	{ "JIMMY", 2, 0, 16, 26, -1, 0, "", 17 },
1565 	{ "JIMMY", 3, 0, 16, 27, -1, 0, "", 17 },
1566 	{ "JIMMY", 4, 0, 16, 28, -1, 0, "", 17 },
1567 	{ "JIMMY", 5, 0, 16, 29, -1, 0, "", 17 },
1568 
1569 	{ "TAMMY", 0, 0, 1, 2, 2, 3, "", 2 },
1570 	{ "TAMMY", 1, 0, 1, 2, 2, 3, "T008,008,009,009,008,008,009,009,008,008,009,009,002,000", 2 },
1571 	{ "TAMMY", 2, 0, 1, 2, 2, 3, "T002,010,010,010,002,000", 2 },
1572 	{ "TAMMY", 3, 0, 1, 2, 2, 3, "T011,011,011,011,011,002,000", 2 },
1573 	{ "TAMMY", 4, 0, 1, 2, 2, 3, "T013,014,015,013,014,015,013,009,001,000", 2 },
1574 
1575 	{ "SKULL", 0, 0, 9, 9, 4, 0, "", 0 },
1576 	{ "SKULL", 1, 0, 1, 9, 4, 0, "001,002,003,004,005,006,007,008,009,000", 0 },
1577 	{ "SKULL", 2, 0, 1, 9, 4, 0, "009,008,007,006,005,004,003,002,001,000", 0 },
1578 
1579 	{ "APE", 0, 0, 1, 6, 7, 0, "", 6 },
1580 	{ "APE", 1, 0, 1, 6, 7, 0, "002,001,000", 6 },
1581 	{ "APE", 2, 0, 1, 6, 7, 0, "002,003,001,000", 6 },
1582 	{ "APE", 3, 0, 1, 6, 7, 0, "004,005,004,005,004,001,000", 6 },
1583 	{ "APE", 4, 0, 1, 6, 7, 0, "001,003,005,004,005,004,001,000", 6 },
1584 
1585 	{ "APE1", 0, 0, 15, 16, 7, 0, "", 16 },
1586 	{ "APE2", 0, 0, 14,  6, 7, 0, "", 6 },
1587 
1588 	{ "SHOWERAM", 0, 0, 1, 2, 3, 0, "", 2 },
1589 	{ "SHOWERAM", 1, 0, 1, 2, 3, 0, "026,027,028,029,001,000", 2 },
1590 	{ "SHOWERAM", 2, 0, 1, 2, 3, 0, "001,029,028,027,026,000", 2 },
1591 
1592 	{ "PRINCESS1", 0, 0, 19, 23,  2, 3, "", 23 },
1593 	{ "PRINCESS1", 1, 0, 19, 41, -1, 0, "", 23 },
1594 	{ "PRINCESS1", 2, 0, 19, 42, -1, 0, "", 23 },
1595 	{ "PRINCESS1", 3, 0, 19, 45, -1, 0, "", 23 },
1596 	{ "PRINCESS1", 4, 0, 19, 40, -1, 0, "", 23 },
1597 	{ "PRINCESS1", 5, 0, 19, 45,  2, 3, "T40,043,044,045,000", 45 },
1598 	{ "PRINCESS1", 6, 0, 19, 45, -1, 0, "T041,038,000", 38 },
1599 	{ "PRINCESS1", 7, 0, 22,  0,  0, 0, "", 0 },
1600 	{ "PRINCESS1", 8, 0, 19, 45,  2, 3, "T045,044,043,040,039,000", 39 },
1601 
1602 	{ "PRINCESS2", 0, 0, 46, 23, 2, 3, "", 23 },
1603 	{ "PRINCESS2", 1, 0, 46, 29, 2, 3, "", 29 },
1604 	{ "PRINCESS2", 2, 0, 46, 29, 2, 3, "T029,036,035,000", 35 },
1605 
1606 	{ "GUARDS", 0, 0, 7, 7, 0, 0, "", 7 },
1607 
1608 	{ "AMGUARD", 0, 0, 19, 22, 2, 3, "", 22 },
1609 
1610 	{ "MAN1", 0, 0, 2, 3, 2, 3, "", 3 },
1611 	{ "MAN2", 0, 0, 9, 10, 1, 2, "", 10 },
1612 
1613 	{ "DOG", 0, 0, 6, 6, 1, 0, "", 0 },
1614 	{ "DOG", 1, 0, 6, 6, 1, 0, "010,009,008,000", 0 },
1615 	{ "DOG", 2, 0, 6, 6, 1, 0, "008,009,010,000", 0 },
1616 
1617 	{ "CHEF", 0, 0, 5, 6, 2, 3, "", 6 },
1618 
1619 	{ "HENRY",  0, 0,  7,  9,  2, 3, "", 9 },
1620 	{ "HENRY",  1, 0,  7, 21, -1, 0, "", 9 },
1621 	{ "HENRY",  2, 0,  7, 19, -1, 0, "", 9 },
1622 	{ "HENRY",  3, 0,  7, 20, -1, 0, "", 9 },
1623 	{ "HENRY",  4, 0,  8,  9,  2, 3, "", 9 },
1624 	{ "HENRY",  5, 0, 23,  9, -1, 0, "", 9 },
1625 	{ "HENRY",  6, 0,  7,  9,  2, 3, "T019,015,017,017,017,017,017,017,017,015,009,000", 9 },
1626 	{ "HENRY",  7, 0,  7,  9,  2, 3, "T018,010,000", 10 },
1627 	{ "HENRY",  8, 0,  7,  9,  2, 3, "T018,016,000", 16 },
1628 	{ "HENRY",  9, 0,  7,  9,  2, 3, "T018,011,000", 11 },
1629 	{ "HENRY", 10, 0, 29, 33,  1, 1, "", 33 },
1630 	{ "HENRY", 11, 0,  7, 30,  2, 0, "", 9 },
1631 	{ "HENRY", 12, 0,  7,  9,  2, 3, "025,026,000", 26 },
1632 	{ "HENRY", 13, 0,  7,  9,  2, 3, "027,028,027,028,000", 28 },
1633 	{ "HENRY", 14, 0,  7,  9,  2, 3, "026,025,007,000", 9 },
1634 
1635 	{ "JOHAN", 0, 0, 1, 15,  2, 3, "", 15 },
1636 	{ "JOHAN", 1, 0, 1,  0,  0, 0, "T006,007,008,000", 15 },
1637 	{ "JOHAN", 2, 0, 1, 15,  2, 3, "T002,003,004,005,004,005,004,005,004,005,004,005,004,003,002,000", 15 },
1638 	{ "JOHAN", 3, 0, 1,  8, -1, 0, "", 15 },
1639 	{ "JOHAN", 4, 0, 1,  0,  0, 0, "T008,007,006,001,000", 15 },
1640 
1641 	{ "KLUNK", 0, 0, 1, 2, 2, 3, "", 2 },
1642 	{ "KLUNK", 1, 0, 1, 2, 2, 3, "019,020,021,022,001,000", 2 },
1643 	{ "KLUNK", 2, 0, 1, 2, 2, 3, "001,022,021,020,019,016,517,000", 2 },
1644 	{ "KLUNK", 3, 0, 1, 2, 2, 3, "T010,011,010,011,010,011,009,000", 2 },
1645 
1646 	{ "FRANK", 0, 0, 13, 14, 2, 3, "", 14 },
1647 	{ "FRANK", 1, 0, 13, 20, 0, 1, "", 14 },
1648 	{ "FRANK", 2, 0, 13, 14, 2, 3, "025,026,027,027,027,026,026,026,027,027,026,026,027,025,013,000", 14 },
1649 	{ "FRANK", 3, 0, 28, 14, 2, 3, "", 14 },
1650 
1651 	{ "DEATH", 0, 0, 1, 2, 2, 3, "", 2 },
1652 	{ "DEATH", 1, 0, 1, 2, 2, 3, "013,014,015,016,017,001,000", 0 },
1653 	{ "DEATH", 2, 0, 1, 2, 2, 3, "001,017,016,015,014,013,000", 0 },
1654 	{ "DEATH", 3, 0, 1, 2, 2, 3, "T018,019,020,021,021,022,022,020,021,022,020,021,022,023,024,524,000", 2 },
1655 	{ "DEATH", 4, 0, 1, 2, 2, 3, "T025,026,027,028,028,028,028,028,028,028,028,028,029,035,000", 2 },
1656 	{ "DEATH", 5, 0, 1, 2, 2, 3, "T030,031,032,033,033,033,033,033,033,033,033,033,034,035,000", 2 },
1657 	{ "DEATH", 6, 0, 1, 2, 2, 3, "T023,022,020,019,018,001,000", 2 },
1658 
1659 	{ "JASPAR", 0, 0, 1, 1, 22, 0, "026,027,028,029,028,029,028,029,030,023,000", 0 },
1660 	{ "JASPAR", 1, 0, 1, 1, 22, 0, "023,026,000", 0 },
1661 
1662 	{ "ORACLE", 0, 0, 1, 5, 3, 0, "", 0 },
1663 
1664 	{ "ZOMBIE", 0, 0, 1,  5,  2, 3, "", 5 },
1665 	{ "ZOMBIE", 1, 0, 1, 12, -1, 0, "", 5 },
1666 	{ "ZOMBIE", 2, 0, 1, 13, -1, 0, "", 5 },
1667 	{ "ZOMBIE", 3, 0, 1,  1,  5, 5, "", 5 },
1668 
1669 	{ "ZOMBIE2", 0, 0, 14, 14, 0, 0, "", 0 },
1670 	{ "ZOMBIE3", 0, 0, 18, 18, 0, 0, "", 0 },
1671 
1672 	{ "ANDERSON", 0, 0, 7,  8,  2, 3, "", 8 },
1673 	{ "ANDERSON", 1, 0, 7,  8,  1, 0, "", 8 },
1674 	{ "ANDERSON", 2, 0, 7, 16, -1, 0, "", 8 },
1675 	{ "ANDERSON", 3, 0, 7, 18, -1, 0, "", 8 },
1676 	{ "ANDERSON", 4, 0, 7, 19, -1, 0, "", 8 },
1677 	{ "ANDERSON", 5, 0, 7, 20, -1, 0, "", 8 },
1678 	{ "ANDERSON", 6, 0, 7, 21,  1, 0, "", 8 },
1679 
1680 	{ "COMPY", 0, 0, 12, 12, -1, 0, "", 0 },
1681 	{ "COMPY", 1, 0, 10, 10, 10, 0, "010,011,012,012,013,014,014,000", 0 },
1682 	{ "COMPY", 2, 0, 10, 10, 10, 0, "014,013,012,000", 0 },
1683 
1684 	{ "DEINO", 0, 0, 13, 13, -1, 0, "", 0 },
1685 	{ "DEINO", 1, 0,  9,  9,  9, 0, "009,010,000", 0 },
1686 
1687 	{ "TMPD", 0, 0, 19, 22, 2, 3, "", 22 },
1688 
1689 	{ "IAN", 0, 0, 7,  9,  2, 3, "", 9 },
1690 	{ "IAN", 1, 0, 8, 25,  3, 0, "", 25 },
1691 	{ "IAN", 2, 0, 7, 21, -1, 0, "", 9 },
1692 	{ "IAN", 3, 0, 7, 22,  1, 0, "", 9 },
1693 	{ "IAN", 4, 0, 7, 22, -1, 0, "", 9 },
1694 	{ "IAN", 5, 0, 7, 24, -1, 0, "", 9 },
1695 	{ "IAN", 6, 0, 7,  9,  2, 3, "034,034,034,035,035,036,036,035,035,036,035,036,035,000", 9 },
1696 	{ "IAN", 7, 0, 7, 31, -1, 0, "", 9 },
1697 
1698 	{ "FAYE-H", 0, 0, 1, 1, 4, 1, "", 1 },
1699 	{ "FAYE-H", 1, 0, 1, 1, 4, 1, "007,000", 7 },
1700 	{ "FAYE-H", 2, 0, 1, 1, 4, 1, "009,010,011,009,001,000", 1 },
1701 	{ "FAYE-H", 3, 0, 1, 1, 4, 1, "E012,013,000", 1 },
1702 	{ "FAYE-H", 4, 0, 1, 1, 4, 1, "E015,000", 1 },
1703 	{ "FAYE-H", 5, 0, 1, 1, 4, 1, "E014,000", 1 },
1704 
1705 	{ "AZURA-H", 0, 0, 1, 1, 4, 1, "", 1 },
1706 	{ "AZURA-H", 1, 0, 1, 1, 4, 1, "007,000", 7 },
1707 	{ "AZURA-H", 2, 0, 1, 1, 4, 1, "009,010,011,009,001,000", 1 },
1708 	{ "AZURA-H", 3, 0, 1, 1, 4, 1, "E012,013, 000", 1 },
1709 	{ "AZURA-H", 4, 0, 1, 1, 4, 1, "E015,000", 1 },
1710 	{ "AZURA-H", 5, 0, 1, 1, 4, 1, "E014,000", 1 },
1711 
1712 	{ "FRANK-H", 0, 0, 1, 1, 4, 1, "", 1 },
1713 	{ "FRANK-H", 1, 0, 1, 1, 4, 1, "E009,000", 1 },
1714 	{ "FRANK-H", 2, 0, 1, 1, 4, 1, "E007,000", 1 },
1715 	{ "FRANK-H", 3, 0, 1, 1, 4, 1, "010,011,012,013,014,015,010,000", 1 },
1716 
1717 	{ "JOE-E", 0, 0, 1, 2, 4, 1, "", 2 },
1718 	{ "JOE-E", 6, 0, 1, 2, 4, 1, "008,009,008,002,000", 2 },
1719 
1720 	{ "AZURA-E", 0, 0, 1, 1, 5, 1, "", 1 },
1721 	{ "AZURA-E", 1, 0, 1, 1, 5, 1, "009,010,009,008,000", 1 },
1722 
1723 	{ "FAYE-E", 0, 0, 1, 4, 4, 1, "", 1 },
1724 	{ "FAYE-E", 1, 0, 1, 4, 4, 1, "002,003,002,001,000", 1 },
1725 
1726 	{ "ANDSON-E", 0, 0, 1, 3, 4, 1, "", 1 },
1727 	{ "ANDSON-E", 1, 0, 1, 3, 4, 1, "002,001,000", 1 },
1728 
1729 	{ "JOE-H", 0, 0, 1,  1, 4, 4, "", 1 },
1730 	{ "JOE-H", 1, 0, 1,  1, 2, 3, "012,013,014,000", 14 },
1731 	{ "JOE-H", 2, 0, 1,  1, 2, 3, "010,011,000", 11 },
1732 	{ "JOE-H", 3, 0, 1,  1, 2, 3, "014,013,012,001,000", 1 },
1733 	{ "JOE-H", 4, 0, 1, 13, 1, 0, "", 13 },
1734 
1735 	{ "RITA-H", 0, 0, 7, 1, 2, 3, "", 1 },
1736 	{ "RITA-H", 1, 0, 7, 0, 0, 0, "009,010,011,012,013,000", 13 },
1737 	{ "RITA-H", 2, 0, 7, 0, 0, 0, "014,015,016,000", 16 },
1738 	{ "RITA-H", 3, 0, 7, 0, 0, 0, "013,012,011,010,000", 10 },
1739 	{ "RITA-H", 4, 0, 7, 0, 0, 0, "009,007,008,007,009,000", 9 },
1740 	{ "RITA-H", 5, 0, 7, 0, 0, 0, "016,015,014,000", 14 },
1741 
1742 	{ "RITA", 0, 0, 1, 4, 2, 3, "", 4 },
1743 	{ "RITA", 1, 0, 2, 4, 2, 3, "", 4 },
1744 
1745 	{ "SPARKY-H", 0, 0, 1, 1, 2, 3, "", 1 },
1746 
1747 	{ "HUGH", 0, 0, 1, 1, 2, 3, "", 1 },
1748 	{ "HUGH", 1, 0, 7, 7, 2, 3, "", 7 },
1749 
1750 	{ "X2_JOE", 0, 0, 1, 1, 2, 3, "", 1 },
1751 	{ "X2_JOE", 1, 0, 1, 1, 2, 3, "001,007,008,008,007,001,000", 1 },
1752 
1753 	{ "X2_RITA", 0, 0, 1, 1, 2, 3, "", 1 },
1754 	{ "X2_RITA", 1, 0, 1, 1, 2, 3, "001,007,008,008,007,001,000", 1 },
1755 
1756 	{ "X3_RITA", 0, 0, 1, 1, 4, 1, "", 1 },
1757 	{ "X3_RITA", 1, 0, 1, 1, 4, 1, "007,000", 7 },
1758 	{ "X3_RITA", 2, 0, 1, 1, 4, 1, "009,010,011,009,001,000", 1 },
1759 	{ "X3_RITA", 3, 0, 1, 1, 4, 1, "E012,013,000", 1 },
1760 	{ "X3_RITA", 4, 0, 1, 1, 4, 1, "E015,000", 1 },
1761 	{ "X3_RITA", 5, 0, 1, 1, 4, 1, "E014,000", 1 },
1762 
1763 	{ "X4_JOE", 0, 0, 1,  1, 3, 4, "", 1 },
1764 	{ "X4_JOE", 1, 0, 1, 13, 2, 3, "", 13 },
1765 	{ "X4_JOE", 2, 0, 1,  1, 3, 4, "009, 010, 011, 012, 013, 000", 13 },
1766 	{ "X4_JOE", 3, 0, 1,  1, 3, 4, "012, 011, 010, 009, 000", 9 },
1767 	{ "X4_JOE", 4, 0, 1,  1, 3, 4, "001, 019, 000", 19 },
1768 
1769 	{ "X4_RITA", 0, 0, 1, 1, 0, 1, "", 1 },
1770 	{ "X4_RITA", 1, 0, 1, 7, 0, 1, "", 7 },
1771 	{ "X4_RITA", 2, 0, 1, 1, 3, 4, "004,005,006,006,006,006,007,000", 7 },
1772 	{ "X4_RITA", 3, 0, 1, 1, 3, 4, "005,004,001,000", 1 },
1773 	{ "X4_RITA", 4, 0, 1, 1, 3, 4, "001,003,000", 3 },
1774 
1775 	{ "X5_SPARKY", 0, 0, 1, 1, 2, 3, "", 1 },
1776 	{ "X5_SPARKY", 1, 0, 1, 1, 2, 3, "001,010,011,011,001,000", 1 },
1777 	{ "X5_SPARKY", 2, 0, 1, 1, 2, 3, "001,007,008,009,000", 9 },
1778 
1779 	{ "X6_HUGH", 0, 0, 1, 1, 2, 3, "", 1 },
1780 	{ "X6_HUGH", 1, 0, 1, 1, 2, 3, "007,007,007,007,,001,000", 1 },
1781 	{ "X6_HUGH", 2, 0, 1, 1, 2, 3, "008,008,008,008,008,009,009,008,008,008,009,008,000", 8 },
1782 
1783 	{ "X10_JOE", 0, 0, 1, 2, 2, 3, "", 2 },
1784 	{ "X10_JOE", 1, 0, 1, 8, 2, 3, "", 8 },
1785 	{ "X10_JOE", 2, 0, 1, 2, 2, 3, "014,014,014,015,015,014,014,015,015,000", 2 },
1786 
1787 	{ "X10_RITA", 0, 0, 1, 2, 2, 3, "", 2 },
1788 
1789 	{ "X11_JOE", 0, 0, 1, 2, 0, 1, "", 2 },
1790 
1791 	{ "X11_RITA", 0, 0, 1, 2, 0, 1, "", 2 },
1792 	{ "X11_RITA", 1, 0, 1, 2, 1, 0, "003,004,000", 4 },
1793 
1794 	{ "JOHN", 0, 0, 1,  2,  2, 3, "", 1 },
1795 	{ "JOHN", 1, 0, 1, 15, -1, 0, "", 1 },
1796 	{ "JOHN", 2, 0, 1, 16, -1, 0, "", 1 },
1797 	{ "JOHN", 3, 0, 1, 17, -1, 0, "", 1 },
1798 
1799 	{ "STEVE", 0, 0, 8,  2,  2, 3, "", 2 },
1800 	{ "STEVE", 1, 0, 8, 16, -1, 0, "", 2 },
1801 	{ "STEVE", 2, 0, 9, 18, -1, 0, "T016,017,017,016,008,000", 2 },
1802 	{ "STEVE", 3, 0, 8, 18, -1, 0, "", 2 },
1803 
1804 	{ "TONY", 0, 0, 1,  2,  2, 3, "", 1 },
1805 	{ "TONY", 1, 0, 1, 12, -1, 0, "", 1 },
1806 
1807 	{ "*", 0, 0, 0, 0, 0, 0, "", 0 }
1808 };
1809 
1810 } // End of namespace Queen
1811