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