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 #include "common/textconsole.h"
24 
25 #include "queen/cutaway.h"
26 
27 #include "queen/bankman.h"
28 #include "queen/display.h"
29 #include "queen/graphics.h"
30 #include "queen/grid.h"
31 #include "queen/input.h"
32 #include "queen/logic.h"
33 #include "queen/queen.h"
34 #include "queen/resource.h"
35 #include "queen/sound.h"
36 #include "queen/talk.h"
37 #include "queen/walk.h"
38 
39 namespace Queen {
40 
run(const char * filename,char * nextFilename,QueenEngine * vm)41 void Cutaway::run(
42 		const char *filename,
43 		char *nextFilename,
44 		QueenEngine *vm) {
45 	Cutaway *cutaway = new Cutaway(filename, vm);
46 	cutaway->run(nextFilename);
47 	delete cutaway;
48 }
49 
Cutaway(const char * filename,QueenEngine * vm)50 Cutaway::Cutaway(
51 		const char *filename,
52 		QueenEngine *vm)
53 	: _vm(vm), _personDataCount(0), _personFaceCount(0), _lastSong(0), _songBeforeComic(0) {
54 	memset(&_bankNames, 0, sizeof(_bankNames));
55 	_vm->input()->cutawayQuitReset();
56 	load(filename);
57 }
58 
~Cutaway()59 Cutaway::~Cutaway() {
60 	delete[] _fileData;
61 }
62 
load(const char * filename)63 void Cutaway::load(const char *filename) {
64 	byte *ptr;
65 
66 	debug(6, "----- Cutaway::load(\"%s\") -----", filename);
67 
68 	ptr = _fileData = _vm->resource()->loadFile(filename, 20);
69 
70 	if (0 == scumm_stricmp(filename, "COMIC.CUT"))
71 		_songBeforeComic = _vm->sound()->lastOverride();
72 
73 	strcpy(_basename, filename);
74 	_basename[strlen(_basename)-4] = '\0';
75 
76 	_comPanel = READ_BE_UINT16(ptr);
77 	ptr += 2;
78 	debug(6, "_comPanel = %i", _comPanel);
79 	_cutawayObjectCount = (int16)READ_BE_INT16(ptr);
80 	ptr += 2;
81 
82 	debug(6, "_cutawayObjectCount = %i", _cutawayObjectCount);
83 
84 	if (_cutawayObjectCount < 0) {
85 		_cutawayObjectCount = -_cutawayObjectCount;
86 		_vm->input()->canQuit(false);
87 	} else
88 		_vm->input()->canQuit(true);
89 
90 	int16 flags1 = (int16)READ_BE_INT16(ptr);
91 	ptr += 2;
92 	debug(6, "flags1 = %i", flags1);
93 
94 	if (flags1 < 0) {
95 		_vm->logic()->entryObj(0);
96 		_finalRoom = -flags1;
97 	} else
98 		_finalRoom = PREVIOUS_ROOM;
99 
100 	_anotherCutaway = (flags1 == 1);
101 
102 	debug(6, "[Cutaway::load] _finalRoom      = %i", _finalRoom);
103 	debug(6, "[Cutaway::load] _anotherCutaway = %i", _anotherCutaway);
104 
105 	/*
106 		 Pointers to other places in the cutaway data
107 	 */
108 
109 	_gameStatePtr = _fileData + READ_BE_UINT16(ptr);
110 	ptr += 2;
111 
112 	_nextSentenceOff = READ_BE_UINT16(ptr);
113 	ptr += 2;
114 
115 	uint16 bankNamesOff = READ_BE_UINT16(ptr);
116 	ptr += 2;
117 
118 	_objectData = ptr;
119 
120 	loadStrings(bankNamesOff);
121 
122 	if (_bankNames[0][0]) {
123 		debug(6, "Loading bank '%s'", _bankNames[0]);
124 		_vm->bankMan()->load(_bankNames[0], CUTAWAY_BANK);
125 	}
126 
127 	char entryString[MAX_STRING_SIZE];
128 	Talk::getString(_fileData, _nextSentenceOff, entryString, MAX_STRING_LENGTH);
129 	debug(6, "Entry string = '%s'", entryString);
130 
131 	_vm->logic()->joeCutFacing(_vm->logic()->joeFacing());
132 	_vm->logic()->joeFace();
133 
134 	if (entryString[0] == '*' &&
135 			entryString[1] == 'F' &&
136 			entryString[3] == '\0') {
137 		switch (entryString[2]) {
138 		case 'L':
139 			_vm->logic()->joeCutFacing(DIR_LEFT);
140 			break;
141 		case 'R':
142 			_vm->logic()->joeCutFacing(DIR_RIGHT);
143 			break;
144 		case 'F':
145 			_vm->logic()->joeCutFacing(DIR_FRONT);
146 			break;
147 		case 'B':
148 			_vm->logic()->joeCutFacing(DIR_BACK);
149 			break;
150 		}
151 	}
152 
153 }
154 
loadStrings(uint16 offset)155 void Cutaway::loadStrings(uint16 offset) {
156 	int bankNameCount = READ_BE_UINT16(_fileData + offset);
157 	offset += 2;
158 
159 	debug(6, "Bank name count = %i", bankNameCount);
160 
161 	/*
162 		 The _bankNames zero-based array is the one-based BANK_NAMEstr array in
163 		 the original source code.
164 	 */
165 
166 	for (int i = 0, j = 0; i < bankNameCount; i++) {
167 		Talk::getString(_fileData, offset, _bankNames[j], MAX_FILENAME_LENGTH);
168 		if (_bankNames[j][0]) {
169 			debug(6, "Bank name %i = '%s'", j, _bankNames[j]);
170 			j++;
171 		}
172 	}
173 
174 	debug(6, "Getting talk file");
175 	Talk::getString(_fileData, offset, _talkFile, MAX_FILENAME_LENGTH);
176 	debug(6, "Talk file = '%s'", _talkFile);
177 
178 	_talkTo = (int16)READ_BE_INT16(_fileData + offset);
179 	debug(6, "_talkTo = %i", _talkTo);
180 }
181 
getCutawayObject(const byte * ptr,CutawayObject & object)182 const byte *Cutaway::getCutawayObject(const byte *ptr, CutawayObject &object) {
183 	const byte *oldPtr = ptr;
184 
185 	object.objectNumber = (int16)READ_BE_INT16(ptr); ptr += 2;
186 	object.moveToX      = (int16)READ_BE_INT16(ptr); ptr += 2;
187 	object.moveToY      = (int16)READ_BE_INT16(ptr); ptr += 2;
188 	object.bank         = (int16)READ_BE_INT16(ptr); ptr += 2;
189 	object.animList     = (int16)READ_BE_INT16(ptr); ptr += 2;
190 	object.execute      = (int16)READ_BE_INT16(ptr); ptr += 2;
191 	object.limitBobX1   = (int16)READ_BE_INT16(ptr); ptr += 2;
192 	object.limitBobY1   = (int16)READ_BE_INT16(ptr); ptr += 2;
193 	object.limitBobX2   = (int16)READ_BE_INT16(ptr); ptr += 2;
194 	object.limitBobY2   = (int16)READ_BE_INT16(ptr); ptr += 2;
195 	object.specialMove  = (int16)READ_BE_INT16(ptr); ptr += 2;
196 	object.animType     = (int16)READ_BE_INT16(ptr); ptr += 2;
197 	object.fromObject   = (int16)READ_BE_INT16(ptr); ptr += 2;
198 	object.bobStartX    = (int16)READ_BE_INT16(ptr); ptr += 2;
199 	object.bobStartY    = (int16)READ_BE_INT16(ptr); ptr += 2;
200 	object.room         = (int16)READ_BE_INT16(ptr); ptr += 2;
201 	object.scale        = (int16)READ_BE_INT16(ptr); ptr += 2;
202 
203 	if ((ptr - oldPtr) != 17*sizeof(int16))
204 		error("Wrong number of values read");
205 
206 	// Make ugly reuse of data less ugly
207 	if (object.limitBobX1 < 0) {
208 		object.song = -object.limitBobX1;
209 		object.limitBobX1 = 0;
210 	} else
211 		object.song = 0;
212 
213 	return ptr;
214 }
215 
dumpCutawayObject(int index,CutawayObject & object)216 void Cutaway::dumpCutawayObject(int index, CutawayObject &object) {
217 	debug(6, "----- CutawayObject[%i] -----", index);
218 
219 	const char *objectNumberStr;
220 
221 	switch (object.objectNumber) {
222 	case -1:
223 		objectNumberStr = "MESSAGE";
224 		break;
225 	case 0:
226 		objectNumberStr = "Joe";
227 		break;
228 	default:
229 		if (object.objectNumber > 0)
230 			objectNumberStr = _vm->logic()->objectName(ABS(_vm->logic()->objectData(object.objectNumber)->name));
231 		else
232 			objectNumberStr = "Unknown!";
233 		break;
234 	}
235 
236 	debug(6, "objectNumber = %i (%s)", object.objectNumber, objectNumberStr);
237 
238 	if (object.moveToX) debug(6, "moveToX = %i", object.moveToX);
239 	if (object.moveToY) debug(6, "moveToY = %i", object.moveToY);
240 	if (object.bank) debug(6, "bank = %i", object.bank);
241 	if (object.animList) debug(6, "animList = %i", object.animList);
242 	if (object.execute) debug(6, "execute = %i", object.execute);
243 	if (object.limitBobX1) debug(6, "limitBobX1 = %i", object.limitBobX1);
244 	if (object.limitBobY1) debug(6, "limitBobY1 = %i", object.limitBobY1);
245 	if (object.limitBobX2) debug(6, "limitBobX2 = %i", object.limitBobX2);
246 	if (object.limitBobY2) debug(6, "limitBobY2 = %i", object.limitBobY2);
247 	if (object.specialMove) debug(6, "specialMove = %i", object.specialMove);
248 	if (object.animType) debug(6, "animType = %i", object.animType);
249 	if (object.fromObject) debug(6, "fromObject = %i", object.fromObject);
250 	if (object.bobStartX) debug(6, "bobStartX = %i", object.bobStartX);
251 	if (object.bobStartY) debug(6, "bobStartY = %i", object.bobStartY);
252 	if (object.room) debug(6, "room = %i", object.room);
253 	if (object.scale) debug(6, "scale = %i", object.scale);
254 
255 }
256 
257 
turnOnPeople(const byte * ptr,CutawayObject & object)258 const byte *Cutaway::turnOnPeople(const byte *ptr, CutawayObject &object) {
259 	// Lines 1248-1259 in cutaway.c
260 	object.personCount = (int16)READ_BE_INT16(ptr);
261 	ptr += 2;
262 
263 	if (object.personCount > MAX_PERSON_COUNT)
264 		error("[Cutaway::turnOnPeople] object.personCount > MAX_PERSON_COUNT");
265 
266 	for (int i = 0; i < object.personCount; i++) {
267 		object.person[i] = (int16)READ_BE_INT16(ptr);
268 		ptr += 2;
269 		debug(7, "[%i] Turn on person %i", i, object.person[i]);
270 	}
271 
272 	return ptr;
273 }
274 
limitBob(CutawayObject & object)275 void Cutaway::limitBob(CutawayObject &object) {
276 	if (object.limitBobX1) {
277 
278 		if (object.objectNumber < 0) {
279 			warning("QueenCutaway::limitBob called with objectNumber = %i", object.objectNumber);
280 			return;
281 		}
282 
283 		BobSlot *bob =
284 			_vm->graphics()->bob(_vm->logic()->findBob(object.objectNumber));
285 
286 		if (!bob) {
287 			warning("Failed to find bob");
288 			return;
289 		}
290 
291 		bob->box.x1 = object.limitBobX1;
292 		bob->box.y1 = object.limitBobY1;
293 		bob->box.x2 = object.limitBobX2;
294 		bob->box.y2 = object.limitBobY2;
295 	}
296 }
297 
restorePersonData()298 void Cutaway::restorePersonData() {
299 	for (int i = 0; i < _personDataCount; i++) {
300 		int index           = _personData[i].index;
301 		ObjectData *objectData  = _vm->logic()->objectData(index);
302 		objectData->name        = _personData[i].name;
303 		objectData->image       = _personData[i].image;
304 	}
305 }
306 
changeRooms(CutawayObject & object)307 void Cutaway::changeRooms(CutawayObject &object) {
308 	// Lines 1291-1385 in cutaway.c
309 
310 	debug(6, "Changing from room %i to room %i",
311 			_temporaryRoom,
312 			object.room);
313 
314 	restorePersonData();
315 	_personDataCount = 0;
316 
317 	if (_finalRoom != object.room) {
318 		int firstObjectInRoom = _vm->logic()->roomData(object.room) + 1;
319 		int lastObjectInRoom  = _vm->logic()->roomData(object.room) + _vm->grid()->objMax(object.room);
320 
321 		for (int i = firstObjectInRoom; i <= lastObjectInRoom; i++) {
322 			ObjectData *objectData  = _vm->logic()->objectData(i);
323 
324 			if (objectData->image == -3 || objectData->image == -4) {
325 
326 				assert(_personDataCount < MAX_PERSON_COUNT);
327 				//  The object is a person! So record the details...
328 				_personData[_personDataCount].index = i;
329 				_personData[_personDataCount].name  = objectData->name;
330 				_personData[_personDataCount].image = objectData->image;
331 				_personDataCount++;
332 
333 				// Now, check to see if we need to keep the person on
334 				bool on = false;
335 				for (int j = 0; j < object.personCount; j++) {
336 					if (object.person[j] == i) {
337 						on = true;
338 						break;
339 					}
340 				}
341 
342 				if (on) {
343 					// It is needed, so ensure it's ON
344 					objectData->name = ABS(objectData->name);
345 				} else {
346 					// Not needed, so switch off!
347 					objectData->name = -ABS(objectData->name);
348 				}
349 
350 			}
351 		} // for ()
352 	}
353 
354 	// set coordinates for Joe if he is on screen
355 
356 	_vm->logic()->joePos(0, 0);
357 
358 	for (int i = 0; i < object.personCount; i++) {
359 		if (PERSON_JOE == object.person[i]) {
360 			_vm->logic()->joePos(object.bobStartX, object.bobStartY);
361 		}
362 	}
363 
364 	_vm->logic()->oldRoom(_initialRoom);
365 
366 	// FIXME: Cutaway c41f is played at the end of the command 0x178. This command
367 	// setups some persons and associates bob slots to them. They should be hidden as
368 	// their y coordinate is > 150, but they aren't ! As a workaround, we display the room
369 	// with the panel area enabled. We do the same problem for cutaway c62c.
370 	int16 comPanel = _comPanel;
371 	if ((strcmp(_basename, "c41f") == 0 && _temporaryRoom == 106 && object.room == 41) ||
372 		(strcmp(_basename, "c62c") == 0 && _temporaryRoom == 105 && object.room == 41)) {
373 		comPanel = 1;
374 	}
375 
376 	// Hide panel before displaying the 'head room' (ie. before palette fading). This doesn't
377 	// match the original engine, but looks better to me.
378 	if (object.room == FAYE_HEAD || object.room == AZURA_HEAD || object.room == FRANK_HEAD) {
379 		comPanel = 2;
380 	}
381 
382 	RoomDisplayMode mode;
383 
384 	if (!_vm->logic()->joeX() && !_vm->logic()->joeY()) {
385 		mode = RDM_FADE_NOJOE;
386 	} else {
387 		// We need to display Joe on screen
388 		if (_roomFade)
389 			mode = RDM_NOFADE_JOE;
390 		else
391 			mode = RDM_FADE_JOE_XY;
392 	}
393 
394 	_vm->logic()->displayRoom(_vm->logic()->currentRoom(), mode, object.scale, comPanel, true);
395 
396 	_currentImage = _vm->graphics()->numFrames();
397 
398 	_temporaryRoom = _vm->logic()->currentRoom();
399 
400 	restorePersonData();
401 }
402 
getObjectType(CutawayObject & object)403 Cutaway::ObjectType Cutaway::getObjectType(CutawayObject &object) {
404 	// Lines 1387-1449 in cutaway.c
405 
406 	ObjectType objectType = OBJECT_TYPE_ANIMATION;
407 
408 	if (object.objectNumber > 0) {
409 		if (!object.animList) {
410 			// No anim frames, so treat as a PERSON, ie. allow to speak/walk
411 			ObjectData *objectData = _vm->logic()->objectData(object.objectNumber);
412 			if (objectData->image == -3 || objectData->image == -4)
413 				objectType = OBJECT_TYPE_PERSON;
414 		}
415 	} else if (object.objectNumber == OBJECT_JOE) {
416 		// It's Joe. See if he's to be treated as a person.
417 		if (!object.animList) {
418 			// There's no animation list, so Joe must be talking.
419 			objectType = OBJECT_TYPE_PERSON;
420 		}
421 	}
422 
423 	if (object.fromObject > 0) {
424 		/* Copy FROM_OBJECT into OBJECT */
425 
426 		if (object.objectNumber != object.fromObject) {
427 			_vm->logic()->objectCopy(object.fromObject, object.objectNumber);
428 		} else {
429 			// Same object, so just turn it on!
430 			ObjectData *objectData = _vm->logic()->objectData(object.objectNumber);
431 			objectData->name = ABS(objectData->name);
432 		}
433 
434 		_vm->graphics()->refreshObject(object.objectNumber);
435 
436 		// Skip doing any anim stuff
437 		objectType = OBJECT_TYPE_NO_ANIMATION;
438 	}
439 
440 	switch (object.objectNumber) {
441 	case -2:
442 		// Text to be spoken
443 		objectType = OBJECT_TYPE_TEXT_SPEAK;
444 		break;
445 	case -3:
446 		// Text to be displayed AND spoken
447 		objectType = OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK;
448 		break;
449 	case -4:
450 		// Text to be displayed only (not spoken)
451 		objectType = OBJECT_TYPE_TEXT_DISPLAY;
452 		break;
453 	}
454 
455 	if (OBJECT_TYPE_ANIMATION == objectType && !object.execute) {
456 		// Execute is not on, and it's an object, so ignore any Anims
457 		objectType = OBJECT_TYPE_NO_ANIMATION;
458 	}
459 
460 	return objectType;
461 }
462 
getCutawayAnim(const byte * ptr,int header,CutawayAnim & anim)463 const byte *Cutaway::getCutawayAnim(const byte *ptr, int header, CutawayAnim &anim) {
464 	// lines 1531-1607 in cutaway.c
465 	debug(6, "[Cutaway::getCutawayAnim] header=%i", header);
466 
467 	anim.currentFrame = 0;
468 	anim.originalFrame = 0;
469 
470 	if (-1 == header)
471 		header = 0;
472 
473 	if (0 == header) {
474 		anim.object = 0;
475 		anim.originalFrame = 31;
476 	} else {
477 		anim.object = _vm->logic()->findBob(header);
478 		anim.originalFrame = _vm->logic()->findFrame(header);
479 	}
480 
481 	anim.unpackFrame = (int16)READ_BE_INT16(ptr);
482 	ptr += 2;
483 
484 	anim.speed = ((int16)READ_BE_INT16(ptr)) / 3 + 1;
485 	ptr += 2;
486 
487 	anim.bank = (int16)READ_BE_INT16(ptr);
488 	ptr += 2;
489 
490 	if (anim.bank == 0) {
491 		anim.bank = 15;
492 	} else {
493 		if (anim.bank != 13) {
494 			assert(anim.bank - 1 < MAX_BANK_NAME_COUNT);
495 			_vm->bankMan()->load(_bankNames[anim.bank-1], CUTAWAY_BANK);
496 			anim.bank = 8;
497 		} else {
498 			// Make sure we ref correct JOE bank (7)
499 			anim.bank = 7;
500 		}
501 	}
502 
503 	anim.mx = (int16)READ_BE_INT16(ptr);
504 	ptr += 2;
505 
506 	anim.my = (int16)READ_BE_INT16(ptr);
507 	ptr += 2;
508 
509 	anim.cx = (int16)READ_BE_INT16(ptr);
510 	ptr += 2;
511 
512 	anim.cy = (int16)READ_BE_INT16(ptr);
513 	ptr += 2;
514 
515 	anim.scale = (int16)READ_BE_INT16(ptr);
516 	ptr += 2;
517 
518 	if ((_vm->resource()->isDemo() && _vm->resource()->getPlatform() == Common::kPlatformDOS) ||
519 		(_vm->resource()->isInterview() && _vm->resource()->getPlatform() == Common::kPlatformAmiga)) {
520 		anim.song = 0;
521 	} else {
522 		anim.song = (int16)READ_BE_INT16(ptr);
523 		ptr += 2;
524 	}
525 
526 	// Extract information that depend on the signedness of values
527 	if (anim.unpackFrame < 0) {
528 		anim.flip = true;
529 		anim.unpackFrame = -anim.unpackFrame;
530 	} else
531 		anim.flip = false;
532 
533 	return ptr;
534 }
535 
dumpCutawayAnim(CutawayAnim & anim)536 void Cutaway::dumpCutawayAnim(CutawayAnim &anim) {
537 	debug(6, "----- CutawayAnim -----");
538 	if (anim.object) debug(6, "object = %i", anim.object);
539 	if (anim.unpackFrame) debug(6, "unpackFrame = %i", anim.unpackFrame);
540 	if (anim.speed) debug(6, "speed = %i", anim.speed);
541 	if (anim.bank) debug(6, "bank = %i", anim.bank);
542 	if (anim.mx) debug(6, "mx = %i", anim.mx);
543 	if (anim.my) debug(6, "my = %i", anim.my);
544 	if (anim.cx) debug(6, "cx = %i", anim.cx);
545 	if (anim.cy) debug(6, "cy = %i", anim.cy);
546 	if (anim.scale) debug(6, "scale = %i", anim.scale);
547 	if (anim.currentFrame) debug(6, "currentFrame = %i", anim.currentFrame);
548 	if (anim.originalFrame) debug(6, "originalFrame = %i", anim.originalFrame);
549 	if (anim.song) debug(6, "song = %i", anim.song);
550 }
551 
handleAnimation(const byte * ptr,CutawayObject & object)552 const byte *Cutaway::handleAnimation(const byte *ptr, CutawayObject &object) {
553 	// lines 1517-1770 in cutaway.c
554 	int frameCount = 0;
555 	int i;
556 
557 	CutawayAnim objAnim[56];
558 
559 	// Read animation frames
560 	for (;;) {
561 
562 		int header = (int16)READ_BE_INT16(ptr);
563 		ptr += 2;
564 
565 		if (-2 == header)
566 			break;
567 
568 		//debug(6, "Animation frame %i, header = %i", frameCount, header);
569 
570 		if (header > 1000)
571 			error("Header too large");
572 
573 		ptr = getCutawayAnim(ptr, header, objAnim[frameCount]);
574 		//dumpCutawayAnim(objAnim[frameCount]);
575 
576 		frameCount++;
577 
578 		if (_vm->input()->cutawayQuit())
579 			return NULL;
580 	}
581 
582 	if (object.animType == 1) {
583 		// lines 1615-1636 in cutaway.c
584 
585 		debug(6, "----- Complex cutaway animation (animType = %i) -----", object.animType);
586 
587 		if ((_vm->logic()->currentRoom() == 47 || _vm->logic()->currentRoom() == 63) &&
588 			objAnim[0].object == 1) {
589 			//CR 2 - 3/3/95, Special harcoded section to make Oracle work...
590 			makeComplexAnimation(_vm->graphics()->personFrames(1) - 1,  objAnim, frameCount);
591 		} else {
592 			_currentImage = makeComplexAnimation(_currentImage, objAnim, frameCount);
593 		}
594 
595 		if (object.bobStartX || object.bobStartY) {
596 			BobSlot *bob = _vm->graphics()->bob(objAnim[0].object);
597 			bob->x = object.bobStartX;
598 			bob->y = object.bobStartY;
599 		}
600 	}
601 
602 	// Setup the SYNCHRO bob channels
603 
604 	for (i = 0; i < frameCount; i++) {
605 		if (objAnim[i].mx || objAnim[i].my) {
606 			BobSlot *bob = _vm->graphics()->bob(objAnim[i].object);
607 			bob->frameNum = objAnim[i].originalFrame;
608 			bob->move(objAnim[i].mx, objAnim[i].my,	(object.specialMove > 0) ? object.specialMove : 4);
609 			// Boat room hard coded
610 			if (_vm->logic()->currentRoom() == ROOM_TEMPLE_OUTSIDE) {
611 				BobSlot *bobJoe = _vm->graphics()->bob(0);
612 				if (bobJoe->x < 320) {
613 					bobJoe->move(bobJoe->x + 346, bobJoe->y,	4);
614 				}
615 			}
616 		}
617 	}
618 
619 	// Normal cutaway
620 
621 	if (object.animType != 1) {
622 		// lines 1657-1761 in cutaway.c
623 
624 		debug(6, "----- Normal cutaway animation (animType = %i) -----", object.animType);
625 
626 		for (i = 0; i < frameCount; i++) {
627 			//debug(6, "===== Animating frame %i =====", i);
628 			//dumpCutawayAnim(objAnim[i]);
629 
630 			BobSlot *bob = _vm->graphics()->bob(objAnim[i].object);
631 			bob->active = true;
632 			if (bob->animating) {
633 				bob->animating = false;
634 				bob->frameNum = objAnim[i].originalFrame;
635 			}
636 
637 			if (objAnim[i].object < 4)
638 				bob->frameNum = 31 + objAnim[i].object;
639 
640 			if (objAnim[i].unpackFrame == 0) {
641 				// Turn off the bob
642 				bob->active = false;
643 			} else {
644 				if (object.animType == 2 || object.animType == 0) {
645 					// Unpack animation, but do not unpack moving people
646 
647 					if (!((objAnim[i].mx > 0 || objAnim[i].my > 0) && inRange(objAnim[i].object, 1, 3))) {
648 						_vm->bankMan()->unpack(
649 								objAnim[i].unpackFrame,
650 								objAnim[i].originalFrame,
651 								objAnim[i].bank);
652 					}
653 
654 					if (0 == objAnim[i].object) {
655 						// Scale Joe
656 						bob->scale = scale(object);
657 					}
658 				}
659 
660 				if (objAnim[i].cx || objAnim[i].cy) {
661 					bob->x = objAnim[i].cx;
662 					bob->y = objAnim[i].cy;
663 				}
664 
665 				// Only flip if we are not moving or it is not a person object
666 				if (!(objAnim[i].object > 0 && objAnim[i].object < 4) ||
667 						!(objAnim[i].mx || objAnim[i].my))
668 					bob->xflip = objAnim[i].flip;
669 
670 				// Add frame alteration
671 				if (!(objAnim[i].object > 0 && objAnim[i].object < 4)) {
672 					bob->frameNum = objAnim[i].originalFrame;
673 				}
674 
675 				int j;
676 				for (j = 0; j < objAnim[i].speed; j++)
677 					_vm->update();
678 			}
679 
680 			if (_vm->input()->cutawayQuit())
681 				return NULL;
682 
683 			if (objAnim[i].song > 0)
684 				_vm->sound()->playSong(objAnim[i].song);
685 
686 		} // for ()
687 	}
688 
689 	bool moving = true;
690 
691 	while (moving) {
692 		moving = false;
693 		_vm->update();
694 
695 		for (i = 0; i < frameCount; i++) {
696 			BobSlot *bob = _vm->graphics()->bob(objAnim[i].object);
697 			if (bob->moving) {
698 				moving = true;
699 				break;
700 			}
701 		}
702 
703 		if (_vm->input()->cutawayQuit())
704 			return NULL;
705 	}
706 
707 	return ptr;
708 }
709 
findCdCut(const char * basename,int index,char * result)710 static void findCdCut(const char *basename, int index, char *result) {
711 	strcpy(result, basename);
712 	for (int i = strlen(basename); i < 5; i++)
713 		result[i] = '_';
714 	snprintf(result + 5, 3, "%02i", index);
715 }
716 
handlePersonRecord(int index,CutawayObject & object,const char * sentence)717 void Cutaway::handlePersonRecord(
718 		int index,
719 		CutawayObject &object,
720 		const char *sentence) {
721 	// Lines 1455-1516 in cutaway.c
722 
723 	Person p;
724 
725 	if (object.objectNumber == OBJECT_JOE) {
726 		if (object.moveToX || object.moveToY) {
727 			_vm->walk()->moveJoe(0, object.moveToX, object.moveToY, true);
728 		}
729 	} else {
730 		_vm->logic()->initPerson(
731 				object.objectNumber - _vm->logic()->currentRoomData(),
732 				"", true, &p);
733 
734 		if (object.bobStartX || object.bobStartY) {
735 			BobSlot *bob = _vm->graphics()->bob(p.actor->bobNum);
736 			bob->scale = scale(object);
737 			bob->x = object.bobStartX;
738 			bob->y = object.bobStartY;
739 		}
740 
741 		if (object.moveToX || object.moveToY)
742 			_vm->walk()->movePerson(
743 					&p,
744 					object.moveToX, object.moveToY,
745 					_currentImage + 1,
746 					_vm->logic()->objectData(object.objectNumber)->image
747 					);
748 	}
749 
750 	if (_vm->input()->cutawayQuit())
751 		return;
752 
753 	if (0 != strcmp(sentence, "*")) {
754 		if (sentence[0] == '#') {
755 			debug(4, "Starting credits '%s'", sentence + 1);
756 			_vm->logic()->startCredits(sentence + 1);
757 		} else {
758 			if (object.objectNumber > 0) {
759 				bool foundPerson = false;
760 
761 				for (int i = 1; i <= _personFaceCount; i++) {
762 					if (_personFace[i].index == object.objectNumber) {
763 						foundPerson = true;
764 						break;
765 					}
766 				}
767 
768 				if (!foundPerson) {
769 					_personFaceCount++;
770 					assert(_personFaceCount < MAX_PERSON_FACE_COUNT);
771 					_personFace[_personFaceCount].index = object.objectNumber;
772 					_personFace[_personFaceCount].image = _vm->logic()->objectData(object.objectNumber)->image;
773 				}
774 			}
775 
776 			char voiceFilePrefix[MAX_STRING_SIZE];
777 			findCdCut(_basename, index, voiceFilePrefix);
778 			_vm->logic()->makePersonSpeak(sentence, (object.objectNumber == OBJECT_JOE) ? NULL : &p, voiceFilePrefix);
779 		}
780 
781 	}
782 
783 	if (_vm->input()->cutawayQuit())
784 		return;
785 }
786 
run(char * nextFilename)787 void Cutaway::run(char *nextFilename) {
788 	int i;
789 	nextFilename[0] = '\0';
790 
791 	_currentImage = _vm->graphics()->numFrames();
792 
793 	BobSlot *joeBob = _vm->graphics()->bob(0);
794 	int initialJoeX = joeBob->x;
795 	int initialJoeY = joeBob->y;
796 	debug(6, "[Cutaway::run] Joe started at (%i, %i)", initialJoeX, initialJoeY);
797 
798 	_vm->input()->cutawayRunning(true);
799 
800 	_initialRoom = _temporaryRoom = _vm->logic()->currentRoom();
801 
802 	_vm->display()->screenMode(_comPanel, true);
803 
804 	if (_comPanel == 0 || _comPanel == 2) {
805 		_vm->logic()->sceneStart();
806 	}
807 
808 	memset(_personFace, 0, sizeof(_personFace));
809 	_personFaceCount = 0;
810 
811 	const byte *ptr = _objectData;
812 
813 	for (i = 0; i < _cutawayObjectCount; i++) {
814 		CutawayObject object;
815 		ptr = getCutawayObject(ptr, object);
816 		//dumpCutawayObject(i, object);
817 
818 		if (!object.moveToX &&
819 				!object.moveToY &&
820 				object.specialMove > 0 &&
821 				object.objectNumber >= 0) {
822 			_vm->logic()->executeSpecialMove(object.specialMove);
823 			object.specialMove = 0;
824 		}
825 
826 		if (CURRENT_ROOM == object.room) {
827 			// Get current room
828 			object.room = _vm->logic()->currentRoom();
829 		} else {
830 			// Change current room
831 			_vm->logic()->currentRoom(object.room);
832 		}
833 
834 		ptr = turnOnPeople(ptr, object);
835 
836 		limitBob(object);
837 
838 		char sentence[MAX_STRING_SIZE];
839 		Talk::getString(_fileData, _nextSentenceOff, sentence, MAX_STRING_LENGTH);
840 
841 		if (OBJECT_ROOMFADE == object.objectNumber) {
842 			_roomFade = true;
843 			object.objectNumber = OBJECT_JOE;
844 		} else {
845 			_roomFade = false;
846 		}
847 
848 		if (object.room != _temporaryRoom)
849 			changeRooms(object);
850 
851 		ObjectType objectType = getObjectType(object);
852 
853 		if (object.song)
854 			_vm->sound()->playSong(object.song);
855 
856 		switch (objectType) {
857 		case OBJECT_TYPE_ANIMATION:
858 			ptr = handleAnimation(ptr, object);
859 			break;
860 		case OBJECT_TYPE_PERSON:
861 			handlePersonRecord(i + 1, object, sentence);
862 			break;
863 		case OBJECT_TYPE_NO_ANIMATION:
864 			// Do nothing?
865 			break;
866 		case OBJECT_TYPE_TEXT_SPEAK:
867 		case OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK:
868 		case OBJECT_TYPE_TEXT_DISPLAY:
869 			handleText(i + 1, objectType, object, sentence);
870 			break;
871 		default:
872 			warning("Unhandled object type: %i", objectType);
873 			break;
874 		}
875 
876 		if (_vm->input()->cutawayQuit())
877 			break;
878 
879 		if (_roomFade) {
880 			_vm->update();
881 			BobSlot *j = _vm->graphics()->bob(0);
882 			_vm->display()->palFadeIn(_vm->logic()->currentRoom(), j->active, j->x, j->y);
883 			_roomFade = false;
884 		}
885 
886 	} // for ()
887 
888 	_vm->display()->clearTexts(0, 198);
889 	// XXX lines 1887-1895 in cutaway.c
890 
891 	stop();
892 
893 	updateGameState();
894 
895 	_vm->bankMan()->close(CUTAWAY_BANK);
896 
897 	talk(nextFilename);
898 
899 	if (_comPanel == 0 || (_comPanel == 2 && !_anotherCutaway)) {
900 		_vm->logic()->sceneStop();
901 		_comPanel = 0;
902 	}
903 
904 	if (nextFilename[0] == '\0' && !_anotherCutaway && _vm->logic()->currentRoom() != ROOM_ENDING_CREDITS) {
905 		_vm->display()->fullscreen(false);
906 
907 		// Lines 2138-2182 in cutaway.c
908 		if (_finalRoom) {
909 			_vm->logic()->newRoom(0);
910 			_vm->logic()->entryObj(0);
911 		} else {
912 			/// No need to stay in current room, so return to previous room
913 			//  if one exists. Reset Joe's X,Y coords to those when first entered
914 
915 			restorePersonData();
916 
917 			debug(6, "_vm->logic()->entryObj() = %i", _vm->logic()->entryObj());
918 			if (_vm->logic()->entryObj() > 0) {
919 				_initialRoom = _vm->logic()->objectData(_vm->logic()->entryObj())->room;
920 			} else {
921 				// We're not returning to new room, so return to old Joe X,Y coords
922 				debug(6, "[Cutaway::run] Moving joe to (%i, %i)", initialJoeX, initialJoeY);
923 				_vm->logic()->joePos(initialJoeX, initialJoeY);
924 			}
925 
926 			if (_vm->logic()->currentRoom() != _initialRoom) {
927 				_vm->logic()->currentRoom(_initialRoom);
928 				_vm->logic()->changeRoom();
929 				if (_vm->logic()->currentRoom() == _vm->logic()->newRoom()) {
930 					_vm->logic()->newRoom(0);
931 				}
932 			}
933 			_vm->logic()->joePos(0, 0);
934 		}
935 
936 		_vm->logic()->joeCutFacing(0);
937 		_comPanel = 0;
938 
939 		int k = 0;
940 		for (i = _vm->logic()->roomData(_vm->logic()->currentRoom());
941 				i <= _vm->logic()->roomData(_vm->logic()->currentRoom() + 1); i++) {
942 
943 			ObjectData *object = _vm->logic()->objectData(i);
944 			if (object->image == -3 || object->image == -4) {
945 				k++;
946 				if (object->name > 0) {
947 					_vm->graphics()->resetPersonAnim(k);
948 				}
949 			}
950 		}
951 
952 		_vm->logic()->removeHotelItemsFromInventory();
953 	}
954 
955 	joeBob->animating = 0;
956 	joeBob->moving    = 0;
957 
958 	// if the cutaway has been cancelled, we must stop the speech and the sfx as well
959 	if (_vm->input()->cutawayQuit()) {
960 		if (_vm->sound()->isSpeechActive())
961 			_vm->sound()->stopSpeech();
962 		_vm->sound()->stopSfx();
963 	}
964 
965 	_vm->input()->cutawayRunning(false);
966 	_vm->input()->cutawayQuitReset();
967 	_vm->input()->quickSaveReset();
968 	_vm->input()->quickLoadReset();
969 
970 	if (_songBeforeComic > 0)
971 		_vm->sound()->playSong(_songBeforeComic);
972 	else if (_lastSong > 0)
973 		_vm->sound()->playSong(_lastSong);
974 }
975 
stop()976 void Cutaway::stop() {
977 	// Lines 1901-2032 in cutaway.c
978 	byte *ptr = _gameStatePtr;
979 
980 	// Skipping GAMESTATE data
981 	int gameStateCount = (int16)READ_BE_INT16(ptr); ptr += 2;
982 	if (gameStateCount > 0)
983 		ptr += (gameStateCount * 12);
984 
985 	// Get the final room and Joe's final position
986 
987 	int16 joeRoom = READ_BE_UINT16(ptr); ptr += 2;
988 	int16 joeX    = READ_BE_UINT16(ptr); ptr += 2;
989 	int16 joeY    = READ_BE_UINT16(ptr); ptr += 2;
990 
991 	debug(6, "[Cutaway::stop] Final position is room %i and coordinates (%i, %i)",
992 			joeRoom, joeX, joeY);
993 
994 	if ((!_vm->input()->cutawayQuit() || (!_anotherCutaway && joeRoom == _finalRoom)) &&
995 			joeRoom != _temporaryRoom &&
996 			joeRoom != 0) {
997 
998 		debug(6, "[Cutaway::stop] Changing rooms and moving Joe");
999 
1000 		_vm->logic()->joePos(joeX, joeY);
1001 		_vm->logic()->currentRoom(joeRoom);
1002 		_vm->logic()->oldRoom(_initialRoom);
1003 		_vm->logic()->displayRoom(_vm->logic()->currentRoom(), RDM_FADE_JOE_XY, 0, _comPanel, true);
1004 	}
1005 
1006 	if (_vm->input()->cutawayQuit()) {
1007 		// Lines 1927-2032 in cutaway.c
1008 		int i;
1009 
1010 		// Stop the credits from running
1011 		_vm->logic()->stopCredits();
1012 
1013 		_vm->graphics()->stopBobs();
1014 
1015 		for (i = 1; i <= _personFaceCount; i++) {
1016 			int index =  _personFace[i].index;
1017 			if (index > 0) {
1018 				_vm->logic()->objectData(_personFace[i].index)->image = _personFace[i].image;
1019 
1020 				_vm->graphics()->bob(_vm->logic()->findBob(index))->xflip =
1021 					(_personFace[i].image != -4);
1022 			}
1023 		}
1024 
1025 		int quitObjectCount = (int16)READ_BE_INT16(ptr); ptr += 2;
1026 
1027 		for (i = 0; i < quitObjectCount; i++) {
1028 			int16 objectIndex  = (int16)READ_BE_INT16(ptr); ptr += 2;
1029 			int16 fromIndex    = (int16)READ_BE_INT16(ptr); ptr += 2;
1030 			int16 x       = (int16)READ_BE_INT16(ptr); ptr += 2;
1031 			int16 y       = (int16)READ_BE_INT16(ptr); ptr += 2;
1032 			int16 room    = (int16)READ_BE_INT16(ptr); ptr += 2;
1033 			int16 frame   = (int16)READ_BE_INT16(ptr); ptr += 2;
1034 			int16 bank    = (int16)READ_BE_INT16(ptr); ptr += 2;
1035 
1036 			int bobIndex = _vm->logic()->findBob(objectIndex);
1037 			ObjectData *object = _vm->logic()->objectData(objectIndex);
1038 
1039 			if (fromIndex > 0) {
1040 				if (fromIndex == objectIndex) {
1041 					// Enable object
1042 					object->name = ABS(object->name);
1043 				} else {
1044 					_vm->logic()->objectCopy(fromIndex, objectIndex);
1045 
1046 					ObjectData *from = _vm->logic()->objectData(fromIndex);
1047 					if (object->image && !from->image && bobIndex && _vm->logic()->currentRoom() == object->room)
1048 						_vm->graphics()->clearBob(bobIndex);
1049 				}
1050 
1051 				if (_vm->logic()->currentRoom() == room)
1052 					_vm->graphics()->refreshObject(objectIndex);
1053 			}
1054 
1055 			if (_vm->logic()->currentRoom() == object->room) {
1056 				BobSlot *pbs = _vm->graphics()->bob(bobIndex);
1057 
1058 				if (x || y) {
1059 					pbs->x = x;
1060 					pbs->y = y;
1061 					if (inRange(object->image, -4, -3))
1062 						pbs->scale = _vm->grid()->findScale(x, y);
1063 				}
1064 
1065 				if (frame) {
1066 					if (0 == bank)
1067 						bank = 15;
1068 					else if (bank != 13) {
1069 						_vm->bankMan()->load(_bankNames[bank-1], CUTAWAY_BANK);
1070 						bank = 8;
1071 					}
1072 
1073 					int objectFrame = _vm->logic()->findFrame(objectIndex);
1074 
1075 					if (objectFrame == 1000) {
1076 						_vm->graphics()->clearBob(bobIndex);
1077 					} else if (objectFrame) {
1078 						_vm->bankMan()->unpack(ABS(frame), objectFrame, bank);
1079 						pbs->frameNum = objectFrame;
1080 						if (frame < 0)
1081 							pbs->xflip = true;
1082 
1083 					}
1084 				}
1085 			}
1086 		} // for ()
1087 
1088 		int16 specialMove = (int16)READ_BE_INT16(ptr); ptr += 2;
1089 		if (specialMove > 0)
1090 			_vm->logic()->executeSpecialMove(specialMove);
1091 
1092 		_lastSong = (int16)READ_BE_INT16(ptr); ptr += 2;
1093 	}
1094 
1095 	if (joeRoom == _temporaryRoom &&
1096 			joeRoom != 37 && joeRoom != 105 && joeRoom != 106 &&
1097 			(joeX || joeY)) {
1098 		BobSlot *joeBob = _vm->graphics()->bob(0);
1099 
1100 		debug(6, "[Cutaway::stop] Moving Joe");
1101 
1102 		joeBob->x = joeX;
1103 		joeBob->y = joeY;
1104 		_vm->logic()->joeScale(_vm->grid()->findScale(joeX, joeY));
1105 		_vm->logic()->joeFace();
1106 	}
1107 }
1108 
updateGameState()1109 void Cutaway::updateGameState() {
1110 	// Lines 2047-2115 in cutaway.c
1111 	byte *ptr = _gameStatePtr;
1112 
1113 	int gameStateCount = (int16)READ_BE_INT16(ptr); ptr += 2;
1114 
1115 	for (int i = 0; i < gameStateCount; i++) {
1116 		int16 stateIndex    = (int16)READ_BE_INT16(ptr); ptr += 2;
1117 		int16 stateValue    = (int16)READ_BE_INT16(ptr); ptr += 2;
1118 		int16 objectIndex   = (int16)READ_BE_INT16(ptr); ptr += 2;
1119 		int16 areaIndex     = (int16)READ_BE_INT16(ptr); ptr += 2;
1120 		int16 areaSubIndex  = (int16)READ_BE_INT16(ptr); ptr += 2;
1121 		int16 fromObject    = (int16)READ_BE_INT16(ptr); ptr += 2;
1122 
1123 		bool update = false;
1124 
1125 		if (stateIndex > 0) {
1126 			if (_vm->logic()->gameState(stateIndex) == stateValue)
1127 				update = true;
1128 		} else {
1129 			_vm->logic()->gameState(ABS(stateIndex), stateValue);
1130 			update = true;
1131 		}
1132 
1133 		if (update) {
1134 
1135 			if (objectIndex > 0) {                    // Show the object
1136 				ObjectData *objectData  = _vm->logic()->objectData(objectIndex);
1137 				objectData->name        = ABS(objectData->name);
1138 				if (fromObject > 0)
1139 					_vm->logic()->objectCopy(fromObject, objectIndex);
1140 				_vm->graphics()->refreshObject(objectIndex);
1141 			} else if (objectIndex < 0) {               // Hide the object
1142 				objectIndex             = -objectIndex;
1143 				ObjectData *objectData  = _vm->logic()->objectData(objectIndex);
1144 				objectData->name        = -ABS(objectData->name);
1145 				_vm->graphics()->refreshObject(objectIndex);
1146 			}
1147 
1148 			if (areaIndex > 0) {
1149 
1150 				// Turn area on or off
1151 
1152 				if (areaSubIndex > 0) {
1153 					Area *area = _vm->grid()->area(areaIndex, areaSubIndex);
1154 					area->mapNeighbors = ABS(area->mapNeighbors);
1155 				} else {
1156 					Area *area = _vm->grid()->area(areaIndex, ABS(areaSubIndex));
1157 					area->mapNeighbors = -ABS(area->mapNeighbors);
1158 				}
1159 			}
1160 
1161 		}
1162 	} // for ()
1163 }
1164 
talk(char * nextFilename)1165 void Cutaway::talk(char *nextFilename) {
1166 	const char *p = strrchr(_talkFile, '.');
1167 	if (p && 0 == scumm_stricmp(p, ".DOG")) {
1168 		nextFilename[0] = '\0';
1169 		assert(_talkTo > 0);
1170 		int personInRoom = _talkTo - _vm->logic()->roomData(_vm->logic()->currentRoom());
1171 		_vm->logic()->startDialogue(_talkFile, personInRoom, nextFilename);
1172 	}
1173 }
1174 
makeComplexAnimation(int16 currentImage,Cutaway::CutawayAnim * objAnim,int frameCount)1175 int Cutaway::makeComplexAnimation(int16 currentImage, Cutaway::CutawayAnim *objAnim, int frameCount) {
1176 	int frameIndex[256];
1177 	int i;
1178 	assert(frameCount < 30);
1179 	AnimFrame cutAnim[30];
1180 
1181 	memset(frameIndex, 0, sizeof(frameIndex));
1182 	debug(6, "[Cutaway::makeComplexAnimation] currentImage = %i", currentImage);
1183 
1184 	for (i = 0; i < frameCount; i++) {
1185 		cutAnim[i].frame = objAnim[i].unpackFrame;
1186 		cutAnim[i].speed = objAnim[i].speed;
1187 		frameIndex[objAnim[i].unpackFrame] = 1;
1188 	}
1189 
1190 	cutAnim[frameCount].frame = 0;
1191 	cutAnim[frameCount].speed = 0;
1192 
1193 	int nextFrameIndex = 1;
1194 
1195 	for (i = 1; i < 256; i++)
1196 		if (frameIndex[i])
1197 			frameIndex[i] = nextFrameIndex++;
1198 
1199 	for (i = 0; i < frameCount; i++) {
1200 		cutAnim[i].frame = currentImage + frameIndex[objAnim[i].unpackFrame];
1201 	}
1202 
1203 	for (i = 1; i < 256; i++) {
1204 		if (frameIndex[i]) {
1205 			currentImage++;
1206 			_vm->bankMan()->unpack(i, currentImage, objAnim[0].bank);
1207 		}
1208 	}
1209 
1210 	_vm->graphics()->setBobCutawayAnim(objAnim[0].object, objAnim[0].flip, cutAnim, frameCount + 1);
1211 	return currentImage;
1212 }
1213 
handleText(int index,ObjectType type,CutawayObject & object,const char * sentence)1214 void Cutaway::handleText(
1215 		int index,
1216 		ObjectType type,
1217 		CutawayObject &object,
1218 		const char *sentence) {
1219 	// lines 1776-1863 in cutaway.c
1220 
1221 	int spaces = countSpaces(type, sentence);
1222 
1223 	int x;
1224 	int flags;
1225 
1226 	if (OBJECT_TYPE_TEXT_DISPLAY == type) {
1227 		x = _vm->display()->textCenterX(sentence);
1228 		flags = 2;
1229 	} else {
1230 		x = object.bobStartX;
1231 		flags = 1;
1232 	}
1233 
1234 	BobSlot *bob =
1235 		_vm->graphics()->bob(_vm->logic()->findBob(ABS(object.objectNumber)));
1236 
1237 	_vm->graphics()->setBobText(bob, sentence, x, object.bobStartY, object.specialMove, flags);
1238 
1239 	if (OBJECT_TYPE_TEXT_SPEAK == type || OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK == type) {
1240 		if (_vm->sound()->speechOn()) {
1241 			char voiceFileName[MAX_STRING_SIZE];
1242 			findCdCut(_basename, index, voiceFileName);
1243 			strcat(voiceFileName, "1");
1244 			_vm->sound()->playSpeech(voiceFileName);
1245 		}
1246 
1247 		if (OBJECT_TYPE_TEXT_SPEAK == type && _vm->sound()->speechOn() && !_vm->subtitles())
1248 			_vm->display()->clearTexts(0, 150);
1249 	}
1250 
1251 	while (1) {
1252 		_vm->update();
1253 
1254 		if (_vm->input()->cutawayQuit())
1255 			return;
1256 
1257 		if (_vm->input()->keyVerb() == VERB_SKIP_TEXT) {
1258 			_vm->input()->clearKeyVerb();
1259 			break;
1260 		}
1261 
1262 		if ((OBJECT_TYPE_TEXT_SPEAK == type || OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK == type) && _vm->sound()->speechOn() && _vm->sound()->speechSfxExists()) {
1263 			if (!_vm->sound()->isSpeechActive()) {
1264 				break;
1265 			}
1266 		} else {
1267 			--spaces;
1268 			if (spaces <= 0) {
1269 				break;
1270 			}
1271 		}
1272 	}
1273 
1274 	_vm->display()->clearTexts(0, 198);
1275 	_vm->update();
1276 }
1277 
countSpaces(ObjectType type,const char * segment)1278 int Cutaway::countSpaces(ObjectType type, const char *segment) {
1279 	int tmp = 0;
1280 
1281 	while (*segment++)
1282 		tmp++;
1283 
1284 	if (tmp < 50)
1285 		tmp = 50;
1286 
1287 	if (OBJECT_TYPE_TEXT_DISPLAY == type)
1288 		tmp *= 3;
1289 
1290 	return (tmp * 2) / (_vm->talkSpeed() / 3);
1291 
1292 }
1293 
scale(CutawayObject & object)1294 int Cutaway::scale(CutawayObject &object) {
1295 	int scaling = 100;
1296 
1297 	if (object.scale > 0)
1298 		scaling = object.scale;
1299 	else if (!object.objectNumber) {
1300 		// Only scale Joe
1301 		int x, y;
1302 
1303 		if (object.bobStartX > 0 || object.bobStartY > 0) {
1304 			x = object.bobStartX;
1305 			y = object.bobStartY;
1306 		} else {
1307 			BobSlot *bob = _vm->graphics()->bob(0);
1308 			x = bob->x;
1309 			y = bob->y;
1310 		}
1311 
1312 		int zone = _vm->grid()->findAreaForPos(GS_ROOM, x, y);
1313 		if (zone > 0) {
1314 			Area *area = _vm->grid()->area(_vm->logic()->currentRoom(), zone);
1315 			scaling = area->calcScale(y);
1316 		}
1317 	}
1318 
1319 	return scaling;
1320 }
1321 
1322 } // End of namespace Queen
1323