aboutsummaryrefslogtreecommitdiff
path: root/engines/queen/cutaway.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/queen/cutaway.cpp')
-rw-r--r--engines/queen/cutaway.cpp1324
1 files changed, 1324 insertions, 0 deletions
diff --git a/engines/queen/cutaway.cpp b/engines/queen/cutaway.cpp
new file mode 100644
index 0000000000..dc24d5b627
--- /dev/null
+++ b/engines/queen/cutaway.cpp
@@ -0,0 +1,1324 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/cutaway.h"
+
+#include "queen/bankman.h"
+#include "queen/display.h"
+#include "queen/graphics.h"
+#include "queen/grid.h"
+#include "queen/input.h"
+#include "queen/logic.h"
+#include "queen/queen.h"
+#include "queen/resource.h"
+#include "queen/sound.h"
+#include "queen/talk.h"
+#include "queen/walk.h"
+
+namespace Queen {
+
+void Cutaway::run(
+ const char *filename,
+ char *nextFilename,
+ QueenEngine *vm) {
+ Cutaway *cutaway = new Cutaway(filename, vm);
+ cutaway->run(nextFilename);
+ delete cutaway;
+}
+
+Cutaway::Cutaway(
+ const char *filename,
+ QueenEngine *vm)
+ : _vm(vm), _personDataCount(0), _personFaceCount(0), _lastSong(0), _songBeforeComic(0) {
+ memset(&_bankNames, 0, sizeof(_bankNames));
+ _vm->input()->cutawayQuitReset();
+ load(filename);
+}
+
+Cutaway::~Cutaway() {
+ delete[] _fileData;
+}
+
+void Cutaway::load(const char *filename) {
+ byte *ptr;
+
+ debug(6, "----- Cutaway::load(\"%s\") -----", filename);
+
+ ptr = _fileData = _vm->resource()->loadFile(filename, 20);
+
+ if (0 == scumm_stricmp(filename, "comic.cut"))
+ _songBeforeComic = _vm->sound()->lastOverride();
+
+ strcpy(_basename, filename);
+ _basename[strlen(_basename)-4] = '\0';
+
+ _comPanel = READ_BE_UINT16(ptr);
+ ptr += 2;
+ debug(6, "_comPanel = %i", _comPanel);
+ _cutawayObjectCount = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ debug(6, "_cutawayObjectCount = %i", _cutawayObjectCount);
+
+ if (_cutawayObjectCount < 0) {
+ _cutawayObjectCount = -_cutawayObjectCount;
+ _vm->input()->canQuit(false);
+ } else
+ _vm->input()->canQuit(true);
+
+ int16 flags1 = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+ debug(6, "flags1 = %i", flags1);
+
+ if (flags1 < 0) {
+ _vm->logic()->entryObj(0);
+ _finalRoom = -flags1;
+ } else
+ _finalRoom = PREVIOUS_ROOM;
+
+ _anotherCutaway = (flags1 == 1);
+
+ debug(6, "[Cutaway::load] _finalRoom = %i", _finalRoom);
+ debug(6, "[Cutaway::load] _anotherCutaway = %i", _anotherCutaway);
+
+ /*
+ Pointers to other places in the cutaway data
+ */
+
+ _gameStatePtr = _fileData + READ_BE_UINT16(ptr);
+ ptr += 2;
+
+ _nextSentenceOff = READ_BE_UINT16(ptr);
+ ptr += 2;
+
+ uint16 bankNamesOff = READ_BE_UINT16(ptr);
+ ptr += 2;
+
+ _objectData = ptr;
+
+ loadStrings(bankNamesOff);
+
+ if (_bankNames[0][0]) {
+ debug(6, "Loading bank '%s'", _bankNames[0]);
+ _vm->bankMan()->load(_bankNames[0], CUTAWAY_BANK);
+ }
+
+ char entryString[MAX_STRING_SIZE];
+ Talk::getString(_fileData, _nextSentenceOff, entryString, MAX_STRING_LENGTH);
+ debug(6, "Entry string = '%s'", entryString);
+
+ _vm->logic()->joeCutFacing(_vm->logic()->joeFacing());
+ _vm->logic()->joeFace();
+
+ if (entryString[0] == '*' &&
+ entryString[1] == 'F' &&
+ entryString[3] == '\0') {
+ switch (entryString[2]) {
+ case 'L':
+ _vm->logic()->joeCutFacing(DIR_LEFT);
+ break;
+ case 'R':
+ _vm->logic()->joeCutFacing(DIR_RIGHT);
+ break;
+ case 'F':
+ _vm->logic()->joeCutFacing(DIR_FRONT);
+ break;
+ case 'B':
+ _vm->logic()->joeCutFacing(DIR_BACK);
+ break;
+ }
+ }
+
+}
+
+void Cutaway::loadStrings(uint16 offset) {
+ int bankNameCount = READ_BE_UINT16(_fileData + offset);
+ offset += 2;
+
+ debug(6, "Bank name count = %i", bankNameCount);
+
+ /*
+ The _bankNames zero-based array is the one-based BANK_NAMEstr array in
+ the original source code.
+ */
+
+ for (int i = 0, j = 0; i < bankNameCount; i++) {
+ Talk::getString(_fileData, offset, _bankNames[j], MAX_FILENAME_LENGTH);
+ if (_bankNames[j][0]) {
+ debug(6, "Bank name %i = '%s'", j, _bankNames[j]);
+ j++;
+ }
+ }
+
+ debug(6, "Getting talk file");
+ Talk::getString(_fileData, offset, _talkFile, MAX_FILENAME_LENGTH);
+ debug(6, "Talk file = '%s'", _talkFile);
+
+ _talkTo = (int16)READ_BE_INT16(_fileData + offset);
+ debug(6, "_talkTo = %i", _talkTo);
+}
+
+const byte *Cutaway::getCutawayObject(const byte *ptr, CutawayObject &object)
+{
+ const byte *oldPtr = ptr;
+
+ object.objectNumber = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.moveToX = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.moveToY = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.bank = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.animList = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.execute = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.limitBobX1 = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.limitBobY1 = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.limitBobX2 = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.limitBobY2 = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.specialMove = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.animType = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.fromObject = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.bobStartX = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.bobStartY = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.room = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.scale = (int16)READ_BE_INT16(ptr); ptr += 2;
+
+ if ((ptr - oldPtr) != 17*sizeof(int16))
+ error("Wrong number of values read");
+
+ // Make ugly reuse of data less ugly
+ if (object.limitBobX1 < 0) {
+ object.song = -object.limitBobX1;
+ object.limitBobX1 = 0;
+ } else
+ object.song = 0;
+
+ return ptr;
+}
+
+void Cutaway::dumpCutawayObject(int index, CutawayObject &object)
+{
+ debug(6, "----- CutawayObject[%i] -----", index);
+
+ const char *objectNumberStr;
+
+ switch (object.objectNumber) {
+ case -1:
+ objectNumberStr = "MESSAGE";
+ break;
+ case 0:
+ objectNumberStr = "Joe";
+ break;
+ default:
+ if (object.objectNumber > 0)
+ objectNumberStr = _vm->logic()->objectName(ABS(_vm->logic()->objectData(object.objectNumber)->name));
+ else
+ objectNumberStr = "Unknown!";
+ break;
+ }
+
+ debug(6, "objectNumber = %i (%s)", object.objectNumber, objectNumberStr);
+
+ if (object.moveToX) debug(6, "moveToX = %i", object.moveToX);
+ if (object.moveToY) debug(6, "moveToY = %i", object.moveToY);
+ if (object.bank) debug(6, "bank = %i", object.bank);
+ if (object.animList) debug(6, "animList = %i", object.animList);
+ if (object.execute) debug(6, "execute = %i", object.execute);
+ if (object.limitBobX1) debug(6, "limitBobX1 = %i", object.limitBobX1);
+ if (object.limitBobY1) debug(6, "limitBobY1 = %i", object.limitBobY1);
+ if (object.limitBobX2) debug(6, "limitBobX2 = %i", object.limitBobX2);
+ if (object.limitBobY2) debug(6, "limitBobY2 = %i", object.limitBobY2);
+ if (object.specialMove) debug(6, "specialMove = %i", object.specialMove);
+ if (object.animType) debug(6, "animType = %i", object.animType);
+ if (object.fromObject) debug(6, "fromObject = %i", object.fromObject);
+ if (object.bobStartX) debug(6, "bobStartX = %i", object.bobStartX);
+ if (object.bobStartY) debug(6, "bobStartY = %i", object.bobStartY);
+ if (object.room) debug(6, "room = %i", object.room);
+ if (object.scale) debug(6, "scale = %i", object.scale);
+
+}
+
+
+const byte *Cutaway::turnOnPeople(const byte *ptr, CutawayObject &object) {
+ // Lines 1248-1259 in cutaway.c
+ object.personCount = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ if (object.personCount > MAX_PERSON_COUNT)
+ error("[Cutaway::turnOnPeople] object.personCount > MAX_PERSON_COUNT");
+
+ for (int i = 0; i < object.personCount; i++) {
+ object.person[i] = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+ debug(7, "[%i] Turn on person %i", i, object.person[i]);
+ }
+
+ return ptr;
+}
+
+void Cutaway::limitBob(CutawayObject &object) {
+ if (object.limitBobX1) {
+
+ if (object.objectNumber < 0) {
+ warning("QueenCutaway::limitBob called with objectNumber = %i", object.objectNumber);
+ return;
+ }
+
+ BobSlot *bob =
+ _vm->graphics()->bob( _vm->logic()->findBob(object.objectNumber) );
+
+ if (!bob) {
+ warning("Failed to find bob");
+ return;
+ }
+
+ bob->box.x1 = object.limitBobX1;
+ bob->box.y1 = object.limitBobY1;
+ bob->box.x2 = object.limitBobX2;
+ bob->box.y2 = object.limitBobY2;
+ }
+}
+
+void Cutaway::restorePersonData() {
+ for (int i = 0; i < _personDataCount; i++) {
+ int index = _personData[i].index;
+ ObjectData *objectData = _vm->logic()->objectData(index);
+ objectData->name = _personData[i].name;
+ objectData->image = _personData[i].image;
+ }
+}
+
+void Cutaway::changeRooms(CutawayObject &object) {
+ // Lines 1291-1385 in cutaway.c
+
+ debug(6, "Changing from room %i to room %i",
+ _temporaryRoom,
+ object.room);
+
+ restorePersonData();
+ _personDataCount = 0;
+
+ if (_finalRoom != object.room) {
+ int firstObjectInRoom = _vm->logic()->roomData(object.room) + 1;
+ int lastObjectInRoom = _vm->logic()->roomData(object.room) + _vm->grid()->objMax(object.room);
+
+ for (int i = firstObjectInRoom; i <= lastObjectInRoom; i++) {
+ ObjectData *objectData = _vm->logic()->objectData(i);
+
+ if (objectData->image == -3 || objectData->image == -4) {
+
+ assert(_personDataCount < MAX_PERSON_COUNT);
+ // The object is a person! So record the details...
+ _personData[_personDataCount].index = i;
+ _personData[_personDataCount].name = objectData->name;
+ _personData[_personDataCount].image = objectData->image;
+ _personDataCount++;
+
+ // Now, check to see if we need to keep the person on
+ bool on = false;
+ for (int j = 0; j < object.personCount; j++) {
+ if (object.person[j] == i) {
+ on = true;
+ break;
+ }
+ }
+
+ if (on) {
+ // It is needed, so ensure it's ON
+ objectData->name = ABS(objectData->name);
+ } else {
+ // Not needed, so switch off!
+ objectData->name = -ABS(objectData->name);
+ }
+
+ }
+ } // for ()
+ }
+
+ // set coordinates for Joe if he is on screen
+
+ _vm->logic()->joePos(0, 0);
+
+ for (int i = 0; i < object.personCount; i++) {
+ if (PERSON_JOE == object.person[i]) {
+ _vm->logic()->joePos(object.bobStartX, object.bobStartY);
+ }
+ }
+
+ _vm->logic()->oldRoom(_initialRoom);
+
+ // FIXME - the first cutaway is played at the end of the command 0x178. This
+ // command setups some persons and associates bob slots to them. They should be
+ // hidden as their y coordinate is > 150, but they aren't ! A (temporary)
+ // workaround is to display the room with the panel area enabled. Same problem
+ // for cutaway c62c.
+ int16 comPanel = _comPanel;
+ if ((strcmp(_basename, "c41f") == 0 && _temporaryRoom == 106 && object.room == 41) ||
+ (strcmp(_basename, "c62c") == 0 && _temporaryRoom == 105 && object.room == 41)) {
+ comPanel = 1;
+ }
+
+ // FIXME - in the original engine, panel is hidden once the 'head room' is displayed, we
+ // do it before (ie before palette fading)
+ if (object.room == FAYE_HEAD || object.room == AZURA_HEAD || object.room == FRANK_HEAD) {
+ comPanel = 2;
+ }
+
+ RoomDisplayMode mode;
+
+ if (!_vm->logic()->joeX() && !_vm->logic()->joeY()) {
+ mode = RDM_FADE_NOJOE;
+ } else {
+ // We need to display Joe on screen
+ if (_roomFade)
+ mode = RDM_NOFADE_JOE;
+ else
+ mode = RDM_FADE_JOE_XY;
+ }
+
+ _vm->logic()->displayRoom(_vm->logic()->currentRoom(), mode, object.scale, comPanel, true);
+
+ _currentImage = _vm->graphics()->numFrames();
+
+ _temporaryRoom = _vm->logic()->currentRoom();
+
+ restorePersonData();
+}
+
+Cutaway::ObjectType Cutaway::getObjectType(CutawayObject &object) {
+ // Lines 1387-1449 in cutaway.c
+
+ ObjectType objectType = OBJECT_TYPE_ANIMATION;
+
+ if (object.objectNumber > 0) {
+ if (!object.animList) {
+ // No anim frames, so treat as a PERSON, ie. allow to speak/walk
+ ObjectData *objectData = _vm->logic()->objectData(object.objectNumber);
+ if (objectData->image == -3 || objectData->image == -4)
+ objectType = OBJECT_TYPE_PERSON;
+ }
+ } else if (object.objectNumber == OBJECT_JOE) {
+ // It's Joe. See if he's to be treated as a person.
+ if (!object.animList) {
+ // There's no animation list, so Joe must be talking.
+ objectType = OBJECT_TYPE_PERSON;
+ }
+ }
+
+ if (object.fromObject > 0) {
+ /* Copy FROM_OBJECT into OBJECT */
+
+ if (object.objectNumber != object.fromObject) {
+ _vm->logic()->objectCopy(object.fromObject, object.objectNumber);
+ } else {
+ // Same object, so just turn it on!
+ ObjectData *objectData = _vm->logic()->objectData(object.objectNumber);
+ objectData->name = ABS(objectData->name);
+ }
+
+ _vm->graphics()->refreshObject(object.objectNumber);
+
+ // Skip doing any anim stuff
+ objectType = OBJECT_TYPE_NO_ANIMATION;
+ }
+
+ switch (object.objectNumber) {
+ case -2:
+ // Text to be spoken
+ objectType = OBJECT_TYPE_TEXT_SPEAK;
+ break;
+ case -3:
+ // Text to be displayed AND spoken
+ objectType = OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK;
+ break;
+ case -4:
+ // Text to be displayed only (not spoken)
+ objectType = OBJECT_TYPE_TEXT_DISPLAY;
+ break;
+ }
+
+ if (OBJECT_TYPE_ANIMATION == objectType && !object.execute) {
+ // Execute is not on, and it's an object, so ignore any Anims
+ objectType = OBJECT_TYPE_NO_ANIMATION;
+ }
+
+ return objectType;
+}
+
+const byte *Cutaway::getCutawayAnim(const byte *ptr, int header, CutawayAnim &anim) {
+ // lines 1531-1607 in cutaway.c
+
+ //debug(6, "[Cutaway::getCutawayAnim] header=%i", header);
+
+ anim.currentFrame = 0;
+ anim.originalFrame = 0;
+
+ if (-1 == header)
+ header = 0;
+
+ if (0 == header) {
+ anim.object = 0;
+ anim.originalFrame = 31;
+ } else {
+ anim.object = _vm->logic()->findBob(header);
+ anim.originalFrame = _vm->logic()->findFrame(header);
+ }
+
+ anim.unpackFrame = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ anim.speed = ((int16)READ_BE_INT16(ptr)) / 3 + 1;
+ ptr += 2;
+
+ anim.bank = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ if (anim.bank == 0) {
+ anim.bank = 15;
+ } else {
+ if (anim.bank != 13) {
+ _vm->bankMan()->load(_bankNames[anim.bank-1], CUTAWAY_BANK);
+ anim.bank = 8;
+ } else {
+ // Make sure we ref correct JOE bank (7)
+ anim.bank = 7;
+ }
+ }
+
+ anim.mx = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ anim.my = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ anim.cx = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ anim.cy = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ anim.scale = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ if (_vm->resource()->isDemo()) {
+ anim.song = 0;
+ } else {
+ anim.song = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+ }
+
+ // Extract information that depend on the signedness of values
+ if (anim.unpackFrame < 0) {
+ anim.flip = true;
+ anim.unpackFrame = -anim.unpackFrame;
+ } else
+ anim.flip = false;
+
+ return ptr;
+}
+
+void Cutaway::dumpCutawayAnim(CutawayAnim &anim) {
+ debug(6, "----- CutawayAnim -----");
+ if (anim.object) debug(6, "object = %i", anim.object);
+ if (anim.unpackFrame) debug(6, "unpackFrame = %i", anim.unpackFrame);
+ if (anim.speed) debug(6, "speed = %i", anim.speed);
+ if (anim.bank) debug(6, "bank = %i", anim.bank);
+ if (anim.mx) debug(6, "mx = %i", anim.mx);
+ if (anim.my) debug(6, "my = %i", anim.my);
+ if (anim.cx) debug(6, "cx = %i", anim.cx);
+ if (anim.cy) debug(6, "cy = %i", anim.cy);
+ if (anim.scale) debug(6, "scale = %i", anim.scale);
+ if (anim.currentFrame) debug(6, "currentFrame = %i", anim.currentFrame);
+ if (anim.originalFrame) debug(6, "originalFrame = %i", anim.originalFrame);
+ if (anim.song) debug(6, "song = %i", anim.song);
+}
+
+const byte *Cutaway::handleAnimation(const byte *ptr, CutawayObject &object) {
+ // lines 1517-1770 in cutaway.c
+ int frameCount = 0;
+ int header = 0;
+ int i;
+
+ CutawayAnim objAnim[56];
+
+ // Read animation frames
+ for (;;) {
+
+ header = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ if (-2 == header)
+ break;
+
+ //debug(6, "Animation frame %i, header = %i", frameCount, header);
+
+ if (header > 1000)
+ error("Header too large");
+
+ ptr = getCutawayAnim(ptr, header, objAnim[frameCount]);
+ //dumpCutawayAnim(objAnim[frameCount]);
+
+ frameCount++;
+
+ if (_vm->input()->cutawayQuit())
+ return NULL;
+ }
+
+ if (object.animType == 1) {
+ // lines 1615-1636 in cutaway.c
+
+ debug(6, "----- Complex cutaway animation (animType = %i) -----", object.animType);
+
+ if ((_vm->logic()->currentRoom() == 47 || _vm->logic()->currentRoom() == 63) &&
+ objAnim[0].object == 1) {
+ //CR 2 - 3/3/95, Special harcoded section to make Oracle work...
+ makeComplexAnimation(_vm->graphics()->personFrames(1) - 1, objAnim, frameCount);
+ } else {
+ _currentImage = makeComplexAnimation(_currentImage, objAnim, frameCount);
+ }
+
+ if (object.bobStartX || object.bobStartY) {
+ BobSlot *bob = _vm->graphics()->bob(objAnim[0].object);
+ bob->x = object.bobStartX;
+ bob->y = object.bobStartY;
+ }
+ }
+
+ // Setup the SYNCHRO bob channels
+
+ for (i = 0; i < frameCount; i++) {
+ if (objAnim[i].mx || objAnim[i].my) {
+ BobSlot *bob = _vm->graphics()->bob(objAnim[i].object);
+ bob->frameNum = objAnim[i].originalFrame;
+ bob->move(objAnim[i].mx, objAnim[i].my, (object.specialMove > 0) ? object.specialMove : 4);
+ // Boat room hard coded
+ if (_vm->logic()->currentRoom() == ROOM_TEMPLE_OUTSIDE) {
+ BobSlot *bobJoe = _vm->graphics()->bob(0);
+ if (bobJoe->x < 320) {
+ bobJoe->move(bobJoe->x + 346, bobJoe->y, 4);
+ }
+ }
+ }
+ }
+
+ // Normal cutaway
+
+ if (object.animType != 1) {
+ // lines 1657-1761 in cutaway.c
+
+ debug(6, "----- Normal cutaway animation (animType = %i) -----", object.animType);
+
+ for (i = 0; i < frameCount; i++) {
+ //debug(6, "===== Animating frame %i =====", i);
+ //dumpCutawayAnim(objAnim[i]);
+
+ BobSlot *bob = _vm->graphics()->bob(objAnim[i].object);
+ bob->active = true;
+ if (bob->animating) {
+ bob->animating = false;
+ bob->frameNum = objAnim[i].originalFrame;
+ }
+
+ if (objAnim[i].object < 4)
+ bob->frameNum = 31 + objAnim[i].object;
+
+ if (objAnim[i].unpackFrame == 0) {
+ // Turn off the bob
+ bob->active = false;
+ } else {
+ if (object.animType == 2 || object.animType == 0) {
+ // Unpack animation, but do not unpack moving people
+
+ if (!((objAnim[i].mx > 0 || objAnim[i].my > 0) && inRange(objAnim[i].object, 1, 3))) {
+ _vm->bankMan()->unpack(
+ objAnim[i].unpackFrame,
+ objAnim[i].originalFrame,
+ objAnim[i].bank);
+ }
+
+ if (0 == objAnim[i].object) {
+ // Scale Joe
+ bob->scale = scale(object);
+ }
+ }
+
+ if (objAnim[i].cx || objAnim[i].cy) {
+ bob->x = objAnim[i].cx;
+ bob->y = objAnim[i].cy;
+ }
+
+ // Only flip if we are not moving or it is not a person object
+ if (!(objAnim[i].object > 0 && objAnim[i].object < 4) ||
+ !(objAnim[i].mx || objAnim[i].my) )
+ bob->xflip = objAnim[i].flip;
+
+ // Add frame alteration
+ if (!(objAnim[i].object > 0 && objAnim[i].object < 4)) {
+ bob->frameNum = objAnim[i].originalFrame;
+ }
+
+ int j;
+ for (j = 0; j < objAnim[i].speed; j++)
+ _vm->update();
+ }
+
+ if (_vm->input()->cutawayQuit())
+ return NULL;
+
+ if (objAnim[i].song > 0)
+ _vm->sound()->playSong(objAnim[i].song);
+
+ } // for ()
+ }
+
+ bool moving = true;
+
+ while (moving) {
+ moving = false;
+ _vm->update();
+
+ for (i = 0; i < frameCount; i++) {
+ BobSlot *bob = _vm->graphics()->bob(objAnim[i].object);
+ if (bob->moving) {
+ moving = true;
+ break;
+ }
+ }
+
+ if (_vm->input()->cutawayQuit())
+ return NULL;
+ }
+
+ return ptr;
+}
+
+static void findCdCut(const char *basename, int index, char *result) {
+ strcpy(result, basename);
+ for (int i = strlen(basename); i < 5; i++)
+ result[i] = '_';
+ snprintf(result + 5, 3, "%02i", index);
+}
+
+void Cutaway::handlePersonRecord(
+ int index,
+ CutawayObject &object,
+ const char *sentence) {
+ // Lines 1455-1516 in cutaway.c
+
+ Person p;
+
+ if (object.objectNumber == OBJECT_JOE) {
+ if (object.moveToX || object.moveToY) {
+ _vm->walk()->moveJoe(0, object.moveToX, object.moveToY, true);
+ }
+ } else {
+ _vm->logic()->initPerson(
+ object.objectNumber - _vm->logic()->currentRoomData(),
+ "", true, &p);
+
+ if (object.bobStartX || object.bobStartY) {
+ BobSlot *bob = _vm->graphics()->bob(p.actor->bobNum);
+ bob->scale = scale(object);
+ bob->x = object.bobStartX;
+ bob->y = object.bobStartY;
+ }
+
+ if (object.moveToX || object.moveToY)
+ _vm->walk()->movePerson(
+ &p,
+ object.moveToX, object.moveToY,
+ _currentImage + 1,
+ _vm->logic()->objectData(object.objectNumber)->image
+ );
+ }
+
+ if (_vm->input()->cutawayQuit())
+ return;
+
+ if (0 != strcmp(sentence, "*")) {
+ if (sentence[0] == '#') {
+ debug(4, "Starting credits '%s'", sentence + 1);
+ _vm->logic()->startCredits(sentence + 1);
+ } else {
+ if (object.objectNumber > 0) {
+ bool foundPerson = false;
+
+ for (int i = 1; i <= _personFaceCount; i++) {
+ if (_personFace[i].index == object.objectNumber) {
+ foundPerson = true;
+ break;
+ }
+ }
+
+ if (!foundPerson) {
+ _personFaceCount++;
+ assert(_personFaceCount < MAX_PERSON_FACE_COUNT);
+ _personFace[_personFaceCount].index = object.objectNumber;
+ _personFace[_personFaceCount].image = _vm->logic()->objectData(object.objectNumber)->image;
+ }
+ }
+
+ char voiceFilePrefix[MAX_STRING_SIZE];
+ findCdCut(_basename, index, voiceFilePrefix);
+ _vm->logic()->makePersonSpeak(sentence, (object.objectNumber == OBJECT_JOE) ? NULL : &p, voiceFilePrefix);
+ }
+
+ }
+
+ if (_vm->input()->cutawayQuit())
+ return;
+}
+
+void Cutaway::run(char *nextFilename) {
+ int i;
+ nextFilename[0] = '\0';
+
+ _currentImage = _vm->graphics()->numFrames();
+
+ BobSlot *joeBob = _vm->graphics()->bob(0);
+ int initialJoeX = joeBob->x;
+ int initialJoeY = joeBob->y;
+ debug(6, "[Cutaway::run] Joe started at (%i, %i)", initialJoeX, initialJoeY);
+
+ _vm->input()->cutawayRunning(true);
+
+ _initialRoom = _temporaryRoom = _vm->logic()->currentRoom();
+
+ _vm->display()->screenMode(_comPanel, true);
+
+ if (_comPanel == 0 || _comPanel == 2) {
+ _vm->logic()->sceneStart();
+ }
+
+ memset(_personFace, 0, sizeof(_personFace));
+ _personFaceCount = 0;
+
+ const byte *ptr = _objectData;
+
+ for (i = 0; i < _cutawayObjectCount; i++) {
+ CutawayObject object;
+ ptr = getCutawayObject(ptr, object);
+ //dumpCutawayObject(i, object);
+
+ if (!object.moveToX &&
+ !object.moveToY &&
+ object.specialMove > 0 &&
+ object.objectNumber >= 0) {
+ _vm->logic()->executeSpecialMove(object.specialMove);
+ object.specialMove = 0;
+ }
+
+ if (CURRENT_ROOM == object.room) {
+ // Get current room
+ object.room = _vm->logic()->currentRoom();
+ } else {
+ // Change current room
+ _vm->logic()->currentRoom(object.room);
+ }
+
+ ptr = turnOnPeople(ptr, object);
+
+ limitBob(object);
+
+ char sentence[MAX_STRING_SIZE];
+ Talk::getString(_fileData, _nextSentenceOff, sentence, MAX_STRING_LENGTH);
+
+ if (OBJECT_ROOMFADE == object.objectNumber) {
+ _roomFade = true;
+ object.objectNumber = OBJECT_JOE;
+ } else {
+ _roomFade = false;
+ }
+
+ if (object.room != _temporaryRoom)
+ changeRooms(object);
+
+ ObjectType objectType = getObjectType(object);
+
+ if (object.song)
+ _vm->sound()->playSong(object.song);
+
+ switch (objectType) {
+ case OBJECT_TYPE_ANIMATION:
+ ptr = handleAnimation(ptr, object);
+ break;
+ case OBJECT_TYPE_PERSON:
+ handlePersonRecord(i + 1, object, sentence);
+ break;
+ case OBJECT_TYPE_NO_ANIMATION:
+ // Do nothing?
+ break;
+ case OBJECT_TYPE_TEXT_SPEAK:
+ case OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK:
+ case OBJECT_TYPE_TEXT_DISPLAY:
+ handleText(i + 1, objectType, object, sentence);
+ break;
+ default:
+ warning("Unhandled object type: %i", objectType);
+ break;
+ }
+
+ if (_vm->input()->cutawayQuit())
+ break;
+
+ if (_roomFade) {
+ _vm->update();
+ BobSlot *j = _vm->graphics()->bob(0);
+ _vm->display()->palFadeIn(_vm->logic()->currentRoom(), j->active, j->x, j->y);
+ _roomFade = false;
+ }
+
+ } // for ()
+
+ _vm->display()->clearTexts(0, 198);
+ // XXX lines 1887-1895 in cutaway.c
+
+ stop();
+
+ updateGameState();
+
+ _vm->bankMan()->close(CUTAWAY_BANK);
+
+ talk(nextFilename);
+
+ if (_comPanel == 0 || (_comPanel == 2 && !_anotherCutaway)) {
+ _vm->logic()->sceneStop();
+ _comPanel = 0;
+ }
+
+ if (nextFilename[0] == '\0' && !_anotherCutaway && _vm->logic()->currentRoom() != ROOM_ENDING_CREDITS) {
+ _vm->display()->fullscreen(false);
+
+ // Lines 2138-2182 in cutaway.c
+ if (_finalRoom) {
+ _vm->logic()->newRoom(0);
+ _vm->logic()->entryObj(0);
+ } else {
+ /// No need to stay in current room, so return to previous room
+ // if one exists. Reset Joe's X,Y coords to those when first entered
+
+ restorePersonData();
+
+ debug(6, "_vm->logic()->entryObj() = %i", _vm->logic()->entryObj());
+ if (_vm->logic()->entryObj() > 0) {
+ _initialRoom = _vm->logic()->objectData(_vm->logic()->entryObj())->room;
+ } else {
+ // We're not returning to new room, so return to old Joe X,Y coords
+ debug(6, "[Cutaway::run] Moving joe to (%i, %i)", initialJoeX, initialJoeY);
+ _vm->logic()->joePos(initialJoeX, initialJoeY);
+ }
+
+ if (_vm->logic()->currentRoom() != _initialRoom) {
+ _vm->logic()->currentRoom(_initialRoom);
+ _vm->logic()->changeRoom();
+ if (_vm->logic()->currentRoom() == _vm->logic()->newRoom()) {
+ _vm->logic()->newRoom(0);
+ }
+ }
+ _vm->logic()->joePos(0, 0);
+ }
+
+ _vm->logic()->joeCutFacing(0);
+ _comPanel = 0;
+
+ int k = 0;
+ for (i = _vm->logic()->roomData(_vm->logic()->currentRoom());
+ i <= _vm->logic()->roomData(_vm->logic()->currentRoom() + 1); i++) {
+
+ ObjectData *object = _vm->logic()->objectData(i);
+ if (object->image == -3 || object->image == -4) {
+ k++;
+ if (object->name > 0) {
+ _vm->graphics()->resetPersonAnim(k);
+ }
+ }
+ }
+
+ _vm->logic()->removeHotelItemsFromInventory();
+ }
+
+ joeBob->animating = 0;
+ joeBob->moving = 0;
+
+ // if the cutaway has been cancelled, we must stop the speech and the sfx as well
+ if (_vm->input()->cutawayQuit()) {
+ if (_vm->sound()->isSpeechActive())
+ _vm->sound()->stopSpeech();
+ _vm->sound()->stopSfx();
+ }
+
+ _vm->input()->cutawayRunning(false);
+ _vm->input()->cutawayQuitReset();
+ _vm->input()->quickSaveReset();
+ _vm->input()->quickLoadReset();
+
+ if (_songBeforeComic > 0)
+ _vm->sound()->playSong(_songBeforeComic);
+ else if (_lastSong > 0)
+ _vm->sound()->playSong(_lastSong);
+}
+
+void Cutaway::stop() {
+ // Lines 1901-2032 in cutaway.c
+ byte *ptr = _gameStatePtr;
+
+ // Skipping GAMESTATE data
+ int gameStateCount = (int16)READ_BE_INT16(ptr); ptr += 2;
+ if (gameStateCount > 0)
+ ptr += (gameStateCount * 12);
+
+ // Get the final room and Joe's final position
+
+ int16 joeRoom = READ_BE_UINT16(ptr); ptr += 2;
+ int16 joeX = READ_BE_UINT16(ptr); ptr += 2;
+ int16 joeY = READ_BE_UINT16(ptr); ptr += 2;
+
+ debug(6, "[Cutaway::stop] Final position is room %i and coordinates (%i, %i)",
+ joeRoom, joeX, joeY);
+
+ if ((!_vm->input()->cutawayQuit() || (!_anotherCutaway && joeRoom == _finalRoom)) &&
+ joeRoom != _temporaryRoom &&
+ joeRoom != 0) {
+
+ debug(6, "[Cutaway::stop] Changing rooms and moving Joe");
+
+ _vm->logic()->joePos(joeX, joeY);
+ _vm->logic()->currentRoom(joeRoom);
+ _vm->logic()->oldRoom(_initialRoom);
+ _vm->logic()->displayRoom(_vm->logic()->currentRoom(), RDM_FADE_JOE_XY, 0, _comPanel, true);
+ }
+
+ if (_vm->input()->cutawayQuit()) {
+ // Lines 1927-2032 in cutaway.c
+ int i;
+
+ // Stop the credits from running
+ _vm->logic()->stopCredits();
+
+ _vm->graphics()->stopBobs();
+
+ for (i = 1; i <= _personFaceCount; i++) {
+ int index = _personFace[i].index;
+ if (index > 0) {
+ _vm->logic()->objectData(_personFace[i].index)->image = _personFace[i].image;
+
+ _vm->graphics()->bob(_vm->logic()->findBob(index))->xflip =
+ (_personFace[i].image != -4);
+ }
+ }
+
+ int quitObjectCount = (int16)READ_BE_INT16(ptr); ptr += 2;
+
+ for (i = 0; i < quitObjectCount; i++) {
+ int16 objectIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 fromIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 x = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 y = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 room = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 frame = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 bank = (int16)READ_BE_INT16(ptr); ptr += 2;
+
+ int bobIndex = _vm->logic()->findBob(objectIndex);
+ ObjectData *object = _vm->logic()->objectData(objectIndex);
+
+ if (fromIndex > 0) {
+ if (fromIndex == objectIndex) {
+ // Enable object
+ object->name = ABS(object->name);
+ } else {
+ _vm->logic()->objectCopy(fromIndex, objectIndex);
+
+ ObjectData *from = _vm->logic()->objectData(fromIndex);
+ if (object->image && !from->image && bobIndex && _vm->logic()->currentRoom() == object->room)
+ _vm->graphics()->bob(bobIndex)->clear();
+ }
+
+ if (_vm->logic()->currentRoom() == room)
+ _vm->graphics()->refreshObject(objectIndex);
+ }
+
+ if (_vm->logic()->currentRoom() == object->room) {
+ BobSlot *pbs = _vm->graphics()->bob(bobIndex);
+
+ if (x || y) {
+ pbs->x = x;
+ pbs->y = y;
+ if (inRange(object->image, -4, -3))
+ pbs->scale = _vm->grid()->findScale(x, y);
+ }
+
+ if (frame) {
+ if (0 == bank)
+ bank = 15;
+ else if (bank != 13) {
+ _vm->bankMan()->load(_bankNames[bank-1], CUTAWAY_BANK);
+ bank = 8;
+ }
+
+ int objectFrame = _vm->logic()->findFrame(objectIndex);
+
+ if (objectFrame == 1000) {
+ _vm->graphics()->bob(bobIndex)->clear();
+ } else if (objectFrame) {
+ _vm->bankMan()->unpack(ABS(frame), objectFrame, bank);
+ pbs->frameNum = objectFrame;
+ if (frame < 0)
+ pbs->xflip = true;
+
+ }
+ }
+ }
+ } // for ()
+
+ int16 specialMove = (int16)READ_BE_INT16(ptr); ptr += 2;
+ if (specialMove > 0)
+ _vm->logic()->executeSpecialMove(specialMove);
+
+ _lastSong = (int16)READ_BE_INT16(ptr); ptr += 2;
+ }
+
+ if (joeRoom == _temporaryRoom &&
+ joeRoom != 37 && joeRoom != 105 && joeRoom != 106 &&
+ (joeX || joeY)) {
+ BobSlot *joeBob = _vm->graphics()->bob(0);
+
+ debug(6, "[Cutaway::stop] Moving Joe");
+
+ joeBob->x = joeX;
+ joeBob->y = joeY;
+ _vm->logic()->joeScale(_vm->grid()->findScale(joeX, joeY));
+ _vm->logic()->joeFace();
+ }
+}
+
+void Cutaway::updateGameState() {
+ // Lines 2047-2115 in cutaway.c
+ byte *ptr = _gameStatePtr;
+
+ int gameStateCount = (int16)READ_BE_INT16(ptr); ptr += 2;
+
+ for (int i = 0; i < gameStateCount; i++) {
+ int16 stateIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 stateValue = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 objectIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 areaIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 areaSubIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 fromObject = (int16)READ_BE_INT16(ptr); ptr += 2;
+
+ bool update = false;
+
+ if (stateIndex > 0) {
+ if (_vm->logic()->gameState(stateIndex) == stateValue)
+ update = true;
+ } else {
+ _vm->logic()->gameState(ABS(stateIndex), stateValue);
+ update = true;
+ }
+
+ if (update) {
+
+ if (objectIndex > 0) { // Show the object
+ ObjectData *objectData = _vm->logic()->objectData(objectIndex);
+ objectData->name = ABS(objectData->name);
+ if (fromObject > 0)
+ _vm->logic()->objectCopy(fromObject, objectIndex);
+ _vm->graphics()->refreshObject(objectIndex);
+ } else if (objectIndex < 0) { // Hide the object
+ objectIndex = -objectIndex;
+ ObjectData *objectData = _vm->logic()->objectData(objectIndex);
+ objectData->name = -ABS(objectData->name);
+ _vm->graphics()->refreshObject(objectIndex);
+ }
+
+ if (areaIndex > 0) {
+
+ // Turn area on or off
+
+ if (areaSubIndex > 0) {
+ Area *area = _vm->grid()->area(areaIndex, areaSubIndex);
+ area->mapNeighbours = ABS(area->mapNeighbours);
+ } else {
+ Area *area = _vm->grid()->area(areaIndex, ABS(areaSubIndex));
+ area->mapNeighbours = -ABS(area->mapNeighbours);
+ }
+ }
+
+ }
+ } // for ()
+}
+
+void Cutaway::talk(char *nextFilename) {
+ const char *p = strrchr(_talkFile, '.');
+ if (p && 0 == scumm_stricmp(p, ".dog")) {
+ nextFilename[0] = '\0';
+ assert(_talkTo > 0);
+ int personInRoom = _talkTo - _vm->logic()->roomData(_vm->logic()->currentRoom());
+ _vm->logic()->startDialogue(_talkFile, personInRoom, nextFilename);
+ }
+}
+
+int Cutaway::makeComplexAnimation(int16 currentImage, Cutaway::CutawayAnim *objAnim, int frameCount) {
+ int frameIndex[256];
+ int i;
+ assert(frameCount < 30);
+ AnimFrame cutAnim[30];
+
+ memset(frameIndex, 0, sizeof(frameIndex));
+ debug(6, "[Cutaway::makeComplexAnimation] currentImage = %i", currentImage);
+
+ for (i = 0; i < frameCount; i++) {
+ cutAnim[i].frame = objAnim[i].unpackFrame;
+ cutAnim[i].speed = objAnim[i].speed;
+ frameIndex[objAnim[i].unpackFrame] = 1;
+ }
+
+ cutAnim[frameCount].frame = 0;
+ cutAnim[frameCount].speed = 0;
+
+ int nextFrameIndex = 1;
+
+ for (i = 1; i < 256; i++)
+ if (frameIndex[i])
+ frameIndex[i] = nextFrameIndex++;
+
+ for (i = 0; i < frameCount; i++) {
+ cutAnim[i].frame = currentImage + frameIndex[objAnim[i].unpackFrame];
+ }
+
+ for (i = 1; i < 256; i++) {
+ if (frameIndex[i]) {
+ currentImage++;
+ _vm->bankMan()->unpack(i, currentImage, objAnim[0].bank);
+ }
+ }
+
+ _vm->graphics()->setBobCutawayAnim(objAnim[0].object, objAnim[0].flip, cutAnim, frameCount + 1);
+ return currentImage;
+}
+
+void Cutaway::handleText(
+ int index,
+ ObjectType type,
+ CutawayObject &object,
+ const char *sentence) {
+ // lines 1776-1863 in cutaway.c
+
+ int spaces = countSpaces(type, sentence);
+
+ int x;
+ int flags;
+
+ if (OBJECT_TYPE_TEXT_DISPLAY == type) {
+ x = _vm->display()->textCenterX(sentence);
+ flags = 2;
+ } else {
+ x = object.bobStartX;
+ flags = 1;
+ }
+
+ BobSlot *bob =
+ _vm->graphics()->bob( _vm->logic()->findBob(ABS(object.objectNumber)) );
+
+ _vm->graphics()->setBobText(bob, sentence, x, object.bobStartY, object.specialMove, flags);
+
+ if (OBJECT_TYPE_TEXT_SPEAK == type || OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK == type) {
+ if (_vm->sound()->speechOn()) {
+ char voiceFileName[MAX_STRING_SIZE];
+ findCdCut(_basename, index, voiceFileName);
+ strcat(voiceFileName, "1");
+ _vm->sound()->playSfx(voiceFileName, true);
+ }
+
+ if (OBJECT_TYPE_TEXT_SPEAK == type && _vm->sound()->speechOn() && !_vm->subtitles())
+ _vm->display()->clearTexts(0, 150);
+ }
+
+ while (1) {
+ _vm->update();
+
+ if (_vm->input()->cutawayQuit())
+ return;
+
+ if (_vm->input()->keyVerb() == VERB_SKIP_TEXT) {
+ _vm->input()->clearKeyVerb();
+ break;
+ }
+
+ if ((OBJECT_TYPE_TEXT_SPEAK == type || OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK == type) && _vm->sound()->speechOn() && _vm->sound()->speechSfxExists()) {
+ if (!_vm->sound()->isSpeechActive()) {
+ break;
+ }
+ } else {
+ --spaces;
+ if (spaces <= 0) {
+ break;
+ }
+ }
+ }
+
+ _vm->display()->clearTexts(0, 198);
+ _vm->update();
+}
+
+int Cutaway::countSpaces(ObjectType type, const char *segment) {
+ int tmp = 0;
+
+ while (*segment++)
+ tmp++;
+
+ if (tmp < 50)
+ tmp = 50;
+
+ if (OBJECT_TYPE_TEXT_DISPLAY == type)
+ tmp *= 3;
+
+ return (tmp * 2) / (_vm->talkSpeed() / 3);
+
+}
+
+int Cutaway::scale(CutawayObject &object) {
+ int scaling = 100;
+
+ if (object.scale > 0)
+ scaling = object.scale;
+ else if (!object.objectNumber) {
+ // Only scale Joe
+ int x, y;
+
+ if (object.bobStartX > 0 || object.bobStartY > 0) {
+ x = object.bobStartX;
+ y = object.bobStartY;
+ } else {
+ BobSlot *bob = _vm->graphics()->bob(0);
+ x = bob->x;
+ y = bob->y;
+ }
+
+ int zone = _vm->grid()->findAreaForPos(GS_ROOM, x, y);
+ if (zone > 0) {
+ Area *area = _vm->grid()->area(_vm->logic()->currentRoom(), zone);
+ scaling = area->calcScale(y);
+ }
+ }
+
+ return scaling;
+}
+
+} // End of namespace Queen