aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra
diff options
context:
space:
mode:
Diffstat (limited to 'engines/kyra')
-rw-r--r--engines/kyra/animator.cpp691
-rw-r--r--engines/kyra/animator.h126
-rw-r--r--engines/kyra/debugger.cpp206
-rw-r--r--engines/kyra/debugger.h57
-rw-r--r--engines/kyra/gui.cpp1020
-rw-r--r--engines/kyra/items.cpp982
-rw-r--r--engines/kyra/kyra.cpp1215
-rw-r--r--engines/kyra/kyra.h961
-rw-r--r--engines/kyra/module.mk33
-rw-r--r--engines/kyra/resource.cpp366
-rw-r--r--engines/kyra/resource.h95
-rw-r--r--engines/kyra/saveload.cpp319
-rw-r--r--engines/kyra/scene.cpp1581
-rw-r--r--engines/kyra/screen.cpp2084
-rw-r--r--engines/kyra/screen.h206
-rw-r--r--engines/kyra/script.cpp565
-rw-r--r--engines/kyra/script.h106
-rw-r--r--engines/kyra/script_v1.cpp1713
-rw-r--r--engines/kyra/seqplayer.cpp640
-rw-r--r--engines/kyra/seqplayer.h123
-rw-r--r--engines/kyra/sequences_v1.cpp1630
-rw-r--r--engines/kyra/sound.cpp477
-rw-r--r--engines/kyra/sound.h155
-rw-r--r--engines/kyra/sprites.cpp569
-rw-r--r--engines/kyra/sprites.h95
-rw-r--r--engines/kyra/staticres.cpp978
-rw-r--r--engines/kyra/text.cpp574
-rw-r--r--engines/kyra/text.h69
-rw-r--r--engines/kyra/timer.cpp280
-rw-r--r--engines/kyra/wsamovie.cpp206
-rw-r--r--engines/kyra/wsamovie.h85
31 files changed, 18207 insertions, 0 deletions
diff --git a/engines/kyra/animator.cpp b/engines/kyra/animator.cpp
new file mode 100644
index 0000000000..e5c5c431c8
--- /dev/null
+++ b/engines/kyra/animator.cpp
@@ -0,0 +1,691 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-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 "kyra/kyra.h"
+#include "kyra/screen.h"
+#include "kyra/animator.h"
+#include "kyra/sprites.h"
+
+#include "common/system.h"
+
+namespace Kyra {
+ScreenAnimator::ScreenAnimator(KyraEngine *vm, OSystem *system) {
+ _vm = vm;
+ _screen = vm->screen();
+ _initOk = false;
+ _updateScreen = false;
+ _system = system;
+ _screenObjects = _actors = _items = _sprites = _objectQueue = 0;
+ _noDrawShapesFlag = 0;
+}
+
+ScreenAnimator::~ScreenAnimator() {
+ close();
+}
+
+void ScreenAnimator::init(int actors_, int items_, int sprites_) {
+ debug(9, "ScreenAnimator::init(%d, %d, %d)", actors_, items_, sprites_);
+ _screenObjects = new AnimObject[actors_ + items_ + sprites_];
+ assert(_screenObjects);
+ memset(_screenObjects, 0, sizeof(AnimObject) * (actors_ + items_ + sprites_));
+ _actors = _screenObjects;
+ _sprites = &_screenObjects[actors_];
+ _items = &_screenObjects[actors_ + items_];
+ _brandonDrawFrame = 113;
+
+ _initOk = true;
+}
+
+void ScreenAnimator::close() {
+ debug(9, "ScreenAnimator::close()");
+ if (_initOk) {
+ _initOk = false;
+ delete [] _screenObjects;
+ _screenObjects = _actors = _items = _sprites = _objectQueue = 0;
+ }
+}
+
+void ScreenAnimator::initAnimStateList() {
+ AnimObject *animStates = _screenObjects;
+ animStates[0].index = 0;
+ animStates[0].active = 1;
+ animStates[0].flags = 0x800;
+ animStates[0].background = _vm->_shapes[2];
+ animStates[0].rectSize = _screen->getRectSize(4, 48);
+ animStates[0].width = 4;
+ animStates[0].height = 48;
+ animStates[0].width2 = 4;
+ animStates[0].height2 = 3;
+
+ for (int i = 1; i <= 4; ++i) {
+ animStates[i].index = i;
+ animStates[i].active = 0;
+ animStates[i].flags = 0x800;
+ animStates[i].background = _vm->_shapes[3];
+ animStates[i].rectSize = _screen->getRectSize(4, 64);
+ animStates[i].width = 4;
+ animStates[i].height = 48;
+ animStates[i].width2 = 4;
+ animStates[i].height2 = 3;
+ }
+
+ for (int i = 5; i < 16; ++i) {
+ animStates[i].index = i;
+ animStates[i].active = 0;
+ animStates[i].flags = 0;
+ }
+
+ for (int i = 16; i < 28; ++i) {
+ animStates[i].index = i;
+ animStates[i].flags = 0;
+ animStates[i].background = _vm->_shapes[349+i];
+ animStates[i].rectSize = _screen->getRectSize(3, 24);
+ animStates[i].width = 3;
+ animStates[i].height = 16;
+ animStates[i].width2 = 0;
+ animStates[i].height2 = 0;
+ }
+}
+
+void ScreenAnimator::preserveAllBackgrounds() {
+ debug(9, "ScreenAnimator::preserveAllBackgrounds()");
+ uint8 curPage = _screen->_curPage;
+ _screen->_curPage = 2;
+
+ AnimObject *curObject = _objectQueue;
+ while (curObject) {
+ if (curObject->active && !curObject->disable) {
+ preserveOrRestoreBackground(curObject, false);
+ curObject->bkgdChangeFlag = 0;
+ }
+ curObject = curObject->nextAnimObject;
+ }
+ _screen->_curPage = curPage;
+}
+
+void ScreenAnimator::flagAllObjectsForBkgdChange() {
+ debug(9, "ScreenAnimator::flagAllObjectsForBkgdChange()");
+ AnimObject *curObject = _objectQueue;
+ while (curObject) {
+ curObject->bkgdChangeFlag = 1;
+ curObject = curObject->nextAnimObject;
+ }
+}
+
+void ScreenAnimator::flagAllObjectsForRefresh() {
+ debug(9, "ScreenAnimator::flagAllObjectsForRefresh()");
+ AnimObject *curObject = _objectQueue;
+ while (curObject) {
+ curObject->refreshFlag = 1;
+ curObject = curObject->nextAnimObject;
+ }
+}
+
+void ScreenAnimator::restoreAllObjectBackgrounds() {
+ debug(9, "ScreenAnimator::restoreAllObjectBackground()");
+ AnimObject *curObject = _objectQueue;
+ _screen->_curPage = 2;
+
+ while (curObject) {
+ if (curObject->active && !curObject->disable) {
+ preserveOrRestoreBackground(curObject, true);
+ curObject->x2 = curObject->x1;
+ curObject->y2 = curObject->y1;
+ }
+ curObject = curObject->nextAnimObject;
+ }
+
+ _screen->_curPage = 0;
+}
+
+void ScreenAnimator::preserveAnyChangedBackgrounds() {
+ debug(9, "ScreenAnimator::preserveAnyChangedBackgrounds()");
+ AnimObject *curObject = _objectQueue;
+ _screen->_curPage = 2;
+
+ while (curObject) {
+ if (curObject->active && !curObject->disable && curObject->bkgdChangeFlag) {
+ preserveOrRestoreBackground(curObject, false);
+ curObject->bkgdChangeFlag = 0;
+ }
+ curObject = curObject->nextAnimObject;
+ }
+
+ _screen->_curPage = 0;
+}
+
+void ScreenAnimator::preserveOrRestoreBackground(AnimObject *obj, bool restore) {
+ debug(9, "ScreenAnimator::preserveOrRestoreBackground(0x%X, restore)", obj, restore);
+ int x = 0, y = 0, width = obj->width << 3, height = obj->height;
+
+ if (restore) {
+ x = obj->x2;
+ y = obj->y2;
+ } else {
+ x = obj->x1;
+ y = obj->y1;
+ }
+
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+
+ int temp;
+
+ temp = x + width;
+ if (temp >= 319) {
+ x = 319 - width;
+ }
+ temp = y + height;
+ if (temp >= 136) {
+ y = 136 - height;
+ }
+
+ if (restore) {
+ _screen->copyBlockToPage(_screen->_curPage, x, y, width, height, obj->background);
+ } else {
+ _screen->copyRegionToBuffer(_screen->_curPage, x, y, width, height, obj->background);
+ }
+}
+
+void ScreenAnimator::prepDrawAllObjects() {
+ debug(9, "ScreenAnimator::prepDrawAllObjects()");
+ AnimObject *curObject = _objectQueue;
+ int drawPage = 2;
+ int flagUnk1 = 0, flagUnk2 = 0, flagUnk3 = 0;
+ if (_noDrawShapesFlag)
+ return;
+ if (_vm->_brandonStatusBit & 0x20)
+ flagUnk1 = 0x200;
+ if (_vm->_brandonStatusBit & 0x40)
+ flagUnk2 = 0x4000;
+
+ while (curObject) {
+ if (curObject->active) {
+ int xpos = curObject->x1;
+ int ypos = curObject->y1;
+
+ int drawLayer = 0;
+ if (!(curObject->flags & 0x800)) {
+ drawLayer = 7;
+ } else if (curObject->disable) {
+ drawLayer = 0;
+ } else {
+ drawLayer = _vm->_sprites->getDrawLayer(curObject->drawY);
+ }
+
+ // talking head functionallity
+ if (_vm->_talkingCharNum != -1) {
+ const int16 baseAnimFrameTable1[] = { 0x11, 0x35, 0x59, 0x00, 0x00, 0x00 };
+ const int16 baseAnimFrameTable2[] = { 0x15, 0x39, 0x5D, 0x00, 0x00, 0x00 };
+ const int8 xOffsetTable1[] = { 2, 4, 0, 5, 2, 0, 0, 0 };
+ const int8 xOffsetTable2[] = { 6, 4, 8, 3, 6, 0, 0, 0 };
+ const int8 yOffsetTable1[] = { 0, 8, 1, 1, 0, 0, 0, 0 };
+ const int8 yOffsetTable2[] = { 0, 8, 1, 1, 0, 0, 0, 0 };
+ if (curObject->index == 0 || curObject->index <= 4) {
+ int shapesIndex = 0;
+ if (curObject->index == _vm->_charSayUnk3) {
+ shapesIndex = _vm->_currHeadShape + baseAnimFrameTable1[curObject->index];
+ } else {
+ shapesIndex = baseAnimFrameTable2[curObject->index];
+ int temp2 = 0;
+ if (curObject->index == 2) {
+ if (_vm->_characterList[2].sceneId == 77 || _vm->_characterList[2].sceneId == 86) {
+ temp2 = 1;
+ } else {
+ temp2 = 0;
+ }
+ } else {
+ temp2 = 1;
+ }
+
+ if (!temp2) {
+ shapesIndex = -1;
+ }
+ }
+
+ xpos = curObject->x1;
+ ypos = curObject->y1;
+
+ int tempX = 0, tempY = 0;
+ if (curObject->flags & 0x1) {
+ tempX = (xOffsetTable1[curObject->index] * _brandonScaleX) >> 8;
+ tempY = yOffsetTable1[curObject->index];
+ } else {
+ tempX = (xOffsetTable2[curObject->index] * _brandonScaleX) >> 8;
+ tempY = yOffsetTable2[curObject->index];
+ }
+ tempY = (tempY * _brandonScaleY) >> 8;
+ xpos += tempX;
+ ypos += tempY;
+
+ if (_vm->_scaleMode && _brandonScaleX != 256) {
+ ++xpos;
+ }
+
+ if (curObject->index == 0 && shapesIndex != -1) {
+ if (!(_vm->_brandonStatusBit & 2)) {
+ flagUnk3 = 0x100;
+ if ((flagUnk1 & 0x200) || (flagUnk2 & 0x4000)) {
+ flagUnk3 = 0;
+ }
+
+ int tempFlags = 0;
+ if (flagUnk3 & 0x100) {
+ tempFlags = curObject->flags & 1;
+ tempFlags |= 0x800 | flagUnk1 | 0x100;
+ }
+
+ if (!(flagUnk3 & 0x100) && (flagUnk2 & 0x4000)) {
+ tempFlags = curObject->flags & 1;
+ tempFlags |= 0x900 | flagUnk1 | 0x4000;
+ _screen->drawShape(drawPage, _vm->_shapes[4+shapesIndex], xpos, ypos, 2, tempFlags | 4, _vm->_brandonPoisonFlagsGFX, int(1), int(_vm->_brandonInvFlag), drawLayer, _brandonScaleX, _brandonScaleY);
+ } else {
+ if (!(flagUnk2 & 0x4000)) {
+ tempFlags = curObject->flags & 1;
+ tempFlags |= 0x900 | flagUnk1;
+ }
+
+ _screen->drawShape(drawPage, _vm->_shapes[4+shapesIndex], xpos, ypos, 2, tempFlags | 4, _vm->_brandonPoisonFlagsGFX, int(1), drawLayer, _brandonScaleX, _brandonScaleY);
+ }
+ }
+ } else {
+ if (shapesIndex != -1) {
+ int tempFlags = 0;
+ if (curObject->flags & 1) {
+ tempFlags = 1;
+ }
+ _screen->drawShape(drawPage, _vm->_shapes[4+shapesIndex], xpos, ypos, 2, tempFlags | 0x800, drawLayer);
+ }
+ }
+ }
+ }
+
+ xpos = curObject->x1;
+ ypos = curObject->y1;
+
+ curObject->flags |= 0x800;
+ if (curObject->index == 0) {
+ flagUnk3 = 0x100;
+
+ if (flagUnk1 & 0x200 || flagUnk2 & 0x4000) {
+ flagUnk3 = 0;
+ }
+
+ if (_vm->_brandonStatusBit & 2) {
+ curObject->flags &= 0xFFFFFFFE;
+ }
+
+ if (!_vm->_scaleMode) {
+ if (flagUnk3 & 0x100) {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x100, (uint8*)_vm->_brandonPoisonFlagsGFX, int(1), drawLayer);
+ } else if (flagUnk3 & 0x4000) {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x4000, int(_vm->_brandonInvFlag), drawLayer);
+ } else {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1, drawLayer);
+ }
+ } else {
+ if (flagUnk3 & 0x100) {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x104, (uint8*)_vm->_brandonPoisonFlagsGFX, int(1), drawLayer, _brandonScaleX, _brandonScaleY);
+ } else if (flagUnk3 & 0x4000) {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x4004, int(_vm->_brandonInvFlag), drawLayer, _brandonScaleX, _brandonScaleY);
+ } else {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x4, drawLayer, _brandonScaleX, _brandonScaleY);
+ }
+ }
+ } else {
+ if (curObject->index >= 16 && curObject->index <= 27) {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | 4, drawLayer, (int)_vm->_scaleTable[curObject->drawY], (int)_vm->_scaleTable[curObject->drawY]);
+ } else {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags, drawLayer);
+ }
+ }
+ }
+ curObject = curObject->nextAnimObject;
+ }
+}
+
+void ScreenAnimator::copyChangedObjectsForward(int refreshFlag) {
+ debug(9, "ScreenAnimator::copyChangedObjectsForward(%d)", refreshFlag);
+ AnimObject *curObject = _objectQueue;
+
+ while (curObject) {
+ if (curObject->active) {
+ if (curObject->refreshFlag || refreshFlag) {
+ int xpos = 0, ypos = 0, width = 0, height = 0;
+ xpos = curObject->x1 - (curObject->width2+1);
+ ypos = curObject->y1 - curObject->height2;
+ width = (curObject->width + ((curObject->width2)>>3)+1)<<3;
+ height = curObject->height + curObject->height2*2;
+
+ if (xpos < 8) {
+ xpos = 8;
+ } else if (xpos + width > 312) {
+ width = 312 - xpos;
+ }
+
+ if (ypos < 8) {
+ ypos = 8;
+ } else if (ypos + height > 136) {
+ height = 136 - ypos;
+ }
+
+ _screen->copyRegion(xpos, ypos, xpos, ypos, width, height, 2, 0, Screen::CR_CLIPPED);
+ curObject->refreshFlag = 0;
+ _updateScreen = true;
+ }
+ }
+ curObject = curObject->nextAnimObject;
+ }
+ if (_updateScreen) {
+ _screen->updateScreen();
+ _updateScreen = false;
+ }
+}
+
+void ScreenAnimator::updateAllObjectShapes() {
+ debug(9, "ScreenAnimator::updateAllObjectShapes()");
+ restoreAllObjectBackgrounds();
+ preserveAnyChangedBackgrounds();
+ prepDrawAllObjects();
+ copyChangedObjectsForward(0);
+}
+
+void ScreenAnimator::animRemoveGameItem(int index) {
+ debug(9, "ScreenAnimator::animRemoveGameItem(%d)", index);
+ restoreAllObjectBackgrounds();
+
+ AnimObject *animObj = &_items[index];
+ animObj->sceneAnimPtr = 0;
+ animObj->animFrameNumber = -1;
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+ updateAllObjectShapes();
+ animObj->active = 0;
+
+ objectRemoveQueue(_objectQueue, animObj);
+}
+
+void ScreenAnimator::animAddGameItem(int index, uint16 sceneId) {
+ debug(9, "ScreenAnimator::animRemoveGameItem(%d, %d)", index, sceneId);
+ restoreAllObjectBackgrounds();
+ assert(sceneId < _vm->_roomTableSize);
+ Room *currentRoom = &_vm->_roomTable[sceneId];
+ AnimObject *animObj = &_items[index];
+ animObj->active = 1;
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+ animObj->drawY = currentRoom->itemsYPos[index];
+ animObj->sceneAnimPtr = _vm->_shapes[220+currentRoom->itemsTable[index]];
+ animObj->animFrameNumber = -1;
+ animObj->x1 = currentRoom->itemsXPos[index];
+ animObj->y1 = currentRoom->itemsYPos[index];
+ animObj->x1 -= fetchAnimWidth(animObj->sceneAnimPtr, _vm->_scaleTable[animObj->drawY]) >> 1;
+ animObj->y1 -= fetchAnimHeight(animObj->sceneAnimPtr, _vm->_scaleTable[animObj->drawY]);
+ animObj->x2 = animObj->x1;
+ animObj->y2 = animObj->y1;
+ animObj->width2 = 0;
+ animObj->height2 = 0;
+ _objectQueue = objectQueue(_objectQueue, animObj);
+ preserveAnyChangedBackgrounds();
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+}
+
+void ScreenAnimator::animAddNPC(int character) {
+ debug(9, "ScreenAnimator::animAddNPC(%d)", character);
+ restoreAllObjectBackgrounds();
+ AnimObject *animObj = &_actors[character];
+ const Character *ch = &_vm->_characterList[character];
+
+ animObj->active = 1;
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+ animObj->drawY = ch->y1;
+ animObj->sceneAnimPtr = _vm->_shapes[4+ch->currentAnimFrame];
+ animObj->x1 = animObj->x2 = ch->x1 + _vm->_defaultShapeTable[ch->currentAnimFrame-7].xOffset;
+ animObj->y1 = animObj->y2 = ch->y1 + _vm->_defaultShapeTable[ch->currentAnimFrame-7].yOffset;
+ if (ch->facing >= 1 && ch->facing <= 3) {
+ animObj->flags |= 1;
+ } else if (ch->facing >= 5 && ch->facing <= 7) {
+ animObj->flags &= 0xFFFFFFFE;
+ }
+ _objectQueue = objectQueue(_objectQueue, animObj);
+ preserveAnyChangedBackgrounds();
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+}
+
+AnimObject *ScreenAnimator::objectRemoveQueue(AnimObject *queue, AnimObject *rem) {
+ debug(9, "ScreenAnimator::objectRemoveQueue(0x%X, 0x%X)", queue, rem);
+ AnimObject *cur = queue;
+ AnimObject *prev = queue;
+
+ while (cur != rem && cur) {
+ AnimObject *temp = cur->nextAnimObject;
+ if (!temp)
+ break;
+ prev = cur;
+ cur = temp;
+ }
+
+ if (cur == queue) {
+ if (!cur)
+ return 0;
+ return cur->nextAnimObject;
+ }
+
+ if (!cur->nextAnimObject) {
+ if (cur == rem) {
+ if (!prev) {
+ return 0;
+ } else {
+ prev->nextAnimObject = 0;
+ }
+ }
+ } else {
+ if (cur == rem) {
+ prev->nextAnimObject = rem->nextAnimObject;
+ }
+ }
+
+ return queue;
+}
+
+AnimObject *ScreenAnimator::objectAddHead(AnimObject *queue, AnimObject *head) {
+ debug(9, "ScreenAnimator::objectAddHead(0x%X, 0x%X)", queue, head);
+ head->nextAnimObject = queue;
+ return head;
+}
+
+AnimObject *ScreenAnimator::objectQueue(AnimObject *queue, AnimObject *add) {
+ debug(9, "ScreenAnimator::objectQueue(0x%X, 0x%X)", queue, add);
+ if (add->drawY <= queue->drawY || !queue) {
+ add->nextAnimObject = queue;
+ return add;
+ }
+ AnimObject *cur = queue;
+ AnimObject *prev = queue;
+ while (add->drawY > cur->drawY) {
+ AnimObject *temp = cur->nextAnimObject;
+ if (!temp)
+ break;
+ prev = cur;
+ cur = temp;
+ }
+
+ if (add->drawY <= cur->drawY) {
+ prev->nextAnimObject = add;
+ add->nextAnimObject = cur;
+ } else {
+ cur->nextAnimObject = add;
+ add->nextAnimObject = 0;
+ }
+ return queue;
+}
+
+void ScreenAnimator::addObjectToQueue(AnimObject *object) {
+ debug(9, "ScreenAnimator::addObjectToQueue(0x%X)", object);
+ if (!_objectQueue) {
+ _objectQueue = objectAddHead(0, object);
+ } else {
+ _objectQueue = objectQueue(_objectQueue, object);
+ }
+}
+
+void ScreenAnimator::refreshObject(AnimObject *object) {
+ debug(9, "ScreenAnimator::refreshObject(0x%X)", object);
+ _objectQueue = objectRemoveQueue(_objectQueue, object);
+ if (_objectQueue) {
+ _objectQueue = objectQueue(_objectQueue, object);
+ } else {
+ _objectQueue = objectAddHead(0, object);
+ }
+}
+
+void ScreenAnimator::makeBrandonFaceMouse() {
+ debug(9, "ScreenAnimator::makeBrandonFaceMouse()");
+ if (_vm->mouseX() >= _vm->_currentCharacter->x1) {
+ _vm->_currentCharacter->facing = 3;
+ } else {
+ _vm->_currentCharacter->facing = 5;
+ }
+ animRefreshNPC(0);
+ updateAllObjectShapes();
+}
+
+int16 ScreenAnimator::fetchAnimWidth(const uint8 *shape, int16 mult) {
+ debug(9, "ScreenAnimator::fetchAnimWidth(0x%X, %d)", shape, mult);
+ if (_vm->features() & GF_TALKIE)
+ shape += 2;
+ return (((int16)READ_LE_UINT16((shape+3))) * mult) >> 8;
+}
+
+int16 ScreenAnimator::fetchAnimHeight(const uint8 *shape, int16 mult) {
+ debug(9, "ScreenAnimator::fetchAnimHeight(0x%X, %d)", shape, mult);
+ if (_vm->features() & GF_TALKIE)
+ shape += 2;
+ return (int16)(((int8)*(shape+2)) * mult) >> 8;
+}
+
+void ScreenAnimator::setBrandonAnimSeqSize(int width, int height) {
+ debug(9, "ScreenAnimator::setBrandonAnimSeqSize(%d, %d)", width, height);
+ restoreAllObjectBackgrounds();
+ _brandonAnimSeqSizeWidth = _actors[0].width;
+ _brandonAnimSeqSizeHeight = _actors[0].height;
+ _actors[0].width = width + 1;
+ _actors[0].height = height;
+ preserveAllBackgrounds();
+}
+
+void ScreenAnimator::resetBrandonAnimSeqSize() {
+ debug(9, "ScreenAnimator::resetBrandonAnimSeqSize()");
+ restoreAllObjectBackgrounds();
+ _actors[0].width = _brandonAnimSeqSizeWidth;
+ _actors[0].height = _brandonAnimSeqSizeHeight;
+ preserveAllBackgrounds();
+}
+
+void ScreenAnimator::animRefreshNPC(int character) {
+ debug(9, "ScreenAnimator::animRefreshNPC(%d)", character);
+ AnimObject *animObj = &_actors[character];
+ Character *ch = &_vm->characterList()[character];
+
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+ int facing = ch->facing;
+ if (facing >= 1 && facing <= 3) {
+ animObj->flags |= 1;
+ } else if (facing >= 5 && facing <= 7) {
+ animObj->flags &= 0xFFFFFFFE;
+ }
+
+ animObj->drawY = ch->y1;
+ animObj->sceneAnimPtr = _vm->shapes()[4+ch->currentAnimFrame];
+ animObj->animFrameNumber = ch->currentAnimFrame;
+ if (character == 0) {
+ if (_vm->brandonStatus() & 10) {
+ animObj->animFrameNumber = 88;
+ ch->currentAnimFrame = 88;
+ }
+ if (_vm->brandonStatus() & 2) {
+ animObj->animFrameNumber = _brandonDrawFrame;
+ ch->currentAnimFrame = _brandonDrawFrame;
+ animObj->sceneAnimPtr = _vm->shapes()[4+_brandonDrawFrame];
+ if (_vm->_brandonStatusBit0x02Flag) {
+ ++_brandonDrawFrame;
+ if (_brandonDrawFrame >= 122)
+ _brandonDrawFrame = 113;
+ _vm->_brandonStatusBit0x02Flag = 0;
+ }
+ }
+ }
+
+ int xOffset = _vm->_defaultShapeTable[ch->currentAnimFrame-7].xOffset;
+ int yOffset = _vm->_defaultShapeTable[ch->currentAnimFrame-7].yOffset;
+
+ if (_vm->_scaleMode) {
+ animObj->x1 = ch->x1;
+ animObj->y1 = ch->y1;
+
+ _brandonScaleX = _vm->_scaleTable[ch->y1];
+ _brandonScaleY = _vm->_scaleTable[ch->y1];
+
+ animObj->x1 += (_brandonScaleX * xOffset) >> 8;
+ animObj->y1 += (_brandonScaleY * yOffset) >> 8;
+ } else {
+ animObj->x1 = ch->x1 + xOffset;
+ animObj->y1 = ch->y1 + yOffset;
+ }
+ animObj->width2 = 4;
+ animObj->height2 = 3;
+
+ refreshObject(animObj);
+}
+
+void ScreenAnimator::setCharacterDefaultFrame(int character) {
+ debug(9, "ScreenAnimator::setCharacterDefaultFrame()");
+ static uint16 initFrameTable[] = {
+ 7, 41, 77, 0, 0
+ };
+ assert(character < ARRAYSIZE(initFrameTable));
+ Character *edit = &_vm->characterList()[character];
+ edit->sceneId = 0xFFFF;
+ edit->facing = 0;
+ edit->currentAnimFrame = initFrameTable[character];
+ // edit->unk6 = 1;
+}
+
+void ScreenAnimator::setCharactersHeight() {
+ debug(9, "ScreenAnimator::setCharactersHeight()");
+ static int8 initHeightTable[] = {
+ 48, 40, 48, 47, 56,
+ 44, 42, 47, 38, 35,
+ 40
+ };
+ for (int i = 0; i < 11; ++i) {
+ _vm->characterList()[i].height = initHeightTable[i];
+ }
+}
+
+} // end of namespace Kyra
diff --git a/engines/kyra/animator.h b/engines/kyra/animator.h
new file mode 100644
index 0000000000..ecc80d1819
--- /dev/null
+++ b/engines/kyra/animator.h
@@ -0,0 +1,126 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-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$
+ *
+ */
+
+#ifndef KYRAANIMATOR_H
+#define KYRAANIMATOR_H
+
+namespace Kyra {
+class KyraEngine;
+class Screen;
+
+struct AnimObject {
+ uint8 index;
+ uint32 active;
+ uint32 refreshFlag;
+ uint32 bkgdChangeFlag;
+ bool disable;
+ uint32 flags;
+ int16 drawY;
+ uint8 *sceneAnimPtr;
+ int16 animFrameNumber;
+ uint8 *background;
+ uint16 rectSize;
+ int16 x1, y1;
+ int16 x2, y2;
+ uint16 width;
+ uint16 height;
+ uint16 width2;
+ uint16 height2;
+ AnimObject *nextAnimObject;
+};
+
+class ScreenAnimator {
+public:
+ ScreenAnimator(KyraEngine *vm, OSystem *system);
+ virtual ~ScreenAnimator();
+
+ operator bool() const { return _initOk; }
+
+ void init(int actors, int items, int sprites);
+ void close();
+
+ AnimObject *objects() { return _screenObjects; }
+ AnimObject *actors() { return _actors; }
+ AnimObject *items() { return _items; }
+ AnimObject *sprites() { return _sprites; }
+
+ void initAnimStateList();
+ void preserveAllBackgrounds();
+ void flagAllObjectsForBkgdChange();
+ void flagAllObjectsForRefresh();
+ void restoreAllObjectBackgrounds();
+ void preserveAnyChangedBackgrounds();
+ virtual void prepDrawAllObjects();
+ void copyChangedObjectsForward(int refreshFlag);
+
+ void updateAllObjectShapes();
+ void animRemoveGameItem(int index);
+ void animAddGameItem(int index, uint16 sceneId);
+ void animAddNPC(int character);
+ void animRefreshNPC(int character);
+
+ void clearQueue() { _objectQueue = 0; }
+ void addObjectToQueue(AnimObject *object);
+ void refreshObject(AnimObject *object);
+
+ void makeBrandonFaceMouse();
+ void setBrandonAnimSeqSize(int width, int height);
+ void resetBrandonAnimSeqSize();
+ void setCharacterDefaultFrame(int character);
+ void setCharactersHeight();
+
+ int16 fetchAnimWidth(const uint8 *shape, int16 mult);
+ int16 fetchAnimHeight(const uint8 *shape, int16 mult);
+
+ int _noDrawShapesFlag;
+ bool _updateScreen;
+ uint16 _brandonDrawFrame;
+ int _brandonScaleX;
+ int _brandonScaleY;
+
+protected:
+ KyraEngine *_vm;
+ Screen *_screen;
+ OSystem *_system;
+ bool _initOk;
+
+ AnimObject *_screenObjects;
+
+ AnimObject *_actors;
+ AnimObject *_items;
+ AnimObject *_sprites;
+
+ AnimObject *objectRemoveQueue(AnimObject *queue, AnimObject *rem);
+ AnimObject *objectAddHead(AnimObject *queue, AnimObject *head);
+ AnimObject *objectQueue(AnimObject *queue, AnimObject *add);
+
+ void preserveOrRestoreBackground(AnimObject *obj, bool restore);
+
+ AnimObject *_objectQueue;
+
+ int _brandonAnimSeqSizeWidth;
+ int _brandonAnimSeqSizeHeight;
+
+};
+} // end of namespace Kyra
+
+#endif
diff --git a/engines/kyra/debugger.cpp b/engines/kyra/debugger.cpp
new file mode 100644
index 0000000000..b78056e3ad
--- /dev/null
+++ b/engines/kyra/debugger.cpp
@@ -0,0 +1,206 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-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 "common/config-manager.h"
+#include "common/debugger.cpp"
+#include "kyra/debugger.h"
+#include "kyra/kyra.h"
+#include "kyra/screen.h"
+
+namespace Kyra {
+
+Debugger::Debugger(KyraEngine *vm)
+ : Common::Debugger<Debugger>() {
+ _vm = vm;
+
+ DCmd_Register("continue", &Debugger::cmd_exit);
+ DCmd_Register("exit", &Debugger::cmd_exit);
+ DCmd_Register("help", &Debugger::cmd_help);
+ DCmd_Register("quit", &Debugger::cmd_exit);
+ DCmd_Register("enter", &Debugger::cmd_enterRoom);
+ DCmd_Register("rooms", &Debugger::cmd_listRooms);
+ DCmd_Register("flags", &Debugger::cmd_listFlags);
+ DCmd_Register("toggleflag", &Debugger::cmd_toggleFlag);
+ DCmd_Register("queryflag", &Debugger::cmd_queryFlag);
+ DCmd_Register("timers", &Debugger::cmd_listTimers);
+ DCmd_Register("settimercountdown", &Debugger::cmd_setTimerCountdown);
+ DCmd_Register("give", &Debugger::cmd_giveItem);
+}
+
+void Debugger::preEnter() {
+ //_vm->midi.pause(1);
+}
+
+void Debugger::postEnter() {
+ //_vm->midi.pause(0);
+}
+
+bool Debugger::cmd_enterRoom(int argc, const char **argv) {
+ uint direction = 0;
+ if (argc > 1) {
+ int room = atoi(argv[1]);
+
+ // game will crash if entering a non-existent room
+ if (room >= _vm->_roomTableSize) {
+ DebugPrintf("room number must be any value between (including) 0 and %d\n", _vm->_roomTableSize-1);
+ return true;
+ }
+
+ if (argc > 2) {
+ direction = atoi(argv[2]);
+ } else {
+ if (_vm->_roomTable[room].northExit != 0xff)
+ direction = 3;
+ else if (_vm->_roomTable[room].eastExit != 0xff)
+ direction = 4;
+ else if (_vm->_roomTable[room].southExit != 0xff)
+ direction = 1;
+ else if (_vm->_roomTable[room].westExit != 0xff)
+ direction = 2;
+ }
+
+ // Dirty way of hiding the debug console while the room entry scripts are running,
+ // otherwise the graphics didn't update.
+ _vm->_system->hideOverlay();
+ _vm->_currentCharacter->facing = direction;
+
+ _vm->enterNewScene(room, _vm->_currentCharacter->facing, 0, 0, 1);
+ _vm->_system->showOverlay();
+ _vm->_screen->_mouseLockCount = 0;
+
+ _detach_now = true;
+ return false;
+ }
+
+ DebugPrintf("Syntax: room <roomnum> <direction>\n");
+ return true;
+}
+
+bool Debugger::cmd_exit(int argc, const char **argv) {
+ _detach_now = true;
+ return false;
+}
+
+bool Debugger::cmd_help(int argc, const char **argv) {
+ // console normally has 39 line width
+ // wrap around nicely
+ int width = 0, size, i;
+
+ DebugPrintf("Commands are:\n");
+ for (i = 0 ; i < _dcmd_count ; i++) {
+ size = strlen(_dcmds[i].name) + 1;
+
+ if ((width + size) >= 39) {
+ DebugPrintf("\n");
+ width = size;
+ } else
+ width += size;
+
+ DebugPrintf("%s ", _dcmds[i].name);
+ }
+ DebugPrintf("\n");
+ return true;
+}
+
+bool Debugger::cmd_listRooms(int argc, const char **argv) {
+ for (int i = 0; i < _vm->_roomTableSize; i++) {
+ DebugPrintf("%-3i: %-10s", i, _vm->_roomFilenameTable[_vm->_roomTable[i].nameIndex]);
+ if (!(i % 8))
+ DebugPrintf("\n");
+ }
+ DebugPrintf("\n");
+ DebugPrintf("Current room: %i\n", _vm->_currentRoom);
+ return true;
+}
+
+bool Debugger::cmd_listFlags(int argc, const char **argv) {
+ for (int i = 0; i < (int)sizeof(_vm->_flagsTable)*8; i++) {
+ DebugPrintf("(%-3i): %-5i", i, _vm->queryGameFlag(i));
+ if (!(i % 10))
+ DebugPrintf("\n");
+ }
+ DebugPrintf("\n");
+ return true;
+}
+
+bool Debugger::cmd_toggleFlag(int argc, const char **argv) {
+ if (argc > 1) {
+ uint flag = atoi(argv[1]);
+ if (_vm->queryGameFlag(flag))
+ _vm->resetGameFlag(flag);
+ else
+ _vm->setGameFlag(flag);
+ DebugPrintf("Flag %i is now %i\n", flag, _vm->queryGameFlag(flag));
+ } else
+ DebugPrintf("Syntax: toggleflag <flag>\n");
+
+ return true;
+}
+
+bool Debugger::cmd_queryFlag(int argc, const char **argv) {
+ if (argc > 1) {
+ uint flag = atoi(argv[1]);
+ DebugPrintf("Flag %i is %i\n", flag, _vm->queryGameFlag(flag));
+ } else
+ DebugPrintf("Syntax: queryflag <flag>\n");
+
+ return true;
+}
+
+bool Debugger::cmd_listTimers(int argc, const char **argv) {
+ for (int i = 0; i < ARRAYSIZE(_vm->_timers); i++)
+ DebugPrintf("Timer %-2i: Active: %-3s Countdown: %-6i\n", i, _vm->_timers[i].active ? "Yes" : "No", _vm->_timers[i].countdown);
+
+ return true;
+}
+
+bool Debugger::cmd_setTimerCountdown(int argc, const char **argv) {
+ if (argc > 2) {
+ uint timer = atoi(argv[1]);
+ uint countdown = atoi(argv[2]);
+ _vm->setTimerCountdown(timer, countdown);
+ DebugPrintf("Timer %i now has countdown %i\n", timer, _vm->_timers[timer].countdown);
+ } else
+ DebugPrintf("Syntax: settimercountdown <timer> <countdown>\n");
+
+ return true;
+}
+
+bool Debugger::cmd_giveItem(int argc, const char **argv) {
+ if (argc == 2) {
+ int item = atoi(argv[1]);
+
+ // Kyrandia 1 has only 108 items (-1 to 106), otherwise it will crash
+ if (item < -1 || item > 106) {
+ DebugPrintf("itemid must be any value between (including) -1 and 106\n");
+ return true;
+ }
+
+ _vm->setMouseItem(item);
+ _vm->_itemInHand = item;
+ } else
+ DebugPrintf("Syntax: give <itemid>\n");
+
+ return true;
+}
+} // End of namespace Kyra
diff --git a/engines/kyra/debugger.h b/engines/kyra/debugger.h
new file mode 100644
index 0000000000..69766f3fc0
--- /dev/null
+++ b/engines/kyra/debugger.h
@@ -0,0 +1,57 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-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$
+ *
+ */
+
+#ifndef KYRA_DEBUGGER_H
+#define KYRA_DEBUGGER_H
+
+#include "common/debugger.h"
+
+namespace Kyra {
+
+class KyraEngine;
+
+class Debugger : public Common::Debugger<Debugger> {
+public:
+ Debugger(KyraEngine *vm);
+ virtual ~Debugger() {} // we need this for __SYMBIAN32__ archaic gcc/UIQ
+
+protected:
+ KyraEngine *_vm;
+
+ virtual void preEnter();
+ virtual void postEnter();
+
+ bool cmd_exit(int argc, const char **argv);
+ bool cmd_help(int argc, const char **argv);
+ bool cmd_enterRoom(int argc, const char **argv);
+ bool cmd_listRooms(int argc, const char **argv);
+ bool cmd_listFlags(int argc, const char **argv);
+ bool cmd_toggleFlag(int argc, const char **argv);
+ bool cmd_queryFlag(int argc, const char **argv);
+ bool cmd_listTimers(int argc, const char **argv);
+ bool cmd_setTimerCountdown(int argc, const char **argv);
+ bool cmd_giveItem(int argc, const char **argv);
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/gui.cpp b/engines/kyra/gui.cpp
new file mode 100644
index 0000000000..a1deccdfcd
--- /dev/null
+++ b/engines/kyra/gui.cpp
@@ -0,0 +1,1020 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-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 "kyra/kyra.h"
+#include "kyra/screen.h"
+#include "kyra/script.h"
+#include "kyra/text.h"
+#include "kyra/animator.h"
+
+#include "common/savefile.h"
+#include "common/system.h"
+
+namespace Kyra {
+
+void KyraEngine::initMainButtonList() {
+ _buttonList = &_buttonData[0];
+ for (int i = 0; _buttonDataListPtr[i]; ++i) {
+ _buttonList = initButton(_buttonList, _buttonDataListPtr[i]);
+ }
+}
+
+Button *KyraEngine::initButton(Button *list, Button *newButton) {
+ if (!newButton)
+ return list;
+ if (!list)
+ return newButton;
+ Button *cur = list;
+ while (true) {
+ if (!cur->nextButton) {
+ break;
+ }
+ cur = cur->nextButton;
+ }
+ cur->nextButton = newButton;
+ return list;
+}
+
+int KyraEngine::buttonInventoryCallback(Button *caller) {
+ int itemOffset = caller->specialValue - 2;
+ uint8 inventoryItem = _currentCharacter->inventoryItems[itemOffset];
+ if (_itemInHand == -1) {
+ if (inventoryItem == 0xFF) {
+ snd_playSoundEffect(0x36);
+ return 0;
+ } else {
+ _screen->hideMouse();
+ _screen->fillRect(_itemPosX[itemOffset], _itemPosY[itemOffset], _itemPosX[itemOffset] + 15, _itemPosY[itemOffset] + 15, 12);
+ snd_playSoundEffect(0x35);
+ setMouseItem(inventoryItem);
+ updateSentenceCommand(_itemList[inventoryItem], _takenList[0], 179);
+ _itemInHand = inventoryItem;
+ _screen->showMouse();
+ _currentCharacter->inventoryItems[itemOffset] = 0xFF;
+ }
+ } else {
+ if (inventoryItem != 0xFF) {
+ snd_playSoundEffect(0x35);
+ _screen->hideMouse();
+ _screen->fillRect(_itemPosX[itemOffset], _itemPosY[itemOffset], _itemPosX[itemOffset] + 15, _itemPosY[itemOffset] + 15, 12);
+ _screen->drawShape(0, _shapes[220+_itemInHand], _itemPosX[itemOffset], _itemPosY[itemOffset], 0, 0);
+ setMouseItem(inventoryItem);
+ updateSentenceCommand(_itemList[inventoryItem], _takenList[1], 179);
+ _screen->showMouse();
+ _currentCharacter->inventoryItems[itemOffset] = _itemInHand;
+ _itemInHand = inventoryItem;
+ } else {
+ snd_playSoundEffect(0x32);
+ _screen->hideMouse();
+ _screen->drawShape(0, _shapes[220+_itemInHand], _itemPosX[itemOffset], _itemPosY[itemOffset], 0, 0);
+ _screen->setMouseCursor(1, 1, _shapes[4]);
+ updateSentenceCommand(_itemList[_itemInHand], _placedList[0], 179);
+ _screen->showMouse();
+ _currentCharacter->inventoryItems[itemOffset] = _itemInHand;
+ _itemInHand = -1;
+ }
+ }
+ _screen->updateScreen();
+ // XXX clearKyrandiaButtonIO
+ return 0;
+}
+
+int KyraEngine::buttonAmuletCallback(Button *caller) {
+ if (!(_deathHandler & 8))
+ return 1;
+ int jewel = caller->specialValue - 0x14;
+ if (_currentCharacter->sceneId == 210) {
+ if (_beadStateVar == 4 || _beadStateVar == 6)
+ return 1;
+ }
+ if (!queryGameFlag(0x2D))
+ return 1;
+ if (_itemInHand != -1) {
+ assert(_putDownFirst);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2000);
+ }
+ characterSays(_putDownFirst[0], 0, -2);
+ return 1;
+ }
+ if (queryGameFlag(0xF1)) {
+ assert(_waitForAmulet);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2001);
+ }
+ characterSays(_waitForAmulet[0], 0, -2);
+ return 1;
+ }
+ if (!queryGameFlag(0x55+jewel)) {
+ assert(_blackJewel);
+ _animator->makeBrandonFaceMouse();
+ drawJewelPress(jewel, 1);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2002);
+ }
+ characterSays(_blackJewel[0], 0, -2);
+ return 1;
+ }
+ drawJewelPress(jewel, 0);
+ drawJewelsFadeOutStart();
+ drawJewelsFadeOutEnd(jewel);
+
+ _scriptInterpreter->initScript(_scriptClick, _scriptClickData);
+ _scriptClick->variables[3] = 0;
+ _scriptClick->variables[6] = jewel;
+ _scriptInterpreter->startScript(_scriptClick, 4);
+
+ while (_scriptInterpreter->validScript(_scriptClick)) {
+ _scriptInterpreter->runScript(_scriptClick);
+ }
+
+ if (_scriptClick->variables[3])
+ return 1;
+
+ _unkAmuletVar = 1;
+ switch (jewel-1) {
+ case 0:
+ if (_brandonStatusBit & 1) {
+ seq_brandonHealing2();
+ } else if (_brandonStatusBit == 0) {
+ seq_brandonHealing();
+ assert(_healingTip);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2003);
+ }
+ characterSays(_healingTip[0], 0, -2);
+ }
+ break;
+
+ case 1:
+ seq_makeBrandonInv();
+ break;
+
+ case 2:
+ if (_brandonStatusBit & 1) {
+ assert(_wispJewelStrings);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2004);
+ }
+ characterSays(_wispJewelStrings[0], 0, -2);
+ } else {
+ if (_brandonStatusBit & 2) {
+ // XXX
+ seq_makeBrandonNormal2();
+ // XXX
+ } else {
+ // do not check for item in hand again as in the original since some strings are missing
+ // in the cd version
+ if (_currentCharacter->sceneId >= 109 && _currentCharacter->sceneId <= 198) {
+ snd_playWanderScoreViaMap(1, 0);
+ seq_makeBrandonWisp();
+ snd_playWanderScoreViaMap(17, 0);
+ } else {
+ seq_makeBrandonWisp();
+ }
+ setGameFlag(0x9E);
+ }
+ }
+ break;
+
+ case 3:
+ seq_dispelMagicAnimation();
+ assert(_magicJewelString);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2007);
+ }
+ characterSays(_magicJewelString[0], 0, -2);
+ break;
+
+ default:
+ break;
+ }
+ _unkAmuletVar = 0;
+ // XXX clearKyrandiaButtonIO (!used before every return in this function!)
+ return 1;
+}
+
+void KyraEngine::processButtonList(Button *list) {
+ while (list) {
+ if (list->flags & 8) {
+ list = list->nextButton;
+ continue;
+ }
+
+ int x = list->x;
+ int y = list->y;
+ assert(list->dimTableIndex < _screen->_screenDimTableCount);
+ if (x < 0) {
+ x += _screen->_screenDimTable[list->dimTableIndex].w << 3;
+ }
+ x += _screen->_screenDimTable[list->dimTableIndex].sx << 3;
+
+ if (y < 0) {
+ y += _screen->_screenDimTable[list->dimTableIndex].h;
+ }
+ y += _screen->_screenDimTable[list->dimTableIndex].sy;
+
+ if (_mouseX >= x && _mouseY >= y && x + list->width >= _mouseX && y + list->height >= _mouseY) {
+ int processMouseClick = 0;
+ if (list->flags & 0x400) {
+ if (_mousePressFlag) {
+ if (!(list->flags2 & 1)) {
+ list->flags2 |= 1;
+ processButton(list);
+ }
+ } else {
+ if (list->flags2 & 1) {
+ list->flags2 &= 0xFFFE;
+ processButton(list);
+ processMouseClick = 1;
+ }
+ }
+ } else if (_mousePressFlag) {
+ processMouseClick = 1;
+ }
+
+ if (processMouseClick) {
+ if (list->buttonCallback) {
+ if ((this->*(list->buttonCallback))(list)) {
+ break;
+ }
+ }
+ }
+ } else {
+ if (list->flags2 & 1) {
+ list->flags2 &= 0xFFFE;
+ processButton(list);
+ }
+ list = list->nextButton;
+ continue;
+ }
+
+ list = list->nextButton;
+ }
+}
+
+void KyraEngine::processButton(Button *button) {
+ if (!button)
+ return;
+
+ int processType = 0;
+ uint8 *shape = 0;
+ Button::ButtonCallback callback = 0;
+
+ int flags = (button->flags2 & 5);
+ if (flags == 1) {
+ processType = button->process2;
+ if (processType == 1) {
+ shape = button->process2PtrShape;
+ } else if (processType == 4) {
+ callback = button->process2PtrCallback;
+ }
+ } else if (flags == 4 || flags == 5) {
+ processType = button->process1;
+ if (processType == 1) {
+ shape = button->process1PtrShape;
+ } else if (processType == 4) {
+ callback = button->process1PtrCallback;
+ }
+ } else {
+ processType = button->process0;
+ if (processType == 1) {
+ shape = button->process0PtrShape;
+ } else if (processType == 4) {
+ callback = button->process0PtrCallback;
+ }
+ }
+
+ int x = button->x;
+ int y = button->y;
+ assert(button->dimTableIndex < _screen->_screenDimTableCount);
+ if (x < 0) {
+ x += _screen->_screenDimTable[button->dimTableIndex].w << 3;
+ }
+
+ if (y < 0) {
+ y += _screen->_screenDimTable[button->dimTableIndex].h;
+ }
+
+ if (processType == 1 && shape) {
+ _screen->drawShape(_screen->_curPage, shape, x, y, button->dimTableIndex, 0x10);
+ } else if (processType == 4 && callback) {
+ (this->*callback)(button);
+ }
+}
+
+void KyraEngine::processAllMenuButtons() {
+ if (!_menuButtonList)
+ return;
+
+ Button *cur = _menuButtonList;
+ while (true) {
+ if (!cur->nextButton) {
+ break;
+ }
+ processMenuButton(cur);
+ cur = cur->nextButton;
+ }
+ return;
+}
+
+void KyraEngine::processMenuButton(Button *button) {
+ if (!_displayMenu)
+ return;
+
+ //_screen->hideMouse();
+
+ if ( !button || (button->flags & 8))
+ return;
+
+ if (button->flags2 & 1)
+ button->flags2 &= 0xf7;
+ else
+ button->flags2 |= 8;
+
+ button->flags2 &= 0xfc;
+
+ if (button->flags2 & 4)
+ button->flags2 |= 0x10;
+ else
+ button->flags2 &= 0xef;
+
+ button->flags2 &= 0xfb;
+
+ processButton(button);
+
+ //_screen->showMouse();
+}
+
+int KyraEngine::drawBoxCallback(Button *button) {
+ if (!_displayMenu)
+ return 0;
+
+ _screen->hideMouse();
+ _screen->drawBox(button->x + 1, button->y + 1, button->x + button->width - 1, button->y + button->height - 1, 0xf8);
+ _screen->showMouse();
+
+ return 0;
+}
+
+int KyraEngine::drawShadedBoxCallback(Button *button) {
+
+ if (!_displayMenu)
+ return 0;
+
+ _screen->hideMouse();
+ _screen->drawShadedBox(button->x, button->y, button->x + button->width, button->y + button->height, 0xf9, 0xfa);
+ _screen->showMouse();
+
+ return 0;
+}
+
+int KyraEngine::buttonMenuCallback(Button *caller) {
+ _displayMenu = true;
+
+ // XXX setLabels
+ if (_currentCharacter->sceneId == 210) {
+ snd_playSoundEffect(0x36);
+ return 0;
+ }
+ // XXX
+
+ for (int i = 0; i < 6; i++) {
+ _menuButtonData[i].process0 = _menuButtonData[i].process1 = _menuButtonData[i].process2 = 4;
+ _menuButtonData[i].process0PtrCallback = &KyraEngine::drawShadedBoxCallback;
+ _menuButtonData[i].process1PtrCallback = &KyraEngine::drawBoxCallback;
+ _menuButtonData[i].process2PtrCallback = &KyraEngine::drawShadedBoxCallback;
+ }
+
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+ gui_fadePalette();
+
+ calcCoords(_menu[0]);
+ calcCoords(_menu[1]);
+ calcCoords(_menu[2]);
+ calcCoords(_menu[3]);
+
+ _menuRestoreScreen = true;
+
+ if (_menuDirectlyToLoad)
+ gui_loadGameMenu(0);
+ else {
+ initMenu(_menu[0]);
+ processAllMenuButtons();
+ }
+
+ while (_displayMenu) {
+ gui_processHighlights(_menu[0]);
+ processButtonList(_menuButtonList);
+ gui_getInput();
+ }
+
+ if (_menuRestoreScreen) {
+ gui_restorePalette();
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _animator->_updateScreen = true;
+ }
+ else
+ _screen->deletePageFromDisk(0);
+
+ return 0;
+}
+
+void KyraEngine::initMenu(Menu menu) {
+ int menu_x2 = menu.width + menu.x - 1;
+ int menu_y2 = menu.height + menu.y - 1;
+
+ _menuButtonList = 0;
+
+ _screen->hideMouse();
+ _screen->fillRect(menu.x + 2, menu.y + 2, menu_x2 - 2, menu_y2 - 2, menu.bgcolor);
+ _screen->drawShadedBox(menu.x, menu.y, menu_x2, menu_y2, menu.color1, menu.color2);
+
+ int textX;
+ int textY;
+
+ if (menu.field_10 != -1)
+ textX = menu.x;
+ else
+ textX = _text->getCenterStringX(menu.menuName, menu.x, menu_x2);
+
+ textY = menu.y + menu.field_12;
+
+ _text->printText(menu.menuName, textX - 1, textY + 1, 12, 248, 0);
+ _text->printText(menu.menuName, textX, textY, menu.textColor, 0, 0);
+
+ int x1, y1, x2, y2;
+ for (int i = 0; i < menu.nrOfItems; i++) {
+ if (!menu.item[i].enabled)
+ continue;
+
+ x1 = menu.x + menu.item[i].x;
+ y1 = menu.y + menu.item[i].y;
+
+ x2 = x1 + menu.item[i].width - 1;
+ y2 = y1 + menu.item[i].height - 1;
+
+ if (i < 6) {
+ _menuButtonData[i].nextButton = 0;
+ _menuButtonData[i].x = x1;
+ _menuButtonData[i].y = y1;
+ _menuButtonData[i].width = menu.item[i].width - 1;
+ _menuButtonData[i].height = menu.item[i].height - 1;
+ _menuButtonData[i].buttonCallback = menu.item[i].callback;
+ _menuButtonData[i].specialValue = menu.item[i].field_1b;
+ //_menuButtonData[i].field_6 = menu.item[i].field_25;
+ //_menuButtonData[i].field_8 = 0;
+
+ if (!_menuButtonList)
+ _menuButtonList = &_menuButtonData[i];
+ else
+ _menuButtonList = initButton(_menuButtonList, &_menuButtonData[i]);
+ }
+ _screen->fillRect(x1, y1, x2, y2, menu.item[i].bgcolor);
+ _screen->drawShadedBox(x1, y1, x2, y2, menu.item[i].color1, menu.item[i].color2);
+
+ if (menu.item[i].field_12 != -1)
+ textX = x1 + menu.item[i].field_12 + 3;
+ else
+ textX = _text->getCenterStringX(menu.item[i].itemString, x1, x2);
+
+ textY = y1 + 2;
+ _text->printText(menu.item[i].itemString, textX - 1, textY + 1, 12, 0, 0);
+
+ if (i == menu.highlightedItem)
+ _text->printText(menu.item[i].itemString, textX, textY, menu.item[i].highlightColor, 0, 0);
+ else
+ _text->printText(menu.item[i].itemString, textX, textY, menu.item[i].textColor, 0, 0);
+
+ if (menu.item[i].labelString) {
+ _text->printText(menu.item[i].labelString, menu.x + menu.item[i].field_21 - 1, menu.y + menu.item[i].field_23 + 1, 12, 0, 0);
+ _text->printText(menu.item[i].labelString, menu.x + menu.item[i].field_21, menu.y + menu.item[i].field_23, 253, 0, 0);
+ }
+ }
+
+ if (menu.scrollUpBtnX != -1) {
+ _scrollUpButton.x = menu.scrollUpBtnX + menu.x;
+ _scrollUpButton.y = menu.scrollUpBtnY + menu.y;
+ _scrollUpButton.buttonCallback = &KyraEngine::gui_scrollUp;
+ _scrollUpButton.nextButton = 0;
+ _menuButtonList = initButton(_menuButtonList, &_scrollUpButton);
+ processMenuButton(&_scrollUpButton);
+
+ _scrollDownButton.x = menu.scrollDownBtnX + menu.x;
+ _scrollDownButton.y = menu.scrollDownBtnY + menu.y;
+ _scrollDownButton.buttonCallback = &KyraEngine::gui_scrollDown;
+ _scrollDownButton.nextButton = 0;
+ _menuButtonList = initButton(_menuButtonList, &_scrollDownButton);
+ processMenuButton(&_scrollDownButton);
+ }
+
+ _screen->showMouse();
+ _screen->updateScreen();
+}
+
+void KyraEngine::calcCoords(Menu &menu) {
+ if (menu.x == -1)
+ menu.x = (320 - menu.width)/2;
+
+ if (menu.y == -1)
+ menu.y = (200 - menu.height)/2;
+
+ assert(menu.nrOfItems < 7);
+ for (int i = 0; i < menu.nrOfItems; i++)
+ if (menu.item[i].x == -1)
+ menu.item[i].x = (menu.width - menu.item[i].width)/2;
+}
+
+void KyraEngine::gui_getInput() {
+ OSystem::Event event;
+ uint32 now = _system->getMillis();
+
+ _mousePressFlag = false;
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_QUIT:
+ quitGame();
+ break;
+ case OSystem::EVENT_LBUTTONUP:
+ _mousePressFlag = true;
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ _system->updateScreen();
+ break;
+ case OSystem::EVENT_KEYDOWN:
+ _keyboardEvent.pending = true;
+ _keyboardEvent.repeat = now + 400;
+ _keyboardEvent.ascii = event.kbd.ascii;
+ break;
+ case OSystem::EVENT_KEYUP:
+ _keyboardEvent.repeat = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!_keyboardEvent.pending && _keyboardEvent.repeat && now >= _keyboardEvent.repeat) {
+ _keyboardEvent.pending = true;
+ _keyboardEvent.repeat = now + 100;
+ }
+ _system->delayMillis(3);
+}
+
+int KyraEngine::gui_resumeGame(Button *button) {
+ debug(9, "KyraEngine::gui_resumeGame()");
+ processMenuButton(button);
+ _displayMenu = false;
+
+ return 0;
+}
+
+const char *KyraEngine::getSavegameFilename(int num) {
+ static char saveLoadSlot[12];
+
+ sprintf(saveLoadSlot, "%s.%.3d", _targetName.c_str(), num);
+ return saveLoadSlot;
+}
+
+int KyraEngine::getNextSavegameSlot() {
+ Common::InSaveFile *in;
+
+ for (int i = 1; i < 1000; i++) {
+ if ((in = _saveFileMan->openForLoading(getSavegameFilename(i)))) {
+ delete in;
+ } else {
+ return i;
+ }
+ }
+ warning("Didn't save: Ran out of savegame filenames!");
+ return 0;
+}
+
+void KyraEngine::setupSavegames(Menu &menu, int num) {
+ Common::InSaveFile *in;
+ static char savenames[5][31];
+ uint8 startSlot;
+ assert(num <= 5);
+
+ if (_savegameOffset == 0) {
+ menu.item[0].itemString = _specialSavegameString;
+ menu.item[0].enabled = 1;
+ menu.item[0].field_1b = 0;
+ startSlot = 1;
+ } else
+ startSlot = 0;
+
+ for (int i = startSlot; i < num; i++) {
+ if ((in = _saveFileMan->openForLoading(getSavegameFilename(i + _savegameOffset)))) {
+ in->skip(8);
+ in->read(savenames[i], 31);
+ menu.item[i].itemString = savenames[i];
+ menu.item[i].enabled = 1;
+ menu.item[i].field_1b = i + _savegameOffset;
+ delete in;
+ } else {
+ menu.item[i].enabled = 0;
+ //menu.item[i].itemString = "";
+ //menu.item[i].field_1b = -1;
+ }
+ }
+}
+
+int KyraEngine::gui_saveGameMenu(Button *button) {
+ debug(9, "KyraEngine::gui_saveGameMenu()");
+ processMenuButton(button);
+ _menu[2].item[5].enabled = true;
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ _menu[2].menuName = "Select a position to save to:";
+ _specialSavegameString = "[ EMPTY SLOT ]";
+ for (int i = 0; i < 5; i++)
+ _menu[2].item[i].callback = &KyraEngine::gui_saveGame;
+
+ _savegameOffset = 0;
+ setupSavegames(_menu[2], 5);
+
+ initMenu(_menu[2]);
+ processAllMenuButtons();
+
+ _displaySubMenu = true;
+ _cancelSubMenu = false;
+
+ while (_displaySubMenu) {
+ gui_getInput();
+ gui_processHighlights(_menu[2]);
+ processButtonList(_menuButtonList);
+ }
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ if (_cancelSubMenu) {
+ initMenu(_menu[0]);
+ processAllMenuButtons();
+ } else {
+ _displayMenu = false;
+ }
+ return 0;
+}
+
+int KyraEngine::gui_loadGameMenu(Button *button) {
+ debug(9, "KyraEngine::gui_loadGameMenu()");
+ if (_menuDirectlyToLoad)
+ _menu[2].item[5].enabled = false;
+ else {
+ processMenuButton(button);
+ _menu[2].item[5].enabled = true;
+ }
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ _specialSavegameString = "[ START A NEW GAME ]";
+ _menu[2].menuName = "Which game would you like to reload?";
+ for (int i = 0; i < 5; i++)
+ _menu[2].item[i].callback = &KyraEngine::gui_loadGame;
+
+ _savegameOffset = 0;
+ setupSavegames(_menu[2], 5);
+
+ initMenu(_menu[2]);
+ processAllMenuButtons();
+
+ _displaySubMenu = true;
+ _cancelSubMenu = false;
+
+ while (_displaySubMenu) {
+ gui_getInput();
+ gui_processHighlights(_menu[2]);
+ processButtonList(_menuButtonList);
+ }
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ if (_cancelSubMenu) {
+ initMenu(_menu[0]);
+ processAllMenuButtons();
+ } else {
+ gui_restorePalette();
+ loadGame(getSavegameFilename(_gameToLoad));
+ _displayMenu = false;
+ _menuRestoreScreen = false;
+ }
+ return 0;
+}
+
+void KyraEngine::gui_redrawTextfield() {
+ _screen->fillRect(38, 91, 287, 102, 250);
+ _text->printText(_savegameName, 38, 92, 253, 0, 0);
+
+ _screen->_charWidth = -2;
+ int width = _screen->getTextWidth(_savegameName);
+ _screen->fillRect(39 + width, 93, 45 + width, 100, 254);
+ _screen->_charWidth = 0;
+
+ _screen->updateScreen();
+}
+
+void KyraEngine::gui_updateSavegameString() {
+ int length;
+
+ if (_keyboardEvent.pending && _keyboardEvent.ascii) {
+ length = strlen(_savegameName);
+
+ if (_keyboardEvent.ascii > 31 && _keyboardEvent.ascii < 127) {
+ if (length < 31) {
+ _savegameName[length] = _keyboardEvent.ascii;
+ _savegameName[length+1] = 0;
+ gui_redrawTextfield();
+ }
+ } else if (_keyboardEvent.ascii == 8 ||_keyboardEvent.ascii == 127) {
+ if (length > 0) {
+ _savegameName[length-1] = 0;
+ gui_redrawTextfield();
+ }
+ } else if (_keyboardEvent.ascii == 13) {
+ _displaySubMenu = false;
+ }
+ }
+
+ _keyboardEvent.pending = false;
+}
+
+int KyraEngine::gui_saveGame(Button *button) {
+ debug(9, "KyraEngine::gui_saveGame()");
+ processMenuButton(button);
+ _gameToLoad = button->specialValue;
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ initMenu(_menu[3]);
+ processAllMenuButtons();
+
+ _displaySubMenu = true;
+ _cancelSubMenu = false;
+
+ if (_savegameOffset == 0 && _gameToLoad == 0) {
+ _savegameName[0] = 0;
+ } else {
+ for (int i = 0; i < 5; i++) {
+ if (_menu[2].item[i].field_1b == _gameToLoad) {
+ strncpy(_savegameName, _menu[2].item[i].itemString, 31);
+ break;
+ }
+ }
+ }
+ gui_redrawTextfield();
+
+ _keyboardEvent.pending = 0;
+ _keyboardEvent.repeat = 0;
+ while (_displaySubMenu) {
+ gui_getInput();
+ gui_updateSavegameString();
+ gui_processHighlights(_menu[3]);
+ processButtonList(_menuButtonList);
+ }
+
+ if (_cancelSubMenu) {
+ _displaySubMenu = true;
+ _cancelSubMenu = false;
+ initMenu(_menu[2]);
+ processAllMenuButtons();
+ } else {
+ if (_savegameOffset == 0 && _gameToLoad == 0)
+ _gameToLoad = getNextSavegameSlot();
+ if (_gameToLoad > 0)
+ saveGame(getSavegameFilename(_gameToLoad), _savegameName);
+ }
+
+ return 0;
+}
+
+int KyraEngine::gui_savegameConfirm(Button *button) {
+ debug(9, "KyraEngine::gui_savegameConfirm()");
+ processMenuButton(button);
+ _displaySubMenu = false;
+
+ return 0;
+}
+
+int KyraEngine::gui_loadGame(Button *button) {
+ debug(9, "KyraEngine::gui_loadGame()");
+ processMenuButton(button);
+ _displaySubMenu = false;
+ _gameToLoad = button->specialValue;
+
+ return 0;
+}
+
+int KyraEngine::gui_cancelSubMenu(Button *button) {
+ debug(9, "KyraEngine::gui_cancelLoadGameMenu()");
+ processMenuButton(button);
+ _displaySubMenu = false;
+ _cancelSubMenu = true;
+
+ return 0;
+}
+
+int KyraEngine::gui_quitPlaying(Button *button) {
+ debug(9, "KyraEngine::gui_quitPlaying()");
+ processMenuButton(button);
+
+ if (gui_quitConfirm("Are you sure you want to quit playing?"))
+ quitGame();
+ else {
+ initMenu(_menu[0]);
+ processAllMenuButtons();
+ }
+
+ return 0;
+}
+
+bool KyraEngine::gui_quitConfirm(const char *str) {
+ debug(9, "KyraEngine::gui_quitConfirm()");
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ _menu[1].menuName = str;
+ initMenu(_menu[1]);
+
+ _displaySubMenu = true;
+ _cancelSubMenu = true;
+
+ while (_displaySubMenu) {
+ gui_getInput();
+ gui_processHighlights(_menu[1]);
+ processButtonList(_menuButtonList);
+ }
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ return !_cancelSubMenu;
+}
+
+int KyraEngine::gui_quitConfirmYes(Button *button) {
+ debug(9, "KyraEngine::gui_quitConfirmYes()");
+ processMenuButton(button);
+ _displaySubMenu = false;
+ _cancelSubMenu = false;
+
+ return 0;
+}
+
+int KyraEngine::gui_quitConfirmNo(Button *button) {
+ debug(9, "KyraEngine::gui_quitConfirmNo()");
+ processMenuButton(button);
+ _displaySubMenu = false;
+ _cancelSubMenu = true;
+
+ return 0;
+}
+
+int KyraEngine::gui_scrollUp(Button *button) {
+ debug(9, "KyraEngine::gui_scrollUp()");
+ processMenuButton(button);
+
+ if (_savegameOffset > 0) {
+ _savegameOffset--;
+ setupSavegames(_menu[2], 5);
+ initMenu(_menu[2]);
+ }
+ return 0;
+}
+
+int KyraEngine::gui_scrollDown(Button *button) {
+ debug(9, "KyraEngine::gui_scrollDown()");
+ processMenuButton(button);
+
+ _savegameOffset++;
+ setupSavegames(_menu[2], 5);
+ initMenu(_menu[2]);
+
+ return 0;
+}
+
+void KyraEngine::gui_processHighlights(Menu &menu) {
+ int x1, y1, x2, y2;
+
+ for (int i = 0; i < menu.nrOfItems; i++) {
+ if (!menu.item[i].enabled)
+ continue;
+
+ x1 = menu.x + menu.item[i].x;
+ y1 = menu.y + menu.item[i].y;
+
+ x2 = x1 + menu.item[i].width;
+ y2 = y1 + menu.item[i].height;
+
+ if (_mouseX > x1 && _mouseX < x2 &&
+ _mouseY > y1 && _mouseY < y2) {
+
+ if (menu.highlightedItem != i) {
+ if (menu.item[menu.highlightedItem].enabled )
+ gui_redrawText(menu);
+
+ menu.highlightedItem = i;
+ gui_redrawHighlight(menu);
+ _screen->updateScreen();
+ }
+ }
+ }
+}
+
+void KyraEngine::gui_redrawText(Menu menu) {
+ int textX;
+ int i = menu.highlightedItem;
+
+ int x1 = menu.x + menu.item[i].x;
+ int y1 = menu.y + menu.item[i].y;
+
+ int x2 = x1 + menu.item[i].width - 1;
+
+ if (menu.item[i].field_12 != -1)
+ textX = x1 + menu.item[i].field_12 + 3;
+ else
+ textX = _text->getCenterStringX(menu.item[i].itemString, x1, x2);
+
+ int textY = y1 + 2;
+ _text->printText(menu.item[i].itemString, textX - 1, textY + 1, 12, 0, 0);
+ _text->printText(menu.item[i].itemString, textX, textY, menu.item[i].textColor, 0, 0);
+}
+
+void KyraEngine::gui_redrawHighlight(Menu menu) {
+ int textX;
+ int i = menu.highlightedItem;
+
+ int x1 = menu.x + menu.item[i].x;
+ int y1 = menu.y + menu.item[i].y;
+
+ int x2 = x1 + menu.item[i].width - 1;
+
+ if (menu.item[i].field_12 != -1)
+ textX = x1 + menu.item[i].field_12 + 3;
+ else
+ textX = _text->getCenterStringX(menu.item[i].itemString, x1, x2);
+
+ int textY = y1 + 2;
+ _text->printText(menu.item[i].itemString, textX - 1, textY + 1, 12, 0, 0);
+ _text->printText(menu.item[i].itemString, textX, textY, menu.item[i].highlightColor, 0, 0);
+}
+
+void KyraEngine::gui_fadePalette() {
+ static int16 menuPalIndexes[] = {248, 249, 250, 251, 252, 253, 254, -1};
+ int index = 0;
+
+ memcpy(_screen->getPalette(2), _screen->_currentPalette, 768);
+
+ for (int i = 0; i < 768; i++) {
+ _screen->_currentPalette[i] /= 2;
+ }
+
+ while( menuPalIndexes[index] != -1) {
+ memcpy(&_screen->_currentPalette[menuPalIndexes[index]*3], &_screen->getPalette(2)[menuPalIndexes[index]*3], 3);
+ index++;
+ }
+
+ _screen->fadePalette(_screen->_currentPalette, 2);
+}
+
+void KyraEngine::gui_restorePalette() {
+ memcpy(_screen->_currentPalette, _screen->getPalette(2), 768);
+ _screen->fadePalette(_screen->_currentPalette, 2);
+}
+
+
+} // end of namespace Kyra
+
diff --git a/engines/kyra/items.cpp b/engines/kyra/items.cpp
new file mode 100644
index 0000000000..c98e1ee607
--- /dev/null
+++ b/engines/kyra/items.cpp
@@ -0,0 +1,982 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-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 "kyra/kyra.h"
+#include "kyra/seqplayer.h"
+#include "kyra/screen.h"
+#include "kyra/resource.h"
+#include "kyra/sound.h"
+#include "kyra/sprites.h"
+#include "kyra/wsamovie.h"
+#include "kyra/animator.h"
+#include "kyra/text.h"
+
+#include "common/system.h"
+#include "common/savefile.h"
+
+namespace Kyra {
+
+int KyraEngine::findDuplicateItemShape(int shape) {
+ static uint8 dupTable[] = {
+ 0x48, 0x46, 0x49, 0x47, 0x4a, 0x46, 0x4b, 0x47,
+ 0x4c, 0x46, 0x4d, 0x47, 0x5b, 0x5a, 0x5c, 0x5a,
+ 0x5d, 0x5a, 0x5e, 0x5a, 0xFF, 0xFF
+ };
+
+ int i = 0;
+
+ while (dupTable[i] != 0xFF) {
+ if (dupTable[i] == shape)
+ return dupTable[i+1];
+ i += 2;
+ }
+ return -1;
+}
+
+void KyraEngine::addToNoDropRects(int x, int y, int w, int h) {
+ debug(9, "KyraEngine::addToNoDropRects(%d, %d, %d, %d)", x, y, w, h);
+ for (int rect = 0; rect < 11; ++rect) {
+ if (_noDropRects[rect].x == -1) {
+ _noDropRects[rect].x = x;
+ _noDropRects[rect].y = y;
+ _noDropRects[rect].x2 = x + w - 1;
+ _noDropRects[rect].y2 = y + h - 1;
+ break;
+ }
+ }
+}
+
+void KyraEngine::clearNoDropRects() {
+ debug(9, "KyraEngine::clearNoDropRects()");
+ memset(_noDropRects, -1, sizeof(_noDropRects));
+}
+
+byte KyraEngine::findFreeItemInScene(int scene) {
+ debug(9, "KyraEngine::findFreeItemInScene(%d)", scene);
+ assert(scene < _roomTableSize);
+ Room *room = &_roomTable[scene];
+ for (int i = 0; i < 12; ++i) {
+ if (room->itemsTable[i] == 0xFF)
+ return i;
+ }
+ return 0xFF;
+}
+
+byte KyraEngine::findItemAtPos(int x, int y) {
+ debug(9, "KyraEngine::findItemAtPos(%d, %d)", x, y);
+ assert(_currentCharacter->sceneId < _roomTableSize);
+ const uint8 *itemsTable = _roomTable[_currentCharacter->sceneId].itemsTable;
+ const uint16 *xposOffset = _roomTable[_currentCharacter->sceneId].itemsXPos;
+ const uint8 *yposOffset = _roomTable[_currentCharacter->sceneId].itemsYPos;
+
+ int highestYPos = -1;
+ byte returnValue = 0xFF;
+
+ for (int i = 0; i < 12; ++i) {
+ if (*itemsTable != 0xFF) {
+ int xpos = *xposOffset - 11;
+ int xpos2 = *xposOffset + 10;
+ if (x > xpos && x < xpos2) {
+ assert(*itemsTable < ARRAYSIZE(_itemTable));
+ int itemHeight = _itemTable[*itemsTable].height;
+ int ypos = *yposOffset + 3;
+ int ypos2 = ypos - itemHeight - 3;
+
+ if (y > ypos2 && ypos > y) {
+ if (highestYPos <= ypos) {
+ returnValue = i;
+ highestYPos = ypos;
+ }
+ }
+ }
+ }
+ ++xposOffset;
+ ++yposOffset;
+ ++itemsTable;
+ }
+
+ return returnValue;
+}
+
+void KyraEngine::placeItemInGenericMapScene(int item, int index) {
+ debug(9, "KyraEngine::placeItemInGenericMapScene(%d, %d)", item, index);
+ static const uint16 itemMapSceneMinTable[] = {
+ 0x0000, 0x0011, 0x006D, 0x0025, 0x00C7, 0x0000
+ };
+ static const uint16 itemMapSceneMaxTable[] = {
+ 0x0010, 0x0024, 0x00C6, 0x006C, 0x00F5, 0x0000
+ };
+
+ int minValue = itemMapSceneMinTable[index];
+ int maxValue = itemMapSceneMaxTable[index];
+
+ while (true) {
+ int room = _rnd.getRandomNumberRng(minValue, maxValue);
+ assert(room < _roomTableSize);
+ int nameIndex = _roomTable[room].nameIndex;
+ bool placeItem = false;
+
+ switch (nameIndex) {
+ case 0: case 1: case 2: case 3:
+ case 4: case 5: case 6: case 11:
+ case 12: case 16: case 17: case 20:
+ case 22: case 23: case 25: case 26:
+ case 27: case 31: case 33: case 34:
+ case 36: case 37: case 58: case 59:
+ case 60: case 61: case 83: case 84:
+ case 85: case 104: case 105: case 106:
+ placeItem = true;
+ break;
+
+ case 51:
+ if (room != 46) {
+ placeItem = true;
+ break;
+ }
+ default:
+ placeItem = false;
+ break;
+ }
+
+ if (placeItem) {
+ Room *roomPtr = &_roomTable[room];
+ if (roomPtr->northExit == 0xFFFF && roomPtr->eastExit == 0xFFFF && roomPtr->southExit == 0xFFFF && roomPtr->westExit == 0xFFFF) {
+ placeItem = false;
+ } else if (_currentCharacter->sceneId == room) {
+ placeItem = false;
+ }
+ }
+
+ if (placeItem) {
+ if (!processItemDrop(room, item, -1, -1, 2, 0))
+ continue;
+ break;
+ }
+ }
+}
+
+void KyraEngine::createMouseItem(int item) {
+ debug(9, "KyraEngine::createMouseItem(%d)", item);
+ _screen->hideMouse();
+ setMouseItem(item);
+ _itemInHand = item;
+ _screen->showMouse();
+}
+
+void KyraEngine::destroyMouseItem() {
+ debug(9, "KyraEngine::destroyMouseItem()");
+ _screen->hideMouse();
+ _screen->setMouseCursor(1, 1, _shapes[4]);
+ _itemInHand = -1;
+ _screen->showMouse();
+}
+
+void KyraEngine::setMouseItem(int item) {
+ debug(9, "KyraEngine::setMouseItem(%d)", item);
+ if (item == -1) {
+ _screen->setMouseCursor(1, 1, _shapes[10]);
+ } else {
+ _screen->setMouseCursor(8, 15, _shapes[220+item]);
+ }
+}
+
+void KyraEngine::wipeDownMouseItem(int xpos, int ypos) {
+ debug(9, "KyraEngine::wipeDownMouseItem(%d, %d)", xpos, ypos);
+ if (_itemInHand == -1)
+ return;
+ xpos -= 8;
+ ypos -= 15;
+ _screen->hideMouse();
+ _screen->backUpRect1(xpos, ypos);
+ int y = ypos;
+ int height = 16;
+
+ while (height >= 0) {
+ _screen->restoreRect1(xpos, ypos);
+ _screen->setNewShapeHeight(_shapes[220+_itemInHand], height);
+ uint32 nextTime = _system->getMillis() + 1 * _tickLength;
+ _screen->drawShape(0, _shapes[220+_itemInHand], xpos, y, 0, 0);
+ _screen->updateScreen();
+ y += 2;
+ height -= 2;
+ while (_system->getMillis() < nextTime) {}
+ }
+ _screen->restoreRect1(xpos, ypos);
+ _screen->resetShapeHeight(_shapes[220+_itemInHand]);
+ destroyMouseItem();
+ _screen->showMouse();
+}
+
+void KyraEngine::setupSceneItems() {
+ debug(9, "KyraEngine::setupSceneItems()");
+ uint16 sceneId = _currentCharacter->sceneId;
+ assert(sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[sceneId];
+ for (int i = 0; i < 12; ++i) {
+ uint8 item = currentRoom->itemsTable[i];
+ if (item == 0xFF || !currentRoom->needInit[i]) {
+ continue;
+ }
+
+ int xpos = 0;
+ int ypos = 0;
+
+ if (currentRoom->itemsXPos[i] == 0xFFFF) {
+ xpos = currentRoom->itemsXPos[i] = _rnd.getRandomNumberRng(24, 296);
+ ypos = currentRoom->itemsYPos[i] = _rnd.getRandomNumberRng(_northExitHeight & 0xFF, 130);
+ } else {
+ xpos = currentRoom->itemsXPos[i];
+ ypos = currentRoom->itemsYPos[i];
+ }
+
+ _lastProcessedItem = i;
+
+ int stop = 0;
+ while (!stop) {
+ stop = processItemDrop(sceneId, item, xpos, ypos, 3, 0);
+ if (!stop) {
+ xpos = currentRoom->itemsXPos[i] = _rnd.getRandomNumberRng(24, 296);
+ ypos = currentRoom->itemsYPos[i] = _rnd.getRandomNumberRng(_northExitHeight & 0xFF, 130);
+ if (countItemsInScene(sceneId) >= 12) {
+ break;
+ }
+ } else {
+ currentRoom->needInit[i] = 0;
+ }
+ }
+ }
+}
+
+int KyraEngine::countItemsInScene(uint16 sceneId) {
+ debug(9, "KyraEngine::countItemsInScene(%d)", sceneId);
+ assert(sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[sceneId];
+
+ int items = 0;
+
+ for (int i = 0; i < 12; ++i) {
+ if (currentRoom->itemsTable[i] != 0xFF) {
+ ++items;
+ }
+ }
+
+ return items;
+}
+
+int KyraEngine::processItemDrop(uint16 sceneId, uint8 item, int x, int y, int unk1, int unk2) {
+ debug(9, "KyraEngine::processItemDrop(%d, %d, %d, %d, %d, %d)", sceneId, item, x, y, unk1, unk2);
+ int freeItem = -1;
+ uint8 itemIndex = findItemAtPos(x, y);
+ if (unk1) {
+ itemIndex = 0xFF;
+ }
+
+ if (itemIndex != 0xFF) {
+ exchangeItemWithMouseItem(sceneId, itemIndex);
+ return 0;
+ }
+
+ assert(sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[sceneId];
+
+ if (unk1 != 3) {
+ for (int i = 0; i < 12; ++i) {
+ if (currentRoom->itemsTable[i] == 0xFF) {
+ freeItem = i;
+ break;
+ }
+ }
+ } else {
+ freeItem = _lastProcessedItem;
+ }
+
+ if (freeItem == -1) {
+ return 0;
+ }
+
+ if (sceneId != _currentCharacter->sceneId) {
+ addItemToRoom(sceneId, item, freeItem, x, y);
+ return 1;
+ }
+
+ int itemHeight = _itemTable[item].height;
+ _lastProcessedItemHeight = itemHeight;
+
+ if (x == -1 && x == -1) {
+ x = _rnd.getRandomNumberRng(16, 304);
+ y = _rnd.getRandomNumberRng(_northExitHeight & 0xFF, 135);
+ }
+
+ int xpos = x;
+ int ypos = y;
+ int destY = -1;
+ int destX = -1;
+ int running = 1;
+
+ while (running) {
+ if ((_northExitHeight & 0xFF) <= ypos) {
+ bool running2 = true;
+
+ if (_screen->getDrawLayer(xpos, ypos) > 1) {
+ if (((_northExitHeight >> 8) & 0xFF) != ypos) {
+ running2 = false;
+ }
+ }
+
+ if (_screen->getDrawLayer2(xpos, ypos, itemHeight) > 1) {
+ if (((_northExitHeight >> 8) & 0xFF) != ypos) {
+ running2 = false;
+ }
+ }
+
+ if (!isDropable(xpos, ypos)) {
+ if (((_northExitHeight >> 8) & 0xFF) != ypos) {
+ running2 = false;
+ }
+ }
+
+ int xpos2 = xpos;
+ int xpos3 = xpos;
+
+ while (running2) {
+ if (isDropable(xpos2, ypos)) {
+ if (_screen->getDrawLayer2(xpos2, ypos, itemHeight) < 7) {
+ if (findItemAtPos(xpos2, ypos) == 0xFF) {
+ destX = xpos2;
+ destY = ypos;
+ running = 0;
+ running2 = false;
+ }
+ }
+ }
+
+ if (isDropable(xpos3, ypos)) {
+ if (_screen->getDrawLayer2(xpos3, ypos, itemHeight) < 7) {
+ if (findItemAtPos(xpos3, ypos) == 0xFF) {
+ destX = xpos3;
+ destY = ypos;
+ running = 0;
+ running2 = false;
+ }
+ }
+ }
+
+ if (!running2)
+ continue;
+
+ xpos2 -= 2;
+ if (xpos2 < 16) {
+ xpos2 = 16;
+ }
+
+ xpos3 += 2;
+ if (xpos3 > 304) {
+ xpos3 = 304;
+ }
+
+ if (xpos2 > 16)
+ continue;
+ if (xpos3 < 304)
+ continue;
+ running2 = false;
+ }
+ }
+
+ if (((_northExitHeight >> 8) & 0xFF) == ypos) {
+ running = 0;
+ destY -= _rnd.getRandomNumberRng(0, 3);
+
+ if ((_northExitHeight & 0xFF) < destY) {
+ continue;
+ }
+
+ destY = (_northExitHeight & 0xFF) + 1;
+ continue;
+ }
+ ypos += 2;
+ if (((_northExitHeight >> 8) & 0xFF) >= ypos) {
+ continue;
+ }
+ ypos = (_northExitHeight >> 8) & 0xFF;
+ }
+
+ if (destX == -1 || destY == -1) {
+ return 0;
+ }
+
+ if (unk1 == 3) {
+ currentRoom->itemsXPos[freeItem] = destX;
+ currentRoom->itemsYPos[freeItem] = destY;
+ return 1;
+ }
+
+ if (unk1 == 2) {
+ itemSpecialFX(x, y, item);
+ }
+
+ if (unk1 == 0) {
+ destroyMouseItem();
+ }
+
+ itemDropDown(x, y, destX, destY, freeItem, item);
+
+ if (unk1 == 0 && unk2 != 0) {
+ assert(_itemList && _droppedList);
+ updateSentenceCommand(_itemList[item], _droppedList[0], 179);
+ }
+
+ return 1;
+}
+
+void KyraEngine::exchangeItemWithMouseItem(uint16 sceneId, int itemIndex) {
+ debug(9, "KyraEngine::exchangeItemWithMouseItem(%d, %d)", sceneId, itemIndex);
+ _screen->hideMouse();
+ _animator->animRemoveGameItem(itemIndex);
+ assert(sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[sceneId];
+
+ int item = currentRoom->itemsTable[itemIndex];
+ currentRoom->itemsTable[itemIndex] = _itemInHand;
+ _itemInHand = item;
+ _animator->animAddGameItem(itemIndex, sceneId);
+ snd_playSoundEffect(53);
+
+ setMouseItem(_itemInHand);
+ assert(_itemList && _takenList);
+ updateSentenceCommand(_itemList[_itemInHand], _takenList[1], 179);
+ _screen->showMouse();
+ clickEventHandler2();
+}
+
+void KyraEngine::addItemToRoom(uint16 sceneId, uint8 item, int itemIndex, int x, int y) {
+ debug(9, "KyraEngine::addItemToRoom(%d, %d, %d, %d, %d)", sceneId, item, itemIndex, x, y);
+ assert(sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[sceneId];
+ currentRoom->itemsTable[itemIndex] = item;
+ currentRoom->itemsXPos[itemIndex] = x;
+ currentRoom->itemsYPos[itemIndex] = y;
+ currentRoom->needInit[itemIndex] = 1;
+}
+
+int KyraEngine::checkNoDropRects(int x, int y) {
+ debug(9, "KyraEngine::checkNoDropRects(%d, %d)", x, y);
+ if (_lastProcessedItemHeight < 1 || _lastProcessedItemHeight > 16) {
+ _lastProcessedItemHeight = 16;
+ }
+ if (_noDropRects[0].x == -1) {
+ return 0;
+ }
+
+ for (int i = 0; i < 11; ++i) {
+ if (_noDropRects[i].x == -1) {
+ break;
+ }
+
+ int xpos = _noDropRects[i].x;
+ int ypos = _noDropRects[i].y;
+ int xpos2 = _noDropRects[i].x2;
+ int ypos2 = _noDropRects[i].y2;
+
+ if (xpos > x + 16)
+ continue;
+ if (xpos2 < x)
+ continue;
+ if (y < ypos)
+ continue;
+ if (ypos2 < y - _lastProcessedItemHeight)
+ continue;
+ return 1;
+ }
+
+ return 0;
+}
+
+int KyraEngine::isDropable(int x, int y) {
+ debug(9, "KyraEngine::isDropable(%d, %d)", x, y);
+ x -= 8;
+ y -= 1;
+
+ if (checkNoDropRects(x, y)) {
+ return 0;
+ }
+
+ for (int xpos = x; xpos < x + 16; ++xpos) {
+ if (_screen->getShapeFlag1(xpos, y) == 0) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void KyraEngine::itemDropDown(int x, int y, int destX, int destY, byte freeItem, int item) {
+ debug(9, "KyraEngine::itemDropDown(%d, %d, %d, %d, %d, %d)", x, y, destX, destY, freeItem, item);
+ assert(_currentCharacter->sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[_currentCharacter->sceneId];
+ if (x == destX && y == destY) {
+ currentRoom->itemsXPos[freeItem] = destX;
+ currentRoom->itemsYPos[freeItem] = destY;
+ currentRoom->itemsTable[freeItem] = item;
+ snd_playSoundEffect(0x32);
+ _animator->animAddGameItem(freeItem, _currentCharacter->sceneId);
+ return;
+ }
+ _screen->hideMouse();
+ if (y <= destY) {
+ int tempY = y;
+ int addY = 2;
+ int drawX = x - 8;
+ int drawY = 0;
+
+ _screen->backUpRect0(drawX, y - 16);
+
+ while (tempY < destY) {
+ _screen->restoreRect0(drawX, tempY - 16);
+ tempY += addY;
+ if (tempY > destY) {
+ tempY = destY;
+ }
+ ++addY;
+ drawY = tempY - 16;
+ _screen->backUpRect0(drawX, drawY);
+ uint32 nextTime = _system->getMillis() + 1 * _tickLength;
+ _screen->drawShape(0, _shapes[220+item], drawX, drawY, 0, 0);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if ((nextTime - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+
+ bool skip = false;
+ if (x == destX) {
+ if (destY - y <= 16) {
+ skip = true;
+ }
+ }
+
+ if (!skip) {
+ snd_playSoundEffect(0x47);
+ if (addY < 6)
+ addY = 6;
+
+ int xDiff = (destX - x) << 4;
+ xDiff /= addY;
+ int startAddY = addY;
+ addY >>= 1;
+ if (destY - y <= 8) {
+ addY >>= 1;
+ }
+ addY = -addY;
+ int unkX = x << 4;
+ while (--startAddY) {
+ drawX = (unkX >> 4) - 8;
+ drawY = tempY - 16;
+ _screen->restoreRect0(drawX, drawY);
+ tempY += addY;
+ unkX += xDiff;
+ if (tempY > destY) {
+ tempY = destY;
+ }
+ ++addY;
+ drawX = (unkX >> 4) - 8;
+ drawY = tempY - 16;
+ _screen->backUpRect0(drawX, drawY);
+ uint32 nextTime = _system->getMillis() + 1 * _tickLength;
+ _screen->drawShape(0, _shapes[220+item], drawX, drawY, 0, 0);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if ((nextTime - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+ _screen->restoreRect0(drawX, drawY);
+ } else {
+ _screen->restoreRect0(drawX, tempY - 16);
+ }
+ }
+ currentRoom->itemsXPos[freeItem] = destX;
+ currentRoom->itemsYPos[freeItem] = destY;
+ currentRoom->itemsTable[freeItem] = item;
+ snd_playSoundEffect(0x32);
+ _animator->animAddGameItem(freeItem, _currentCharacter->sceneId);
+ _screen->showMouse();
+}
+
+void KyraEngine::dropItem(int unk1, int item, int x, int y, int unk2) {
+ debug(9, "KyraEngine::dropItem(%d, %d, %d, %d, %d)", unk1, item, x, y, unk2);
+ if (processItemDrop(_currentCharacter->sceneId, item, x, y, unk1, unk2))
+ return;
+ snd_playSoundEffect(54);
+ if (12 == countItemsInScene(_currentCharacter->sceneId)) {
+ assert(_noDropList);
+ drawSentenceCommand(_noDropList[0], 6);
+ } else {
+ assert(_noDropList);
+ drawSentenceCommand(_noDropList[1], 6);
+ }
+}
+
+void KyraEngine::itemSpecialFX(int x, int y, int item) {
+ debug(9, "KyraEngine::itemSpecialFX(%d, %d, %d)", x, y, item);
+ if (item == 41) {
+ itemSpecialFX1(x, y, item);
+ } else {
+ itemSpecialFX2(x, y, item);
+ }
+}
+
+void KyraEngine::itemSpecialFX1(int x, int y, int item) {
+ debug(9, "KyraEngine::itemSpecialFX1(%d, %d, %d)", x, y, item);
+ uint8 *shape = _shapes[220+item];
+ x -= 8;
+ int startY = y;
+ y -= 15;
+ _screen->hideMouse();
+ _screen->backUpRect0(x, y);
+ for (int i = 1; i <= 16; ++i) {
+ _screen->setNewShapeHeight(shape, i);
+ --startY;
+ _screen->restoreRect0(x, y);
+ uint32 nextTime = _system->getMillis() + 1 * _tickLength;
+ _screen->drawShape(0, shape, x, startY, 0, 0);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if ((nextTime - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+ _screen->restoreRect0(x, y);
+ _screen->showMouse();
+}
+
+void KyraEngine::itemSpecialFX2(int x, int y, int item) {
+ debug(9, "KyraEngine::itemSpecialFX2(%d, %d, %d)", x, y, item);
+ x -= 8;
+ y -= 15;
+ int yAdd = (int8)(((16 - _itemTable[item].height) >> 1) & 0xFF);
+ _screen->backUpRect0(x, y);
+ if (item >= 80 && item <= 89) {
+ snd_playSoundEffect(55);
+ }
+
+ for (int i = 201; i <= 205; ++i) {
+ _screen->restoreRect0(x, y);
+ uint32 nextTime = _system->getMillis() + 3 * _tickLength;
+ _screen->drawShape(0, _shapes[4+i], x, y + yAdd, 0, 0);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if ((nextTime - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+
+ for (int i = 204; i >= 201; --i) {
+ _screen->restoreRect0(x, y);
+ uint32 nextTime = _system->getMillis() + 3 * _tickLength;
+ _screen->drawShape(0, _shapes[220+item], x, y, 0, 0);
+ _screen->drawShape(0, _shapes[4+i], x, y + yAdd, 0, 0);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if ((nextTime - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+ _screen->restoreRect0(x, y);
+}
+
+void KyraEngine::magicOutMouseItem(int animIndex, int itemPos) {
+ debug(9, "KyraEngine::magicOutMouseItem(%d, %d)", animIndex, itemPos);
+ int videoPageBackUp = _screen->_curPage;
+ _screen->_curPage = 0;
+ int x = 0, y = 0;
+ if (itemPos == -1) {
+ x = _mouseX - 12;
+ y = _mouseY - 18;
+ } else {
+ x = _itemPosX[itemPos] - 4;
+ y = _itemPosY[itemPos] - 3;
+ }
+
+ if (_itemInHand == -1 && itemPos == -1) {
+ return;
+ }
+
+ int tableIndex = 0, loopStart = 0, maxLoops = 0;
+ if (animIndex == 0) {
+ tableIndex = _rnd.getRandomNumberRng(0, 5);
+ loopStart = 35;
+ maxLoops = 9;
+ } else if (animIndex == 1) {
+ tableIndex = _rnd.getRandomNumberRng(0, 11);
+ loopStart = 115;
+ maxLoops = 8;
+ } else if (animIndex == 2) {
+ tableIndex = 0;
+ loopStart = 124;
+ maxLoops = 4;
+ } else {
+ tableIndex = -1;
+ }
+
+ if (animIndex == 2) {
+ snd_playSoundEffect(0x5E);
+ } else {
+ snd_playSoundEffect(0x37);
+ }
+ _screen->hideMouse();
+ _screen->backUpRect1(x, y);
+
+ for (int shape = _magicMouseItemStartFrame[animIndex]; shape <= _magicMouseItemEndFrame[animIndex]; ++shape) {
+ _screen->restoreRect1(x, y);
+ uint32 nextTime = _system->getMillis() + 4 * _tickLength;
+ _screen->drawShape(0, _shapes[220+_itemInHand], x + 4, y + 3, 0, 0);
+ if (tableIndex == -1) {
+ _screen->drawShape(0, _shapes[4+shape], x, y, 0, 0);
+ } else {
+ specialMouseItemFX(shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
+ }
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if (nextTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+
+ if (itemPos != -1) {
+ _screen->restoreRect1(x, y);
+ _screen->fillRect(_itemPosX[itemPos], _itemPosY[itemPos], _itemPosX[itemPos] + 15, _itemPosY[itemPos] + 15, 12, 0);
+ _screen->backUpRect1(x, y);
+ }
+
+ for (int shape = _magicMouseItemStartFrame2[animIndex]; shape <= _magicMouseItemEndFrame2[animIndex]; ++shape) {
+ _screen->restoreRect1(x, y);
+ uint32 nextTime = _system->getMillis() + 4 * _tickLength;
+ _screen->drawShape(0, _shapes[220+_itemInHand], x + 4, y + 3, 0, 0);
+ if (tableIndex == -1) {
+ _screen->drawShape(0, _shapes[4+shape], x, y, 0, 0);
+ } else {
+ specialMouseItemFX(shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
+ }
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if (nextTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+ _screen->restoreRect1(x, y);
+ if (itemPos == -1) {
+ _screen->setMouseCursor(1, 1, _shapes[4]);
+ _itemInHand = -1;
+ } else {
+ _characterList[0].inventoryItems[itemPos] = 0xFF;
+ _screen->hideMouse();
+ _screen->fillRect(_itemPosX[itemPos], _itemPosY[itemPos], _itemPosX[itemPos] + 15, _itemPosY[itemPos] + 15, 12, 0);
+ _screen->showMouse();
+ }
+ _screen->showMouse();
+ _screen->_curPage = videoPageBackUp;
+}
+
+void KyraEngine::magicInMouseItem(int animIndex, int item, int itemPos) {
+ debug(9, "KyraEngine::magicInMouseItem(%d, %d, %d)", animIndex, item, itemPos);
+ int videoPageBackUp = _screen->_curPage;
+ _screen->_curPage = 0;
+ int x = 0, y = 0;
+ if (itemPos == -1) {
+ x = _mouseX - 12;
+ y = _mouseY - 18;
+ } else {
+ x = _itemPosX[itemPos] - 4;
+ y = _itemPosX[itemPos] - 3;
+ }
+ if (item < 0)
+ return;
+
+ int tableIndex = -1, loopStart = 0, maxLoops = 0;
+ if (animIndex == 0) {
+ tableIndex = _rnd.getRandomNumberRng(0, 5);
+ loopStart = 35;
+ maxLoops = 9;
+ } else if (animIndex == 1) {
+ tableIndex = _rnd.getRandomNumberRng(0, 11);
+ loopStart = 115;
+ maxLoops = 8;
+ } else if (animIndex == 2) {
+ tableIndex = 0;
+ loopStart = 124;
+ maxLoops = 4;
+ }
+
+ _screen->hideMouse();
+ _screen->backUpRect1(x, y);
+ if (animIndex == 2) {
+ snd_playSoundEffect(0x5E);
+ } else {
+ snd_playSoundEffect(0x37);
+ }
+
+ for (int shape = _magicMouseItemStartFrame[animIndex]; shape <= _magicMouseItemEndFrame[animIndex]; ++shape) {
+ _screen->restoreRect1(x, y);
+ uint32 nextTime = _system->getMillis() + 4 * _tickLength;
+ if (tableIndex == -1) {
+ _screen->drawShape(0, _shapes[4+shape], x, y, 0, 0);
+ } else {
+ specialMouseItemFX(shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
+ }
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if (nextTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+
+ for (int shape = _magicMouseItemStartFrame2[animIndex]; shape <= _magicMouseItemEndFrame2[animIndex]; ++shape) {
+ _screen->restoreRect1(x, y);
+ uint32 nextTime = _system->getMillis() + 4 * _tickLength;
+ if (tableIndex == -1) {
+ _screen->drawShape(0, _shapes[4+shape], x, y, 0, 0);
+ } else {
+ specialMouseItemFX(shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
+ }
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if (nextTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+ _screen->restoreRect1(x, y);
+ if (itemPos == -1) {
+ _screen->setMouseCursor(8, 15, _shapes[220+item]);
+ _itemInHand = item;
+ } else {
+ _characterList[0].inventoryItems[itemPos] = item;
+ _screen->hideMouse();
+ _screen->drawShape(0, _shapes[220+item], _itemPosX[itemPos], _itemPosY[itemPos], 0, 0);
+ _screen->showMouse();
+ }
+ _screen->showMouse();
+ _screen->_curPage = videoPageBackUp;
+}
+
+void KyraEngine::specialMouseItemFX(int shape, int x, int y, int animIndex, int tableIndex, int loopStart, int maxLoops) {
+ debug(9, "KyraEngine::specialMouseItemFX(%d, %d, %d, %d, %d, %d, %d)", shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
+ static const uint8 table1[] = {
+ 0x23, 0x45, 0x55, 0x72, 0x84, 0xCF, 0x00, 0x00
+ };
+ static const uint8 table2[] = {
+ 0x73, 0xB5, 0x80, 0x21, 0x13, 0x39, 0x45, 0x55, 0x62, 0xB4, 0xCF, 0xD8
+ };
+ static const uint8 table3[] = {
+ 0x7C, 0xD0, 0x74, 0x84, 0x87, 0x00, 0x00, 0x00
+ };
+ int tableValue = 0;
+ if (animIndex == 0) {
+ tableValue = table1[tableIndex];
+ } else if (animIndex == 1) {
+ tableValue = table2[tableIndex];
+ } else if (animIndex == 2) {
+ tableValue = table3[tableIndex];
+ } else {
+ return;
+ }
+ processSpecialMouseItemFX(shape, x, y, tableValue, loopStart, maxLoops);
+}
+
+void KyraEngine::processSpecialMouseItemFX(int shape, int x, int y, int tableValue, int loopStart, int maxLoops) {
+ debug(9, "KyraEngine::processSpecialMouseItemFX(%d, %d, %d, %d, %d, %d)", shape, x, y, tableValue, loopStart, maxLoops);
+ uint8 shapeColorTable[16];
+ uint8 *shapePtr = _shapes[4+shape] + 10;
+ if (_features & GF_TALKIE)
+ shapePtr += 2;
+ for (int i = 0; i < 16; ++i) {
+ shapeColorTable[i] = shapePtr[i];
+ }
+ for (int i = loopStart; i < loopStart + maxLoops; ++i) {
+ for (int i2 = 0; i2 < 16; ++i2) {
+ if (shapePtr[i2] == i) {
+ shapeColorTable[i2] = (i + tableValue) - loopStart;
+ }
+ }
+ }
+ _screen->drawShape(0, _shapes[4+shape], x, y, 0, 0x8000, shapeColorTable);
+}
+
+void KyraEngine::updatePlayerItemsForScene() {
+ debug(9, "KyraEngine::updatePlayerItemsForScene()");
+ if (_itemInHand >= 29 && _itemInHand < 33) {
+ ++_itemInHand;
+ if (_itemInHand > 33)
+ _itemInHand = 33;
+ _screen->hideMouse();
+ _screen->setMouseCursor(8, 15, _shapes[220+_itemInHand]);
+ _screen->showMouse();
+ }
+
+ bool redraw = false;
+ for (int i = 0; i < 10; ++i) {
+ uint8 item = _currentCharacter->inventoryItems[i];
+ if (item >= 29 && item < 33) {
+ ++item;
+ if (item > 33)
+ item = 33;
+ _currentCharacter->inventoryItems[i] = item;
+ redraw = true;
+ }
+ }
+
+ if (redraw) {
+ _screen->hideMouse();
+ redrawInventory(0);
+ _screen->showMouse();
+ }
+
+ if (_itemInHand == 33) {
+ magicOutMouseItem(2, -1);
+ }
+
+ _screen->hideMouse();
+ for (int i = 0; i < 10; ++i) {
+ uint8 item = _currentCharacter->inventoryItems[i];
+ if (item == 33) {
+ magicOutMouseItem(2, i);
+ }
+ }
+ _screen->showMouse();
+}
+
+void KyraEngine::redrawInventory(int page) {
+ int videoPageBackUp = _screen->_curPage;
+ _screen->_curPage = page;
+ _screen->hideMouse();
+ for (int i = 0; i < 10; ++i) {
+ _screen->fillRect(_itemPosX[i], _itemPosY[i], _itemPosX[i] + 15, _itemPosY[i] + 15, 12, page);
+ if (_currentCharacter->inventoryItems[i] != 0xFF) {
+ uint8 item = _currentCharacter->inventoryItems[i];
+ _screen->drawShape(page, _shapes[220+item], _itemPosX[i], _itemPosY[i], 0, 0);
+ }
+ }
+ _screen->showMouse();
+ _screen->_curPage = videoPageBackUp;
+ _screen->updateScreen();
+}
+
+} // end of namespace Kyra
diff --git a/engines/kyra/kyra.cpp b/engines/kyra/kyra.cpp
new file mode 100644
index 0000000000..c85ce9b323
--- /dev/null
+++ b/engines/kyra/kyra.cpp
@@ -0,0 +1,1215 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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 "backends/fs/fs.h"
+
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "common/md5.h"
+#include "common/savefile.h"
+
+#include "sound/mixer.h"
+#include "sound/mididrv.h"
+
+#include "gui/message.h"
+
+#include "kyra/kyra.h"
+#include "kyra/resource.h"
+#include "kyra/screen.h"
+#include "kyra/script.h"
+#include "kyra/seqplayer.h"
+#include "kyra/sound.h"
+#include "kyra/sprites.h"
+#include "kyra/wsamovie.h"
+#include "kyra/animator.h"
+#include "kyra/text.h"
+#include "kyra/debugger.h"
+
+using namespace Kyra;
+
+enum {
+ // We only compute MD5 of the first megabyte of our data files.
+ kMD5FileSizeLimit = 1024 * 1024
+};
+
+// Kyra MD5 detection brutally ripped from the Gobliins engine.
+struct KyraGameSettings {
+ const char *gameid;
+ const char *description;
+ byte id;
+ uint32 features;
+ const char *md5sum;
+ const char *checkFile;
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { gameid, description, features };
+ return dummy;
+ }
+};
+
+// We could get rid of md5 detection at least for kyra 1 since we can locate all
+// needed files for detecting the right language and version (Floppy, Talkie)
+static const KyraGameSettings kyra_games[] = {
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_ENGLISH | GF_FLOPPY, // english floppy 1.0 from Malice
+ "3c244298395520bb62b5edfe41688879", "GEMCUT.EMC" },
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_ENGLISH | GF_FLOPPY,
+ "796e44863dd22fa635b042df1bf16673", "GEMCUT.EMC" },
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_FRENCH | GF_FLOPPY,
+ "abf8eb360e79a6c2a837751fbd4d3d24", "GEMCUT.EMC" },
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_GERMAN | GF_FLOPPY,
+ "6018e1dfeaca7fe83f8d0b00eb0dd049", "GEMCUT.EMC"},
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_GERMAN | GF_FLOPPY, // from Arne.F
+ "f0b276781f47c130f423ec9679fe9ed9", "GEMCUT.EMC"},
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_SPANISH | GF_FLOPPY, // from VooD
+ "8909b41596913b3f5deaf3c9f1017b01", "GEMCUT.EMC"},
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_SPANISH | GF_FLOPPY, // floppy 1.8 from clemmy
+ "747861d2a9c643c59fdab570df5b9093", "GEMCUT.EMC"},
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_ENGLISH | GF_TALKIE,
+ "fac399fe62f98671e56a005c5e94e39f", "GEMCUT.PAK" },
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_GERMAN | GF_TALKIE,
+ "230f54e6afc007ab4117159181a1c722", "GEMCUT.PAK" },
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_FRENCH | GF_TALKIE,
+ "b037c41768b652a040360ffa3556fd2a", "GEMCUT.PAK" },
+ { "kyra1", "The Legend of Kyrandia Demo", GI_KYRA1, GF_DEMO | GF_ENGLISH,
+ "fb722947d94897512b13b50cc84fd648", "DEMO1.WSA" },
+ { 0, 0, 0, 0, 0, 0 }
+};
+
+// Keep list of different supported games
+struct KyraGameList {
+ const char *gameid;
+ const char *description;
+ uint32 features;
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { gameid, description, features };
+ return dummy;
+ }
+};
+
+static const KyraGameList kyra_list[] = {
+ { "kyra1", "The Legend of Kyrandia", 0 },
+ { 0, 0, 0 }
+};
+
+struct KyraLanguageTable {
+ const char *file;
+ uint32 language;
+ Common::Language detLanguage;
+};
+
+static const KyraLanguageTable kyra_languages[] = {
+ { "MAIN15.CPS", GF_ENGLISH, Common::EN_USA },
+ { "MAIN_ENG.CPS", GF_ENGLISH, Common::EN_USA },
+ { "MAIN_FRE.CPS", GF_FRENCH, Common::FR_FRA },
+ { "MAIN_GER.CPS", GF_GERMAN, Common::DE_DEU },
+ { "MAIN_SPA.CPS", GF_SPANISH, Common::ES_ESP },
+ { 0, 0, Common::UNK_LANG }
+};
+
+static Common::Language convertKyraLang(uint32 features) {
+ if (features & GF_ENGLISH) {
+ return Common::EN_USA;
+ } else if (features & GF_FRENCH) {
+ return Common::FR_FRA;
+ } else if (features & GF_GERMAN) {
+ return Common::DE_DEU;
+ } else if (features & GF_SPANISH) {
+ return Common::ES_ESP;
+ }
+ return Common::UNK_LANG;
+}
+
+GameList Engine_KYRA_gameList() {
+ GameList games;
+ const KyraGameList *g = kyra_list;
+
+ while (g->gameid) {
+ games.push_back(g->toGameSettings());
+ g++;
+ }
+ return games;
+}
+
+DetectedGameList Engine_KYRA_detectGames(const FSList &fslist) {
+ DetectedGameList detectedGames;
+ const KyraGameSettings *g;
+ FSList::const_iterator file;
+
+ // Iterate over all files in the given directory
+ bool isFound = false;
+ for (file = fslist.begin(); file != fslist.end(); file++) {
+ if (file->isDirectory())
+ continue;
+
+ for (g = kyra_games; g->gameid; g++) {
+ if (scumm_stricmp(file->displayName().c_str(), g->checkFile) == 0)
+ isFound = true;
+ }
+ if (isFound)
+ break;
+ }
+
+ if (file == fslist.end())
+ return detectedGames;
+
+ uint8 md5sum[16];
+ char md5str[32 + 1];
+
+ if (Common::md5_file(file->path().c_str(), md5sum, NULL, kMD5FileSizeLimit)) {
+ for (int i = 0; i < 16; i++) {
+ sprintf(md5str + i * 2, "%02x", (int)md5sum[i]);
+ }
+ for (g = kyra_games; g->gameid; g++) {
+ if (strcmp(g->md5sum, (char *)md5str) == 0) {
+ detectedGames.push_back(DetectedGame(g->toGameSettings(), convertKyraLang(g->features), Common::kPlatformUnknown));
+ }
+ }
+ if (detectedGames.isEmpty()) {
+ debug("Unknown MD5 (%s)! Please report the details (language, platform, etc.) of this game to the ScummVM team\n", md5str);
+
+ const KyraGameList *g1 = kyra_list;
+ while (g1->gameid) {
+ detectedGames.push_back(g1->toGameSettings());
+ g1++;
+ }
+ }
+ }
+ return detectedGames;
+}
+
+Engine *Engine_KYRA_create(GameDetector *detector, OSystem *system) {
+ return new KyraEngine(detector, system);
+}
+
+REGISTER_PLUGIN(KYRA, "Legend of Kyrandia Engine")
+
+namespace Kyra {
+
+KyraEngine::KyraEngine(GameDetector *detector, OSystem *system)
+ : Engine(system) {
+ _seq_Forest = _seq_KallakWriting = _seq_KyrandiaLogo = _seq_KallakMalcolm =
+ _seq_MalcolmTree = _seq_WestwoodLogo = _seq_Demo1 = _seq_Demo2 = _seq_Demo3 =
+ _seq_Demo4 = 0;
+
+ _seq_WSATable = _seq_CPSTable = _seq_COLTable = _seq_textsTable = 0;
+ _seq_WSATable_Size = _seq_CPSTable_Size = _seq_COLTable_Size = _seq_textsTable_Size = 0;
+
+ _roomFilenameTable = _characterImageTable = 0;
+ _roomFilenameTableSize = _characterImageTableSize = 0;
+ _itemList = _takenList = _placedList = _droppedList = _noDropList = 0;
+ _itemList_Size = _takenList_Size = _placedList_Size = _droppedList_Size = _noDropList_Size = 0;
+ _putDownFirst = _waitForAmulet = _blackJewel = _poisonGone = _healingTip = 0;
+ _putDownFirst_Size = _waitForAmulet_Size = _blackJewel_Size = _poisonGone_Size = _healingTip_Size = 0;
+ _thePoison = _fluteString = _wispJewelStrings = _magicJewelString = _flaskFull = _fullFlask = 0;
+ _thePoison_Size = _fluteString_Size = _wispJewelStrings_Size = 0;
+ _magicJewelString_Size = _flaskFull_Size = _fullFlask_Size = 0;
+
+ _defaultShapeTable = _healingShapeTable = _healingShape2Table = 0;
+ _defaultShapeTableSize = _healingShapeTableSize = _healingShape2TableSize = 0;
+ _posionDeathShapeTable = _fluteAnimShapeTable = 0;
+ _posionDeathShapeTableSize = _fluteAnimShapeTableSize = 0;
+ _winterScrollTable = _winterScroll1Table = _winterScroll2Table = 0;
+ _winterScrollTableSize = _winterScroll1TableSize = _winterScroll2TableSize = 0;
+ _drinkAnimationTable = _brandonToWispTable = _magicAnimationTable = _brandonStoneTable = 0;
+ _drinkAnimationTableSize = _brandonToWispTableSize = _magicAnimationTableSize = _brandonStoneTableSize = 0;
+
+ // Setup mixer
+ if (!_mixer->isReady()) {
+ warning("Sound initialization failed.");
+ }
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume"));
+
+ // Detect game features based on MD5. Again brutally ripped from Gobliins.
+ uint8 md5sum[16];
+ char md5str[32 + 1];
+
+ const KyraGameSettings *g;
+ bool found = false;
+
+ // TODO
+ // Fallback. Maybe we will be able to determine game type from game
+ // data contents
+ _features = 0;
+
+ for (g = kyra_games; g->gameid; g++) {
+ if (!Common::File::exists(g->checkFile))
+ continue;
+
+ if (Common::md5_file(g->checkFile, md5sum, ConfMan.get("path").c_str(), kMD5FileSizeLimit)) {
+ for (int j = 0; j < 16; j++) {
+ sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
+ }
+ } else
+ continue;
+
+ if (strcmp(g->md5sum, (char *)md5str) == 0) {
+ _features = g->features;
+ _game = g->id;
+
+ if (g->description)
+ g_system->setWindowCaption(g->description);
+
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ debug("Unknown MD5 (%s)! Please report the details (language, platform, etc.) of this game to the ScummVM team", md5str);
+ _features = 0;
+ _game = GI_KYRA1;
+ Common::File test;
+ if (test.open("INTRO.VRM")) {
+ _features |= GF_TALKIE;
+ } else {
+ _features |= GF_FLOPPY;
+ }
+
+ // tries to detect the language
+ const KyraLanguageTable *lang = kyra_languages;
+ for (; lang->file; ++lang) {
+ if (test.open(lang->file)) {
+ _features |= lang->language;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ _features |= GF_LNGUNK;
+ }
+ }
+}
+
+int KyraEngine::init(GameDetector &detector) {
+ _system->beginGFXTransaction();
+ initCommonGFX(detector);
+ //for debug reasons (see Screen::updateScreen)
+ //_system->initSize(640, 200);
+ _system->initSize(320, 200);
+ _system->endGFXTransaction();
+
+ // for now we prefer MIDI-to-Adlib conversion over native midi
+ int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB/* | MDT_PREFER_MIDI*/);
+ bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+
+ MidiDriver *driver = MidiDriver::createMidi(midiDriver);
+ if (midiDriver == MD_ADLIB) {
+ // In this case we should play the Adlib tracks, but for now
+ // the automagic MIDI-to-Adlib conversion will do.
+ } else if (native_mt32) {
+ driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+ }
+
+ _sound = new SoundPC(driver, _mixer, this);
+ assert(_sound);
+ static_cast<SoundPC*>(_sound)->hasNativeMT32(native_mt32);
+ _sound->setVolume(255);
+
+ _saveFileMan = _system->getSavefileManager();
+ assert(_saveFileMan);
+ _res = new Resource(this);
+ assert(_res);
+ _screen = new Screen(this, _system);
+ assert(_screen);
+ _sprites = new Sprites(this, _system);
+ assert(_sprites);
+ _seq = new SeqPlayer(this, _system);
+ assert(_seq);
+ _animator = new ScreenAnimator(this, _system);
+ assert(_animator);
+ _animator->init(5, 11, 12);
+ assert(*_animator);
+ _text = new TextDisplayer(_screen);
+ assert(_text);
+
+ _paletteChanged = 1;
+ _currentCharacter = 0;
+ _characterList = new Character[11];
+ assert(_characterList);
+ for (int i = 0; i < 11; ++i) {
+ memset(&_characterList[i], 0, sizeof(Character));
+ memset(_characterList[i].inventoryItems, 0xFF, sizeof(_characterList[i].inventoryItems));
+ }
+ _characterList[0].sceneId = 5;
+ _characterList[0].height = 48;
+ _characterList[0].facing = 3;
+ _characterList[0].currentAnimFrame = 7;
+
+ _scriptInterpreter = new ScriptHelper(this);
+ assert(_scriptInterpreter);
+
+ _npcScriptData = new ScriptData;
+ memset(_npcScriptData, 0, sizeof(ScriptData));
+ assert(_npcScriptData);
+ _npcScript = new ScriptState;
+ assert(_npcScript);
+ memset(_npcScript, 0, sizeof(ScriptState));
+
+ _scriptMain = new ScriptState;
+ assert(_scriptMain);
+ memset(_scriptMain, 0, sizeof(ScriptState));
+
+ _scriptClickData = new ScriptData;
+ assert(_scriptClickData);
+ memset(_scriptClickData, 0, sizeof(ScriptData));
+ _scriptClick = new ScriptState;
+ assert(_scriptClick);
+ memset(_scriptClick, 0, sizeof(ScriptState));
+
+ _debugger = new Debugger(this);
+ assert(_debugger);
+ memset(_shapes, 0, sizeof(_shapes));
+
+ for (int i = 0; i < ARRAYSIZE(_movieObjects); ++i) {
+ _movieObjects[i] = createWSAMovie();
+ }
+
+ memset(_flagsTable, 0, sizeof(_flagsTable));
+
+ _abortWalkFlag = false;
+ _abortWalkFlag2 = false;
+ _talkingCharNum = -1;
+ _charSayUnk3 = -1;
+ _mouseX = _mouseY = -1;
+ memset(_currSentenceColor, 0, 3);
+ _startSentencePalIndex = -1;
+ _fadeText = false;
+
+ _cauldronState = 0;
+ _crystalState[0] = _crystalState[1] = -1;
+
+ _brandonStatusBit = 0;
+ _brandonStatusBit0x02Flag = _brandonStatusBit0x20Flag = 10;
+ _brandonPosX = _brandonPosY = -1;
+ _deathHandler = 0xFF;
+ _poisonDeathCounter = 0;
+
+ memset(_itemTable, 0, sizeof(_itemTable));
+ memset(_exitList, 0xFFFF, sizeof(_exitList));
+ _exitListPtr = 0;
+ _pathfinderFlag = _pathfinderFlag2 = 0;
+ _lastFindWayRet = 0;
+ _sceneChangeState = _loopFlag2 = 0;
+ _timerNextRun = 0;
+
+ _movFacingTable = new int[150];
+ assert(_movFacingTable);
+ _movFacingTable[0] = 8;
+
+ _configTalkspeed = 1;
+
+ _marbleVaseItem = -1;
+ memset(_foyerItemTable, -1, sizeof(_foyerItemTable));
+ _mouseState = _itemInHand = -1;
+ _handleInput = false;
+
+ _currentRoom = 0xFFFF;
+ _scenePhasingFlag = 0;
+ _lastProcessedItem = 0;
+ _lastProcessedItemHeight = 16;
+
+ _unkScreenVar1 = 1;
+ _unkScreenVar2 = 0;
+ _unkScreenVar3 = 0;
+ _unkAmuletVar = 0;
+
+ _endSequenceNeedLoading = 1;
+ _malcolmFlag = 0;
+ _beadStateVar = 0;
+ _endSequenceSkipFlag = 0;
+ _unkEndSeqVar2 = 0;
+ _endSequenceBackUpRect = 0;
+ _unkEndSeqVar4 = 0;
+ _unkEndSeqVar5 = 0;
+ _lastDisplayedPanPage = 0;
+ memset(_panPagesTable, 0, sizeof(_panPagesTable));
+ _finalA = _finalB = _finalC = 0;
+ memset(&_kyragemFadingState, 0, sizeof(_kyragemFadingState));
+ _kyragemFadingState.gOffset = 0x13;
+ _kyragemFadingState.bOffset = 0x13;
+
+ memset(_specialPalettes, 0, sizeof(_specialPalettes));
+ _mousePressFlag = false;
+
+ _targetName = detector._targetName;
+ _menuDirectlyToLoad = false;
+
+ _lastMusicCommand = 0;
+
+ _gameSpeed = 60;
+ _tickLength = (uint8)(1000.0 / _gameSpeed);
+
+ return 0;
+}
+
+KyraEngine::~KyraEngine() {
+ closeFinalWsa();
+ _scriptInterpreter->unloadScript(_npcScriptData);
+ _scriptInterpreter->unloadScript(_scriptClickData);
+
+ delete _debugger;
+ delete _sprites;
+ delete _animator;
+ delete _screen;
+ delete _res;
+ delete _sound;
+ delete _saveFileMan;
+ delete _seq;
+ delete _scriptInterpreter;
+ delete _text;
+
+ delete _npcScriptData;
+ delete _scriptMain;
+
+ delete _scriptClickData;
+ delete _scriptClick;
+
+ delete [] _characterList;
+
+ delete [] _movFacingTable;
+
+ free(_scrollUpButton.process0PtrShape);
+ free(_scrollUpButton.process1PtrShape);
+ free(_scrollUpButton.process2PtrShape);
+ free(_scrollDownButton.process0PtrShape);
+ free(_scrollDownButton.process1PtrShape);
+ free(_scrollDownButton.process2PtrShape);
+
+ for (int i = 0; i < ARRAYSIZE(_shapes); ++i) {
+ if (_shapes[i] != 0) {
+ free(_shapes[i]);
+ _shapes[i] = 0;
+ for (int i2 = 0; i2 < ARRAYSIZE(_shapes); i2++) {
+ if (_shapes[i2] == _shapes[i] && i2 != i) {
+ _shapes[i2] = 0;
+ }
+ }
+ }
+ }
+ for (int i = 0; i < ARRAYSIZE(_sceneAnimTable); ++i) {
+ free(_sceneAnimTable[i]);
+ }
+}
+
+void KyraEngine::errorString(const char *buf1, char *buf2) {
+ strcpy(buf2, buf1);
+}
+
+int KyraEngine::go() {
+ _quitFlag = false;
+ uint32 sz;
+
+ res_loadResources();
+ if (_features & GF_FLOPPY) {
+ _screen->loadFont(Screen::FID_6_FNT, _res->fileData("6.FNT", &sz));
+ }
+ _screen->loadFont(Screen::FID_8_FNT, _res->fileData("8FAT.FNT", &sz));
+ _screen->setScreenDim(0);
+
+ _abortIntroFlag = false;
+
+ if (_features & GF_DEMO) {
+ seq_demo();
+ } else {
+ setGameFlag(0xF3);
+ setGameFlag(0xFD);
+ setGameFlag(0xEF);
+ seq_intro();
+ if (_skipIntroFlag &&_abortIntroFlag)
+ resetGameFlag(0xEF);
+ startup();
+ resetGameFlag(0xEF);
+ mainLoop();
+ }
+ quitGame();
+ return 0;
+}
+
+void KyraEngine::startup() {
+ debug(9, "KyraEngine::startup()");
+ static const uint8 colorMap[] = { 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0 };
+ _screen->setTextColorMap(colorMap);
+// _screen->setFont(Screen::FID_6_FNT);
+ _screen->setAnimBlockPtr(3750);
+ memset(_sceneAnimTable, 0, sizeof(_sceneAnimTable));
+ loadMouseShapes();
+ _currentCharacter = &_characterList[0];
+ for (int i = 1; i < 5; ++i)
+ _animator->setCharacterDefaultFrame(i);
+ for (int i = 5; i <= 10; ++i)
+ setCharactersPositions(i);
+ _animator->setCharactersHeight();
+ resetBrandonPoisonFlags();
+ _maskBuffer = _screen->getPagePtr(5);
+ _screen->_curPage = 0;
+ // XXX
+ for (int i = 0; i < 0x0C; ++i) {
+ int size = _screen->getRectSize(3, 24);
+ _shapes[365+i] = (byte*)malloc(size);
+ }
+ _shapes[0] = (uint8*)malloc(_screen->getRectSize(3, 24));
+ memset(_shapes[0], 0, _screen->getRectSize(3, 24));
+ _shapes[1] = (uint8*)malloc(_screen->getRectSize(4, 32));
+ memset(_shapes[1], 0, _screen->getRectSize(4, 32));
+ _shapes[2] = (uint8*)malloc(_screen->getRectSize(8, 69));
+ memset(_shapes[2], 0, _screen->getRectSize(8, 69));
+ _shapes[3] = (uint8*)malloc(_screen->getRectSize(8, 69));
+ memset(_shapes[3], 0, _screen->getRectSize(8, 69));
+ for (int i = 0; i < _roomTableSize; ++i) {
+ for (int item = 0; item < 12; ++item) {
+ _roomTable[i].itemsTable[item] = 0xFF;
+ _roomTable[i].itemsXPos[item] = 0xFFFF;
+ _roomTable[i].itemsYPos[item] = 0xFF;
+ _roomTable[i].needInit[item] = 0;
+ }
+ }
+ loadCharacterShapes();
+ loadSpecialEffectShapes();
+ loadItems();
+ loadButtonShapes();
+ initMainButtonList();
+ loadMainScreen();
+ setupTimers();
+ loadPalette("PALETTE.COL", _screen->_currentPalette);
+
+ // XXX
+ _animator->initAnimStateList();
+ setCharactersInDefaultScene();
+
+ if (!_scriptInterpreter->loadScript("_STARTUP.EMC", _npcScriptData, _opcodeTable, _opcodeTableSize, 0)) {
+ error("Could not load \"_STARTUP.EMC\" script");
+ }
+ _scriptInterpreter->initScript(_scriptMain, _npcScriptData);
+ if (!_scriptInterpreter->startScript(_scriptMain, 0)) {
+ error("Could not start script function 0 of script \"_STARTUP.EMC\"");
+ }
+ while (_scriptInterpreter->validScript(_scriptMain)) {
+ _scriptInterpreter->runScript(_scriptMain);
+ }
+
+ _scriptInterpreter->unloadScript(_npcScriptData);
+ if (!_scriptInterpreter->loadScript("_NPC.EMC", _npcScriptData, _opcodeTable, _opcodeTableSize, 0)) {
+ error("Could not load \"_NPC.EMC\" script");
+ }
+
+ snd_playTheme(1);
+ snd_setSoundEffectFile(1);
+ enterNewScene(_currentCharacter->sceneId, _currentCharacter->facing, 0, 0, 1);
+
+ if (_abortIntroFlag && _skipFlag) {
+ _menuDirectlyToLoad = true;
+ _screen->setMouseCursor(1, 1, _shapes[4]);
+ buttonMenuCallback(0);
+ _menuDirectlyToLoad = false;
+ } else
+ saveGame(getSavegameFilename(0), "New game");
+}
+
+void KyraEngine::mainLoop() {
+ debug(9, "KyraEngine::mainLoop()");
+
+ while (!_quitFlag) {
+ int32 frameTime = (int32)_system->getMillis();
+ _skipFlag = false;
+
+ if (_currentCharacter->sceneId == 210) {
+ updateKyragemFading();
+ if (seq_playEnd()) {
+ if (_deathHandler != 8)
+ break;
+ }
+ }
+
+ if (_deathHandler != 0xFF) {
+ // this is only used until the original gui is implemented
+ GUI::MessageDialog dialog("Brandon is dead! Game over!", "Quit");
+ dialog.runModal();
+ break;
+ }
+
+ if (_brandonStatusBit & 2) {
+ if (_brandonStatusBit0x02Flag)
+ _animator->animRefreshNPC(0);
+ }
+ if (_brandonStatusBit & 0x20) {
+ if (_brandonStatusBit0x20Flag) {
+ _animator->animRefreshNPC(0);
+ _brandonStatusBit0x20Flag = 0;
+ }
+ }
+
+ _screen->showMouse();
+
+ processButtonList(_buttonList);
+ updateMousePointer();
+ updateGameTimers();
+ updateTextFade();
+
+ _handleInput = true;
+ delay((frameTime + _gameSpeed) - _system->getMillis(), true, true);
+ _handleInput = false;
+ }
+}
+
+void KyraEngine::quitGame() {
+ res_unloadResources(RES_ALL);
+
+ for (int i = 0; i < ARRAYSIZE(_movieObjects); ++i) {
+ _movieObjects[i]->close();
+ delete _movieObjects[i];
+ _movieObjects[i] = 0;
+ }
+
+ _system->quit();
+}
+
+void KyraEngine::delay(uint32 amount, bool update, bool isMainLoop) {
+ OSystem::Event event;
+ char saveLoadSlot[20];
+ char savegameName[14];
+
+ _mousePressFlag = false;
+ uint32 start = _system->getMillis();
+ do {
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ if (event.kbd.keycode >= '1' && event.kbd.keycode <= '9' &&
+ (event.kbd.flags == OSystem::KBD_CTRL || event.kbd.flags == OSystem::KBD_ALT) && isMainLoop) {
+ sprintf(saveLoadSlot, "%s.00%d", _targetName.c_str(), event.kbd.keycode - '0');
+ if (event.kbd.flags == OSystem::KBD_CTRL)
+ loadGame(saveLoadSlot);
+ else {
+ sprintf(savegameName, "Quicksave %d", event.kbd.keycode - '0');
+ saveGame(saveLoadSlot, savegameName);
+ }
+ } else if (event.kbd.flags == OSystem::KBD_CTRL) {
+ if (event.kbd.keycode == 'd')
+ _debugger->attach();
+ else if (event.kbd.keycode == 'q')
+ _quitFlag = true;
+ } else if (event.kbd.keycode == '.')
+ _skipFlag = true;
+ else if (event.kbd.keycode == 13 || event.kbd.keycode == 32 || event.kbd.keycode == 27) {
+ _abortIntroFlag = true;
+ _skipFlag = true;
+ }
+
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ _system->updateScreen();
+ break;
+ case OSystem::EVENT_QUIT:
+ quitGame();
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+ _mousePressFlag = true;
+ if (_abortWalkFlag2) {
+ _abortWalkFlag = true;
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ }
+ if (_handleInput) {
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ _handleInput = false;
+ processInput(_mouseX, _mouseY);
+ _handleInput = true;
+ } else
+ _skipFlag = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (_debugger->isAttached())
+ _debugger->onFrame();
+
+ if (update)
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+
+ if (_currentCharacter && _currentCharacter->sceneId == 210) {
+ updateKyragemFading();
+ }
+
+ if (amount > 0 && !_skipFlag) {
+ _system->delayMillis((amount > 10) ? 10 : amount);
+ }
+ } while (!_skipFlag && _system->getMillis() < start + amount);
+
+}
+
+void KyraEngine::waitForEvent() {
+ bool finished = false;
+ OSystem::Event event;
+ while (!finished) {
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ finished = true;
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ break;
+ case OSystem::EVENT_QUIT:
+ quitGame();
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+ finished = true;
+ _skipFlag = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (_debugger->isAttached())
+ _debugger->onFrame();
+
+ _system->delayMillis(10);
+ }
+}
+
+void KyraEngine::delayWithTicks(int ticks) {
+ uint32 nextTime = _system->getMillis() + ticks * _tickLength;
+ while (_system->getMillis() < nextTime && !_skipFlag) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (_currentCharacter->sceneId == 210) {
+ updateKyragemFading();
+ seq_playEnd();
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark - Animation/shape specific code
+#pragma mark -
+
+void KyraEngine::setupShapes123(const Shape *shapeTable, int endShape, int flags) {
+ debug(9, "KyraEngine::setupShapes123(0x%X, startShape, flags)", shapeTable, endShape, flags);
+ for (int i = 123; i <= 172; ++i) {
+ _shapes[4+i] = NULL;
+ }
+ uint8 curImage = 0xFF;
+ int curPageBackUp = _screen->_curPage;
+ _screen->_curPage = 8; // we are using page 8 here in the original page 2 was backuped and then used for this stuff
+ int shapeFlags = 2;
+ if (flags)
+ shapeFlags = 3;
+ for (int i = 123; i < 123+endShape; ++i) {
+ uint8 newImage = shapeTable[i-123].imageIndex;
+ if (newImage != curImage && newImage != 0xFF) {
+ assert(_characterImageTable);
+ loadBitmap(_characterImageTable[newImage], 8, 8, 0);
+ curImage = newImage;
+ }
+ _shapes[4+i] = _screen->encodeShape(shapeTable[i-123].x<<3, shapeTable[i-123].y, shapeTable[i-123].w<<3, shapeTable[i-123].h, shapeFlags);
+ assert(i-7 < _defaultShapeTableSize);
+ _defaultShapeTable[i-7].xOffset = shapeTable[i-123].xOffset;
+ _defaultShapeTable[i-7].yOffset = shapeTable[i-123].yOffset;
+ _defaultShapeTable[i-7].w = shapeTable[i-123].w;
+ _defaultShapeTable[i-7].h = shapeTable[i-123].h;
+ }
+ _screen->_curPage = curPageBackUp;
+}
+
+void KyraEngine::freeShapes123() {
+ debug(9, "KyraEngine::freeShapes123()");
+ for (int i = 123; i <= 172; ++i) {
+ free(_shapes[4+i]);
+ _shapes[4+i] = NULL;
+ }
+}
+
+#pragma mark -
+#pragma mark - Misc stuff
+#pragma mark -
+
+Movie *KyraEngine::createWSAMovie() {
+ // for kyra2 here could be added then WSAMovieV2
+ return new WSAMovieV1(this);
+}
+
+int KyraEngine::setGameFlag(int flag) {
+ _flagsTable[flag >> 3] |= (1 << (flag & 7));
+ return 1;
+}
+
+int KyraEngine::queryGameFlag(int flag) {
+ return ((_flagsTable[flag >> 3] >> (flag & 7)) & 1);
+}
+
+int KyraEngine::resetGameFlag(int flag) {
+ _flagsTable[flag >> 3] &= ~(1 << (flag & 7));
+ return 0;
+}
+
+void KyraEngine::setBrandonPoisonFlags(int reset) {
+ debug(9, "KyraEngine::setBrandonPoisonFlags(%d)", reset);
+ _brandonStatusBit |= 1;
+ if (reset)
+ _poisonDeathCounter = 0;
+ for (int i = 0; i < 0x100; ++i) {
+ _brandonPoisonFlagsGFX[i] = i;
+ }
+ _brandonPoisonFlagsGFX[0x99] = 0x34;
+ _brandonPoisonFlagsGFX[0x9A] = 0x35;
+ _brandonPoisonFlagsGFX[0x9B] = 0x37;
+ _brandonPoisonFlagsGFX[0x9C] = 0x38;
+ _brandonPoisonFlagsGFX[0x9D] = 0x2B;
+}
+
+void KyraEngine::resetBrandonPoisonFlags() {
+ debug(9, "KyraEngine::resetBrandonPoisonFlags()");
+ _brandonStatusBit = 0;
+ for (int i = 0; i < 0x100; ++i) {
+ _brandonPoisonFlagsGFX[i] = i;
+ }
+}
+
+#pragma mark -
+#pragma mark - Input
+#pragma mark -
+
+void KyraEngine::processInput(int xpos, int ypos) {
+ debug(9, "KyraEngine::processInput(%d, %d)", xpos, ypos);
+ _abortWalkFlag2 = false;
+
+ if (processInputHelper(xpos, ypos)) {
+ return;
+ }
+ uint8 item = findItemAtPos(xpos, ypos);
+ if (item == 0xFF) {
+ _changedScene = false;
+ int handled = clickEventHandler(xpos, ypos);
+ if (_changedScene || handled)
+ return;
+ }
+
+ // XXX _deathHandler specific
+ if (ypos <= 158) {
+ uint16 exit = 0xFFFF;
+ if (xpos < 12) {
+ exit = _walkBlockWest;
+ } else if (xpos >= 308) {
+ exit = _walkBlockEast;
+ } else if (ypos >= 136) {
+ exit = _walkBlockSouth;
+ } else if (ypos < 12) {
+ exit = _walkBlockNorth;
+ }
+
+ if (exit != 0xFFFF) {
+ _abortWalkFlag2 = true;
+ handleSceneChange(xpos, ypos, 1, 1);
+ _abortWalkFlag2 = false;
+ return;
+ } else {
+ int script = checkForNPCScriptRun(xpos, ypos);
+ if (script >= 0) {
+ runNpcScript(script);
+ return;
+ }
+ if (_itemInHand != -1) {
+ if (ypos < 155) {
+ if (hasClickedOnExit(xpos, ypos)) {
+ _abortWalkFlag2 = true;
+ handleSceneChange(xpos, ypos, 1, 1);
+ _abortWalkFlag2 = false;
+ return;
+ }
+ dropItem(0, _itemInHand, xpos, ypos, 1);
+ }
+ } else {
+ if (ypos <= 155) {
+ _abortWalkFlag2 = true;
+ handleSceneChange(xpos, ypos, 1, 1);
+ _abortWalkFlag2 = false;
+ }
+ }
+ }
+ }
+}
+
+int KyraEngine::processInputHelper(int xpos, int ypos) {
+ debug(9, "KyraEngine::processInputHelper(%d, %d)", xpos, ypos);
+ uint8 item = findItemAtPos(xpos, ypos);
+ if (item != 0xFF) {
+ if (_itemInHand == -1) {
+ _screen->hideMouse();
+ _animator->animRemoveGameItem(item);
+ snd_playSoundEffect(53);
+ assert(_currentCharacter->sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[_currentCharacter->sceneId];
+ int item2 = currentRoom->itemsTable[item];
+ currentRoom->itemsTable[item] = 0xFF;
+ setMouseItem(item2);
+ assert(_itemList && _takenList);
+ updateSentenceCommand(_itemList[item2], _takenList[0], 179);
+ _itemInHand = item2;
+ _screen->showMouse();
+ clickEventHandler2();
+ return 1;
+ } else {
+ exchangeItemWithMouseItem(_currentCharacter->sceneId, item);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int KyraEngine::clickEventHandler(int xpos, int ypos) {
+ debug(9, "KyraEngine::clickEventHandler(%d, %d)", xpos, ypos);
+ _scriptInterpreter->initScript(_scriptClick, _scriptClickData);
+ _scriptClick->variables[1] = xpos;
+ _scriptClick->variables[2] = ypos;
+ _scriptClick->variables[3] = 0;
+ _scriptClick->variables[4] = _itemInHand;
+ _scriptInterpreter->startScript(_scriptClick, 1);
+
+ while (_scriptInterpreter->validScript(_scriptClick)) {
+ _scriptInterpreter->runScript(_scriptClick);
+ }
+
+ return _scriptClick->variables[3];
+}
+
+void KyraEngine::updateMousePointer(bool forceUpdate) {
+ int shape = 0;
+
+ int newMouseState = 0;
+ int newX = 0;
+ int newY = 0;
+ if (_mouseY <= 158) {
+ if (_mouseX >= 12) {
+ if (_mouseX >= 308) {
+ if (_walkBlockEast == 0xFFFF) {
+ newMouseState = -2;
+ } else {
+ newMouseState = -5;
+ shape = 3;
+ newX = 7;
+ newY = 5;
+ }
+ } else if (_mouseY >= 136) {
+ if (_walkBlockSouth == 0xFFFF) {
+ newMouseState = -2;
+ } else {
+ newMouseState = -4;
+ shape = 4;
+ newX = 5;
+ newY = 7;
+ }
+ } else if (_mouseY < 12) {
+ if (_walkBlockNorth == 0xFFFF) {
+ newMouseState = -2;
+ } else {
+ newMouseState = -6;
+ shape = 2;
+ newX = 5;
+ newY = 1;
+ }
+ }
+ } else {
+ if (_walkBlockWest == 0xFFFF) {
+ newMouseState = -2;
+ } else {
+ newMouseState = -3;
+ newX = 1;
+ newY = shape = 5;
+ }
+ }
+ }
+
+ if (_mouseX >= _entranceMouseCursorTracks[0] && _mouseY >= _entranceMouseCursorTracks[1]
+ && _mouseX <= _entranceMouseCursorTracks[2] && _mouseY <= _entranceMouseCursorTracks[3]) {
+ switch (_entranceMouseCursorTracks[4]) {
+ case 0:
+ newMouseState = -6;
+ shape = 2;
+ newX = 5;
+ newY = 1;
+ break;
+
+ case 2:
+ newMouseState = -5;
+ shape = 3;
+ newX = 7;
+ newY = 5;
+ break;
+
+ case 4:
+ newMouseState = -4;
+ shape = 4;
+ newX = 5;
+ newY = 7;
+ break;
+
+ case 6:
+ newMouseState = -3;
+ shape = 5;
+ newX = 1;
+ newY = 5;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (newMouseState == -2) {
+ shape = 6;
+ newX = 4;
+ newY = 4;
+ }
+
+ if ((newMouseState && _mouseState != newMouseState) || (newMouseState && forceUpdate)) {
+ _mouseState = newMouseState;
+ _screen->hideMouse();
+ _screen->setMouseCursor(newX, newY, _shapes[4+shape]);
+ _screen->showMouse();
+ }
+
+ if (!newMouseState) {
+ if (_mouseState != _itemInHand || forceUpdate) {
+ if (_mouseY > 158 || (_mouseX >= 12 && _mouseX < 308 && _mouseY < 136 && _mouseY >= 12) || forceUpdate) {
+ _mouseState = _itemInHand;
+ _screen->hideMouse();
+ if (_itemInHand == -1) {
+ _screen->setMouseCursor(1, 1, _shapes[4]);
+ } else {
+ _screen->setMouseCursor(8, 15, _shapes[220+_itemInHand]);
+ }
+ _screen->showMouse();
+ }
+ }
+ }
+}
+
+bool KyraEngine::hasClickedOnExit(int xpos, int ypos) {
+ debug(9, "KyraEngine::hasClickedOnExit(%d, %d)", xpos, ypos);
+ if (xpos < 16 || xpos >= 304) {
+ return true;
+ }
+ if (ypos < 8)
+ return true;
+ if (ypos < 136 || ypos > 155) {
+ return false;
+ }
+ return true;
+}
+
+void KyraEngine::clickEventHandler2() {
+ debug(9, "KyraEngine::clickEventHandler2()");
+ _scriptInterpreter->initScript(_scriptClick, _scriptClickData);
+ _scriptClick->variables[0] = _currentCharacter->sceneId;
+ _scriptClick->variables[1] = _mouseX;
+ _scriptClick->variables[2] = _mouseY;
+ _scriptClick->variables[4] = _itemInHand;
+ _scriptInterpreter->startScript(_scriptClick, 6);
+
+ while (_scriptInterpreter->validScript(_scriptClick)) {
+ _scriptInterpreter->runScript(_scriptClick);
+ }
+}
+
+int KyraEngine::checkForNPCScriptRun(int xpos, int ypos) {
+ debug(9, "KyraEngine::checkForNPCScriptRun(%d, %d)", xpos, ypos);
+ int returnValue = -1;
+ const Character *currentChar = _currentCharacter;
+ int charLeft = 0, charRight = 0, charTop = 0, charBottom = 0;
+
+ int scaleFactor = _scaleTable[currentChar->y1];
+ int addX = (((scaleFactor*8)*3)>>8)>>1;
+ int addY = ((scaleFactor*3)<<4)>>8;
+
+ charLeft = currentChar->x1 - addX;
+ charRight = currentChar->x1 + addX;
+ charTop = currentChar->y1 - addY;
+ charBottom = currentChar->y1;
+
+ if (xpos >= charLeft && charRight >= xpos && charTop <= ypos && charBottom >= ypos) {
+ return 0;
+ }
+
+ if (xpos > 304 || xpos < 16) {
+ return -1;
+ }
+
+ for (int i = 1; i < 5; ++i) {
+ currentChar = &_characterList[i];
+
+ if (currentChar->sceneId != _currentCharacter->sceneId)
+ continue;
+
+ charLeft = currentChar->x1 - 12;
+ charRight = currentChar->x1 + 11;
+ charTop = currentChar->y1 - 48;
+ // if (!i) {
+ // charBottom = currentChar->y2 - 16;
+ // } else {
+ charBottom = currentChar->y1;
+ // }
+
+ if (xpos < charLeft || xpos > charRight || ypos < charTop || charBottom < ypos) {
+ continue;
+ }
+
+ if (returnValue != -1) {
+ if (currentChar->y1 >= _characterList[returnValue].y1) {
+ returnValue = i;
+ }
+ } else {
+ returnValue = i;
+ }
+ }
+
+ return returnValue;
+}
+
+void KyraEngine::runNpcScript(int func) {
+ debug(9, "KyraEngine::runNpcScript(%d)", func);
+ _scriptInterpreter->initScript(_npcScript, _npcScriptData);
+ _scriptInterpreter->startScript(_npcScript, func);
+ _npcScript->variables[0] = _currentCharacter->sceneId;
+ _npcScript->variables[4] = _itemInHand;
+ _npcScript->variables[5] = func;
+
+ while (_scriptInterpreter->validScript(_npcScript)) {
+ _scriptInterpreter->runScript(_npcScript);
+ }
+}
+} // End of namespace Kyra
diff --git a/engines/kyra/kyra.h b/engines/kyra/kyra.h
new file mode 100644
index 0000000000..f1112fa799
--- /dev/null
+++ b/engines/kyra/kyra.h
@@ -0,0 +1,961 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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$
+ *
+ */
+
+#ifndef KYRA_H
+#define KYRA_H
+
+#include "base/engine.h"
+#include "common/rect.h"
+#include "sound/mixer.h"
+#include "common/file.h"
+
+class AudioStream;
+
+namespace Kyra {
+
+class Movie;
+class Sound;
+class SeqPlayer;
+class Resource;
+class PAKFile;
+class Screen;
+class Sprites;
+class ScriptHelper;
+class Debugger;
+class ScreenAnimator;
+class TextDisplayer;
+class KyraEngine;
+
+struct ScriptState;
+struct ScriptData;
+
+enum {
+ GF_FLOPPY = 1 << 0,
+ GF_TALKIE = 1 << 1,
+ GF_AUDIOCD = 1 << 2, // FM-Towns versions seems to use audio CD
+ GF_DEMO = 1 << 3,
+ GF_ENGLISH = 1 << 4,
+ GF_FRENCH = 1 << 5,
+ GF_GERMAN = 1 << 6,
+ GF_SPANISH = 1 << 7,
+ // other languages here
+ GF_LNGUNK = 1 << 16
+};
+
+enum {
+ GI_KYRA1 = 0
+};
+
+struct Character {
+ uint16 sceneId;
+ uint8 height;
+ uint8 facing;
+ uint16 currentAnimFrame;
+ uint8 inventoryItems[10];
+ int16 x1, y1, x2, y2;
+};
+
+struct Shape {
+ uint8 imageIndex;
+ int8 xOffset, yOffset;
+ uint8 x, y, w, h;
+};
+
+struct Room {
+ uint8 nameIndex;
+ uint16 northExit;
+ uint16 eastExit;
+ uint16 southExit;
+ uint16 westExit;
+ uint8 itemsTable[12];
+ uint16 itemsXPos[12];
+ uint8 itemsYPos[12];
+ uint8 needInit[12];
+};
+
+struct Rect {
+ int x, y;
+ int x2, y2;
+};
+
+struct Item {
+ uint8 unk1;
+ uint8 height;
+ uint8 unk2;
+ uint8 unk3;
+};
+
+struct SeqLoop {
+ const uint8 *ptr;
+ uint16 count;
+};
+
+struct SceneExits {
+ uint16 northXPos;
+ uint8 northYPos;
+ uint16 eastXPos;
+ uint8 eastYPos;
+ uint16 southXPos;
+ uint8 southYPos;
+ uint16 westXPos;
+ uint8 westYPos;
+};
+
+struct BeadState {
+ int16 x;
+ int16 y;
+ int16 width;
+ int16 height;
+ int16 dstX;
+ int16 dstY;
+ int16 width2;
+ int16 unk8;
+ int16 unk9;
+ int16 tableIndex;
+};
+
+struct Timer {
+ uint8 active;
+ int32 countdown;
+ uint32 nextRun;
+ void (KyraEngine::*func)(int timerNum);
+};
+
+struct Button {
+ Button *nextButton;
+ uint16 specialValue;
+ // uint8 unk[4];
+ uint8 process0;
+ uint8 process1;
+ uint8 process2;
+ // uint8 unk
+ uint16 flags;
+ typedef int (KyraEngine::*ButtonCallback)(Button*);
+ // using 6 pointers instead of 3 as in the orignal here (safer for use with classes)
+ uint8 *process0PtrShape;
+ uint8 *process1PtrShape;
+ uint8 *process2PtrShape;
+ ButtonCallback process0PtrCallback;
+ ButtonCallback process1PtrCallback;
+ ButtonCallback process2PtrCallback;
+ uint16 dimTableIndex;
+ uint16 x;
+ uint16 y;
+ uint16 width;
+ uint16 height;
+ // uint8 unk[8];
+ uint32 flags2;
+ ButtonCallback buttonCallback;
+ // uint8 unk[8];
+};
+
+struct MenuItem {
+ bool enabled;
+ uint16 field_1;
+ uint8 field_3;
+ const char *itemString;
+ int16 x;
+ int16 field_9;
+ uint16 y;
+ uint16 width;
+ uint16 height;
+ uint8 textColor;
+ uint8 highlightColor;
+ int16 field_12;
+ uint8 field_13;
+ uint8 bgcolor;
+ uint8 color1;
+ uint8 color2;
+ int (KyraEngine::*callback)(Button*);
+ int16 field_1b;
+ const char *labelString;
+ uint16 field_21;
+ uint8 field_23;
+ uint8 field_24;
+ uint32 field_25;
+};
+
+struct Menu {
+ int16 x;
+ int16 y;
+ uint16 width;
+ uint16 height;
+ uint8 bgcolor;
+ uint8 color1;
+ uint8 color2;
+ const char *menuName;
+ uint8 textColor;
+ int16 field_10;
+ uint16 field_12;
+ uint16 highlightedItem;
+ uint8 nrOfItems;
+ int16 scrollUpBtnX;
+ int16 scrollUpBtnY;
+ int16 scrollDownBtnX;
+ int16 scrollDownBtnY;
+ MenuItem item[6];
+};
+
+struct KeyboardEvent {
+ bool pending;
+ uint32 repeat;
+ uint8 ascii;
+};
+
+class KyraEngine : public Engine {
+ friend class MusicPlayer;
+ friend class Debugger;
+ friend class ScreenAnimator;
+public:
+
+ enum {
+ MUSIC_INTRO = 0
+ };
+
+ KyraEngine(GameDetector *detector, OSystem *system);
+ ~KyraEngine();
+
+ void errorString(const char *buf_input, char *buf_output);
+
+ Resource *resource() { return _res; }
+ Screen *screen() { return _screen; }
+ ScreenAnimator *animator() { return _animator; }
+ TextDisplayer *text() { return _text; }
+ Sound *sound() { return _sound; }
+ uint32 tickLength() const { return _tickLength; }
+ Movie *createWSAMovie();
+
+ uint8 game() const { return _game; }
+ uint32 features() const { return _features; }
+
+ uint8 **shapes() { return _shapes; }
+ Character *currentCharacter() { return _currentCharacter; }
+ Character *characterList() { return _characterList; }
+ uint16 brandonStatus() { return _brandonStatusBit; }
+
+ int _paletteChanged;
+ Common::RandomSource _rnd;
+ int16 _northExitHeight;
+
+ typedef void (KyraEngine::*IntroProc)();
+ typedef int (KyraEngine::*OpcodeProc)(ScriptState *script);
+
+ const char **seqWSATable() { return const_cast<const char **>(_seq_WSATable); }
+ const char **seqCPSTable() { return const_cast<const char **>(_seq_CPSTable); }
+ const char **seqCOLTable() { return const_cast<const char **>(_seq_COLTable); }
+ const char **seqTextsTable() { return const_cast<const char **>(_seq_textsTable); }
+
+ const uint8 **palTable1() { return const_cast<const uint8 **>(&_specialPalettes[0]); }
+ const uint8 **palTable2() { return const_cast<const uint8 **>(&_specialPalettes[29]); }
+
+ bool seq_skipSequence() const;
+ void delay(uint32 millis, bool update = false, bool mainLoop = false);
+ void quitGame();
+ void loadBitmap(const char *filename, int tempPage, int dstPage, uint8 *palData);
+
+ void snd_playTheme(int file, int track = 0);
+ void snd_playVoiceFile(int id);
+ void snd_voiceWaitForFinish(bool ingame = true);
+ void snd_playSoundEffect(int track);
+ void snd_playWanderScoreViaMap(int command, int restart);
+
+ void drawSentenceCommand(char *sentence, int unk1);
+ void updateSentenceCommand(char *str1, char *str2, int unk1);
+ void updateTextFade();
+
+ void updateGameTimers();
+ void clearNextEventTickCount();
+ void setTimerCountdown(uint8 timer, int32 countdown);
+ void setTimerDelay(uint8 timer, int32 countdown);
+ int16 getTimerDelay(uint8 timer);
+ void enableTimer(uint8 timer);
+ void disableTimer(uint8 timer);
+
+ void waitTicks(int ticks);
+ void delayWithTicks(int ticks);
+
+ void saveGame(const char *fileName, const char *saveName);
+ void loadGame(const char *fileName);
+
+ int mouseX() { return _mouseX; }
+ int mouseY() { return _mouseY; }
+
+ // all opcode procs (maybe that is somehow useless atm)
+ int cmd_magicInMouseItem(ScriptState *script);
+ int cmd_characterSays(ScriptState *script);
+ int cmd_pauseTicks(ScriptState *script);
+ int cmd_drawSceneAnimShape(ScriptState *script);
+ int cmd_queryGameFlag(ScriptState *script);
+ int cmd_setGameFlag(ScriptState *script);
+ int cmd_resetGameFlag(ScriptState *script);
+ int cmd_runNPCScript(ScriptState *script);
+ int cmd_setSpecialExitList(ScriptState *script);
+ int cmd_blockInWalkableRegion(ScriptState *script);
+ int cmd_blockOutWalkableRegion(ScriptState *script);
+ int cmd_walkPlayerToPoint(ScriptState *script);
+ int cmd_dropItemInScene(ScriptState *script);
+ int cmd_drawAnimShapeIntoScene(ScriptState *script);
+ int cmd_createMouseItem(ScriptState *script);
+ int cmd_savePageToDisk(ScriptState *script);
+ int cmd_sceneAnimOn(ScriptState *script);
+ int cmd_sceneAnimOff(ScriptState *script);
+ int cmd_getElapsedSeconds(ScriptState *script);
+ int cmd_mouseIsPointer(ScriptState *script);
+ int cmd_destroyMouseItem(ScriptState *script);
+ int cmd_runSceneAnimUntilDone(ScriptState *script);
+ int cmd_fadeSpecialPalette(ScriptState *script);
+ int cmd_playAdlibSound(ScriptState *script);
+ int cmd_playAdlibScore(ScriptState *script);
+ int cmd_phaseInSameScene(ScriptState *script);
+ int cmd_setScenePhasingFlag(ScriptState *script);
+ int cmd_resetScenePhasingFlag(ScriptState *script);
+ int cmd_queryScenePhasingFlag(ScriptState *script);
+ int cmd_sceneToDirection(ScriptState *script);
+ int cmd_setBirthstoneGem(ScriptState *script);
+ int cmd_placeItemInGenericMapScene(ScriptState *script);
+ int cmd_setBrandonStatusBit(ScriptState *script);
+ int cmd_pauseSeconds(ScriptState *script);
+ int cmd_getCharactersLocation(ScriptState *script);
+ int cmd_runNPCSubscript(ScriptState *script);
+ int cmd_magicOutMouseItem(ScriptState *script);
+ int cmd_internalAnimOn(ScriptState *script);
+ int cmd_forceBrandonToNormal(ScriptState *script);
+ int cmd_poisonDeathNow(ScriptState *script);
+ int cmd_setScaleMode(ScriptState *script);
+ int cmd_openWSAFile(ScriptState *script);
+ int cmd_closeWSAFile(ScriptState *script);
+ int cmd_runWSAFromBeginningToEnd(ScriptState *script);
+ int cmd_displayWSAFrame(ScriptState *script);
+ int cmd_enterNewScene(ScriptState *script);
+ int cmd_setSpecialEnterXAndY(ScriptState *script);
+ int cmd_runWSAFrames(ScriptState *script);
+ int cmd_popBrandonIntoScene(ScriptState *script);
+ int cmd_restoreAllObjectBackgrounds(ScriptState *script);
+ int cmd_setCustomPaletteRange(ScriptState *script);
+ int cmd_loadPageFromDisk(ScriptState *script);
+ int cmd_customPrintTalkString(ScriptState *script);
+ int cmd_restoreCustomPrintBackground(ScriptState *script);
+ int cmd_hideMouse(ScriptState *script);
+ int cmd_showMouse(ScriptState *script);
+ int cmd_getCharacterX(ScriptState *script);
+ int cmd_getCharacterY(ScriptState *script);
+ int cmd_changeCharactersFacing(ScriptState *script);
+ int cmd_copyWSARegion(ScriptState *script);
+ int cmd_printText(ScriptState *script);
+ int cmd_random(ScriptState *script);
+ int cmd_loadSoundFile(ScriptState *script);
+ int cmd_displayWSAFrameOnHidPage(ScriptState *script);
+ int cmd_displayWSASequentialFrames(ScriptState *script);
+ int cmd_drawCharacterStanding(ScriptState *script);
+ int cmd_internalAnimOff(ScriptState *script);
+ int cmd_changeCharactersXAndY(ScriptState *script);
+ int cmd_clearSceneAnimatorBeacon(ScriptState *script);
+ int cmd_querySceneAnimatorBeacon(ScriptState *script);
+ int cmd_refreshSceneAnimator(ScriptState *script);
+ int cmd_placeItemInOffScene(ScriptState *script);
+ int cmd_wipeDownMouseItem(ScriptState *script);
+ int cmd_placeCharacterInOtherScene(ScriptState *script);
+ int cmd_getKey(ScriptState *script);
+ int cmd_specificItemInInventory(ScriptState *script);
+ int cmd_popMobileNPCIntoScene(ScriptState *script);
+ int cmd_mobileCharacterInScene(ScriptState *script);
+ int cmd_hideMobileCharacter(ScriptState *script);
+ int cmd_unhideMobileCharacter(ScriptState *script);
+ int cmd_setCharactersLocation(ScriptState *script);
+ int cmd_walkCharacterToPoint(ScriptState *script);
+ int cmd_specialEventDisplayBrynnsNote(ScriptState *script);
+ int cmd_specialEventRemoveBrynnsNote(ScriptState *script);
+ int cmd_setLogicPage(ScriptState *script);
+ int cmd_fatPrint(ScriptState *script);
+ int cmd_preserveAllObjectBackgrounds(ScriptState *script);
+ int cmd_updateSceneAnimations(ScriptState *script);
+ int cmd_sceneAnimationActive(ScriptState *script);
+ int cmd_setCharactersMovementDelay(ScriptState *script);
+ int cmd_getCharactersFacing(ScriptState *script);
+ int cmd_bkgdScrollSceneAndMasksRight(ScriptState *script);
+ int cmd_dispelMagicAnimation(ScriptState *script);
+ int cmd_findBrightestFireberry(ScriptState *script);
+ int cmd_setFireberryGlowPalette(ScriptState *script);
+ int cmd_setDeathHandlerFlag(ScriptState *script);
+ int cmd_drinkPotionAnimation(ScriptState *script);
+ int cmd_makeAmuletAppear(ScriptState *script);
+ int cmd_drawItemShapeIntoScene(ScriptState *script);
+ int cmd_setCharactersCurrentFrame(ScriptState *script);
+ int cmd_waitForConfirmationMouseClick(ScriptState *script);
+ int cmd_pageFlip(ScriptState *script);
+ int cmd_setSceneFile(ScriptState *script);
+ int cmd_getItemInMarbleVase(ScriptState *script);
+ int cmd_setItemInMarbleVase(ScriptState *script);
+ int cmd_addItemToInventory(ScriptState *script);
+ int cmd_intPrint(ScriptState *script);
+ int cmd_shakeScreen(ScriptState *script);
+ int cmd_createAmuletJewel(ScriptState *script);
+ int cmd_setSceneAnimCurrXY(ScriptState *script);
+ int cmd_poisonBrandonAndRemaps(ScriptState *script);
+ int cmd_fillFlaskWithWater(ScriptState *script);
+ int cmd_getCharactersMovementDelay(ScriptState *script);
+ int cmd_getBirthstoneGem(ScriptState *script);
+ int cmd_queryBrandonStatusBit(ScriptState *script);
+ int cmd_playFluteAnimation(ScriptState *script);
+ int cmd_playWinterScrollSequence(ScriptState *script);
+ int cmd_getIdolGem(ScriptState *script);
+ int cmd_setIdolGem(ScriptState *script);
+ int cmd_totalItemsInScene(ScriptState *script);
+ int cmd_restoreBrandonsMovementDelay(ScriptState *script);
+ int cmd_setMousePos(ScriptState *script);
+ int cmd_getMouseState(ScriptState *script);
+ int cmd_setEntranceMouseCursorTrack(ScriptState *script);
+ int cmd_itemAppearsOnGround(ScriptState *script);
+ int cmd_setNoDrawShapesFlag(ScriptState *script);
+ int cmd_fadeEntirePalette(ScriptState *script);
+ int cmd_itemOnGroundHere(ScriptState *script);
+ int cmd_queryCauldronState(ScriptState *script);
+ int cmd_setCauldronState(ScriptState *script);
+ int cmd_queryCrystalState(ScriptState *script);
+ int cmd_setCrystalState(ScriptState *script);
+ int cmd_setPaletteRange(ScriptState *script);
+ int cmd_shrinkBrandonDown(ScriptState *script);
+ int cmd_growBrandonUp(ScriptState *script);
+ int cmd_setBrandonScaleXAndY(ScriptState *script);
+ int cmd_resetScaleMode(ScriptState *script);
+ int cmd_getScaleDepthTableValue(ScriptState *script);
+ int cmd_setScaleDepthTableValue(ScriptState *script);
+ int cmd_message(ScriptState *script);
+ int cmd_checkClickOnNPC(ScriptState *script);
+ int cmd_getFoyerItem(ScriptState *script);
+ int cmd_setFoyerItem(ScriptState *script);
+ int cmd_setNoItemDropRegion(ScriptState *script);
+ int cmd_walkMalcolmOn(ScriptState *script);
+ int cmd_passiveProtection(ScriptState *script);
+ int cmd_setPlayingLoop(ScriptState *script);
+ int cmd_brandonToStoneSequence(ScriptState *script);
+ int cmd_brandonHealingSequence(ScriptState *script);
+ int cmd_protectCommandLine(ScriptState *script);
+ int cmd_pauseMusicSeconds(ScriptState *script);
+ int cmd_resetMaskRegion(ScriptState *script);
+ int cmd_setPaletteChangeFlag(ScriptState *script);
+ int cmd_fillRect(ScriptState *script);
+ int cmd_dummy(ScriptState *script);
+ int cmd_vocUnload(ScriptState *script);
+ int cmd_vocLoad(ScriptState *script);
+
+protected:
+
+ int go();
+ int init(GameDetector &detector);
+
+ void startup();
+ void mainLoop();
+ int initCharacterChat(int8 charNum);
+ int8 getChatPartnerNum();
+ void backupChatPartnerAnimFrame(int8 charNum);
+ void restoreChatPartnerAnimFrame(int8 charNum);
+ void endCharacterChat(int8 charNum, int16 arg_4);
+ void waitForChatToFinish(int16 chatDuration, char *str, uint8 charNum);
+ void characterSays(char *chatStr, int8 charNum, int8 chatDuration);
+
+ void setCharactersPositions(int character);
+ int setGameFlag(int flag);
+ int queryGameFlag(int flag);
+ int resetGameFlag(int flag);
+
+ void enterNewScene(int sceneId, int facing, int unk1, int unk2, int brandonAlive);
+ void transcendScenes(int roomIndex, int roomName);
+ void setSceneFile(int roomIndex, int roomName);
+ void moveCharacterToPos(int character, int facing, int xpos, int ypos);
+ void setCharacterPositionWithUpdate(int character);
+ int setCharacterPosition(int character, int *facingTable);
+ void setCharacterPositionHelper(int character, int *facingTable);
+ int getOppositeFacingDirection(int dir);
+ void loadSceneMSC();
+ void startSceneScript(int brandonAlive);
+ void setupSceneItems();
+ void initSceneData(int facing, int unk1, int brandonAlive);
+ void clearNoDropRects();
+ void addToNoDropRects(int x, int y, int w, int h);
+ byte findFreeItemInScene(int scene);
+ byte findItemAtPos(int x, int y);
+ void placeItemInGenericMapScene(int item, int index);
+ void initSceneObjectList(int brandonAlive);
+ void initSceneScreen(int brandonAlive);
+ int findDuplicateItemShape(int shape);
+ int findWay(int x, int y, int toX, int toY, int *moveTable, int moveTableSize);
+ int findSubPath(int x, int y, int toX, int toY, int *moveTable, int start, int end);
+ int getFacingFromPointToPoint(int x, int y, int toX, int toY);
+ void changePosTowardsFacing(int &x, int &y, int facing);
+ bool lineIsPassable(int x, int y);
+ int getMoveTableSize(int *moveTable);
+ int handleSceneChange(int xpos, int ypos, int unk1, int frameReset);
+ int processSceneChange(int *table, int unk1, int frameReset);
+ int changeScene(int facing);
+ void createMouseItem(int item);
+ void destroyMouseItem();
+ void setMouseItem(int item);
+ void wipeDownMouseItem(int xpos, int ypos);
+ void setBrandonPoisonFlags(int reset);
+ void resetBrandonPoisonFlags();
+
+ void processInput(int xpos, int ypos);
+ int processInputHelper(int xpos, int ypos);
+ int clickEventHandler(int xpos, int ypos);
+ void clickEventHandler2();
+ void updateMousePointer(bool forceUpdate = false);
+ bool hasClickedOnExit(int xpos, int ypos);
+ int checkForNPCScriptRun(int xpos, int ypos);
+ void runNpcScript(int func);
+
+ int countItemsInScene(uint16 sceneId);
+ int processItemDrop(uint16 sceneId, uint8 item, int x, int y, int unk1, int unk2);
+ void exchangeItemWithMouseItem(uint16 sceneId, int itemIndex);
+ void addItemToRoom(uint16 sceneId, uint8 item, int itemIndex, int x, int y);
+ int checkNoDropRects(int x, int y);
+ int isDropable(int x, int y);
+ void itemDropDown(int x, int y, int destX, int destY, byte freeItem, int item);
+ void dropItem(int unk1, int item, int x, int y, int unk2);
+ void itemSpecialFX(int x, int y, int item);
+ void itemSpecialFX1(int x, int y, int item);
+ void itemSpecialFX2(int x, int y, int item);
+ void magicOutMouseItem(int animIndex, int itemPos);
+ void magicInMouseItem(int animIndex, int item, int itemPos);
+ void specialMouseItemFX(int shape, int x, int y, int animIndex, int tableIndex, int loopStart, int maxLoops);
+ void processSpecialMouseItemFX(int shape, int x, int y, int tableValue, int loopStart, int maxLoops);
+ void updatePlayerItemsForScene();
+ void redrawInventory(int page);
+
+ void drawJewelPress(int jewel, int drawSpecial);
+ void drawJewelsFadeOutStart();
+ void drawJewelsFadeOutEnd(int jewel);
+ void setupShapes123(const Shape *shapeTable, int endShape, int flags);
+ void freeShapes123();
+
+ void seq_demo();
+ void seq_intro();
+ void seq_introLogos();
+ void seq_introStory();
+ void seq_introMalcolmTree();
+ void seq_introKallakWriting();
+ void seq_introKallakMalcolm();
+ void seq_createAmuletJewel(int jewel, int page, int noSound, int drawOnly);
+ void seq_brandonHealing();
+ void seq_brandonHealing2();
+ void seq_poisonDeathNow(int now);
+ void seq_poisonDeathNowAnim();
+ void seq_playFluteAnimation();
+ void seq_winterScroll1();
+ void seq_winterScroll2();
+ void seq_makeBrandonInv();
+ void seq_makeBrandonNormal();
+ void seq_makeBrandonNormal2();
+ void seq_makeBrandonWisp();
+ void seq_dispelMagicAnimation();
+ void seq_fillFlaskWithWater(int item, int type);
+ void seq_playDrinkPotionAnim(int unk1, int unk2, int flags);
+ int seq_playEnd();
+ void seq_brandonToStone();
+ void seq_playEnding();
+ void seq_playCredits();
+ void updateKyragemFading();
+
+ void snd_setSoundEffectFile(int file);
+
+ static OpcodeProc _opcodeTable[];
+ static const int _opcodeTableSize;
+
+ enum {
+ RES_ALL = 0,
+ RES_INTRO = (1 << 0),
+ RES_INGAME = (1 << 1),
+ RES_OUTRO = (1 << 2)
+ };
+
+ void res_loadResources(int type = RES_ALL);
+ void res_unloadResources(int type = RES_ALL);
+ void res_loadLangTable(const char *filename, PAKFile *res, byte ***loadTo, int *size, bool nativ);
+ void res_loadTable(const byte *src, byte ***loadTo, int *size);
+ void res_loadRoomTable(const byte *src, Room **loadTo, int *size);
+ void res_loadShapeTable(const byte *src, Shape **loadTo, int *size);
+ void res_freeLangTable(char ***sting, int *size);
+
+ void waitForEvent();
+ void loadPalette(const char *filename, uint8 *palData);
+ void loadMouseShapes();
+ void loadCharacterShapes();
+ void loadSpecialEffectShapes();
+ void loadItems();
+ void loadButtonShapes();
+ void initMainButtonList();
+ void loadMainScreen(int page = 3);
+ void setCharactersInDefaultScene();
+ void setupPanPages();
+ void freePanPages();
+ void closeFinalWsa();
+ int handleMalcolmFlag();
+ int handleBeadState();
+ void initBeadState(int x, int y, int x2, int y2, int unk1, BeadState *ptr);
+ int processBead(int x, int y, int &x2, int &y2, BeadState *ptr);
+
+ void setTimer19();
+ void setupTimers();
+ void timerUpdateHeadAnims(int timerNum);
+ void timerSetFlags1(int timerNum);
+ void timerSetFlags2(int timerNum);
+ void timerSetFlags3(int timerNum);
+ void timerCheckAnimFlag1(int timerNum);
+ void timerCheckAnimFlag2(int timerNum);
+ void checkAmuletAnimFlags();
+ void timerRedrawAmulet(int timerNum);
+ void timerFadeText(int timerNum);
+ void updateAnimFlag1(int timerNum);
+ void updateAnimFlag2(int timerNum);
+ void drawAmulet();
+ void setTextFadeTimerCountdown(int16 countdown);
+
+ int buttonInventoryCallback(Button *caller);
+ int buttonAmuletCallback(Button *caller);
+ int buttonMenuCallback(Button *caller);
+ int drawBoxCallback(Button *button);
+ int drawShadedBoxCallback(Button *button);
+ void calcCoords(Menu &menu);
+ void initMenu(Menu menu);
+
+ Button *initButton(Button *list, Button *newButton);
+ void processButtonList(Button *list);
+ void processButton(Button *button);
+ void processMenuButton(Button *button);
+ void processAllMenuButtons();
+
+ const char *getSavegameFilename(int num);
+ void setupSavegames(Menu &menu, int num);
+ int getNextSavegameSlot();
+
+ int gui_resumeGame(Button *button);
+ int gui_loadGameMenu(Button *button);
+ int gui_saveGameMenu(Button *button);
+ int gui_quitPlaying(Button *button);
+ int gui_quitConfirmYes(Button *button);
+ int gui_quitConfirmNo(Button *button);
+ int gui_loadGame(Button *button);
+ int gui_saveGame(Button *button);
+ int gui_savegameConfirm(Button *button);
+ int gui_cancelSubMenu(Button *button);
+ int gui_scrollUp(Button *button);
+ int gui_scrollDown(Button *button);
+
+ bool gui_quitConfirm(const char *str);
+ void gui_getInput();
+ void gui_redrawText(Menu menu);
+ void gui_redrawHighlight(Menu menu);
+ void gui_processHighlights(Menu &menu);
+ void gui_updateSavegameString();
+ void gui_redrawTextfield();
+ void gui_fadePalette();
+ void gui_restorePalette();
+
+ uint8 _game;
+ bool _quitFlag;
+ bool _skipFlag;
+ bool _skipIntroFlag;
+ bool _abortIntroFlag;
+ bool _menuDirectlyToLoad;
+ bool _abortWalkFlag;
+ bool _abortWalkFlag2;
+ bool _mousePressFlag;
+ uint8 _flagsTable[53];
+ uint8 *_shapes[377];
+ uint16 _gameSpeed;
+ uint16 _tickLength;
+ uint32 _features;
+ int _mouseX, _mouseY;
+ int8 _itemInHand;
+ int _mouseState;
+ bool _handleInput;
+ bool _changedScene;
+ int _unkScreenVar1, _unkScreenVar2, _unkScreenVar3;
+ int _beadStateVar;
+ int _unkAmuletVar;
+
+ int _malcolmFlag;
+ int _endSequenceSkipFlag;
+ int _endSequenceNeedLoading;
+ int _unkEndSeqVar2;
+ uint8 *_endSequenceBackUpRect;
+ int _unkEndSeqVar4;
+ int _unkEndSeqVar5;
+ int _lastDisplayedPanPage;
+ uint8 *_panPagesTable[20];
+ Movie *_finalA, *_finalB, *_finalC;
+
+ Movie *_movieObjects[10];
+
+ uint16 _entranceMouseCursorTracks[8];
+ uint16 _walkBlockNorth;
+ uint16 _walkBlockEast;
+ uint16 _walkBlockSouth;
+ uint16 _walkBlockWest;
+
+ int32 _scaleMode;
+ int16 _scaleTable[145];
+
+ Rect _noDropRects[11];
+
+ int8 _birthstoneGemTable[4];
+ int8 _idolGemsTable[3];
+
+ int8 _marbleVaseItem;
+ int8 _foyerItemTable[3];
+
+ int8 _cauldronState;
+ int8 _crystalState[2];
+
+ uint16 _brandonStatusBit;
+ uint8 _brandonStatusBit0x02Flag;
+ uint8 _brandonStatusBit0x20Flag;
+ uint8 _brandonPoisonFlagsGFX[256];
+ uint8 _deathHandler;
+ int16 _brandonInvFlag;
+ uint8 _poisonDeathCounter;
+ int _brandonPosX;
+ int _brandonPosY;
+
+ uint16 _currentChatPartnerBackupFrame;
+ uint16 _currentCharAnimFrame;
+
+ int8 *_sceneAnimTable[50];
+
+ Item _itemTable[145];
+ int _lastProcessedItem;
+ int _lastProcessedItemHeight;
+
+ int16 *_exitListPtr;
+ int16 _exitList[11];
+ SceneExits _sceneExits;
+ uint16 _currentRoom;
+ int _scenePhasingFlag;
+ uint8 *_maskBuffer;
+
+ int _sceneChangeState;
+ int _loopFlag2;
+
+ int _pathfinderFlag;
+ int _pathfinderFlag2;
+ int _lastFindWayRet;
+ int *_movFacingTable;
+
+ int8 _talkingCharNum;
+ int8 _charSayUnk2;
+ int8 _charSayUnk3;
+ int8 _currHeadShape;
+ uint8 _currSentenceColor[3];
+ int8 _startSentencePalIndex;
+ bool _fadeText;
+
+ uint8 _configTalkspeed;
+
+ Common::String _targetName;
+
+ int _curMusicTheme;
+ int _newMusicTheme;
+ int16 _lastMusicCommand;
+
+ Resource *_res;
+ Screen *_screen;
+ ScreenAnimator *_animator;
+ Sound *_sound;
+ SeqPlayer *_seq;
+ Sprites *_sprites;
+ TextDisplayer *_text;
+ ScriptHelper *_scriptInterpreter;
+ Debugger *_debugger;
+ Common::SaveFileManager *_saveFileMan;
+
+ ScriptState *_scriptMain;
+
+ ScriptState *_npcScript;
+ ScriptData *_npcScriptData;
+
+ ScriptState *_scriptClick;
+ ScriptData *_scriptClickData;
+
+ Character *_characterList;
+ Character *_currentCharacter;
+
+ Button *_buttonList;
+ Button *_menuButtonList;
+ bool _displayMenu;
+ bool _menuRestoreScreen;
+ bool _displaySubMenu;
+ bool _cancelSubMenu;
+ int _savegameOffset;
+ int _gameToLoad;
+ char _savegameName[31];
+ const char *_specialSavegameString;
+ KeyboardEvent _keyboardEvent;
+
+ struct KyragemState {
+ uint16 nextOperation;
+ uint16 rOffset;
+ uint16 gOffset;
+ uint16 bOffset;
+ uint32 timerCount;
+ } _kyragemFadingState;
+
+ uint8 *_seq_Forest;
+ uint8 *_seq_KallakWriting;
+ uint8 *_seq_KyrandiaLogo;
+ uint8 *_seq_KallakMalcolm;
+ uint8 *_seq_MalcolmTree;
+ uint8 *_seq_WestwoodLogo;
+ uint8 *_seq_Demo1;
+ uint8 *_seq_Demo2;
+ uint8 *_seq_Demo3;
+ uint8 *_seq_Demo4;
+ uint8 *_seq_Reunion;
+
+ char **_seq_WSATable;
+ char **_seq_CPSTable;
+ char **_seq_COLTable;
+ char **_seq_textsTable;
+
+ int _seq_WSATable_Size;
+ int _seq_CPSTable_Size;
+ int _seq_COLTable_Size;
+ int _seq_textsTable_Size;
+
+ char **_itemList;
+ char **_takenList;
+ char **_placedList;
+ char **_droppedList;
+ char **_noDropList;
+ char **_putDownFirst;
+ char **_waitForAmulet;
+ char **_blackJewel;
+ char **_poisonGone;
+ char **_healingTip;
+ char **_thePoison;
+ char **_fluteString;
+ char **_wispJewelStrings;
+ char **_magicJewelString;
+ char **_flaskFull;
+ char **_fullFlask;
+ char **_veryClever;
+ char **_homeString;
+
+ int _itemList_Size;
+ int _takenList_Size;
+ int _placedList_Size;
+ int _droppedList_Size;
+ int _noDropList_Size;
+ int _putDownFirst_Size;
+ int _waitForAmulet_Size;
+ int _blackJewel_Size;
+ int _poisonGone_Size;
+ int _healingTip_Size;
+ int _thePoison_Size;
+ int _fluteString_Size;
+ int _wispJewelStrings_Size;
+ int _magicJewelString_Size;
+ int _flaskFull_Size;
+ int _fullFlask_Size;
+ int _veryClever_Size;
+ int _homeString_Size;
+
+ char **_characterImageTable;
+ int _characterImageTableSize;
+
+ Shape *_defaultShapeTable;
+ int _defaultShapeTableSize;
+
+ Shape *_healingShapeTable;
+ int _healingShapeTableSize;
+ Shape *_healingShape2Table;
+ int _healingShape2TableSize;
+
+ Shape *_posionDeathShapeTable;
+ int _posionDeathShapeTableSize;
+
+ Shape *_fluteAnimShapeTable;
+ int _fluteAnimShapeTableSize;
+
+ Shape *_winterScrollTable;
+ int _winterScrollTableSize;
+ Shape *_winterScroll1Table;
+ int _winterScroll1TableSize;
+ Shape *_winterScroll2Table;
+ int _winterScroll2TableSize;
+
+ Shape *_drinkAnimationTable;
+ int _drinkAnimationTableSize;
+
+ Shape *_brandonToWispTable;
+ int _brandonToWispTableSize;
+
+ Shape *_magicAnimationTable;
+ int _magicAnimationTableSize;
+
+ Shape *_brandonStoneTable;
+ int _brandonStoneTableSize;
+
+ Room *_roomTable;
+ int _roomTableSize;
+ char **_roomFilenameTable;
+ int _roomFilenameTableSize;
+
+ uint8 *_amuleteAnim;
+
+ uint8 *_specialPalettes[33];
+
+ Timer _timers[34];
+ uint32 _timerNextRun;
+ static const char *_xmidiFiles[];
+ static const int _xmidiFilesCount;
+
+ static const int8 _charXPosTable[];
+ static const int8 _addXPosTable[];
+ static const int8 _charYPosTable[];
+ static const int8 _addYPosTable[];
+
+ // positions of the inventory
+ static const uint16 _itemPosX[];
+ static const uint8 _itemPosY[];
+
+ static Button _buttonData[];
+ static Button *_buttonDataListPtr[];
+ static Button _menuButtonData[];
+ static Button _scrollUpButton;
+ static Button _scrollDownButton;
+
+ static Menu _menu[];
+
+ static const uint8 _magicMouseItemStartFrame[];
+ static const uint8 _magicMouseItemEndFrame[];
+ static const uint8 _magicMouseItemStartFrame2[];
+ static const uint8 _magicMouseItemEndFrame2[];
+
+ static const uint16 _amuletX[];
+ static const uint16 _amuletY[];
+ static const uint16 _amuletX2[];
+ static const uint16 _amuletY2[];
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
new file mode 100644
index 0000000000..943c628831
--- /dev/null
+++ b/engines/kyra/module.mk
@@ -0,0 +1,33 @@
+MODULE := engines/kyra
+
+MODULE_OBJS := \
+ engines/kyra/kyra.o \
+ engines/kyra/resource.o \
+ engines/kyra/screen.o \
+ engines/kyra/script_v1.o \
+ engines/kyra/script.o \
+ engines/kyra/seqplayer.o \
+ engines/kyra/sound.o \
+ engines/kyra/staticres.o \
+ engines/kyra/sprites.o \
+ engines/kyra/wsamovie.o \
+ engines/kyra/debugger.o \
+ engines/kyra/animator.o \
+ engines/kyra/gui.o \
+ engines/kyra/sequences_v1.o \
+ engines/kyra/items.o \
+ engines/kyra/scene.o \
+ engines/kyra/text.o \
+ engines/kyra/timer.o \
+ engines/kyra/saveload.o
+
+MODULE_DIRS += \
+ engines/kyra
+
+# This module can be built as a plugin
+ifdef BUILD_PLUGINS
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/common.rules
diff --git a/engines/kyra/resource.cpp b/engines/kyra/resource.cpp
new file mode 100644
index 0000000000..0dd76ff5de
--- /dev/null
+++ b/engines/kyra/resource.cpp
@@ -0,0 +1,366 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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 "common/file.h"
+#include "kyra/resource.h"
+#include "kyra/script.h"
+#include "kyra/wsamovie.h"
+#include "kyra/screen.h"
+
+namespace Kyra {
+Resource::Resource(KyraEngine *engine) {
+ _engine = engine;
+
+ // No PAK files in the demo version
+ if (_engine->features() & GF_DEMO)
+ return;
+
+ // prefetches all PAK Files
+
+ // ugly a hardcoded list
+ // TODO: use the FS Backend to get all .PAK Files and load them
+ // or any other thing to get all files
+ static const char *kyra1Filelist[] = {
+ "A_E.PAK", "DAT.PAK", "F_L.PAK", "MAP_5.PAK", "MSC.PAK", "M_S.PAK",
+ "S_Z.PAK", "WSA1.PAK", "WSA2.PAK", "WSA3.PAK", "WSA4.PAK", "WSA5.PAK",
+ "WSA6.PAK", 0
+ };
+
+ static const char *kyra1CDFilelist[] = {
+ "ALTAR.APK", "BELROOM.APK", "BONKBG.APK", "BROKEN.APK", "CASTLE.APK", "CAVE.APK", "CGATE.APK",
+ "DEAD.APK", "DNSTAIR.APK", "DRAGON1.APK", "DRAGON2.APK", "EXTPOT.APK", "FORESTA.APK", "FORESTB.APK",
+ "FOUNTN.APK", "FOYER.APK", "GATECV.APK", "GEM.APK", "GEMCUT.APK", "GENHALL.APK", "GLADE.APK",
+ "GRAVE.APK", "HEALER.APK", "LAGOON.APK", "LANDING.APK", "LAVA.APK", "LEPHOLE.APK", "LIBRARY.APK",
+ "MIX.APK", "MOONCAV.APK", "POTION.APK", "SONG.APK", "SORROW.APK", "SPELL.APK", "STUMP.APK",
+ "TEMPLE.APK", "TRUNK.APK", "WILLOW.APK", "XEDGE.APK",
+
+ "ADL.PAK", "BRINS.PAK", "CLIFF.PAK", "ENTER.PAK", "FORESTA.PAK", "GEM.PAK", "INTRO1.PAK",
+ "LEPHOLE.PAK", "OAKS.PAK", "SPELL.PAK", "WILLOW.PAK", "ALCHEMY.PAK", "BROKEN.PAK", "COL.PAK",
+ "EXTHEAL.PAK", "FORESTB.PAK", "GEMCUT.PAK", "INTRO2.PAK", "LIBRARY.PAK", "PLATEAU.PAK", "SPRING.PAK",
+ "WISE.PAK", "ALGAE.PAK", "BURN.PAK", "DARMS.PAK", "EXTPOT.PAK", "FORESTC.PAK", "GENCAVB.PAK",
+ "INTRO3.PAK", "MISC.PAK", "PLTCAVE.PAK", "SQUARE.PAK", "XEDGE.PAK", "ALTAR.PAK", "CASTLE.PAK",
+ "DEAD.PAK", "EXTSPEL.PAK", "FOUNTN.PAK", "GENHALL.PAK", "INTRO4.PAK", "MIX.PAK", "POTION.PAK",
+ "STARTUP.PAK", "XEDGEB.PAK", "ARCH.PAK", "CATACOM.PAK", "DNSTAIR.PAK", "FALLS.PAK", "FOYER.PAK",
+ "GEN_CAV.PAK", "KITCHEN.PAK", "MOONCAV.PAK", "RUBY.PAK", "STUMP.PAK", "XEDGEC.PAK", "BALCONY.PAK",
+ "CAVE.PAK", "DRAGON.PAK", "FESTSTH.PAK", "FSOUTH.PAK", "GLADE.PAK", "KYRAGEM.PAK", "NCLIFF.PAK",
+ "SICKWIL.PAK", "TEMPLE.PAK", "XMI.PAK", "BELROOM.PAK", "CAVEB.PAK", "EDGE.PAK", "FGOWEST.PAK",
+ "FSOUTHB.PAK", "GRAVE.PAK", "LAGOON.PAK", "NCLIFFB.PAK", "SND.PAK", "TRUNK.PAK", "ZROCK.PAK",
+ "BONKBG.PAK", "CGATE.PAK", "EDGEB.PAK", "FINALE.PAK", "FWSTSTH.PAK", "GRTHALL.PAK", "LANDING.PAK",
+ "NWCLIFB.PAK", "SONG.PAK", "UPSTAIR.PAK", "BRIDGE.PAK", "CHASM.PAK", "EMCAV.PAK", "FNORTH.PAK",
+ "GATECV.PAK", "HEALER.PAK", "LAVA.PAK", "NWCLIFF.PAK", "SORROW.PAK", "WELL.PAK",
+
+ "CHAPTER1.VRM", 0
+ };
+
+ const char **usedFilelist = 0;
+
+ if (_engine->features() & GF_FLOPPY)
+ usedFilelist = kyra1Filelist;
+ else if (_engine->features() & GF_TALKIE)
+ usedFilelist = kyra1CDFilelist;
+ else
+ error("no filelist found for this game");
+
+ for (uint32 tmp = 0; usedFilelist[tmp]; ++tmp) {
+ // prefetch file
+ PAKFile *file = new PAKFile(usedFilelist[tmp]);
+ assert(file);
+
+ PakFileEntry newPak;
+ newPak._file = file;
+ strncpy(newPak._filename, usedFilelist[tmp], 32);
+ if (file->isOpen() && file->isValid())
+ _pakfiles.push_back(newPak);
+ else {
+ delete file;
+ debug(3, "couldn't load file '%s' correctly", usedFilelist[tmp]);
+ }
+ }
+}
+
+Resource::~Resource() {
+ Common::List<PakFileEntry>::iterator start = _pakfiles.begin();
+
+ for (;start != _pakfiles.end(); ++start) {
+ delete start->_file;
+ start->_file = 0;
+ }
+}
+
+bool Resource::loadPakFile(const char *filename) {
+ if (isInPakList(filename))
+ return true;
+ PAKFile *file = new PAKFile(filename);
+ if (!file) {
+ error("couldn't load file: '%s'", filename);
+ }
+ PakFileEntry newPak;
+ newPak._file = file;
+ strncpy(newPak._filename, filename, 32);
+ _pakfiles.push_back(newPak);
+ return true;
+}
+
+void Resource::unloadPakFile(const char *filename) {
+ Common::List<PakFileEntry>::iterator start = _pakfiles.begin();
+ for (;start != _pakfiles.end(); ++start) {
+ if (scumm_stricmp(start->_filename, filename) == 0) {
+ delete start->_file;
+ _pakfiles.erase(start);
+ break;
+ }
+ }
+ return;
+}
+
+bool Resource::isInPakList(const char *filename) {
+ Common::List<PakFileEntry>::iterator start = _pakfiles.begin();
+ for (;start != _pakfiles.end(); ++start) {
+ if (scumm_stricmp(start->_filename, filename) == 0)
+ return true;
+ }
+ return false;
+}
+
+uint8 *Resource::fileData(const char *file, uint32 *size) {
+ uint8 *buffer = 0;
+ Common::File file_;
+
+ // test to open it in the main dir
+ if (file_.open(file)) {
+
+ *size = file_.size();
+ buffer = new uint8[*size];
+ assert(buffer);
+
+ file_.read(buffer, *size);
+
+ file_.close();
+ } else {
+ // opens the file in a PAK File
+ Common::List<PakFileEntry>::iterator start = _pakfiles.begin();
+
+ for (;start != _pakfiles.end(); ++start) {
+ *size = start->_file->getFileSize(file);
+
+ if (!(*size))
+ continue;
+
+ buffer = start->_file->getFile(file);
+ break;
+ }
+ }
+
+ if (!buffer || !(*size)) {
+ return 0;
+ }
+
+ return buffer;
+}
+
+bool Resource::fileHandle(const char *file, uint32 *size, Common::File &filehandle) {
+ filehandle.close();
+
+ if (filehandle.open(file))
+ return true;
+
+ Common::List<PakFileEntry>::iterator start = _pakfiles.begin();
+
+ for (;start != _pakfiles.end(); ++start) {
+ *size = start->_file->getFileSize(file);
+
+ if (!(*size))
+ continue;
+
+ if (start->_file->getFileHandle(file, filehandle)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+///////////////////////////////////////////
+// Pak file manager
+#define PAKFile_Iterate Common::List<PakChunk*>::iterator start=_files.begin();start != _files.end(); ++start
+PAKFile::PAKFile(const Common::String& file) {
+ _filename = 0;
+
+ Common::File pakfile;
+ uint8 *buffer = 0;
+ _open = false;
+
+ if (!pakfile.open(file.c_str())) {
+ debug(3, "couldn't open pakfile '%s'\n", file.c_str());
+ return;
+ }
+
+ uint32 filesize = pakfile.size();
+ buffer = new uint8[filesize];
+ assert(buffer);
+
+ pakfile.read(buffer, filesize);
+ pakfile.close();
+
+ // works with the file
+ uint32 pos = 0, startoffset = 0, endoffset = 0;
+
+ startoffset = READ_LE_UINT32(buffer + pos);
+ pos += 4;
+
+ while (pos < filesize) {
+ PakChunk* chunk = new PakChunk;
+ assert(chunk);
+
+ // saves the name
+ chunk->_name = new char[strlen((const char*)buffer + pos) + 1];
+ assert(chunk->_name);
+ strcpy(chunk->_name, (const char*)buffer + pos);
+ pos += strlen(chunk->_name) + 1;
+ if (!(*chunk->_name))
+ break;
+
+ endoffset = READ_LE_UINT32(buffer + pos);
+ pos += 4;
+
+ if (endoffset == 0) {
+ endoffset = filesize;
+ }
+
+ chunk->_start = startoffset;
+ chunk->_size = endoffset - startoffset;
+
+ _files.push_back(chunk);
+
+ if (endoffset == filesize)
+ break;
+
+ startoffset = endoffset;
+ }
+ _open = true;
+ delete [] buffer;
+
+ _filename = new char[file.size()+1];
+ assert(_filename);
+ strcpy(_filename, file.c_str());
+}
+
+PAKFile::~PAKFile() {
+ delete [] _filename;
+ _filename = 0;
+ _open = false;
+
+ for (PAKFile_Iterate) {
+ delete [] (*start)->_name;
+ (*start)->_name = 0;
+ delete *start;
+ *start = 0;
+ }
+}
+
+uint8 *PAKFile::getFile(const char *file) {
+ for (PAKFile_Iterate) {
+ if (!scumm_stricmp((*start)->_name, file)) {
+ Common::File pakfile;
+ if (!pakfile.open(_filename)) {
+ debug(3, "couldn't open pakfile '%s'\n", _filename);
+ return 0;
+ }
+ pakfile.seek((*start)->_start);
+ uint8 *buffer = new uint8[(*start)->_size];
+ assert(buffer);
+ pakfile.read(buffer, (*start)->_size);
+ return buffer;
+ }
+ }
+ return 0;
+}
+
+bool PAKFile::getFileHandle(const char *file, Common::File &filehandle) {
+ filehandle.close();
+
+ for (PAKFile_Iterate) {
+ if (!scumm_stricmp((*start)->_name, file)) {
+ if (!filehandle.open(_filename)) {
+ debug(3, "couldn't open pakfile '%s'\n", _filename);
+ return 0;
+ }
+ filehandle.seek((*start)->_start);
+ return true;
+ }
+ }
+ return false;
+}
+
+uint32 PAKFile::getFileSize(const char* file) {
+ for (PAKFile_Iterate) {
+ if (!scumm_stricmp((*start)->_name, file))
+ return (*start)->_size;
+ }
+ return 0;
+}
+
+void KyraEngine::loadPalette(const char *filename, uint8 *palData) {
+ debug(9, "KyraEngine::loadPalette('%s' 0x%X)", filename, palData);
+ uint32 fileSize = 0;
+ uint8 *srcData = _res->fileData(filename, &fileSize);
+
+ if (palData && fileSize) {
+ debug(9, "Loading a palette of size %i from '%s'", fileSize, filename);
+ memcpy(palData, srcData, fileSize);
+ }
+ delete [] srcData;
+}
+
+void KyraEngine::loadBitmap(const char *filename, int tempPage, int dstPage, uint8 *palData) {
+ debug(9, "KyraEngine::copyBitmap('%s', %d, %d, 0x%X)", filename, tempPage, dstPage, palData);
+ uint32 fileSize;
+ uint8 *srcData = _res->fileData(filename, &fileSize);
+ uint8 compType = srcData[2];
+ uint32 imgSize = READ_LE_UINT32(srcData + 4);
+ uint16 palSize = READ_LE_UINT16(srcData + 8);
+ if (palData && palSize) {
+ debug(9, "Loading a palette of size %i from %s", palSize, filename);
+ memcpy(palData, srcData + 10, palSize);
+ }
+ uint8 *srcPtr = srcData + 10 + palSize;
+ uint8 *dstData = _screen->getPagePtr(dstPage);
+ switch (compType) {
+ case 0:
+ memcpy(dstData, srcPtr, imgSize);
+ break;
+ case 3:
+ Screen::decodeFrame3(srcPtr, dstData, imgSize);
+ break;
+ case 4:
+ Screen::decodeFrame4(srcPtr, dstData, imgSize);
+ break;
+ default:
+ error("Unhandled bitmap compression %d", compType);
+ break;
+ }
+ delete[] srcData;
+}
+
+} // end of namespace Kyra
+
diff --git a/engines/kyra/resource.h b/engines/kyra/resource.h
new file mode 100644
index 0000000000..1a479c0b73
--- /dev/null
+++ b/engines/kyra/resource.h
@@ -0,0 +1,95 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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$
+ *
+ */
+
+#ifndef RESOURCE_H
+#define RESOURCE_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "common/list.h"
+#include "common/map.h"
+
+#include "kyra/kyra.h"
+
+namespace Kyra {
+
+// standard Package format for Kyrandia games
+class PAKFile {
+ struct PakChunk {
+ char* _name;
+ uint32 _start;
+ uint32 _size;
+ };
+
+public:
+
+ PAKFile(const Common::String &file);
+ ~PAKFile();
+
+ uint8* getFile(const char *file);
+ bool getFileHandle(const char *file, Common::File &filehandle);
+ uint32 getFileSize(const char *file);
+
+ bool isValid(void) const { return (_filename != 0); }
+ bool isOpen(void) const { return _open; }
+
+private:
+
+ bool _open;
+ char *_filename;
+ Common::List<PakChunk*> _files; // the entries
+};
+
+// some resource types
+class Movie;
+class VMContext;
+
+class Resource {
+public:
+
+ Resource(KyraEngine *engine);
+ ~Resource();
+
+ bool loadPakFile(const char *filename);
+ void unloadPakFile(const char *filename);
+ bool isInPakList(const char *filename);
+
+ uint8* fileData(const char *file, uint32 *size);
+ // it gives back a file handle (used for the speech player)
+ // it could be that the needed file is embedded in the returned
+ // handle
+ bool fileHandle(const char *file, uint32 *size, Common::File &filehandle);
+
+protected:
+ struct PakFileEntry {
+ PAKFile *_file;
+ char _filename[32];
+ };
+
+ KyraEngine* _engine;
+ Common::List<PakFileEntry> _pakfiles;
+};
+
+} // end of namespace Kyra
+
+#endif
diff --git a/engines/kyra/saveload.cpp b/engines/kyra/saveload.cpp
new file mode 100644
index 0000000000..fcc6dbfe66
--- /dev/null
+++ b/engines/kyra/saveload.cpp
@@ -0,0 +1,319 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-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 "kyra/kyra.h"
+#include "kyra/animator.h"
+#include "kyra/screen.h"
+#include "kyra/resource.h"
+
+#include "common/savefile.h"
+#include "common/system.h"
+
+#define CURRENT_VERSION 3
+
+namespace Kyra {
+void KyraEngine::loadGame(const char *fileName) {
+ debug(9, "loadGame('%s')", fileName);
+ Common::InSaveFile *in;
+
+ if (!(in = _saveFileMan->openForLoading(fileName))) {
+ warning("Can't open file '%s', game not loaded", fileName);
+ return;
+ }
+
+ uint32 type = in->readUint32BE();
+ if (type != MKID('KYRA')) {
+ warning("No Kyrandia 1 savefile header");
+ delete in;
+ return;
+ }
+ uint32 version = in->readUint32BE();
+ if (version > CURRENT_VERSION) {
+ warning("Savegame is not the right version (%d)", version);
+ delete in;
+ return;
+ }
+
+ char saveName[31];
+ in->read(saveName, 31);
+
+ if (version >= 2) {
+ uint32 gameFlags = in->readUint32BE();
+ if ((gameFlags & GF_FLOPPY) && !(_features & GF_FLOPPY)) {
+ warning("can not load floppy savefile for this (non floppy) gameversion!");
+ delete in;
+ return;
+ } else if ((gameFlags & GF_TALKIE) && !(_features & GF_TALKIE)) {
+ warning("can not load cdrom savefile for this (non cdrom) gameversion!");
+ delete in;
+ return;
+ }
+ } else {
+ warning("Make sure your savefile was from this version! (too old savefile version to detect that)");
+ }
+
+ snd_playSoundEffect(0x0A);
+ snd_playWanderScoreViaMap(0, 1);
+
+ // unload the current voice file should fix some problems with voices
+ if (_currentRoom != 0xFFFF && (_features & GF_TALKIE)) {
+ char file[32];
+ assert(_currentRoom < _roomTableSize);
+ int tableId = _roomTable[_currentRoom].nameIndex;
+ assert(tableId < _roomFilenameTableSize);
+ strcpy(file, _roomFilenameTable[tableId]);
+ strcat(file, ".VRM");
+ _res->unloadPakFile(file);
+ }
+
+ int brandonX = 0, brandonY = 0;
+ for (int i = 0; i < 11; i++) {
+ _characterList[i].sceneId = in->readUint16BE();
+ _characterList[i].height = in->readByte();
+ _characterList[i].facing = in->readByte();
+ _characterList[i].currentAnimFrame = in->readUint16BE();
+ //_characterList[i].unk6 = in->readUint32BE();
+ in->read(_characterList[i].inventoryItems, 10);
+ _characterList[i].x1 = in->readSint16BE();
+ _characterList[i].y1 = in->readSint16BE();
+ _characterList[i].x2 = in->readSint16BE();
+ _characterList[i].y2 = in->readSint16BE();
+ if (i == 0) {
+ brandonX = _characterList[i].x1;
+ brandonY = _characterList[i].y1;
+ }
+ //_characterList[i].field_20 = in->readUint16BE();
+ //_characterList[i].field_23 = in->readUint16BE();
+ }
+
+ _marbleVaseItem = in->readSint16BE();
+ _itemInHand = in->readByte();
+
+ for (int i = 0; i < 4; ++i) {
+ _birthstoneGemTable[i] = in->readByte();
+ }
+ for (int i = 0; i < 3; ++i) {
+ _idolGemsTable[i] = in->readByte();
+ }
+ for (int i = 0; i < 3; ++i) {
+ _foyerItemTable[i] = in->readByte();
+ }
+ _cauldronState = in->readByte();
+ for (int i = 0; i < 2; ++i) {
+ _crystalState[i] = in->readByte();
+ }
+
+ _brandonStatusBit = in->readUint16BE();
+ _brandonStatusBit0x02Flag = in->readByte();
+ _brandonStatusBit0x20Flag = in->readByte();
+ in->read(_brandonPoisonFlagsGFX, 256);
+ _brandonInvFlag = in->readSint16BE();
+ _poisonDeathCounter = in->readByte();
+ _animator->_brandonDrawFrame = in->readUint16BE();
+
+ for (int i = 0; i < 32; i++) {
+ _timers[i].active = in->readByte();
+ _timers[i].countdown = in->readSint32BE();
+ _timers[i].nextRun = in->readUint32BE();
+ if (_timers[i].nextRun != 0)
+ _timers[i].nextRun += _system->getMillis();
+ }
+ _timerNextRun = 0;
+
+ uint32 flagsSize = in->readUint32BE();
+ assert(flagsSize == sizeof(_flagsTable));
+ in->read(_flagsTable, flagsSize);
+
+ for (int i = 0; i < _roomTableSize; ++i) {
+ for (int item = 0; item < 12; ++item) {
+ _roomTable[i].itemsTable[item] = 0xFF;
+ _roomTable[i].itemsXPos[item] = 0xFFFF;
+ _roomTable[i].itemsYPos[item] = 0xFF;
+ _roomTable[i].needInit[item] = 0;
+ }
+ }
+
+ uint16 sceneId = 0;
+
+ while (true) {
+ sceneId = in->readUint16BE();
+ if (sceneId == 0xFFFF)
+ break;
+ assert(sceneId < _roomTableSize);
+ _roomTable[sceneId].nameIndex = in->readByte();
+
+ for (int i = 0; i < 12; i++) {
+ _roomTable[sceneId].itemsTable[i] = in->readByte();
+ _roomTable[sceneId].itemsXPos[i] = in->readUint16BE();
+ _roomTable[sceneId].itemsYPos[i] = in->readUint16BE();
+ _roomTable[sceneId].needInit[i] = in->readByte();
+ }
+ }
+ if (version >= 3) {
+ _lastMusicCommand = in->readSint16BE();
+ if (_lastMusicCommand != -1)
+ snd_playWanderScoreViaMap(_lastMusicCommand, 1);
+ }
+
+ if (queryGameFlag(0x2D)) {
+ loadMainScreen(8);
+ loadBitmap("AMULET3.CPS", 10, 10, 0);
+ if (!queryGameFlag(0xF1)) {
+ for (int i = 0x55; i <= 0x5A; ++i) {
+ if (queryGameFlag(i)) {
+ seq_createAmuletJewel(i-0x55, 10, 1, 1);
+ }
+ }
+ }
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 10, 8);
+ uint8 *_pageSrc = _screen->getPagePtr(8);
+ uint8 *_pageDst = _screen->getPagePtr(0);
+ memcpy(_pageDst, _pageSrc, 320*200);
+ } else {
+ loadMainScreen(8);
+ }
+
+ createMouseItem(_itemInHand);
+ _animator->setBrandonAnimSeqSize(5, 48);
+ _animator->_noDrawShapesFlag = 1;
+ enterNewScene(_currentCharacter->sceneId, _currentCharacter->facing, 0, 0, 1);
+ _animator->_noDrawShapesFlag = 0;
+
+ _currentCharacter->x1 = brandonX;
+ _currentCharacter->y1 = brandonY;
+ _animator->animRefreshNPC(0);
+ _animator->restoreAllObjectBackgrounds();
+ _animator->preserveAnyChangedBackgrounds();
+ _animator->prepDrawAllObjects();
+ _animator->copyChangedObjectsForward(0);
+ _screen->copyRegion(8, 8, 8, 8, 304, 128, 2, 0);
+ redrawInventory(0);
+
+ _abortWalkFlag = true;
+ _abortWalkFlag2 = false;
+ _mousePressFlag = false;
+ _mouseX = brandonX;
+ _mouseY = brandonY;
+ _system->warpMouse(brandonX, brandonY);
+
+ if (in->ioFailed())
+ error("Load failed.");
+ else
+ debug(1, "Loaded savegame '%s.'", saveName);
+
+ delete in;
+}
+
+void KyraEngine::saveGame(const char *fileName, const char *saveName) {
+ debug(9, "saveGame('%s', '%s')", fileName, saveName);
+ Common::OutSaveFile *out;
+
+ if (!(out = _saveFileMan->openForSaving(fileName))) {
+ warning("Can't create file '%s', game not saved", fileName);
+ return;
+ }
+
+ // Savegame version
+ out->writeUint32BE(MKID('KYRA'));
+ out->writeUint32BE(CURRENT_VERSION);
+ out->write(saveName, 31);
+ out->writeUint32BE(_features);
+
+ for (int i = 0; i < 11; i++) {
+ out->writeUint16BE(_characterList[i].sceneId);
+ out->writeByte(_characterList[i].height);
+ out->writeByte(_characterList[i].facing);
+ out->writeUint16BE(_characterList[i].currentAnimFrame);
+ //out->writeUint32BE(_characterList[i].unk6);
+ out->write(_characterList[i].inventoryItems, 10);
+ out->writeSint16BE(_characterList[i].x1);
+ out->writeSint16BE(_characterList[i].y1);
+ out->writeSint16BE(_characterList[i].x2);
+ out->writeSint16BE(_characterList[i].y2);
+ //out->writeUint16BE(_characterList[i].field_20);
+ //out->writeUint16BE(_characterList[i].field_23);
+ }
+
+ out->writeSint16BE(_marbleVaseItem);
+ out->writeByte(_itemInHand);
+
+ for (int i = 0; i < 4; ++i) {
+ out->writeByte(_birthstoneGemTable[i]);
+ }
+ for (int i = 0; i < 3; ++i) {
+ out->writeByte(_idolGemsTable[i]);
+ }
+ for (int i = 0; i < 3; ++i) {
+ out->writeByte(_foyerItemTable[i]);
+ }
+ out->writeByte(_cauldronState);
+ for (int i = 0; i < 2; ++i) {
+ out->writeByte(_crystalState[i]);
+ }
+
+ out->writeUint16BE(_brandonStatusBit);
+ out->writeByte(_brandonStatusBit0x02Flag);
+ out->writeByte(_brandonStatusBit0x20Flag);
+ out->write(_brandonPoisonFlagsGFX, 256);
+ out->writeSint16BE(_brandonInvFlag);
+ out->writeByte(_poisonDeathCounter);
+ out->writeUint16BE(_animator->_brandonDrawFrame);
+
+ for (int i = 0; i < 32; i++) {
+ out->writeByte(_timers[i].active);
+ out->writeSint32BE(_timers[i].countdown);
+ if (_system->getMillis() >= _timers[i].nextRun) {
+ out->writeUint32BE(0);
+ } else {
+ out->writeUint32BE(_timers[i].nextRun - _system->getMillis());
+ }
+ }
+
+ out->writeUint32BE(sizeof(_flagsTable));
+ out->write(_flagsTable, sizeof(_flagsTable));
+
+ for (uint16 i = 0; i < _roomTableSize; i++) {
+ out->writeUint16BE(i);
+ out->writeByte(_roomTable[i].nameIndex);
+ for (int a = 0; a < 12; a++) {
+ out->writeByte(_roomTable[i].itemsTable[a]);
+ out->writeUint16BE(_roomTable[i].itemsXPos[a]);
+ out->writeUint16BE(_roomTable[i].itemsYPos[a]);
+ out->writeByte(_roomTable[i].needInit[a]);
+ }
+ }
+ // room table terminator
+ out->writeUint16BE(0xFFFF);
+
+ out->writeSint16BE(_lastMusicCommand);
+
+ out->flush();
+
+ // check for errors
+ if (out->ioFailed())
+ warning("Can't write file '%s'. (Disk full?)", fileName);
+ else
+ debug(1, "Saved game '%s.'", saveName);
+
+ delete out;
+}
+} // end of namespace Kyra
diff --git a/engines/kyra/scene.cpp b/engines/kyra/scene.cpp
new file mode 100644
index 0000000000..a94050a296
--- /dev/null
+++ b/engines/kyra/scene.cpp
@@ -0,0 +1,1581 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-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 "kyra/kyra.h"
+#include "kyra/seqplayer.h"
+#include "kyra/screen.h"
+#include "kyra/resource.h"
+#include "kyra/sound.h"
+#include "kyra/sprites.h"
+#include "kyra/wsamovie.h"
+#include "kyra/animator.h"
+#include "kyra/text.h"
+#include "kyra/script.h"
+
+#include "common/system.h"
+#include "common/savefile.h"
+
+namespace Kyra {
+
+void KyraEngine::enterNewScene(int sceneId, int facing, int unk1, int unk2, int brandonAlive) {
+ debug(9, "KyraEngine::enterNewScene(%d, %d, %d, %d, %d)", sceneId, facing, unk1, unk2, brandonAlive);
+ int unkVar1 = 1;
+ _screen->hideMouse();
+ _handleInput = false;
+ _abortWalkFlag = false;
+ _abortWalkFlag2 = false;
+ if (_currentCharacter->sceneId == 7 && sceneId == 24) {
+ _newMusicTheme = 3;
+ } else if (_currentCharacter->sceneId == 25 && sceneId == 109) {
+ _newMusicTheme = 4;
+ } else if (_currentCharacter->sceneId == 120 && sceneId == 37) {
+ _newMusicTheme = 5;
+ } else if (_currentCharacter->sceneId == 52 && sceneId == 199) {
+ _newMusicTheme = 6;
+ } else if (_currentCharacter->sceneId == 37 && sceneId == 120) {
+ _newMusicTheme = 4;
+ } else if (_currentCharacter->sceneId == 109 && sceneId == 25) {
+ _newMusicTheme = 3;
+ } else if (_currentCharacter->sceneId == 24 && sceneId == 7) {
+ _newMusicTheme = 2;
+ }
+ if (_newMusicTheme != _curMusicTheme) {
+ snd_playTheme(_newMusicTheme);
+ }
+
+ switch (_currentCharacter->sceneId) {
+ case 1:
+ if (sceneId == 0) {
+ moveCharacterToPos(0, 0, _currentCharacter->x1, 84);
+ unkVar1 = 0;
+ }
+ break;
+
+ case 3:
+ if (sceneId == 2) {
+ moveCharacterToPos(0, 6, 155, _currentCharacter->y1);
+ unkVar1 = 0;
+ }
+ break;
+
+ case 26:
+ if (sceneId == 27) {
+ moveCharacterToPos(0, 6, 155, _currentCharacter->y1);
+ unkVar1 = 0;
+ }
+ break;
+
+ case 44:
+ if (sceneId == 45) {
+ moveCharacterToPos(0, 2, 192, _currentCharacter->y1);
+ unkVar1 = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (unkVar1 && unk1) {
+ int xpos = _currentCharacter->x1;
+ int ypos = _currentCharacter->y1;
+ switch (facing) {
+ case 0:
+ ypos = _currentCharacter->y1 - 6;
+ break;
+
+ case 2:
+ xpos = 336;
+ break;
+
+ case 4:
+ ypos = 143;
+ break;
+
+ case 6:
+ xpos = -16;
+ break;
+
+ default:
+ break;
+ }
+
+ moveCharacterToPos(0, facing, xpos, ypos);
+ }
+
+ for (int i = 0; i < ARRAYSIZE(_movieObjects); ++i) {
+ _movieObjects[i]->close();
+ }
+
+ if (!brandonAlive) {
+ _scriptInterpreter->initScript(_scriptClick, _scriptClickData);
+ _scriptInterpreter->startScript(_scriptClick, 5);
+ while (_scriptInterpreter->validScript(_scriptClick)) {
+ _scriptInterpreter->runScript(_scriptClick);
+ }
+ }
+
+ memset(_entranceMouseCursorTracks, 0xFFFF, sizeof(uint16)*4);
+ _currentCharacter->sceneId = sceneId;
+
+ assert(sceneId < _roomTableSize);
+ assert(_roomTable[sceneId].nameIndex < _roomFilenameTableSize);
+
+ Room *currentRoom = &_roomTable[sceneId];
+
+ if (_currentRoom != 0xFFFF && (_features & GF_TALKIE)) {
+ char file[32];
+ assert(_currentRoom < _roomTableSize);
+ int tableId = _roomTable[_currentRoom].nameIndex;
+ assert(tableId < _roomFilenameTableSize);
+ strcpy(file, _roomFilenameTable[tableId]);
+ strcat(file, ".VRM");
+ _res->unloadPakFile(file);
+ }
+
+ _currentRoom = sceneId;
+
+ int tableId = _roomTable[_currentCharacter->sceneId].nameIndex;
+ char fileNameBuffer[32];
+ strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
+ strcat(fileNameBuffer, ".DAT");
+ _sprites->loadDAT(fileNameBuffer, _sceneExits);
+ _sprites->setupSceneAnims();
+ _scriptInterpreter->unloadScript(_scriptClickData);
+ loadSceneMSC();
+
+ if ((_features & GF_TALKIE)) {
+ strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
+ strcat(fileNameBuffer, ".VRM");
+ _res->loadPakFile(fileNameBuffer);
+ }
+
+ _walkBlockNorth = currentRoom->northExit;
+ _walkBlockEast = currentRoom->eastExit;
+ _walkBlockSouth = currentRoom->southExit;
+ _walkBlockWest = currentRoom->westExit;
+
+ if (_walkBlockNorth == 0xFFFF) {
+ _screen->blockOutRegion(0, 0, 320, (_northExitHeight & 0xFF)+3);
+ }
+ if (_walkBlockEast == 0xFFFF) {
+ _screen->blockOutRegion(312, 0, 8, 139);
+ }
+ if (_walkBlockSouth == 0xFFFF) {
+ _screen->blockOutRegion(0, 135, 320, 8);
+ }
+ if (_walkBlockWest == 0xFFFF) {
+ _screen->blockOutRegion(0, 0, 8, 139);
+ }
+
+ if (!brandonAlive) {
+ updatePlayerItemsForScene();
+ }
+
+ startSceneScript(brandonAlive);
+ setupSceneItems();
+
+ initSceneData(facing, unk2, brandonAlive);
+
+ _loopFlag2 = 0;
+ _screen->showMouse();
+ if (!brandonAlive) {
+ seq_poisonDeathNow(0);
+ }
+ updateMousePointer(true);
+ _changedScene = true;
+}
+
+void KyraEngine::transcendScenes(int roomIndex, int roomName) {
+ debug(9, "KyraEngine::transcendScenes(%d, %d)", roomIndex, roomName);
+ assert(roomIndex < _roomTableSize);
+ if (_features & GF_TALKIE) {
+ char file[32];
+ assert(roomIndex < _roomTableSize);
+ int tableId = _roomTable[roomIndex].nameIndex;
+ assert(tableId < _roomFilenameTableSize);
+ strcpy(file, _roomFilenameTable[tableId]);
+ strcat(file, ".VRM");
+ _res->unloadPakFile(file);
+ }
+ _roomTable[roomIndex].nameIndex = roomName;
+ _unkScreenVar2 = 1;
+ _unkScreenVar3 = 1;
+ _unkScreenVar1 = 0;
+ _brandonPosX = _currentCharacter->x1;
+ _brandonPosY = _currentCharacter->y1;
+ enterNewScene(roomIndex, _currentCharacter->facing, 0, 0, 0);
+ _unkScreenVar1 = 1;
+ _unkScreenVar2 = 0;
+ _unkScreenVar3 = 0;
+}
+
+void KyraEngine::setSceneFile(int roomIndex, int roomName) {
+ debug(9, "KyraEngine::setSceneFile(%d, %d)", roomIndex, roomName);
+ assert(roomIndex < _roomTableSize);
+ _roomTable[roomIndex].nameIndex = roomName;
+}
+
+void KyraEngine::moveCharacterToPos(int character, int facing, int xpos, int ypos) {
+ debug(9, "KyraEngine::moveCharacterToPos(%d, %d, %d, %d)", character, facing, xpos, ypos);
+ Character *ch = &_characterList[character];
+ ch->facing = facing;
+ _screen->hideMouse();
+ xpos = (int16)(xpos & 0xFFFC);
+ ypos = (int16)(ypos & 0xFFFE);
+ disableTimer(19);
+ disableTimer(14);
+ disableTimer(18);
+ uint32 nextFrame = 0;
+ switch (facing) {
+ case 0:
+ while (ypos < ch->y1) {
+ nextFrame = getTimerDelay(5 + character) * _tickLength + _system->getMillis();
+ setCharacterPositionWithUpdate(character);
+ while (_system->getMillis() < nextFrame) { updateGameTimers(); }
+ }
+ break;
+
+ case 2:
+ while (ch->x1 < xpos) {
+ nextFrame = getTimerDelay(5 + character) * _tickLength + _system->getMillis();
+ setCharacterPositionWithUpdate(character);
+ while (_system->getMillis() < nextFrame) { updateGameTimers(); }
+ }
+ break;
+
+ case 4:
+ while (ypos > ch->y1) {
+ nextFrame = getTimerDelay(5 + character) * _tickLength + _system->getMillis();
+ setCharacterPositionWithUpdate(character);
+ while (_system->getMillis() < nextFrame) { updateGameTimers(); }
+ }
+ break;
+
+ case 6:
+ while (ch->x1 > xpos) {
+ nextFrame = getTimerDelay(5 + character) * _tickLength + _system->getMillis();
+ setCharacterPositionWithUpdate(character);
+ while (_system->getMillis() < nextFrame) { updateGameTimers(); }
+ }
+ break;
+
+ default:
+ break;
+ }
+ enableTimer(19);
+ enableTimer(14);
+ enableTimer(18);
+ _screen->showMouse();
+}
+
+void KyraEngine::setCharacterPositionWithUpdate(int character) {
+ debug(9, "KyraEngine::setCharacterPositionWithUpdate(%d)", character);
+ setCharacterPosition(character, 0);
+ _sprites->updateSceneAnims();
+ updateGameTimers();
+ _animator->updateAllObjectShapes();
+ updateTextFade();
+
+ if (_currentCharacter->sceneId == 210) {
+ updateKyragemFading();
+ }
+}
+
+int KyraEngine::setCharacterPosition(int character, int *facingTable) {
+ debug(9, "KyraEngine::setCharacterPosition(%d, 0x%X)", character, facingTable);
+ if (character == 0) {
+ _currentCharacter->x1 += _charXPosTable[_currentCharacter->facing];
+ _currentCharacter->y1 += _charYPosTable[_currentCharacter->facing];
+ setCharacterPositionHelper(0, facingTable);
+ return 1;
+ } else {
+ _characterList[character].x1 += _charXPosTable[_characterList[character].facing];
+ _characterList[character].y1 += _charYPosTable[_characterList[character].facing];
+ if (_characterList[character].sceneId == _currentCharacter->sceneId) {
+ setCharacterPositionHelper(character, 0);
+ }
+ }
+ return 0;
+}
+
+void KyraEngine::setCharacterPositionHelper(int character, int *facingTable) {
+ debug(9, "KyraEngine::setCharacterPositionHelper(%d, 0x%X)", character, facingTable);
+ Character *ch = &_characterList[character];
+ ++ch->currentAnimFrame;
+ int facing = ch->facing;
+ if (facingTable) {
+ if (*facingTable != *(facingTable - 1)) {
+ if (*(facingTable - 1) == *(facingTable + 1)) {
+ facing = getOppositeFacingDirection(*(facingTable - 1));
+ *facingTable = *(facingTable - 1);
+ }
+ }
+ }
+
+ static uint8 facingIsZero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static uint8 facingIsFour[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ if (facing == 0) {
+ ++facingIsZero[character];
+ } else {
+ bool resetTables = false;
+ if (facing != 7) {
+ if (facing - 1 != 0) {
+ if (facing != 4) {
+ if (facing == 3 || facing == 5) {
+ if (facingIsFour[character] > 2) {
+ facing = 4;
+ }
+ resetTables = true;
+ }
+ } else {
+ ++facingIsFour[character];
+ }
+ } else {
+ if (facingIsZero[character] > 2) {
+ facing = 0;
+ }
+ resetTables = true;
+ }
+ } else {
+ if (facingIsZero[character] > 2) {
+ facing = 0;
+ }
+ resetTables = true;
+ }
+
+ if (resetTables) {
+ facingIsZero[character] = 0;
+ facingIsFour[character] = 0;
+ }
+ }
+
+ static const uint16 maxAnimationFrame[] = {
+ 0x000F, 0x0031, 0x0055, 0x0000, 0x0000, 0x0000,
+ 0x0008, 0x002A, 0x004E, 0x0000, 0x0000, 0x0000,
+ 0x0022, 0x0046, 0x006A, 0x0000, 0x0000, 0x0000,
+ 0x001D, 0x0041, 0x0065, 0x0000, 0x0000, 0x0000,
+ 0x001F, 0x0043, 0x0067, 0x0000, 0x0000, 0x0000,
+ 0x0028, 0x004C, 0x0070, 0x0000, 0x0000, 0x0000,
+ 0x0023, 0x0047, 0x006B, 0x0000, 0x0000, 0x0000
+ };
+
+ if (facing == 0) {
+ if (maxAnimationFrame[36+character] > ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[36+character];
+ }
+ if (maxAnimationFrame[30+character] < ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[36+character];
+ }
+ } else if (facing == 4) {
+ if (maxAnimationFrame[18+character] > ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[18+character];
+ }
+ if (maxAnimationFrame[12+character] < ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[18+character];
+ }
+ } else {
+ if (maxAnimationFrame[18+character] < ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[30+character];
+ }
+ if (maxAnimationFrame[character] == ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[6+character];
+ }
+ if (maxAnimationFrame[character] < ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[6+character]+2;
+ }
+ }
+
+ if (character == 0) {
+ if (_brandonStatusBit & 0x10)
+ ch->currentAnimFrame = 88;
+ }
+
+ _animator->animRefreshNPC(character);
+}
+
+int KyraEngine::getOppositeFacingDirection(int dir) {
+ debug(9, "KyraEngine::getOppositeFacingDirection(%d)", dir);
+ switch (dir) {
+ case 0:
+ return 2;
+ break;
+
+ case 1:
+ return 1;
+ break;
+
+ case 3:
+ return 7;
+ break;
+
+ case 4:
+ return 6;
+ break;
+
+ case 5:
+ return 5;
+ break;
+
+ case 6:
+ return 4;
+ break;
+
+ case 7:
+ return 3;
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+void KyraEngine::loadSceneMSC() {
+ assert(_currentCharacter->sceneId < _roomTableSize);
+ int tableId = _roomTable[_currentCharacter->sceneId].nameIndex;
+ assert(tableId < _roomFilenameTableSize);
+ char fileNameBuffer[32];
+ strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
+ strcat(fileNameBuffer, ".MSC");
+ _screen->fillRect(0, 0, 319, 199, 0, 5);
+ loadBitmap(fileNameBuffer, 3, 5, 0);
+}
+
+void KyraEngine::startSceneScript(int brandonAlive) {
+ debug(9, "KyraEngine::startSceneScript(%d)", brandonAlive);
+ assert(_currentCharacter->sceneId < _roomTableSize);
+ int tableId = _roomTable[_currentCharacter->sceneId].nameIndex;
+ assert(tableId < _roomFilenameTableSize);
+ char fileNameBuffer[32];
+ strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
+ strcat(fileNameBuffer, ".CPS");
+ loadBitmap(fileNameBuffer, 3, 3, 0);
+ _sprites->loadSceneShapes();
+ _exitListPtr = 0;
+
+ _screen->setScreenPalette(_screen->_currentPalette);
+
+ _scaleMode = 1;
+ for (int i = 0; i < 145; ++i) {
+ _scaleTable[i] = 256;
+ }
+
+ clearNoDropRects();
+ _scriptInterpreter->initScript(_scriptClick, _scriptClickData);
+ strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
+ strcat(fileNameBuffer, ".EMC");
+ _scriptInterpreter->unloadScript(_scriptClickData);
+ _scriptInterpreter->loadScript(fileNameBuffer, _scriptClickData, _opcodeTable, _opcodeTableSize, 0);
+ _scriptInterpreter->startScript(_scriptClick, 0);
+ _scriptClick->variables[0] = _currentCharacter->sceneId;
+ _scriptClick->variables[7] = brandonAlive;
+
+ while (_scriptInterpreter->validScript(_scriptClick)) {
+ _scriptInterpreter->runScript(_scriptClick);
+ }
+}
+
+void KyraEngine::initSceneData(int facing, int unk1, int brandonAlive) {
+ debug(9, "KyraEngine::initSceneData(%d, %d, %d)", facing, unk1, brandonAlive);
+
+ int16 xpos2 = 0;
+ int setFacing = 1;
+
+ int16 xpos = 0, ypos = 0;
+
+ if (_brandonPosX == -1 && _brandonPosY == -1) {
+ switch (facing+1) {
+ case 0:
+ xpos = ypos = -1;
+ break;
+
+ case 1: case 2: case 8:
+ xpos = _sceneExits.southXPos;
+ ypos = _sceneExits.southYPos;
+ break;
+
+ case 3:
+ xpos = _sceneExits.westXPos;
+ ypos = _sceneExits.westYPos;
+ break;
+
+ case 4: case 5: case 6:
+ xpos = _sceneExits.northXPos;
+ ypos = _sceneExits.northYPos;
+ break;
+
+ case 7:
+ xpos = _sceneExits.eastXPos;
+ ypos = _sceneExits.eastYPos;
+ break;
+
+ default:
+ break;
+ }
+
+ if ((uint8)(_northExitHeight & 0xFF) + 2 >= ypos) {
+ ypos = (_northExitHeight & 0xFF) + 4;
+ }
+ if (xpos >= 308) {
+ xpos = 304;
+ }
+ if ((uint8)(_northExitHeight >> 8) - 2 <= ypos) {
+ ypos = (_northExitHeight >> 8) - 4;
+ }
+ if (xpos <= 12) {
+ xpos = 16;
+ }
+ }
+
+ if (_brandonPosX > -1) {
+ xpos = _brandonPosX;
+ }
+ if (_brandonPosY > -1) {
+ ypos = _brandonPosY;
+ }
+
+ int16 ypos2 = 0;
+ if (_brandonPosX > -1 && _brandonPosY > -1) {
+ switch (_currentCharacter->sceneId) {
+ case 1:
+ _currentCharacter->x1 = xpos;
+ _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = ypos;
+ _currentCharacter->y2 = ypos;
+ facing = 4;
+ xpos2 = 192;
+ ypos2 = 104;
+ setFacing = 0;
+ unk1 = 1;
+ break;
+
+ case 3:
+ _currentCharacter->x1 = xpos;
+ _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = ypos;
+ _currentCharacter->y2 = ypos;
+ facing = 2;
+ xpos2 = 204;
+ ypos2 = 94;
+ setFacing = 0;
+ unk1 = 1;
+ break;
+
+ case 26:
+ _currentCharacter->x1 = xpos;
+ _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = ypos;
+ _currentCharacter->y2 = ypos;
+ facing = 2;
+ xpos2 = 192;
+ ypos2 = 128;
+ setFacing = 0;
+ unk1 = 1;
+ break;
+
+ case 44:
+ _currentCharacter->x1 = xpos;
+ _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = ypos;
+ _currentCharacter->y2 = ypos;
+ facing = 6;
+ xpos2 = 156;
+ ypos2 = 96;
+ setFacing = 0;
+ unk1 = 1;
+ break;
+
+ case 37:
+ _currentCharacter->x1 = xpos;
+ _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = ypos;
+ _currentCharacter->y2 = ypos;
+ facing = 2;
+ xpos2 = 148;
+ ypos2 = 114;
+ setFacing = 0;
+ unk1 = 1;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ _brandonPosX = _brandonPosY = -1;
+
+ if (unk1 && setFacing) {
+ ypos2 = ypos;
+ xpos2 = xpos;
+ switch (facing) {
+ case 0:
+ ypos = 142;
+ break;
+
+ case 2:
+ xpos = -16;
+ break;
+
+ case 4:
+ ypos = (uint8)(_northExitHeight & 0xFF) - 4;
+ break;
+
+ case 6:
+ xpos = 336;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ xpos2 = (int16)(xpos2 & 0xFFFC);
+ ypos2 = (int16)(ypos2 & 0xFFFE);
+ xpos = (int16)(xpos & 0xFFFC);
+ ypos = (int16)(ypos & 0xFFFE);
+ _currentCharacter->facing = facing;
+ _currentCharacter->x1 = xpos;
+ _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = ypos;
+ _currentCharacter->y2 = ypos;
+
+ initSceneObjectList(brandonAlive);
+
+ if (unk1 && brandonAlive == 0) {
+ moveCharacterToPos(0, facing, xpos2, ypos2);
+ }
+
+ _scriptClick->variables[4] = _itemInHand;
+ _scriptClick->variables[7] = brandonAlive;
+ _scriptInterpreter->startScript(_scriptClick, 3);
+ while (_scriptInterpreter->validScript(_scriptClick)) {
+ _scriptInterpreter->runScript(_scriptClick);
+ }
+}
+
+void KyraEngine::initSceneObjectList(int brandonAlive) {
+ debug(9, "KyraEngine::initSceneObjectList(%d)", brandonAlive);
+ for (int i = 0; i < 28; ++i) {
+ _animator->actors()[i].active = 0;
+ }
+
+ int startAnimFrame = 0;
+
+ AnimObject *curAnimState = _animator->actors();
+ curAnimState->active = 1;
+ curAnimState->drawY = _currentCharacter->y1;
+ curAnimState->sceneAnimPtr = _shapes[4+_currentCharacter->currentAnimFrame];
+ curAnimState->animFrameNumber = _currentCharacter->currentAnimFrame;
+ startAnimFrame = _currentCharacter->currentAnimFrame-7;
+ int xOffset = _defaultShapeTable[startAnimFrame].xOffset;
+ int yOffset = _defaultShapeTable[startAnimFrame].yOffset;
+ if (_scaleMode) {
+ curAnimState->x1 = _currentCharacter->x1;
+ curAnimState->y1 = _currentCharacter->y1;
+
+ _animator->_brandonScaleX = _scaleTable[_currentCharacter->y1];
+ _animator->_brandonScaleY = _scaleTable[_currentCharacter->y1];
+
+ curAnimState->x1 += (_animator->_brandonScaleX * xOffset) >> 8;
+ curAnimState->y1 += (_animator->_brandonScaleY * yOffset) >> 8;
+ } else {
+ curAnimState->x1 = _currentCharacter->x1 + xOffset;
+ curAnimState->y1 = _currentCharacter->y1 + yOffset;
+ }
+ curAnimState->x2 = curAnimState->x1;
+ curAnimState->y2 = curAnimState->y1;
+ curAnimState->refreshFlag = 1;
+ curAnimState->bkgdChangeFlag = 1;
+ _animator->clearQueue();
+ _animator->addObjectToQueue(curAnimState);
+
+ int listAdded = 0;
+ int addedObjects = 1;
+
+ for (int i = 1; i < 5; ++i) {
+ Character *ch = &_characterList[i];
+ curAnimState = &_animator->actors()[addedObjects];
+ if (ch->sceneId != _currentCharacter->sceneId) {
+ curAnimState->active = 0;
+ curAnimState->refreshFlag = 0;
+ curAnimState->bkgdChangeFlag = 0;
+ ++addedObjects;
+ continue;
+ }
+
+ curAnimState->drawY = ch->y1;
+ curAnimState->sceneAnimPtr = _shapes[4+ch->currentAnimFrame];
+ curAnimState->animFrameNumber = ch->currentAnimFrame;
+ startAnimFrame = ch->currentAnimFrame-7;
+ xOffset = _defaultShapeTable[startAnimFrame].xOffset;
+ yOffset = _defaultShapeTable[startAnimFrame].yOffset;
+ if (_scaleMode) {
+ curAnimState->x1 = ch->x1;
+ curAnimState->y1 = ch->y1;
+
+ _animator->_brandonScaleX = _scaleTable[ch->y1];
+ _animator->_brandonScaleY = _scaleTable[ch->y1];
+
+ curAnimState->x1 += (_animator->_brandonScaleX * xOffset) >> 8;
+ curAnimState->y1 += (_animator->_brandonScaleY * yOffset) >> 8;
+ } else {
+ curAnimState->x1 = ch->x1 + xOffset;
+ curAnimState->y1 = ch->y1 + yOffset;
+ }
+ curAnimState->x2 = curAnimState->x1;
+ curAnimState->y2 = curAnimState->y1;
+ curAnimState->active = 1;
+ curAnimState->refreshFlag = 1;
+ curAnimState->bkgdChangeFlag = 1;
+
+ if (ch->facing >= 1 && ch->facing <= 3) {
+ curAnimState->flags |= 1;
+ } else if (ch->facing >= 5 && ch->facing <= 7) {
+ curAnimState->flags &= 0xFFFFFFFE;
+ }
+
+ _animator->addObjectToQueue(curAnimState);
+
+ ++addedObjects;
+ ++listAdded;
+ if (listAdded < 2)
+ i = 5;
+ }
+
+ for (int i = 0; i < 11; ++i) {
+ curAnimState = &_animator->sprites()[i];
+
+ if (_sprites->_anims[i].play) {
+ curAnimState->active = 1;
+ curAnimState->refreshFlag = 1;
+ curAnimState->bkgdChangeFlag = 1;
+ } else {
+ curAnimState->active = 0;
+ curAnimState->refreshFlag = 0;
+ curAnimState->bkgdChangeFlag = 0;
+ }
+ curAnimState->height = _sprites->_anims[i].height;
+ curAnimState->height2 = _sprites->_anims[i].height2;
+ curAnimState->width = _sprites->_anims[i].width + 1;
+ curAnimState->width2 = _sprites->_anims[i].width2;
+ curAnimState->drawY = _sprites->_anims[i].drawY;
+ curAnimState->x1 = curAnimState->x2 = _sprites->_anims[i].x;
+ curAnimState->y1 = curAnimState->y2 = _sprites->_anims[i].y;
+ curAnimState->background = _sprites->_anims[i].background;
+ curAnimState->sceneAnimPtr = _sprites->_sceneShapes[_sprites->_anims[i].sprite];
+
+ curAnimState->disable = _sprites->_anims[i].disable;
+
+ if (_sprites->_anims[i].unk2)
+ curAnimState->flags = 0x800;
+ else
+ curAnimState->flags = 0;
+
+ if (_sprites->_anims[i].flipX)
+ curAnimState->flags |= 0x1;
+
+ _animator->addObjectToQueue(curAnimState);
+ }
+
+ for (int i = 0; i < 12; ++i) {
+ curAnimState = &_animator->items()[i];
+ Room *curRoom = &_roomTable[_currentCharacter->sceneId];
+ byte curItem = curRoom->itemsTable[i];
+ if (curItem != 0xFF) {
+ curAnimState->drawY = curRoom->itemsYPos[i];
+ curAnimState->sceneAnimPtr = _shapes[220+curItem];
+ curAnimState->animFrameNumber = (int16)0xFFFF;
+ curAnimState->y1 = curRoom->itemsYPos[i];
+ curAnimState->x1 = curRoom->itemsXPos[i];
+
+ curAnimState->x1 -= (_animator->fetchAnimWidth(curAnimState->sceneAnimPtr, _scaleTable[curAnimState->drawY])) >> 1;
+ curAnimState->y1 -= _animator->fetchAnimHeight(curAnimState->sceneAnimPtr, _scaleTable[curAnimState->drawY]);
+
+ curAnimState->x2 = curAnimState->x1;
+ curAnimState->y2 = curAnimState->y1;
+
+ curAnimState->active = 1;
+ curAnimState->refreshFlag = 1;
+ curAnimState->bkgdChangeFlag = 1;
+
+ _animator->addObjectToQueue(curAnimState);
+ } else {
+ curAnimState->active = 0;
+ curAnimState->refreshFlag = 0;
+ curAnimState->bkgdChangeFlag = 0;
+ }
+ }
+
+ _animator->preserveAnyChangedBackgrounds();
+ curAnimState = _animator->actors();
+ curAnimState->bkgdChangeFlag = 1;
+ curAnimState->refreshFlag = 1;
+ for (int i = 1; i < 28; ++i) {
+ curAnimState = &_animator->objects()[i];
+ if (curAnimState->active) {
+ curAnimState->bkgdChangeFlag = 1;
+ curAnimState->refreshFlag = 1;
+ }
+ }
+ _animator->restoreAllObjectBackgrounds();
+ _animator->preserveAnyChangedBackgrounds();
+ _animator->prepDrawAllObjects();
+ initSceneScreen(brandonAlive);
+ _animator->copyChangedObjectsForward(0);
+}
+
+void KyraEngine::initSceneScreen(int brandonAlive) {
+ // XXX (Pointless?) Palette stuff
+ if (_unkScreenVar2 == 1) {
+ _screen->shuffleScreen(8, 8, 304, 128, 2, 0, _unkScreenVar3, false);
+ } else {
+ _screen->copyRegion(8, 8, 8, 8, 304, 128, 2, 0);
+ }
+ _screen->updateScreen();
+ // XXX More (pointless?) palette stuff
+
+ if (!_scriptInterpreter->startScript(_scriptClick, 2))
+ error("Could not start script function 2 of scene script");
+
+ _scriptClick->variables[7] = brandonAlive;
+
+ while (_scriptInterpreter->validScript(_scriptClick))
+ _scriptInterpreter->runScript(_scriptClick);
+
+ setTextFadeTimerCountdown(-1);
+ if (_currentCharacter->sceneId == 210) {
+ if (_itemInHand != -1)
+ magicOutMouseItem(2, -1);
+
+ _screen->hideMouse();
+ for (int i = 0; i < 10; ++i) {
+ if (_currentCharacter->inventoryItems[i] != 0xFF)
+ magicOutMouseItem(2, i);
+ }
+ _screen->showMouse();
+ }
+}
+
+int KyraEngine::handleSceneChange(int xpos, int ypos, int unk1, int frameReset) {
+ debug(9, "KyraEngine::handleSceneChange(%d, %d, %d, %d)", xpos, ypos, unk1, frameReset);
+ if (queryGameFlag(0xEF)) {
+ unk1 = 0;
+ }
+ int sceneId = _currentCharacter->sceneId;
+ _pathfinderFlag = 0;
+ if (xpos < 12) {
+ if (_roomTable[sceneId].westExit != 0xFFFF) {
+ xpos = 12;
+ ypos = _sceneExits.westYPos;
+ _pathfinderFlag = 7;
+ }
+ } else if(xpos >= 308) {
+ if (_roomTable[sceneId].eastExit != 0xFFFF) {
+ xpos = 307;
+ ypos = _sceneExits.eastYPos;
+ _pathfinderFlag = 13;
+ }
+ }
+
+ if (ypos <= (_northExitHeight&0xFF)+2) {
+ if (_roomTable[sceneId].northExit != 0xFFFF) {
+ xpos = _sceneExits.northXPos;
+ ypos = _northExitHeight & 0xFF;
+ _pathfinderFlag = 14;
+ }
+ } else if (ypos >= 136) {
+ if (_roomTable[sceneId].southExit != 0xFFFF) {
+ xpos = _sceneExits.southXPos;
+ ypos = 136;
+ _pathfinderFlag = 11;
+ }
+ }
+
+ int temp = xpos - _currentCharacter->x1;
+ if (ABS(temp) < 4) {
+ temp = ypos - _currentCharacter->y1;
+ if (ABS(temp) < 2) {
+ return 0;
+ }
+ }
+
+ int x = (int16)(_currentCharacter->x1 & 0xFFFC);
+ int y = (int16)(_currentCharacter->y1 & 0xFFFE);
+ xpos = (int16)(xpos & 0xFFFC);
+ ypos = (int16)(ypos & 0xFFFE);
+ int ret = findWay(x, y, xpos, ypos, _movFacingTable, 150);
+ _pathfinderFlag = 0;
+ if (ret >= _lastFindWayRet) {
+ _lastFindWayRet = ret;
+ }
+ if (ret == 0x7D00 || ret == 0) {
+ return 0;
+ }
+ return processSceneChange(_movFacingTable, unk1, frameReset);
+}
+
+int KyraEngine::processSceneChange(int *table, int unk1, int frameReset) {
+ debug(9, "KyraEngine::processSceneChange(0x%X, %d, %d)", table, unk1, frameReset);
+ if (queryGameFlag(0xEF)) {
+ unk1 = 0;
+ }
+ int *tableStart = table;
+ _sceneChangeState = 0;
+ _loopFlag2 = 0;
+ bool running = true;
+ int returnValue = 0;
+ uint32 nextFrame = 0;
+ _abortWalkFlag = false;
+ _mousePressFlag = false;
+
+ while (running) {
+ if (_abortWalkFlag) {
+ *table = 8;
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+ processInput(_mouseX, _mouseY);
+ return 0;
+ }
+ bool forceContinue = false;
+ switch (*table) {
+ case 0: case 1: case 2:
+ case 3: case 4: case 5:
+ case 6: case 7:
+ _currentCharacter->facing = getOppositeFacingDirection(*table);
+ break;
+
+ case 8:
+ forceContinue = true;
+ running = false;
+ break;
+
+ default:
+ ++table;
+ forceContinue = true;
+ break;
+ }
+
+ returnValue = changeScene(_currentCharacter->facing);
+ if (returnValue) {
+ running = false;
+ _abortWalkFlag = false;
+ }
+
+ if (unk1) {
+ if (_mousePressFlag) {
+ running = false;
+ _sceneChangeState = 1;
+ }
+ }
+
+ if (forceContinue || !running) {
+ continue;
+ }
+
+ int temp = 0;
+ if (table == tableStart || table[1] == 8) {
+ temp = setCharacterPosition(0, 0);
+ } else {
+ temp = setCharacterPosition(0, table);
+ }
+ if (temp) {
+ ++table;
+ }
+
+ nextFrame = getTimerDelay(5) * _tickLength + _system->getMillis();
+ while (_system->getMillis() < nextFrame) {
+ _sprites->updateSceneAnims();
+ updateMousePointer();
+ updateGameTimers();
+ _animator->updateAllObjectShapes();
+ updateTextFade();
+ if (_currentCharacter->sceneId == 210) {
+ updateKyragemFading();
+ if (seq_playEnd() || _beadStateVar == 4 || _beadStateVar == 5) {
+ *table = 8;
+ running = false;
+ break;
+ }
+ }
+ if ((nextFrame - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+
+ if (frameReset && !(_brandonStatusBit & 2)) {
+ _currentCharacter->currentAnimFrame = 7;
+ }
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+ return returnValue;
+}
+
+int KyraEngine::changeScene(int facing) {
+ debug(9, "KyraEngine::changeScene(%d)", facing);
+ if (queryGameFlag(0xEF)) {
+ if (_currentCharacter->sceneId == 5) {
+ return 0;
+ }
+ }
+
+ int xpos = _charXPosTable[facing] + _currentCharacter->x1;
+ int ypos = _charYPosTable[facing] + _currentCharacter->y1;
+
+ if (xpos >= 12 && xpos <= 308) {
+ if (!lineIsPassable(xpos, ypos))
+ return false;
+ }
+
+ if (_exitListPtr) {
+ int16 *ptr = _exitListPtr;
+ // this loop should be only entered on time, seems to be some hack in the original
+ while (true) {
+ if (*ptr == -1)
+ break;
+
+ if (*ptr > _currentCharacter->x1 || _currentCharacter->y1 < ptr[1] || _currentCharacter->x1 > ptr[2] || _currentCharacter->y1 > ptr[3]) {
+ ptr += 10;
+ break;
+ }
+ _brandonPosX = ptr[6];
+ _brandonPosY = ptr[7];
+ uint16 sceneId = ptr[5];
+ facing = ptr[4];
+ int unk1 = ptr[8];
+ int unk2 = ptr[9];
+ if (sceneId == 0xFFFF) {
+ switch (facing) {
+ case 0:
+ sceneId = _roomTable[_currentCharacter->sceneId].northExit;
+ break;
+
+ case 2:
+ sceneId = _roomTable[_currentCharacter->sceneId].eastExit;
+ break;
+
+ case 4:
+ sceneId = _roomTable[_currentCharacter->sceneId].southExit;
+ break;
+
+ case 6:
+ sceneId = _roomTable[_currentCharacter->sceneId].westExit;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ _currentCharacter->facing = facing;
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+ enterNewScene(sceneId, facing, unk1, unk2, 0);
+ resetGameFlag(0xEE);
+ return 1;
+ }
+ }
+
+ int returnValue = 0;
+ facing = 0;
+
+ if ((_northExitHeight & 0xFF) + 2 >= ypos || (_northExitHeight & 0xFF) + 2 >= _currentCharacter->y1) {
+ facing = 0;
+ returnValue = 1;
+ }
+
+ if (xpos >= 308 || (_currentCharacter->x1 + 4) >= 308) {
+ facing = 2;
+ returnValue = 1;
+ }
+
+ if (((_northExitHeight >> 8) & 0xFF) - 2 < ypos || ((_northExitHeight >> 8) & 0xFF) - 2 < _currentCharacter->y1) {
+ facing = 4;
+ returnValue = 1;
+ }
+
+ if (xpos <= 12 || _currentCharacter->y1 <= 12) {
+ facing = 6;
+ returnValue = 1;
+ }
+
+ if (!returnValue)
+ return 0;
+
+ uint16 sceneId = 0xFFFF;
+ switch (facing) {
+ case 0:
+ sceneId = _roomTable[_currentCharacter->sceneId].northExit;
+ break;
+
+ case 2:
+ sceneId = _roomTable[_currentCharacter->sceneId].eastExit;
+ break;
+
+ case 4:
+ sceneId = _roomTable[_currentCharacter->sceneId].southExit;
+ break;
+
+ default:
+ sceneId = _roomTable[_currentCharacter->sceneId].westExit;
+ break;
+ }
+
+ if (sceneId == 0xFFFF)
+ return 0;
+
+ enterNewScene(sceneId, facing, 1, 1, 0);
+ return returnValue;
+}
+
+void KyraEngine::setCharactersInDefaultScene() {
+ static const uint32 defaultSceneTable[][4] = {
+ { 0xFFFF, 0x0004, 0x0003, 0xFFFF },
+ { 0xFFFF, 0x0022, 0xFFFF, 0x0000 },
+ { 0xFFFF, 0x001D, 0x0021, 0xFFFF },
+ { 0xFFFF, 0x0000, 0x0000, 0xFFFF }
+ };
+
+ for (int i = 1; i < 5; ++i) {
+ Character *cur = &_characterList[i];
+ //cur->field_20 = 0;
+ const uint32 *curTable = defaultSceneTable[i-1];
+ cur->sceneId = curTable[0];
+ if (cur->sceneId == _currentCharacter->sceneId) {
+ //++cur->field_20;
+ cur->sceneId = curTable[1/*cur->field_20*/];
+ }
+ //cur->field_23 = curTable[cur->field_20+1];
+ }
+}
+
+void KyraEngine::setCharactersPositions(int character) {
+ static uint16 initXPosTable[] = {
+ 0x3200, 0x0024, 0x2230, 0x2F00, 0x0020, 0x002B,
+ 0x00CA, 0x00F0, 0x0082, 0x00A2, 0x0042
+ };
+ static uint8 initYPosTable[] = {
+ 0x00, 0xA2, 0x00, 0x42, 0x00,
+ 0x67, 0x67, 0x60, 0x5A, 0x71,
+ 0x76
+ };
+ assert(character < ARRAYSIZE(initXPosTable));
+ Character *edit = &_characterList[character];
+ edit->x1 = edit->x2 = initXPosTable[character];
+ edit->y1 = edit->y2 = initYPosTable[character];
+}
+
+#pragma mark -
+#pragma mark - Pathfinder
+#pragma mark -
+
+int KyraEngine::findWay(int x, int y, int toX, int toY, int *moveTable, int moveTableSize) {
+ debug(9, "KyraEngine::findWay(%d, %d, %d, %d, 0x%X, %d)", x, y, toX, toY, moveTable, moveTableSize);
+ x &= 0xFFFC; toX &= 0xFFFC;
+ y &= 0xFFFE; toY &= 0xFFFE;
+ x = (int16)x; y = (int16)y; toX = (int16)toX; toY = (int16)toY;
+
+ if (x == toY && y == toY) {
+ moveTable[0] = 8;
+ return 0;
+ }
+
+ int curX = x;
+ int curY = y;
+ int lastUsedEntry = 0;
+ int tempValue = 0;
+ int *pathTable1 = new int[0x7D0];
+ int *pathTable2 = new int[0x7D0];
+ assert(pathTable1 && pathTable2);
+
+ while (true) {
+ int newFacing = getFacingFromPointToPoint(x, y, toX, toY);
+ changePosTowardsFacing(curX, curY, newFacing);
+
+ if (curX == toX && curY == toY) {
+ if (!lineIsPassable(curX, curY))
+ break;
+ moveTable[lastUsedEntry++] = newFacing;
+ break;
+ }
+
+ if (lineIsPassable(curX, curY)) {
+ if (lastUsedEntry == moveTableSize) {
+ delete [] pathTable1;
+ delete [] pathTable2;
+ return 0x7D00;
+ }
+ // debug drawing
+ //if (curX >= 0 && curY >= 0 && curX < 320 && curY < 200) {
+ // _screen->setPagePixel(0, curX, curY, 11);
+ // _screen->updateScreen();
+ // waitTicks(5);
+ //}
+ moveTable[lastUsedEntry++] = newFacing;
+ x = curX;
+ y = curY;
+ continue;
+ }
+
+ int temp = 0;
+ while (true) {
+ newFacing = getFacingFromPointToPoint(curX, curY, toX, toY);
+ changePosTowardsFacing(curX, curY, newFacing);
+ // debug drawing
+ //if (curX >= 0 && curY >= 0 && curX < 320 && curY < 200) {
+ // _screen->setPagePixel(0, curX, curY, 8);
+ // _screen->updateScreen();
+ // waitTicks(5);
+ //}
+
+ if (!lineIsPassable(curX, curY)) {
+ if (curX != toX || curY != toY)
+ continue;
+ }
+
+ if (curX == toX && curY == toY) {
+ if (!lineIsPassable(curX, curY)) {
+ tempValue = 0;
+ temp = 0;
+ break;
+ }
+ }
+
+ temp = findSubPath(x, y, curX, curY, pathTable1, 1, 0x7D0);
+ tempValue = findSubPath(x, y, curX, curY, pathTable2, 0, 0x7D0);
+ if (curX == toX && curY == toY) {
+ if (temp == 0x7D00 && tempValue == 0x7D00) {
+ delete [] pathTable1;
+ delete [] pathTable2;
+ return 0x7D00;
+ }
+ }
+
+ if (temp != 0x7D00 || tempValue != 0x7D00) {
+ break;
+ }
+ }
+
+ if (temp < tempValue) {
+ if (lastUsedEntry + temp > moveTableSize) {
+ delete [] pathTable1;
+ delete [] pathTable2;
+ return 0x7D00;
+ }
+ memcpy(&moveTable[lastUsedEntry], pathTable1, temp*sizeof(int));
+ lastUsedEntry += temp;
+ } else {
+ if (lastUsedEntry + tempValue > moveTableSize) {
+ delete [] pathTable1;
+ delete [] pathTable2;
+ return 0x7D00;
+ }
+ memcpy(&moveTable[lastUsedEntry], pathTable2, tempValue*sizeof(int));
+ lastUsedEntry += tempValue;
+ }
+ x = curX;
+ y = curY;
+ if (curX == toX && curY == toY) {
+ break;
+ }
+ }
+ delete [] pathTable1;
+ delete [] pathTable2;
+ moveTable[lastUsedEntry] = 8;
+ return getMoveTableSize(moveTable);
+}
+
+int KyraEngine::findSubPath(int x, int y, int toX, int toY, int *moveTable, int start, int end) {
+ debug(9, "KyraEngine::findSubPath(%d, %d, %d, %d, 0x%X, %d, %d)", x, y, toX, toY, moveTable, start, end);
+ // only used for debug specific code
+ //static uint16 unkTable[] = { 8, 5 };
+ static const int8 facingTable1[] = { 7, 0, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 0 };
+ static const int8 facingTable2[] = { -1, 0, -1, 2, -1, 4, -1, 6, -1, 2, -1, 4, -1, 6, -1, 0 };
+ static const int8 facingTable3[] = { 2, 4, 4, 6, 6, 0, 0, 2, 6, 6, 0, 0, 2, 2, 4, 4 };
+ static const int8 addPosTableX[] = { -1, 0, -1, 4, -1, 0, -1, -4, -1, -4, -1, 0, -1, 4, -1, 0 };
+ static const int8 addPosTableY[] = { -1, 2, -1, 0, -1, -2, -1, 0, -1, 0, -1, 2, -1, 0, -1, -2 };
+
+ // debug specific
+ //++unkTable[start];
+ //while (_screen->getPalette(0)[unkTable[start]] != 0x0F) {
+ // ++unkTable[start];
+ //}
+
+ int xpos1 = x, xpos2 = x;
+ int ypos1 = y, ypos2 = y;
+ int newFacing = getFacingFromPointToPoint(x, y, toX, toY);
+ int position = 0;
+
+ while (position != end) {
+ int newFacing2 = newFacing;
+ while (true) {
+ changePosTowardsFacing(xpos1, ypos1, facingTable1[start*8 + newFacing2]);
+ if (!lineIsPassable(xpos1, ypos1)) {
+ if (facingTable1[start*8 + newFacing2] == newFacing) {
+ return 0x7D00;
+ }
+ newFacing2 = facingTable1[start*8 + newFacing2];
+ xpos1 = x;
+ ypos1 = y;
+ continue;
+ }
+ newFacing = facingTable1[start*8 + newFacing2];
+ break;
+ }
+ // debug drawing
+ //if (xpos1 >= 0 && ypos1 >= 0 && xpos1 < 320 && ypos1 < 200) {
+ // _screen->setPagePixel(0, xpos1, ypos1, unkTable[start]);
+ // _screen->updateScreen();
+ // waitTicks(5);
+ //}
+ if (newFacing & 1) {
+ int temp = xpos1 + addPosTableX[newFacing + start * 8];
+ if (toX == temp) {
+ temp = ypos1 + addPosTableY[newFacing + start * 8];
+ if (toY == temp) {
+ moveTable[position++] = facingTable2[newFacing + start * 8];
+ return position;
+ }
+ }
+ }
+ moveTable[position++] = newFacing;
+ x = xpos1;
+ y = ypos1;
+ if (x == toX && y == toY) {
+ return position;
+ }
+
+ if (xpos1 == xpos2 && ypos1 == ypos2) {
+ break;
+ }
+
+ newFacing = facingTable3[start*8 + newFacing];
+ }
+ return 0x7D00;
+}
+
+int KyraEngine::getFacingFromPointToPoint(int x, int y, int toX, int toY) {
+ debug(9, "KyraEngine::getFacingFromPointToPoint(%d, %d, %d, %d)", x, y, toX, toY);
+ static const int facingTable[] = {
+ 1, 0, 1, 2, 3, 4, 3, 2, 7, 0, 7, 6, 5, 4, 5, 6
+ };
+
+ int facingEntry = 0;
+ int ydiff = y - toY;
+ if (ydiff < 0) {
+ ++facingEntry;
+ ydiff = -ydiff;
+ }
+ facingEntry <<= 1;
+
+ int xdiff = toX - x;
+ if (xdiff < 0) {
+ ++facingEntry;
+ xdiff = -xdiff;
+ }
+
+ if (xdiff >= ydiff) {
+ int temp = ydiff;
+ ydiff = xdiff;
+ xdiff = temp;
+
+ facingEntry <<= 1;
+ } else {
+ facingEntry <<= 1;
+ facingEntry += 1;
+ }
+ int temp = (ydiff + 1) >> 1;
+
+ if (xdiff < temp) {
+ facingEntry <<= 1;
+ facingEntry += 1;
+ } else {
+ facingEntry <<= 1;
+ }
+ assert(facingEntry < ARRAYSIZE(facingTable));
+ return facingTable[facingEntry];
+}
+
+void KyraEngine::changePosTowardsFacing(int &x, int &y, int facing) {
+ debug(9, "KyraEngine::changePosTowardsFacing(%d, %d, %d)", x, y, facing);
+ x += _addXPosTable[facing];
+ y += _addYPosTable[facing];
+}
+
+bool KyraEngine::lineIsPassable(int x, int y) {
+ debug(9, "KyraEngine::lineIsPassable(%d, %d)", x, y);
+ if (queryGameFlag(0xEF)) {
+ if (_currentCharacter->sceneId == 5)
+ return true;
+ }
+
+ if (_pathfinderFlag & 2) {
+ if (x >= 312)
+ return false;
+ }
+
+ if (_pathfinderFlag & 4) {
+ if (y >= 136)
+ return false;
+ }
+
+ if (_pathfinderFlag & 8) {
+ if (x < 8)
+ return false;
+ }
+
+ if (_pathfinderFlag2) {
+ if (x <= 8 || x >= 312)
+ return true;
+ if (y < (_northExitHeight & 0xFF) || y > 135)
+ return true;
+ }
+
+ if (y > 137) {
+ return false;
+ }
+
+ int ypos = 8;
+ if (_scaleMode) {
+ ypos = (_scaleTable[y] >> 5) + 1;
+ if (8 < ypos)
+ ypos = 8;
+ }
+
+ x -= (ypos >> 1);
+ if (y < 0)
+ y = 0;
+
+ int xpos = x;
+ int xtemp = xpos + ypos - 1;
+ if (x < 0)
+ xpos = 0;
+
+ if (xtemp > 319)
+ xtemp = 319;
+
+ for (; xpos < xtemp; ++xpos) {
+ if (!_screen->getShapeFlag1(xpos, y))
+ return false;
+ }
+ return true;
+}
+
+int KyraEngine::getMoveTableSize(int *moveTable) {
+ debug(9, "KyraEngine::getMoveTableSize(0x%X)", moveTable);
+ int retValue = 0;
+ if (moveTable[0] == 8)
+ return 0;
+
+ static const int facingTable[] = {
+ 4, 5, 6, 7, 0, 1, 2, 3
+ };
+ static const int unkTable[] = {
+ -1, -1, 1, 2, -1, 6, 7, -1,
+ -1, -1, -1, -1, 2, -1, 0, -1,
+ 1, -1, -1, -1, 3, 4, -1, 0,
+ 2, -1, -1, -1, -1, -1, 4, -1,
+ -1, 2, 3, -1, -1, -1, 5, 6,
+ 6, -1, 4, -1, -1, -1, -1, -1,
+ 7, 0, -1, 4, 5, -1, -1, -1,
+ -1, -1, 0, -1, 6, -1, -1, -1
+ };
+
+ int *oldPosition = moveTable;
+ int *tempPosition = moveTable;
+ int *curPosition = moveTable + 1;
+ retValue = 1;
+
+ while (*curPosition != 8) {
+ if (*oldPosition == facingTable[*curPosition]) {
+ retValue -= 2;
+ *oldPosition = 9;
+ *curPosition = 9;
+
+ while (tempPosition != moveTable) {
+ --tempPosition;
+ if (*tempPosition != 9)
+ break;
+ }
+
+ if (tempPosition == moveTable && *tempPosition == 9) {
+ while (*tempPosition != 8 && *tempPosition == 9) {
+ ++tempPosition;
+ }
+ if (*tempPosition == 8) {
+ return 0;
+ }
+ }
+
+ oldPosition = tempPosition;
+ curPosition = oldPosition+1;
+ while (*curPosition != 8 && *curPosition == 9) {
+ ++curPosition;
+ }
+ continue;
+ }
+
+ if (unkTable[*curPosition+((*oldPosition)*8)] != -1) {
+ --retValue;
+ *oldPosition = unkTable[*curPosition+((*oldPosition)*8)];
+ *curPosition = 9;
+
+ if (tempPosition != oldPosition) {
+ curPosition = oldPosition;
+ oldPosition = tempPosition;
+ while (true) {
+ if (tempPosition == moveTable) {
+ break;
+ }
+ --tempPosition;
+ if (*tempPosition != 9) {
+ break;
+ }
+ }
+ } else {
+ while (true) {
+ ++curPosition;
+ if (*curPosition != 9) {
+ break;
+ }
+ }
+ }
+ continue;
+ }
+
+ tempPosition = oldPosition;
+ oldPosition = curPosition;
+ ++retValue;
+ while (true) {
+ ++curPosition;
+ if (*curPosition != 9) {
+ break;
+ }
+ }
+ }
+
+ return retValue;
+}
+
+} // end of namespace Kyra
diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp
new file mode 100644
index 0000000000..4bf2d8da75
--- /dev/null
+++ b/engines/kyra/screen.cpp
@@ -0,0 +1,2084 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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 "common/system.h"
+#include "kyra/screen.h"
+#include "kyra/kyra.h"
+
+namespace Kyra {
+
+#define BITBLIT_RECTS 10
+
+Screen::Screen(KyraEngine *vm, OSystem *system)
+ : _system(system), _vm(vm) {
+ _curPage = 0;
+ for (int pageNum = 0; pageNum < SCREEN_PAGE_NUM; pageNum += 2) {
+ uint8 *pagePtr = (uint8 *)malloc(SCREEN_PAGE_SIZE);
+ if (pagePtr) {
+ memset(pagePtr, 0, SCREEN_PAGE_SIZE);
+ _pagePtrs[pageNum] = _pagePtrs[pageNum + 1] = pagePtr;
+ }
+ }
+ memset(_shapePages, 0, sizeof(_shapePages));
+ _currentPalette = (uint8 *)malloc(768);
+ if (_currentPalette) {
+ memset(_currentPalette, 0, 768);
+ }
+ _screenPalette = (uint8 *)malloc(768);
+ if (_screenPalette) {
+ memset(_screenPalette, 0, 768);
+ }
+ for (int i = 0; i < 3; ++i) {
+ _palettes[i] = (uint8 *)malloc(768);
+ if (_palettes[i]) {
+ memset(_palettes[i], 0, 768);
+ }
+ }
+ _curDim = &_screenDimTable[0];
+ _charWidth = 0;
+ _charOffset = 0;
+ memset(_fonts, 0, sizeof(_fonts));
+ for (int i = 0; i < ARRAYSIZE(_textColorsMap); ++i) {
+ _textColorsMap[i] = i;
+ }
+ _decodeShapeBuffer = NULL;
+ _decodeShapeBufferSize = 0;
+ _animBlockPtr = NULL;
+ _animBlockSize = 0;
+ _mouseLockCount = 0;
+
+ _bitBlitRects = new Rect[BITBLIT_RECTS];
+ assert(_bitBlitRects);
+ memset(_bitBlitRects, 0, sizeof(Rect)*BITBLIT_RECTS);
+ _bitBlitNum = 0;
+ memset(_saveLoadPage, 0, sizeof(_saveLoadPage));
+
+ _unkPtr1 = (uint8*)malloc(getRectSize(1, 144));
+ memset(_unkPtr1, 0, getRectSize(1, 144));
+ _unkPtr2 = (uint8*)malloc(getRectSize(1, 144));
+ memset(_unkPtr2, 0, getRectSize(1, 144));
+}
+
+Screen::~Screen() {
+ for (int pageNum = 0; pageNum < SCREEN_PAGE_NUM; pageNum += 2) {
+ free(_pagePtrs[pageNum]);
+ _pagePtrs[pageNum] = _pagePtrs[pageNum + 1] = 0;
+ }
+ for (int f = 0; f < ARRAYSIZE(_fonts); ++f) {
+ delete[] _fonts[f].fontData;
+ _fonts[f].fontData = NULL;
+ }
+ free(_currentPalette);
+ free(_screenPalette);
+ free(_decodeShapeBuffer);
+ free(_animBlockPtr);
+ for (int i = 0; i < 3; ++i) {
+ free(_palettes[i]);
+ }
+ delete [] _bitBlitRects;
+ for (int i = 0; i < ARRAYSIZE(_saveLoadPage); ++i) {
+ delete [] _saveLoadPage[i];
+ _saveLoadPage[i] = 0;
+ }
+
+ free(_unkPtr1);
+ free(_unkPtr2);
+}
+
+void Screen::updateScreen() {
+ debug(9, "Screen::updateScreen()");
+ _system->copyRectToScreen(getPagePtr(0), SCREEN_W, 0, 0, SCREEN_W, SCREEN_H);
+ //for debug reasons (needs 640x200 screen)
+ //_system->copyRectToScreen(getPagePtr(2), SCREEN_W, 320, 0, SCREEN_W, SCREEN_H);
+ _system->updateScreen();
+}
+
+uint8 *Screen::getPagePtr(int pageNum) {
+ debug(9, "Screen::getPagePtr(%d)", pageNum);
+ assert(pageNum < SCREEN_PAGE_NUM);
+ return _pagePtrs[pageNum];
+}
+
+void Screen::clearPage(int pageNum) {
+ debug(9, "Screen::clearPage(%d)", pageNum);
+ assert(pageNum < SCREEN_PAGE_NUM);
+ memset(getPagePtr(pageNum), 0, SCREEN_PAGE_SIZE);
+}
+
+int Screen::setCurPage(int pageNum) {
+ debug(9, "Screen::setCurPage(%d)", pageNum);
+ assert(pageNum < SCREEN_PAGE_NUM);
+ int previousPage = _curPage;
+ _curPage = pageNum;
+ return previousPage;
+}
+
+void Screen::clearCurPage() {
+ debug(9, "Screen::clearCurPage()");
+ memset(getPagePtr(_curPage), 0, SCREEN_PAGE_SIZE);
+}
+
+uint8 Screen::getPagePixel(int pageNum, int x, int y) {
+ debug(9, "Screen::getPagePixel(%d, %d, %d)", pageNum, x, y);
+ assert(pageNum < SCREEN_PAGE_NUM);
+ assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H);
+ return _pagePtrs[pageNum][y * SCREEN_W + x];
+}
+
+void Screen::setPagePixel(int pageNum, int x, int y, uint8 color) {
+ debug(9, "Screen::setPagePixel(%d, %d, %d, %d)", pageNum, x, y, color);
+ assert(pageNum < SCREEN_PAGE_NUM);
+ assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H);
+ _pagePtrs[pageNum][y * SCREEN_W + x] = color;
+}
+
+void Screen::fadeFromBlack() {
+ debug(9, "Screen::fadeFromBlack()");
+ fadePalette(_currentPalette, 0x54);
+}
+
+void Screen::fadeToBlack() {
+ debug(9, "Screen::fadeToBlack()");
+ uint8 blackPal[768];
+ memset(blackPal, 0, 768);
+ fadePalette(blackPal, 0x54);
+}
+
+void Screen::fadeSpecialPalette(int palIndex, int startIndex, int size, int fadeTime) {
+ debug(9, "fadeSpecialPalette(%d, %d, %d, %d)", palIndex, startIndex, size, fadeTime);
+ assert(_vm->palTable1()[palIndex]);
+ assert(_currentPalette);
+ uint8 tempPal[768];
+ memcpy(tempPal, _currentPalette, 768);
+ memcpy(&tempPal[startIndex*3], _vm->palTable1()[palIndex], size*3);
+ fadePalette(tempPal, fadeTime*18);
+ memcpy(&_currentPalette[startIndex*3], &tempPal[startIndex*3], size*3);
+ setScreenPalette(_currentPalette);
+ _system->updateScreen();
+}
+
+void Screen::fadePalette(const uint8 *palData, int delay) {
+ debug(9, "Screen::fadePalette(0x%X, %d)", palData, delay);
+ uint8 fadePal[768];
+ memcpy(fadePal, _screenPalette, 768);
+ uint8 diff, maxDiff = 0;
+ for (int i = 0; i < 768; ++i) {
+ diff = ABS(palData[i] - fadePal[i]);
+ if (diff > maxDiff) {
+ maxDiff = diff;
+ }
+ }
+ int16 delayInc = delay << 8;
+ if (maxDiff != 0) {
+ delayInc /= maxDiff;
+ }
+ delay = delayInc;
+ for (diff = 1; diff <= maxDiff; ++diff) {
+ if (delayInc >= 512) {
+ break;
+ }
+ delayInc += delay;
+ }
+ int delayAcc = 0;
+ while (1) {
+ delayAcc += delayInc;
+ bool needRefresh = false;
+ for (int i = 0; i < 768; ++i) {
+ int c1 = palData[i];
+ int c2 = fadePal[i];
+ if (c1 != c2) {
+ needRefresh = true;
+ if (c1 > c2) {
+ c2 += diff;
+ if (c1 < c2) {
+ c2 = c1;
+ }
+ }
+ if (c1 < c2) {
+ c2 -= diff;
+ if (c1 > c2) {
+ c2 = c1;
+ }
+ }
+ fadePal[i] = (uint8)c2;
+ }
+ }
+ if (!needRefresh) {
+ break;
+ }
+ setScreenPalette(fadePal);
+ _system->updateScreen();
+ //_system->delayMillis((delayAcc >> 8) * 1000 / 60);
+ _vm->delay((delayAcc >> 8) * 1000 / 60);
+ delayAcc &= 0xFF;
+ }
+}
+
+void Screen::setScreenPalette(const uint8 *palData) {
+ debug(9, "Screen::setScreenPalette(0x%X)", palData);
+ memcpy(_screenPalette, palData, 768);
+ uint8 screenPal[256 * 4];
+ for (int i = 0; i < 256; ++i) {
+ screenPal[4 * i + 0] = (palData[0] << 2) | (palData[0] & 3);
+ screenPal[4 * i + 1] = (palData[1] << 2) | (palData[1] & 3);
+ screenPal[4 * i + 2] = (palData[2] << 2) | (palData[2] & 3);
+ screenPal[4 * i + 3] = 0;
+ palData += 3;
+ }
+ _system->setPalette(screenPal, 0, 256);
+}
+
+void Screen::copyToPage0(int y, int h, uint8 page, uint8 *seqBuf) {
+ debug(9, "Screen::copyToPage0(%d, %d, %d, 0x%X)", y, h, page, seqBuf);
+ assert(y + h <= SCREEN_H);
+ const uint8 *src = getPagePtr(page) + y * SCREEN_W;
+ uint8 *dstPage = getPagePtr(0) + y * SCREEN_W;
+ for (int i = 0; i < h; ++i) {
+ for (int x = 0; x < SCREEN_W; ++x) {
+ if (seqBuf[x] != src[x]) {
+ seqBuf[x] = src[x];
+ dstPage[x] = src[x];
+ }
+ }
+ src += SCREEN_W;
+ seqBuf += SCREEN_W;
+ dstPage += SCREEN_W;
+ }
+}
+
+void Screen::copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage, int flags) {
+ debug(9, "Screen::copyRegion(%d, %d, %d, %d, %d, %d, %d, %d, %d)", x1, y1, x2, y2, w, h, srcPage, dstPage, flags);
+
+ if (flags & CR_CLIPPED) {
+ if (x2 < 0) {
+ if (x2 <= -w)
+ return;
+ w += x2;
+ x1 -= x2;
+ x2 = 0;
+ } else if (x2 + w >= SCREEN_W) {
+ if (x2 > SCREEN_W)
+ return;
+ w = SCREEN_W - x2;
+ }
+
+ if (y2 < 0) {
+ if (y2 <= -h )
+ return;
+ h += y2;
+ y1 -= y2;
+ y2 = 0;
+ } else if (y2 + h >= SCREEN_H) {
+ if (y2 > SCREEN_H)
+ return;
+ h = SCREEN_H - y2;
+ }
+ }
+
+ assert(x1 + w <= SCREEN_W && y1 + h <= SCREEN_H);
+ const uint8 *src = getPagePtr(srcPage) + y1 * SCREEN_W + x1;
+ assert(x2 + w <= SCREEN_W && y2 + h <= SCREEN_H);
+ uint8 *dst = getPagePtr(dstPage) + y2 * SCREEN_W + x2;
+
+ if (flags & CR_X_FLIPPED) {
+ while (h--) {
+ for (int i = 0; i < w; ++i) {
+ if (src[i]) {
+ dst[w-i] = src[i];
+ }
+ }
+ src += SCREEN_W;
+ dst += SCREEN_W;
+ }
+ } else {
+ while (h--) {
+ for (int i = 0; i < w; ++i) {
+ if (src[i]) {
+ dst[i] = src[i];
+ }
+ }
+ src += SCREEN_W;
+ dst += SCREEN_W;
+ }
+ }
+}
+
+void Screen::copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 *dest) {
+ debug(9, "Screen::copyRegionToBuffer(%d, %d, %d, %d, %d)", pageNum, x, y, w, h);
+ assert(x >= 0 && x < Screen::SCREEN_W && y >= 0 && y < Screen::SCREEN_H && dest);
+ uint8 *pagePtr = getPagePtr(pageNum);
+ for (int i = y; i < y + h; i++) {
+ memcpy(dest + (i - y) * w, pagePtr + i * SCREEN_W + x, w);
+ }
+}
+
+void Screen::copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint8 *src) {
+ debug(9, "Screen::copyBlockToPage(%d, %d, %d, %d, %d, 0x%X)", pageNum, x, y, w, h, src);
+ assert(x >= 0 && x < Screen::SCREEN_W && y >= 0 && y < Screen::SCREEN_H);
+ uint8 *dst = getPagePtr(pageNum) + y * SCREEN_W + x;
+ while (h--) {
+ memcpy(dst, src, w);
+ dst += SCREEN_W;
+ src += w;
+ }
+}
+
+void Screen::copyFromCurPageBlock(int x, int y, int w, int h, const uint8 *src) {
+ debug(9, "Screen::copyFromCurPageBlock(%d, %d, %d, %d, 0x%X)", x, y, w, h, src);
+ if (x < 0) {
+ x = 0;
+ } else if (x >= 40) {
+ return;
+ }
+ if (x + w > 40) {
+ w = 40 - x;
+ }
+ if (y < 0) {
+ y = 0;
+ } else if (y >= 200) {
+ return;
+ }
+ if (y + h > 200) {
+ h = 200 - y;
+ }
+ uint8 *dst = getPagePtr(_curPage) + y * SCREEN_W + x * 8;
+ while (h--) {
+ memcpy(dst, src, w*8);
+ dst += SCREEN_W;
+ src += w*8;
+ }
+}
+
+void Screen::copyCurPageBlock(int x, int y, int w, int h, uint8 *dst) {
+ debug(9, "Screen::copyCurPageBlock(%d, %d, %d, %d, 0x%X)", x, y, w, h, dst);
+ assert(dst);
+ if (x < 0) {
+ x = 0;
+ } else if (x >= 40) {
+ return;
+ }
+ if (x + w > 40) {
+ w = 40 - x;
+ }
+ if (y < 0) {
+ y = 0;
+ } else if (y >= 200) {
+ return;
+ }
+ if (y + h > 200) {
+ h = 200 - y;
+ }
+ const uint8 *src = getPagePtr(_curPage) + y * SCREEN_W + x * 8;
+ while (h--) {
+ memcpy(dst, src, w*8);
+ dst += w*8;
+ src += SCREEN_W;
+ }
+}
+
+void Screen::shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPage, int ticks, bool transparent) {
+ debug(9, "Screen::shuffleScreen(%d, %d, %d, %d, %d, %d, %d, %d)", sx, sy, w, h, srcPage, dstPage, ticks, transparent);
+ assert(sx >= 0 && w <= SCREEN_W);
+ int x;
+ uint16 x_offs[SCREEN_W];
+ for (x = 0; x < SCREEN_W; ++x) {
+ x_offs[x] = x;
+ }
+ for (x = 0; x < w; ++x) {
+ int i = _vm->_rnd.getRandomNumber(w - 1);
+ SWAP(x_offs[x], x_offs[i]);
+ }
+
+ assert(sy >= 0 && h <= SCREEN_H);
+ int y;
+ uint8 y_offs[SCREEN_H];
+ for (y = 0; y < SCREEN_H; ++y) {
+ y_offs[y] = y;
+ }
+ for (y = 0; y < h; ++y) {
+ int i = _vm->_rnd.getRandomNumber(h - 1);
+ SWAP(y_offs[y], y_offs[i]);
+ }
+
+ int32 start, now;
+ int wait;
+ for (y = 0; y < h; ++y) {
+ start = (int32)_system->getMillis();
+ int y_cur = y;
+ for (x = 0; x < w; ++x) {
+ int i = sx + x_offs[x];
+ int j = sy + y_offs[y_cur];
+ ++y_cur;
+ if (y_cur >= h) {
+ y_cur = 0;
+ }
+ uint8 color = getPagePixel(srcPage, i, j);
+ if (!transparent || color != 0) {
+ setPagePixel(dstPage, i, j, color);
+ }
+ }
+ updateScreen();
+ now = (int32)_system->getMillis();
+ wait = ticks * _vm->tickLength() - (now - start);
+ if (wait > 0) {
+ _vm->delay(wait);
+ }
+ }
+}
+
+void Screen::fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum) {
+ debug(9, "Screen::fillRect(%d, %d, %d, %d, %d, %d)", x1, y1, x2, y2, color, pageNum);
+ assert(x2 < SCREEN_W && y2 < SCREEN_H);
+ if (pageNum == -1) {
+ pageNum = _curPage;
+ }
+ uint8 *dst = getPagePtr(pageNum) + y1 * SCREEN_W + x1;
+ for (; y1 <= y2; ++y1) {
+ memset(dst, color, x2 - x1 + 1);
+ dst += SCREEN_W;
+ }
+}
+
+void Screen::drawBox(int x1, int y1, int x2, int y2, int color) {
+ debug(9, "Screen::drawBox(%i, %i, %i, %i, %i)", x1, y1, x2, y2, color);
+
+ drawClippedLine(x1, y1, x2, y1, color);
+ drawClippedLine(x1, y1, x1, y2, color);
+ drawClippedLine(x2, y1, x2, y2, color);
+ drawClippedLine(x1, y2, x2, y2, color);
+}
+
+void Screen::drawShadedBox(int x1, int y1, int x2, int y2, int color1, int color2) {
+ debug(9, "Screen::drawShadedBox(%i, %i, %i, %i, %i, %i)", x1, y1, x2, y2, color1, color2);
+ assert(x1 > 0 && y1 > 0);
+ hideMouse();
+
+ fillRect(x1, y1, x2, y1 + 1, color1);
+ fillRect(x2 - 1, y1, x2, y2, color1);
+
+ drawClippedLine(x1, y1, x1, y2, color2);
+ drawClippedLine(x1 + 1, y1 + 1, x1 + 1, y2 - 1, color2);
+ drawClippedLine(x1, y2, x2, y2, color2);
+ drawClippedLine(x1, y2 - 1, x2 - 1, y2 - 1, color2);
+
+ showMouse();
+}
+
+void Screen::drawClippedLine(int x1, int y1, int x2, int y2, int color) {
+ debug(9, "Screen::drawClippedLine(%i, %i, %i, %i, %i)", x1, y1, x2, y2, color);
+
+ if (x1 < 0)
+ x1 = 0;
+ else if (x1 > 319)
+ x1 = 319;
+
+ if (x2 < 0)
+ x2 = 0;
+ else if (x2 > 319)
+ x2 = 319;
+
+ if (y1 < 0)
+ y1 = 0;
+ else if (y1 > 199)
+ y1 = 199;
+
+ if (y2 < 0)
+ y2 = 0;
+ else if (y2 > 199)
+ y2 = 199;
+
+ if (x1 == x2)
+ if (y1 > y2)
+ drawLine(true, x1, y2, y1 - y2 + 1, color);
+ else
+ drawLine(true, x1, y1, y2 - y1 + 1, color);
+ else
+ if (x1 > x2)
+ drawLine(false, x2, y1, x1 - x2 + 1, color);
+ else
+ drawLine(false, x1, y1, x2 - x1 + 1, color);
+}
+
+void Screen::drawLine(bool horizontal, int x, int y, int length, int color) {
+ debug(9, "Screen::drawLine(%i, %i, %i, %i, %i)", horizontal, x, y, length, color);
+
+ uint8 *ptr = getPagePtr(_curPage) + y * SCREEN_W + x;
+
+ if (horizontal) {
+ assert((y + length) <= SCREEN_H);
+ int currLine = 0;
+ while (currLine < length) {
+ *ptr = color;
+ ptr += SCREEN_W;
+ currLine++;
+ }
+ } else {
+ assert((x + length) <= SCREEN_W);
+ memset(ptr, color, length);
+ }
+}
+
+void Screen::setAnimBlockPtr(int size) {
+ debug(9, "Screen::setAnimBlockPtr(%d)", size);
+ free(_animBlockPtr);
+ _animBlockPtr = (uint8 *)malloc(size);
+ assert(_animBlockPtr);
+ memset(_animBlockPtr, 0, size);
+ _animBlockSize = size;
+}
+
+void Screen::setTextColorMap(const uint8 *cmap) {
+ debug(9, "Screen::setTextColorMap(0x%X)", cmap);
+ setTextColor(cmap, 0, 11);
+}
+
+void Screen::setTextColor(const uint8 *cmap, int a, int b) {
+ debug(9, "Screen::setTextColor(0x%X, %d, %d)", cmap, a, b);
+ for (int i = a; i <= b; ++i) {
+ _textColorsMap[i] = *cmap++;
+ }
+}
+
+void Screen::loadFont(FontId fontId, uint8 *fontData) {
+ debug(9, "Screen::loadFont(%d, 0x%X)", fontId, fontData);
+ Font *fnt = &_fonts[fontId];
+ assert(fontData && !fnt->fontData);
+ fnt->fontData = fontData;
+ uint16 fontSig = READ_LE_UINT16(fontData + 2);
+ if (fontSig != 0x500) {
+ error("Invalid font data");
+ }
+ fnt->charWidthTable = fontData + READ_LE_UINT16(fontData + 8);
+ fnt->charBoxHeight = READ_LE_UINT16(fontData + 4);
+ fnt->charBitmapOffset = READ_LE_UINT16(fontData + 6);
+ fnt->charWidthTableOffset = READ_LE_UINT16(fontData + 8);
+ fnt->charHeightTableOffset = READ_LE_UINT16(fontData + 0xC);
+}
+
+Screen::FontId Screen::setFont(FontId fontId) {
+ debug(9, "Screen::setFont(%d)", fontId);
+ FontId prev = _currentFont;
+ _currentFont = fontId;
+ return prev;
+}
+
+int Screen::getCharWidth(uint8 c) const {
+ debug(9, "Screen::getCharWidth('%c')", c);
+ return (int)_fonts[_currentFont].charWidthTable[c] + _charWidth;
+}
+
+int Screen::getTextWidth(const char *str) const {
+ debug(9, "Screen::getTextWidth('%s')", str);
+ int curLineLen = 0;
+ int maxLineLen = 0;
+ while (1) {
+ char c = *str++;
+ if (c == 0) {
+ break;
+ } else if (c == '\r') {
+ if (curLineLen > maxLineLen) {
+ maxLineLen = curLineLen;
+ } else {
+ curLineLen = 0;
+ }
+ } else {
+ curLineLen += getCharWidth(c);
+ }
+ }
+ return MAX(curLineLen, maxLineLen);
+}
+
+void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2) {
+ debug(9, "Screen::printText('%s', %d, %d, 0x%X, 0x%X)", str, x, y, color1, color2);
+ uint8 cmap[2];
+ cmap[0] = color2;
+ cmap[1] = color1;
+ setTextColor(cmap, 0, 1);
+
+ Font *fnt = &_fonts[_currentFont];
+ uint8 charHeight = *(fnt->fontData + fnt->charBoxHeight + 4);
+
+ if (x < 0) {
+ x = 0;
+ } else if (x >= SCREEN_W) {
+ return;
+ }
+ int x_start = x;
+ if (y < 0) {
+ y = 0;
+ } else if (y >= SCREEN_H) {
+ return;
+ }
+
+ while (1) {
+ char c = *str++;
+ if (c == 0) {
+ break;
+ } else if (c == '\r') {
+ x = x_start;
+ y += charHeight + _charOffset;
+ } else {
+ int charWidth = getCharWidth(c);
+ if (x + charWidth > SCREEN_W) {
+ x = x_start;
+ y += charHeight + _charOffset;
+ if (y >= SCREEN_H) {
+ break;
+ }
+ }
+ drawChar(c, x, y);
+ x += charWidth;
+ }
+ }
+}
+
+void Screen::drawChar(uint8 c, int x, int y) {
+ debug(9, "Screen::drawChar('%c', %d, %d)", c, x, y);
+ Font *fnt = &_fonts[_currentFont];
+ uint8 *dst = getPagePtr(_curPage) + y * SCREEN_W + x;
+ uint16 bitmapOffset = READ_LE_UINT16(fnt->fontData + fnt->charBitmapOffset + c * 2);
+ if (bitmapOffset == 0) {
+ return;
+ }
+ uint8 charWidth = *(fnt->fontData + fnt->charWidthTableOffset + c);
+ if (charWidth + x > SCREEN_W) {
+ return;
+ }
+ uint8 charH0 = *(fnt->fontData + fnt->charBoxHeight + 4);
+ if (charH0 + y > SCREEN_H) {
+ return;
+ }
+ uint8 charH1 = *(fnt->fontData + fnt->charHeightTableOffset + c * 2);
+ uint8 charH2 = *(fnt->fontData + fnt->charHeightTableOffset + c * 2 + 1);
+ charH0 -= charH1 + charH2;
+
+ const uint8 *src = fnt->fontData + bitmapOffset;
+ const int pitch = SCREEN_W - charWidth;
+
+ while (charH1--) {
+ uint8 col = _textColorsMap[0];
+ for (int i = 0; i < charWidth; ++i) {
+ if (col != 0) {
+ *dst = col;
+ }
+ ++dst;
+ }
+ dst += pitch;
+ }
+
+ while (charH2--) {
+ uint8 b = 0;
+ for (int i = 0; i < charWidth; ++i) {
+ uint8 col;
+ if (i & 1) {
+ col = _textColorsMap[b >> 4];
+ } else {
+ b = *src++;
+ col = _textColorsMap[b & 0xF];
+ }
+ if (col != 0) {
+ *dst = col;
+ }
+ ++dst;
+ }
+ dst += pitch;
+ }
+
+ while (charH0--) {
+ uint8 col = _textColorsMap[0];
+ for (int i = 0; i < charWidth; ++i) {
+ if (col != 0) {
+ *dst = col;
+ }
+ ++dst;
+ }
+ dst += pitch;
+ }
+}
+
+void Screen::setScreenDim(int dim) {
+ debug(9, "setScreenDim(%d)", dim);
+ assert(dim < _screenDimTableCount);
+ _curDim = &_screenDimTable[dim];
+ // XXX
+}
+
+void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...) {
+ debug(9, "Screen::drawShape(%d, 0x%X, %d, %d, %d, 0x%.04X, ...)", pageNum, shapeData, x, y, sd, flags);
+ if (!shapeData)
+ return;
+ va_list args;
+ va_start(args, flags);
+
+ static int drawShapeVar1 = 0;
+ static int drawShapeVar2[] = {
+ 1, 3, 2, 5, 4, 3, 2, 1
+ };
+ static int drawShapeVar3 = 1;
+ static int drawShapeVar4 = 0;
+ static int drawShapeVar5 = 0;
+
+ uint8 *table = 0;
+ int tableLoopCount = 0;
+ int drawLayer = 0;
+ uint8 *table2 = 0;
+ uint8 *table3 = 0;
+ uint8 *table4 = 0;
+
+ if (flags & 0x8000) {
+ table2 = va_arg(args, uint8*);
+ }
+ if (flags & 0x100) {
+ table = va_arg(args, uint8*);
+ tableLoopCount = va_arg(args, int);
+ if (!tableLoopCount)
+ flags &= 0xFFFFFEFF;
+ }
+ if (flags & 0x1000) {
+ table3 = va_arg(args, uint8*);
+ table4 = va_arg(args, uint8*);
+ }
+ if (flags & 0x200) {
+ drawShapeVar1 += 1;
+ drawShapeVar1 &= 7;
+ drawShapeVar3 = drawShapeVar2[drawShapeVar1];
+ drawShapeVar4 = 0;
+ drawShapeVar5 = 256;
+ }
+ if (flags & 0x4000) {
+ drawShapeVar5 = va_arg(args, int);
+ }
+ if (flags & 0x800) {
+ drawLayer = va_arg(args, int);
+ }
+ int scale_w, scale_h;
+ if (flags & DSF_SCALE) {
+ scale_w = va_arg(args, int);
+ scale_h = va_arg(args, int);
+ } else {
+ scale_w = 0x100;
+ scale_h = 0x100;
+ }
+
+ int ppc = (flags >> 8) & 0x3F;
+
+ const uint8 *src = shapeData;
+ if (_vm->features() & GF_TALKIE) {
+ src += 2;
+ }
+ uint16 shapeFlags = READ_LE_UINT16(src); src += 2;
+
+ int shapeHeight = *src++;
+ int scaledShapeHeight = (shapeHeight * scale_h) >> 8;
+ if (scaledShapeHeight == 0) {
+ va_end(args);
+ return;
+ }
+
+ int shapeWidth = READ_LE_UINT16(src); src += 2;
+ int scaledShapeWidth = (shapeWidth * scale_w) >> 8;
+ if (scaledShapeWidth == 0) {
+ va_end(args);
+ return;
+ }
+
+ if (flags & DSF_CENTER) {
+ x -= scaledShapeWidth >> 1;
+ y -= scaledShapeHeight >> 1;
+ }
+
+ src += 3;
+
+ uint16 frameSize = READ_LE_UINT16(src); src += 2;
+ if ((shapeFlags & 1) || (flags & 0x400)) {
+ src += 0x10;
+ }
+ if (!(shapeFlags & 2)) {
+ decodeFrame4(src, _animBlockPtr, frameSize);
+ src = _animBlockPtr;
+ }
+
+ int shapeSize = shapeWidth * shapeHeight;
+ if (_decodeShapeBufferSize < shapeSize) {
+ free(_decodeShapeBuffer);
+ _decodeShapeBuffer = (uint8 *)malloc(shapeSize);
+ _decodeShapeBufferSize = shapeSize;
+ }
+ if (!_decodeShapeBuffer) {
+ _decodeShapeBufferSize = 0;
+ va_end(args);
+ return;
+ }
+ memset(_decodeShapeBuffer, 0, _decodeShapeBufferSize);
+ uint8 *decodedShapeFrame = _decodeShapeBuffer;
+
+ // only used if shapeFlag & 1 is NOT zero
+ const uint8 *colorTable = shapeData + 10;
+ if (_vm->features() & GF_TALKIE) {
+ colorTable += 2;
+ }
+
+ for (int j = 0; j < shapeHeight; ++j) {
+ uint8 *dsbNextLine = decodedShapeFrame + shapeWidth;
+ int count = shapeWidth;
+ while (count > 0) {
+ uint8 code = *src++;
+ if (code != 0) {
+ // this is guessed
+ if (shapeFlags & 1) {
+ if (code < 16) {
+ *decodedShapeFrame++ = colorTable[code];
+ }
+ } else {
+ *decodedShapeFrame++ = code;
+ }
+ --count;
+ } else {
+ code = *src++;
+ decodedShapeFrame += code;
+ count -= code;
+ }
+ }
+ decodedShapeFrame = dsbNextLine;
+ }
+
+ uint16 sx1 = _screenDimTable[sd].sx * 8;
+ uint16 sy1 = _screenDimTable[sd].sy;
+ uint16 sx2 = sx1 + _screenDimTable[sd].w * 8;
+ uint16 sy2 = sy1 + _screenDimTable[sd].h;
+ if (flags & DSF_WND_COORDS) {
+ x += sx1;
+ y += sy1;
+ }
+
+ int x1, x2;
+ if (x >= 0) {
+ x1 = 0;
+ if (x + scaledShapeWidth < sx2) {
+ x2 = scaledShapeWidth;
+ } else {
+ x2 = sx2 - x;
+ }
+ } else {
+ x2 = scaledShapeWidth;
+ x1 = -x;
+ x = 0;
+ if (x2 > sx2) {
+ x2 = sx2;
+ }
+ }
+
+ int y1, y2;
+ if (y >= 0) {
+ y1 = 0;
+ if (y + scaledShapeHeight < sy2) {
+ y2 = scaledShapeHeight;
+ } else {
+ y2 = sy2 - y;
+ }
+ } else {
+ y2 = scaledShapeHeight;
+ y1 = -y;
+ y = 0;
+ if (y2 > sy2) {
+ y2 = sy2;
+ }
+ }
+
+ uint8 *dst = getPagePtr(pageNum) + y * SCREEN_W + x;
+ uint8 *dstStart = getPagePtr(pageNum);
+
+ int scaleYTable[SCREEN_H];
+ assert(y1 >= 0 && y2 < SCREEN_H);
+ for (y = y1; y < y2; ++y) {
+ scaleYTable[y] = (y << 8) / scale_h;
+ }
+ int scaleXTable[SCREEN_W];
+ assert(x1 >= 0 && x2 < SCREEN_W);
+ for (x = x1; x < x2; ++x) {
+ scaleXTable[x] = (x << 8) / scale_w;
+ }
+
+ const uint8 *shapeBuffer = _decodeShapeBuffer;
+ if (flags & DSF_Y_FLIPPED) {
+ shapeBuffer += shapeWidth * (shapeHeight - 1);
+ }
+ if (flags & DSF_X_FLIPPED) {
+ shapeBuffer += shapeWidth - 1;
+ }
+
+ for (y = y1; y < y2; ++y) {
+ uint8 *dstNextLine = dst + SCREEN_W;
+ int j = scaleYTable[y];
+ if (flags & DSF_Y_FLIPPED) {
+ j = -j;
+ }
+ for (x = x1; x < x2; ++x) {
+ int xpos = scaleXTable[x];
+ if (flags & DSF_X_FLIPPED) {
+ xpos = -xpos;
+ }
+ uint8 color = shapeBuffer[j * shapeWidth + xpos];
+ if (color != 0) {
+ switch (ppc) {
+ case 0:
+ *dst = color;
+ break;
+
+ case 1:
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ break;
+
+ case 2: {
+ int temp = drawShapeVar4 + drawShapeVar5;
+ if (temp & 0xFF00) {
+ drawShapeVar4 = temp & 0xFF;
+ dst += drawShapeVar3;
+ color = *dst;
+ dst -= drawShapeVar3;
+ } else {
+ drawShapeVar4 = temp;
+ }
+ } break;
+
+ case 7:
+ case 3:
+ color = *dst;
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ break;
+
+ case 4:
+ color = table2[color];
+ break;
+
+ case 5:
+ color = table2[color];
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ break;
+
+ case 6: {
+ int temp = drawShapeVar4 + drawShapeVar5;
+ if (temp & 0xFF00) {
+ drawShapeVar4 = temp & 0xFF;
+ dst += drawShapeVar3;
+ color = *dst;
+ dst -= drawShapeVar3;
+ } else {
+ drawShapeVar4 = temp;
+ color = table2[color];
+ }
+ } break;
+
+ case 8: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ }
+ } break;
+
+ case 9: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ } else {
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ }
+ } break;
+
+ case 10: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ drawShapeVar4 = pixel;
+ } else {
+ int temp = drawShapeVar4 + drawShapeVar5;
+ if (temp & 0xFF00) {
+ dst += drawShapeVar3;
+ color = *dst;
+ dst -= drawShapeVar3;
+ }
+ drawShapeVar4 = temp & 0xFF;
+ }
+ } break;
+
+ case 15:
+ case 11: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ } else {
+ color = *dst;
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ }
+ } break;
+
+ case 12: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ } else {
+ color = table2[color];
+ }
+ } break;
+
+ case 13: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ } else {
+ color = table2[color];
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ }
+ } break;
+
+ case 14: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ drawShapeVar4 = pixel;
+ } else {
+ int temp = drawShapeVar4 + drawShapeVar5;
+ if (temp & 0xFF00) {
+ dst += drawShapeVar3;
+ color = *dst;
+ dst -= drawShapeVar3;
+ drawShapeVar4 = temp % 0xFF;
+ } else {
+ drawShapeVar4 = temp;
+ color = table2[color];
+ }
+ }
+ } break;
+
+ case 16: {
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } break;
+
+ case 17: {
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } break;
+
+ case 18: {
+ int temp = drawShapeVar4 + drawShapeVar5;
+ if (temp & 0xFF00) {
+ drawShapeVar4 = temp & 0xFF;
+ dst += drawShapeVar3;
+ color = *dst;
+ dst -= drawShapeVar3;
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } else {
+ drawShapeVar4 = temp;
+ }
+ } break;
+
+ case 23:
+ case 19: {
+ color = *dst;
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } break;
+
+ case 20: {
+ color = table2[color];
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } break;
+
+ case 21: {
+ color = table2[color];
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } break;
+
+ case 22: {
+ int temp = drawShapeVar4 + drawShapeVar5;
+ if (temp & 0xFF00) {
+ drawShapeVar4 = temp & 0xFF;
+ dst += drawShapeVar3;
+ color = *dst;
+ dst -= drawShapeVar3;
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } else {
+ drawShapeVar4 = temp;
+ color = table2[color];
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ }
+ } break;
+
+ case 24: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ }
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } break;
+
+ default:
+ warning("unhandled ppc: %d", ppc);
+ break;
+ }
+ *dst = color;
+ }
+ ++dst;
+ }
+ dst = dstNextLine;
+ }
+ va_end(args);
+}
+
+void Screen::decodeFrame3(const uint8 *src, uint8 *dst, uint32 size) {
+ debug(9, "Screen::decodeFrame3(0x%X, 0x%X, %d)", src, dst, size);
+ const uint8 *dstEnd = dst + size;
+ while (dst < dstEnd) {
+ int8 code = *src++;
+ if (code == 0) {
+ uint16 sz = READ_BE_UINT16(src);
+ src += 2;
+ memset(dst, *src++, sz);
+ dst += sz;
+ } else if (code < 0) {
+ memset(dst, *src++, -code);
+ dst -= code;
+ } else {
+ memcpy(dst, src, code);
+ dst += code;
+ src += code;
+ }
+ }
+}
+
+void Screen::decodeFrame4(const uint8 *src, uint8 *dst, uint32 dstSize) {
+ debug(9, "Screen::decodeFrame4(0x%X, 0x%X, %d)", src, dst, dstSize);
+ uint8 *dstOrig = dst;
+ uint8 *dstEnd = dst + dstSize;
+ while (1) {
+ int count = dstEnd - dst;
+ if (count == 0) {
+ break;
+ }
+ uint8 code = *src++;
+ if (!(code & 0x80)) {
+ int len = MIN(count, (code >> 4) + 3);
+ int offs = ((code & 0xF) << 8) | *src++;
+ const uint8 *dstOffs = dst - offs;
+ while (len--) {
+ *dst++ = *dstOffs++;
+ }
+ } else if (code & 0x40) {
+ int len = (code & 0x3F) + 3;
+ if (code == 0xFE) {
+ len = READ_LE_UINT16(src); src += 2;
+ if (len > count) {
+ len = count;
+ }
+ memset(dst, *src++, len); dst += len;
+ } else {
+ if (code == 0xFF) {
+ len = READ_LE_UINT16(src); src += 2;
+ }
+ int offs = READ_LE_UINT16(src); src += 2;
+ if (len > count) {
+ len = count;
+ }
+ const uint8 *dstOffs = dstOrig + offs;
+ while (len--) {
+ *dst++ = *dstOffs++;
+ }
+ }
+ } else if (code != 0x80) {
+ int len = MIN(count, code & 0x3F);
+ while (len--) {
+ *dst++ = *src++;
+ }
+ } else {
+ break;
+ }
+ }
+}
+
+void Screen::decodeFrameDelta(uint8 *dst, const uint8 *src) {
+ debug(9, "Screen::decodeFrameDelta(0x%X, 0x%X)", dst, src);
+ while (1) {
+ uint8 code = *src++;
+ if (code == 0) {
+ uint8 len = *src++;
+ code = *src++;
+ while (len--) {
+ *dst++ ^= code;
+ }
+ } else if (code & 0x80) {
+ code -= 0x80;
+ if (code != 0) {
+ dst += code;
+ } else {
+ uint16 subcode = READ_LE_UINT16(src); src += 2;
+ if (subcode == 0) {
+ break;
+ } else if (subcode & 0x8000) {
+ subcode -= 0x8000;
+ if (subcode & 0x4000) {
+ uint16 len = subcode - 0x4000;
+ code = *src++;
+ while (len--) {
+ *dst++ ^= code;
+ }
+ } else {
+ while (subcode--) {
+ *dst++ ^= *src++;
+ }
+ }
+ } else {
+ dst += subcode;
+ }
+ }
+ } else {
+ while (code--) {
+ *dst++ ^= *src++;
+ }
+ }
+ }
+}
+
+void Screen::decodeFrameDeltaPage(uint8 *dst, const uint8 *src, int pitch, int noXor) {
+ debug(9, "Screen::decodeFrameDeltaPage(0x%X, 0x%X, %d, %d)", dst, src, pitch, noXor);
+ int count = 0;
+ uint8 *dstNext = dst;
+ while (1) {
+ uint8 code = *src++;
+ if (code == 0) {
+ uint8 len = *src++;
+ code = *src++;
+ while (len--) {
+ if (noXor) {
+ *dst++ = code;
+ } else {
+ *dst++ ^= code;
+ }
+ if (++count == pitch) {
+ count = 0;
+ dstNext += SCREEN_W;
+ dst = dstNext;
+ }
+ }
+ } else if (code & 0x80) {
+ code -= 0x80;
+ if (code != 0) {
+ dst += code;
+
+ count += code;
+ while (count >= pitch) {
+ count -= pitch;
+ dstNext += SCREEN_W;
+ dst = dstNext + count;
+ }
+ } else {
+ uint16 subcode = READ_LE_UINT16(src); src += 2;
+ if (subcode == 0) {
+ break;
+ } else if (subcode & 0x8000) {
+ subcode -= 0x8000;
+ if (subcode & 0x4000) {
+ uint16 len = subcode - 0x4000;
+ code = *src++;
+ while (len--) {
+ if (noXor) {
+ *dst++ = code;
+ } else {
+ *dst++ ^= code;
+ }
+ if (++count == pitch) {
+ count = 0;
+ dstNext += SCREEN_W;
+ dst = dstNext;
+ }
+ }
+ } else {
+ while (subcode--) {
+ if (noXor) {
+ *dst++ = *src++;
+ } else {
+ *dst++ ^= *src++;
+ }
+ if (++count == pitch) {
+ count = 0;
+ dstNext += SCREEN_W;
+ dst = dstNext;
+ }
+ }
+ }
+ } else {
+ dst += subcode;
+
+ count += subcode;
+ while (count >= pitch) {
+ count -= pitch;
+ dstNext += SCREEN_W;
+ dst = dstNext + count;
+ }
+
+ }
+ }
+ } else {
+ while (code--) {
+ if (noXor) {
+ *dst++ = *src++;
+ } else {
+ *dst++ ^= *src++;
+ }
+ if (++count == pitch) {
+ count = 0;
+ dstNext += SCREEN_W;
+ dst = dstNext;
+ }
+ }
+ }
+ }
+}
+
+uint8 *Screen::encodeShape(int x, int y, int w, int h, int flags) {
+ debug(9, "Screen::encodeShape(%d, %d, %d, %d, %d)", x, y, w, h, flags);
+ uint8 *srcPtr = &_pagePtrs[_curPage][y * SCREEN_W + x];
+ int16 shapeSize = 0;
+ uint8 *tmp = srcPtr;
+ int xpos = w;
+
+ for (int i = h; i > 0; --i) {
+ uint8 *start = tmp;
+ shapeSize += w;
+ xpos = w;
+ while (xpos) {
+ uint8 value = *tmp++;
+ --xpos;
+
+ if (!value) {
+ shapeSize += 2;
+ int16 curX = xpos;
+ bool skip = false;
+
+ while (xpos) {
+ value = *tmp++;
+ --xpos;
+
+ if (value) {
+ skip = true;
+ break;
+ }
+ }
+
+ if (!skip)
+ ++curX;
+
+ curX -= xpos;
+ shapeSize -= curX;
+
+ while (curX > 0xFF) {
+ curX -= 0xFF;
+ shapeSize += 2;
+ }
+ }
+ }
+
+ tmp = start + SCREEN_W;
+ }
+
+ int16 shapeSize2 = shapeSize;
+ if (_vm->features() & GF_TALKIE) {
+ shapeSize += 12;
+ } else {
+ shapeSize += 10;
+ }
+ if (flags & 1)
+ shapeSize += 16;
+
+ static uint8 table[274];
+ int tableIndex = 0;
+
+ uint8 *newShape = NULL;
+ newShape = (uint8*)malloc(shapeSize+16);
+ assert(newShape);
+
+ byte *dst = newShape;
+ if (_vm->features() & GF_TALKIE)
+ dst += 2;
+ WRITE_LE_UINT16(dst, (flags & 3)); dst += 2;
+ *dst = h; dst += 1;
+ WRITE_LE_UINT16(dst, w); dst += 2;
+ *dst = h; dst += 1;
+ WRITE_LE_UINT16(dst, shapeSize); dst += 2;
+ WRITE_LE_UINT16(dst, shapeSize2); dst += 2;
+
+ byte *src = srcPtr;
+ if (flags & 1) {
+ dst += 16;
+ memset(table, 0, sizeof(uint8)*274);
+ tableIndex = 1;
+ }
+
+ for (int ypos = h; ypos > 0; --ypos) {
+ uint8 *srcBackUp = src;
+ xpos = w;
+ while (xpos) {
+ uint8 value = *src++;
+ if (value) {
+ if (flags & 1) {
+ if (!table[value]) {
+ if (tableIndex == 16) {
+ value = 1;
+ } else {
+ table[0x100+tableIndex] = value;
+ table[value] = tableIndex;
+ ++tableIndex;
+ value = table[value];
+ }
+ } else {
+ value = table[value];
+ }
+ }
+ --xpos;
+ *dst++ = value;
+ } else {
+ int16 temp = 1;
+ --xpos;
+
+ while (xpos) {
+ if (*src)
+ break;
+ ++src;
+ ++temp;
+ --xpos;
+ }
+
+ while (temp > 0xFF) {
+ *dst++ = 0;
+ *dst++ = 0xFF;
+ temp -= 0xFF;
+ }
+
+ if (temp & 0xFF) {
+ *dst++ = 0;
+ *dst++ = temp & 0xFF;
+ }
+ }
+ }
+ src = srcBackUp + SCREEN_W;
+ }
+
+ if (!(flags & 2)) {
+ if (shapeSize > _animBlockSize) {
+ dst = newShape;
+ if (_vm->features() & GF_TALKIE) {
+ dst += 2;
+ }
+ flags = READ_LE_UINT16(dst);
+ flags |= 2;
+ WRITE_LE_UINT16(dst, flags);
+ } else {
+ src = newShape;
+ if (_vm->features() & GF_TALKIE) {
+ src += 2;
+ }
+ if (flags & 1) {
+ src += 16;
+ }
+ src += 10;
+ uint8 *shapePtrBackUp = src;
+ dst = _animBlockPtr;
+ memcpy(dst, src, shapeSize2);
+
+ int16 size = encodeShapeAndCalculateSize(_animBlockPtr, shapePtrBackUp, shapeSize2);
+ if (size > shapeSize2) {
+ shapeSize -= shapeSize2 - size;
+ newShape = (uint8*)realloc(newShape, shapeSize);
+ assert(newShape);
+ } else {
+ dst = shapePtrBackUp;
+ src = _animBlockPtr;
+ memcpy(dst, src, shapeSize2);
+ dst = newShape;
+ flags = READ_LE_UINT16(dst);
+ flags |= 2;
+ WRITE_LE_UINT16(dst, flags);
+ }
+ }
+ }
+
+ dst = newShape;
+ if (_vm->features() & GF_TALKIE) {
+ dst += 2;
+ }
+ WRITE_LE_UINT16((dst + 6), shapeSize);
+
+ if (flags & 1) {
+ dst = newShape + 10;
+ if (_vm->features() & GF_TALKIE) {
+ dst += 2;
+ }
+ src = &table[0x100];
+ memcpy(dst, src, sizeof(uint8)*16);
+ }
+
+ return newShape;
+}
+
+int16 Screen::encodeShapeAndCalculateSize(uint8 *from, uint8 *to, int size_to) {
+ debug(9, "Screen::encodeShapeAndCalculateSize(0x%X, 0x%X, %d)", from, to, size_to);
+ byte *fromPtrEnd = from + size_to;
+ bool skipPixel = true;
+ byte *tempPtr = 0;
+ byte *toPtr = to;
+ byte *fromPtr = from;
+ byte *toPtr2 = to;
+
+ *to++ = 0x81;
+ *to++ = *from++;
+
+ while (from < fromPtrEnd) {
+ byte *curToPtr = to;
+ to = fromPtr;
+ int size = 1;
+
+ while (true) {
+ byte curPixel = *from;
+ if (curPixel == *(from+0x40)) {
+ byte *toBackUp = to;
+ to = from;
+
+ for (int i = 0; i < (fromPtrEnd - from); ++i) {
+ if (*to++ != curPixel)
+ break;
+ }
+ --to;
+ uint16 diffSize = (to - from);
+ if (diffSize >= 0x41) {
+ skipPixel = false;
+ from = to;
+ to = curToPtr;
+ *to++ = 0xFE;
+ WRITE_LE_UINT16(to, diffSize); to += 2;
+ *to++ = (size >> 8) & 0xFF;
+ curToPtr = to;
+ to = toBackUp;
+ continue;
+ } else {
+ to = toBackUp;
+ }
+ }
+
+ bool breakLoop = false;
+ while (true) {
+ if ((from - to) == 0) {
+ breakLoop = true;
+ break;
+ }
+ for (int i = 0; i < (from - to); ++i) {
+ if (*to++ == curPixel)
+ break;
+ }
+ if (*to == curPixel) {
+ if (*(from+size-1) == *(to+size-2))
+ break;
+
+ byte *fromBackUp = from;
+ byte *toBackUp = to;
+ --to;
+ for (int i = 0; i < (fromPtrEnd - from); ++i) {
+ if (*from++ != *to++)
+ break;
+ }
+ if (*(from - 1) == *(to - 1))
+ ++to;
+ from = fromBackUp;
+ int temp = to - toBackUp;
+ to = toBackUp;
+ if (temp >= size) {
+ size = temp;
+ tempPtr = toBackUp - 1;
+ }
+ break;
+ } else {
+ breakLoop = true;
+ break;
+ }
+ }
+
+ if (breakLoop)
+ break;
+ }
+
+ to = curToPtr;
+ if (size > 2) {
+ uint16 word = 0;
+ if (size <= 0x0A) {
+ uint16 diffSize = from - tempPtr;
+ if (size <= 0x0FFF) {
+ byte highByte = ((diffSize & 0xFF00) >> 8) + (((size & 0xFF) - 3) << 4);
+ word = ((diffSize & 0xFF) << 8) | highByte;
+ WRITE_LE_UINT16(to, word); to += 2;
+ from += size;
+ skipPixel = false;
+ continue;
+ }
+ }
+
+ if (size > 0x40) {
+ *to++ = 0xFF;
+ WRITE_LE_UINT16(to, size); to += 2;
+ } else {
+ *to++ = ((size & 0xFF) - 3) | 0xC0;
+ }
+
+ word = tempPtr - fromPtr;
+ WRITE_LE_UINT16(to, word); to += 2;
+ from += size;
+ skipPixel = false;
+ } else {
+ if (!skipPixel) {
+ toPtr2 = to;
+ *to++ = 0x80;
+ }
+
+ if (*toPtr2 == 0xBF) {
+ toPtr2 = to;
+ *to++ = 0x80;
+ }
+
+ ++(*toPtr2);
+ *to++ = *from++;
+ skipPixel = true;
+ }
+ }
+ *to++ = 0x80;
+
+ return (to - toPtr);
+}
+
+int Screen::getRectSize(int x, int y) {
+ if (x < 1) {
+ x = 1;
+ } else if (x > 40) {
+ x = 40;
+ }
+
+ if (y < 1) {
+ y = 1;
+ } else if (y > 200) {
+ y = 200;
+ }
+
+ return ((x*y) << 3);
+}
+
+void Screen::hideMouse() {
+ debug(9, "Screen::hideMouse()");
+ ++_mouseLockCount;
+ _system->showMouse(false);
+}
+
+void Screen::showMouse() {
+ debug(9, "Screen::showMouse()");
+
+ if (_mouseLockCount == 1)
+ _system->showMouse(true);
+
+ if (_mouseLockCount > 0)
+ _mouseLockCount--;
+
+}
+
+void Screen::setShapePages(int page1, int page2) {
+ debug(9, "Screen::setShapePages(%d, %d)", page1, page2);
+ _shapePages[0] = _pagePtrs[page1];
+ _shapePages[1] = _pagePtrs[page2];
+}
+
+void Screen::setMouseCursor(int x, int y, byte *shape) {
+ debug(9, "Screen::setMouseCursor(%d, %d, 0x%X)", x, y, shape);
+ if (!shape)
+ return;
+ // if mouseDisabled
+ // return _mouseShape
+
+ if (_vm->features() & GF_TALKIE)
+ shape += 2;
+
+ int mouseHeight = *(shape+2);
+ int mouseWidth = (READ_LE_UINT16(shape + 3)) + 2;
+
+ if (_vm->features() & GF_TALKIE)
+ shape -= 2;
+
+ uint8 *cursor = (uint8 *)malloc(mouseHeight * mouseWidth);
+ fillRect(0, 0, mouseWidth, mouseHeight, 0, 8);
+ drawShape(8, shape, 0, 0, 0, 0);
+
+ _system->showMouse(false);
+ copyRegionToBuffer(8, 0, 0, mouseWidth, mouseHeight, cursor);
+ _system->setMouseCursor(cursor, mouseWidth, mouseHeight, x, y, 0);
+ _system->showMouse(true);
+ free(cursor);
+
+ return;
+
+}
+
+void Screen::copyScreenFromRect(int x, int y, int w, int h, uint8 *ptr) {
+ debug(9, "Screen::copyScreenFromRect(%d, %d, %d, %d, 0x%X)", x, y, w, h, ptr);
+ x <<= 3; w <<= 3;
+ uint8 *src = ptr;
+ uint8 *dst = &_pagePtrs[0][y * SCREEN_W + x];
+ for (int i = 0; i < h; ++i) {
+ memcpy(dst, src, w);
+ src += w;
+ dst += SCREEN_W;
+ }
+}
+
+void Screen::copyScreenToRect(int x, int y, int w, int h, uint8 *ptr) {
+ debug(9, "Screen::copyScreenToRect(%d, %d, %d, %d, 0x%X)", x, y, w, h, ptr);
+ x <<= 3; w <<= 3;
+ uint8 *src = &_pagePtrs[0][y * SCREEN_W + x];
+ uint8 *dst = ptr;
+ for (int i = 0; i < h; ++i) {
+ memcpy(dst, src, w);
+ dst += w;
+ src += SCREEN_W;
+ }
+}
+
+uint8 *Screen::getPalette(int num) {
+ debug(9, "Screen::getPalette(%d)", num);
+ assert(num >= 0 && num < 4);
+ if (num == 0) {
+ return _screenPalette;
+ }
+
+ return _palettes[num-1];
+}
+
+byte Screen::getShapeFlag1(int x, int y) {
+ debug(9, "Screen::getShapeFlag1(%d, %d)", x, y);
+ uint8 color = _shapePages[0][y * SCREEN_W + x];
+ color &= 0x80;
+ color ^= 0x80;
+
+ if (color & 0x80) {
+ return 1;
+ }
+ return 0;
+}
+
+byte Screen::getShapeFlag2(int x, int y) {
+ debug(9, "Screen::getShapeFlag2(%d, %d)", x, y);
+ uint8 color = _shapePages[0][y * SCREEN_W + x];
+ color &= 0x7F;
+ color &= 0x87;
+ return color;
+}
+
+int Screen::setNewShapeHeight(uint8 *shape, int height) {
+ debug(9, "Screen::setNewShapeHeight(0x%X, %d)", shape, height);
+ if (_vm->features() & GF_TALKIE)
+ shape += 2;
+ int oldHeight = shape[2];
+ shape[2] = height;
+ return oldHeight;
+}
+
+int Screen::resetShapeHeight(uint8 *shape) {
+ debug(9, "Screen::setNewShapeHeight(0x%X)", shape);
+ if (_vm->features() & GF_TALKIE)
+ shape += 2;
+ int oldHeight = shape[2];
+ shape[2] = shape[5];
+ return oldHeight;
+}
+
+void Screen::addBitBlitRect(int x, int y, int w, int h) {
+ debug(9, "Screen::addBitBlitRects(%d, %d, %d, %d)", x, y, w, h);
+ if (_bitBlitNum >= BITBLIT_RECTS) {
+ error("too many bit blit rects");
+ }
+ _bitBlitRects[_bitBlitNum].x = x;
+ _bitBlitRects[_bitBlitNum].y = y;
+ _bitBlitRects[_bitBlitNum].x2 = w;
+ _bitBlitRects[_bitBlitNum].y2 = h;
+ ++_bitBlitNum;
+}
+
+void Screen::bitBlitRects() {
+ debug(9, "Screen::bitBlitRects()");
+ Rect *cur = _bitBlitRects;
+ while (_bitBlitNum) {
+ _bitBlitNum--;
+ copyRegion(cur->x, cur->y, cur->x, cur->y, cur->x2, cur->y2, 2, 0);
+ ++cur;
+ }
+}
+
+void Screen::savePageToDisk(const char *file, int page) {
+ debug(9, "Screen::savePageToDisk('%s', %d)", file, page);
+ if (!_saveLoadPage[page/2]) {
+ _saveLoadPage[page/2] = new uint8[SCREEN_W * SCREEN_H];
+ assert(_saveLoadPage[page/2]);
+ }
+ memcpy(_saveLoadPage[page/2], getPagePtr(page), SCREEN_W * SCREEN_H);
+}
+
+void Screen::loadPageFromDisk(const char *file, int page) {
+ debug(9, "Screen::loadPageFromDisk('%s', %d)", file, page);
+ copyBlockToPage(page, 0, 0, SCREEN_W, SCREEN_H, _saveLoadPage[page/2]);
+ delete [] _saveLoadPage[page/2];
+ _saveLoadPage[page/2] = 0;
+}
+
+void Screen::deletePageFromDisk(int page) {
+ debug(9, "Screen::deletePageFromDisk(%d)", page);
+ delete [] _saveLoadPage[page/2];
+ _saveLoadPage[page/2] = 0;
+}
+
+void Screen::blockInRegion(int x, int y, int width, int height) {
+ debug(9, "Screen::blockInRegion(%d, %d, %d, %d)", x, y, width, height);
+ assert(_shapePages[0]);
+ byte *toPtr = _shapePages[0] + (y * 320 + x);
+ for (int i = 0; i < height; ++i) {
+ byte *backUpTo = toPtr;
+ for (int i2 = 0; i2 < width; ++i2) {
+ *toPtr++ &= 0x7F;
+ }
+ toPtr = (backUpTo + 320);
+ }
+}
+
+void Screen::blockOutRegion(int x, int y, int width, int height) {
+ debug(9, "Screen::blockOutRegion(%d, %d, %d, %d)", x, y, width, height);
+ assert(_shapePages[0]);
+ byte *toPtr = _shapePages[0] + (y * 320 + x);
+ for (int i = 0; i < height; ++i) {
+ byte *backUpTo = toPtr;
+ for (int i2 = 0; i2 < width; ++i2) {
+ *toPtr++ |= 0x80;
+ }
+ toPtr = (backUpTo + 320);
+ }
+}
+
+void Screen::rectClip(int &x, int &y, int w, int h) {
+ if (x < 0) {
+ x = 0;
+ } else if (x + w >= 320) {
+ x = 320 - w;
+ }
+ if (y < 0) {
+ y = 0;
+ } else if (y + h >= 200) {
+ y = 200 - h;
+ }
+}
+
+void Screen::backUpRect0(int xpos, int ypos) {
+ debug(9, "Screen::backUpRect0(%d, %d)", xpos, ypos);
+ rectClip(xpos, ypos, 3<<3, 24);
+ copyRegionToBuffer(_curPage, xpos, ypos, 3<<3, 24, _vm->shapes()[0]);
+}
+
+void Screen::restoreRect0(int xpos, int ypos) {
+ debug(9, "Screen::restoreRect0(%d, %d)", xpos, ypos);
+ rectClip(xpos, ypos, 3<<3, 24);
+ copyBlockToPage(_curPage, xpos, ypos, 3<<3, 24, _vm->shapes()[0]);
+}
+
+void Screen::backUpRect1(int xpos, int ypos) {
+ debug(9, "Screen::backUpRect1(%d, %d)", xpos, ypos);
+ rectClip(xpos, ypos, 4<<3, 32);
+ copyRegionToBuffer(_curPage, xpos, ypos, 4<<3, 32, _vm->shapes()[1]);
+}
+
+void Screen::restoreRect1(int xpos, int ypos) {
+ debug(9, "Screen::restoreRect1(%d, %d)", xpos, ypos);
+ rectClip(xpos, ypos, 4<<3, 32);
+ copyBlockToPage(_curPage, xpos, ypos, 4<<3, 32, _vm->shapes()[1]);
+}
+
+int Screen::getDrawLayer(int x, int y) {
+ debug(9, "Screen::getDrawLayer(%d, %d)", x, y);
+ int xpos = x - 8;
+ int ypos = y - 1;
+ int layer = 1;
+ for (int curX = xpos; curX < xpos + 16; ++curX) {
+ int tempLayer = getShapeFlag2(curX, ypos);
+ if (layer < tempLayer) {
+ layer = tempLayer;
+ }
+ if (layer >= 7) {
+ return 7;
+ }
+ }
+ return layer;
+}
+
+int Screen::getDrawLayer2(int x, int y, int height) {
+ debug(9, "Screen::getDrawLayer2(%d, %d, %d)", x, y, height);
+ int xpos = x - 8;
+ int ypos = y - 1;
+ int layer = 1;
+
+ for (int useX = xpos; useX < xpos + 16; ++useX) {
+ for (int useY = ypos - height; useY < ypos; ++useY) {
+ int tempLayer = getShapeFlag2(useX, useY);
+ if (tempLayer > layer) {
+ layer = tempLayer;
+ }
+
+ if (tempLayer >= 7) {
+ return 7;
+ }
+ }
+ }
+ return layer;
+}
+
+void Screen::copyBackgroundBlock(int x, int page, int flag) {
+ debug(9, "Screen::copyBackgroundBlock(%d, %d, %d)", x, page, flag);
+
+ if (x < 1)
+ return;
+
+ int height = 128;
+ if (flag)
+ height += 8;
+ if (!(x & 1))
+ ++x;
+ if (x == 19)
+ x = 17;
+ uint8 *ptr1 = _unkPtr1;
+ uint8 *ptr2 = _unkPtr2;
+ int oldVideoPage = _curPage;
+ _curPage = page;
+
+ int curX = x;
+ hideMouse();
+ copyRegionToBuffer(_curPage, 8, 8, 8, height, ptr2);
+ for (int i = 0; i < 19; ++i) {
+ int tempX = curX + 1;
+ copyRegionToBuffer(_curPage, tempX<<3, 8, 8, height, ptr1);
+ copyBlockToPage(_curPage, tempX<<3, 8, 8, height, ptr2);
+ int newXPos = curX + x;
+ if (newXPos > 37) {
+ newXPos = newXPos % 38;
+ }
+ tempX = newXPos + 1;
+ copyRegionToBuffer(_curPage, tempX<<3, 8, 8, height, ptr2);
+ copyBlockToPage(_curPage, tempX<<3, 8, 8, height, ptr1);
+ curX += x*2;
+ if (curX > 37) {
+ curX = curX % 38;
+ }
+ }
+ showMouse();
+ _curPage = oldVideoPage;
+}
+
+void Screen::copyBackgroundBlock2(int x) {
+ copyBackgroundBlock(x, 4, 1);
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/screen.h b/engines/kyra/screen.h
new file mode 100644
index 0000000000..0f2c59cbea
--- /dev/null
+++ b/engines/kyra/screen.h
@@ -0,0 +1,206 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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$
+ *
+ */
+
+#ifndef KYRASCREEN_H
+#define KYRASCREEN_H
+
+#include "common/util.h"
+#include <stdarg.h>
+
+class OSystem;
+
+namespace Kyra {
+
+class KyraEngine;
+class Debugger;
+struct Rect;
+
+struct ScreenDim {
+ uint16 sx;
+ uint16 sy;
+ uint16 w;
+ uint16 h;
+ uint16 unk8;
+ uint16 unkA;
+ uint16 unkC;
+ uint16 unkE;
+};
+
+struct Font {
+ uint8 *fontData;
+ uint8 *charWidthTable;
+ uint16 charBoxHeight;
+ uint16 charBitmapOffset;
+ uint16 charWidthTableOffset;
+ uint16 charHeightTableOffset;
+};
+
+class Screen {
+ friend class Debugger;
+public:
+
+ enum {
+ SCREEN_W = 320,
+ SCREEN_H = 200,
+ SCREEN_PAGE_SIZE = 320 * 200 + 1024,
+ SCREEN_PAGE_NUM = 16
+ };
+
+ enum CopyRegionFlags {
+ CR_X_FLIPPED = 0x01,
+ CR_CLIPPED = 0x02
+ };
+
+ enum DrawShapeFlags {
+ DSF_X_FLIPPED = 0x01,
+ DSF_Y_FLIPPED = 0x02,
+ DSF_SCALE = 0x04,
+ DSF_WND_COORDS = 0x10,
+ DSF_CENTER = 0x20
+ };
+
+ enum FontId {
+ FID_6_FNT = 0,
+ FID_8_FNT,
+ FID_CRED6_FNT,
+ FID_CRED8_FNT,
+ FID_NUM
+ };
+
+ Screen(KyraEngine *vm, OSystem *system);
+ ~Screen();
+
+ void updateScreen();
+ uint8 *getPagePtr(int pageNum);
+ void clearPage(int pageNum);
+ int setCurPage(int pageNum);
+ void clearCurPage();
+ uint8 getPagePixel(int pageNum, int x, int y);
+ void setPagePixel(int pageNum, int x, int y, uint8 color);
+ void fadeFromBlack();
+ void fadeToBlack();
+ void fadeSpecialPalette(int palIndex, int startIndex, int size, int fadeTime);
+ void fadePalette(const uint8 *palData, int delay);
+ void setScreenPalette(const uint8 *palData);
+ void copyToPage0(int y, int h, uint8 page, uint8 *seqBuf);
+ void copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage, int flags=0);
+ void copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint8 *src);
+ void copyFromCurPageBlock(int x, int y, int w, int h, const uint8 *src);
+ void copyCurPageBlock(int x, int y, int w, int h, uint8 *dst);
+ void shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPage, int ticks, bool transparent);
+ void fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum = -1);
+ void drawLine(bool horizontal, int x, int y, int length, int color);
+ void drawClippedLine(int x1, int y1, int x2, int y2, int color);
+ void drawShadedBox(int x1, int y1, int x2, int y2, int color1, int color2);
+ void drawBox(int x1, int y1, int x2, int y2, int color);
+ void setAnimBlockPtr(int size);
+ void setTextColorMap(const uint8 *cmap);
+ void setTextColor(const uint8 *cmap, int a, int b);
+ void loadFont(FontId fontId, uint8 *fontData);
+ FontId setFont(FontId fontId);
+ int getCharWidth(uint8 c) const;
+ int getTextWidth(const char *str) const;
+ void printText(const char *str, int x, int y, uint8 color1, uint8 color2);
+ void drawChar(uint8 c, int x, int y);
+ void setScreenDim(int dim);
+ void drawShapePlotPixelCallback1(uint8 *dst, uint8 color);
+ void drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...);
+ static void decodeFrame3(const uint8 *src, uint8 *dst, uint32 size);
+ static void decodeFrame4(const uint8 *src, uint8 *dst, uint32 dstSize);
+ static void decodeFrameDelta(uint8 *dst, const uint8 *src);
+ static void decodeFrameDeltaPage(uint8 *dst, const uint8 *src, int pitch, int noXor);
+ uint8 *encodeShape(int x, int y, int w, int h, int flags);
+ void copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 *dest);
+
+ int getRectSize(int x, int y);
+ void hideMouse();
+ void showMouse();
+ void setShapePages(int page1, int page2);
+ void setMouseCursor(int x, int y, byte *shape);
+ uint8 *getPalette(int num);
+
+ byte getShapeFlag1(int x, int y);
+ byte getShapeFlag2(int x, int y);
+ int setNewShapeHeight(uint8 *shape, int height);
+ int resetShapeHeight(uint8 *shape);
+
+ void addBitBlitRect(int x, int y, int w, int h);
+ void bitBlitRects();
+
+ void savePageToDisk(const char *file, int page);
+ void loadPageFromDisk(const char *file, int page);
+ void deletePageFromDisk(int page);
+
+ void blockInRegion(int x, int y, int width, int height);
+ void blockOutRegion(int x, int y, int width, int height);
+
+ void backUpRect0(int xpos, int ypos);
+ void restoreRect0(int xpos, int ypos);
+ void backUpRect1(int xpos, int ypos);
+ void restoreRect1(int xpos, int ypos);
+ void copyBackgroundBlock(int x, int page, int flag);
+ void copyBackgroundBlock2(int x);
+ void rectClip(int &x, int &y, int w, int h);
+ int getDrawLayer(int x, int y);
+ int getDrawLayer2(int x, int y, int height);
+
+ int _charWidth;
+ int _charOffset;
+ int _curPage;
+ uint8 *_currentPalette;
+ uint8 *_shapePages[2];
+
+ const ScreenDim *_curDim;
+
+ static const ScreenDim _screenDimTable[];
+ static const int _screenDimTableCount;
+private:
+ int16 encodeShapeAndCalculateSize(uint8 *from, uint8 *to, int size);
+ void restoreMouseRect();
+ void copyMouseToScreen();
+ void copyScreenFromRect(int x, int y, int w, int h, uint8 *ptr);
+ void copyScreenToRect(int x, int y, int w, int h, uint8 *ptr);
+
+ uint8 *_pagePtrs[16];
+ uint8 *_saveLoadPage[8];
+ uint8 *_screenPalette;
+ uint8 *_palettes[3];
+ FontId _currentFont;
+ Font _fonts[FID_NUM];
+ uint8 _textColorsMap[16];
+ uint8 *_decodeShapeBuffer;
+ int _decodeShapeBufferSize;
+ uint8 *_animBlockPtr;
+ int _animBlockSize;
+ int _mouseLockCount;
+
+ Rect *_bitBlitRects;
+ int _bitBlitNum;
+ uint8 *_unkPtr1, *_unkPtr2;
+
+ OSystem *_system;
+ KyraEngine *_vm;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/script.cpp b/engines/kyra/script.cpp
new file mode 100644
index 0000000000..b43766dba7
--- /dev/null
+++ b/engines/kyra/script.cpp
@@ -0,0 +1,565 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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 "common/stream.h"
+#include "common/util.h"
+#include "common/system.h"
+#include "kyra/kyra.h"
+#include "kyra/resource.h"
+#include "kyra/script.h"
+
+#define FORM_CHUNK 0x4D524F46
+#define TEXT_CHUNK 0x54584554
+#define DATA_CHUNK 0x41544144
+#define ORDR_CHUNK 0x5244524F
+
+namespace Kyra {
+ScriptHelper::ScriptHelper(KyraEngine *vm) : _vm(vm) {
+#define COMMAND(x) { &ScriptHelper::x, #x }
+ // now we create a list of all Command/Opcode procs and so
+ static CommandEntry commandProcs[] = {
+ // 0x00
+ COMMAND(c1_jmpTo),
+ COMMAND(c1_setRetValue),
+ COMMAND(c1_pushRetOrPos),
+ COMMAND(c1_push),
+ // 0x04
+ COMMAND(c1_push),
+ COMMAND(c1_pushVar),
+ COMMAND(c1_pushBPNeg),
+ COMMAND(c1_pushBPAdd),
+ // 0x08
+ COMMAND(c1_popRetOrPos),
+ COMMAND(c1_popVar),
+ COMMAND(c1_popBPNeg),
+ COMMAND(c1_popBPAdd),
+ // 0x0C
+ COMMAND(c1_addSP),
+ COMMAND(c1_subSP),
+ COMMAND(c1_execOpcode),
+ COMMAND(c1_ifNotJmp),
+ // 0x10
+ COMMAND(c1_negate),
+ COMMAND(c1_eval),
+ COMMAND(c1_setRetAndJmp)
+ };
+ _commands = commandProcs;
+#undef COMMAND
+}
+
+ScriptHelper::~ScriptHelper() {
+}
+
+bool ScriptHelper::loadScript(const char *filename, ScriptData *scriptData, KyraEngine::OpcodeProc *opcodes, int opcodeSize, byte *specialPtr) {
+ uint32 size = 0;
+ uint8 *data = _vm->resource()->fileData(filename, &size);
+ byte *curData = data;
+
+ uint32 formBlockSize = getFORMBlockSize(curData);
+ if (formBlockSize == (uint32)-1) {
+ delete [] data;
+ error("No FORM chunk found in file: '%s'", filename);
+ return false;
+ }
+
+ uint32 chunkSize = getIFFBlockSize(data, curData, size, TEXT_CHUNK);
+ if (chunkSize != (uint32)-1) {
+ if (specialPtr) {
+ scriptData->mustBeFreed = 0;
+ scriptData->text = specialPtr;
+ specialPtr += chunkSize;
+ } else {
+ scriptData->mustBeFreed = 1;
+ scriptData->text = new byte[chunkSize];
+ }
+ if (!loadIFFBlock(data, curData, size, TEXT_CHUNK, scriptData->text, chunkSize)) {
+ delete [] data;
+ unloadScript(scriptData);
+ error("Couldn't load TEXT chunk from file: '%s'", filename);
+ return false;
+ }
+ }
+
+ chunkSize = getIFFBlockSize(data, curData, size, ORDR_CHUNK);
+ if (chunkSize == (uint32)-1) {
+ delete [] data;
+ unloadScript(scriptData);
+ error("No ORDR chunk found in file: '%s'", filename);
+ return false;
+ }
+ if (specialPtr) {
+ scriptData->mustBeFreed = 0;
+ scriptData->ordr = specialPtr;
+ specialPtr += chunkSize;
+ } else {
+ scriptData->mustBeFreed = 1;
+ scriptData->ordr = new byte[chunkSize];
+ }
+ if (!loadIFFBlock(data, curData, size, ORDR_CHUNK, scriptData->ordr, chunkSize)) {
+ delete [] data;
+ unloadScript(scriptData);
+ error("Couldn't load ORDR chunk from file: '%s'", filename);
+ return false;
+ }
+ chunkSize = chunkSize / 2;
+ while (chunkSize--) {
+ ((uint16*)scriptData->ordr)[chunkSize] = READ_BE_UINT16(&((uint16*)scriptData->ordr)[chunkSize]);
+ }
+
+ chunkSize = getIFFBlockSize(data, curData, size, DATA_CHUNK);
+ if (chunkSize == (uint32)-1) {
+ delete [] data;
+ unloadScript(scriptData);
+ error("No DATA chunk found in file: '%s'", filename);
+ return false;
+ }
+ if (specialPtr) {
+ scriptData->mustBeFreed = 0;
+ scriptData->data = specialPtr;
+ specialPtr += chunkSize;
+ } else {
+ scriptData->mustBeFreed = 1;
+ scriptData->data = new byte[chunkSize];
+ }
+ if (!loadIFFBlock(data, curData, size, DATA_CHUNK, scriptData->data, chunkSize)) {
+ delete [] data;
+ unloadScript(scriptData);
+ error("Couldn't load DATA chunk from file: '%s'", filename);
+ return false;
+ }
+ scriptData->dataSize = chunkSize / 2;
+ scriptData->opcodes = opcodes;
+ scriptData->opcodeSize = opcodeSize;
+
+ delete [] data;
+ return true;
+}
+
+void ScriptHelper::unloadScript(ScriptData *data) {
+ if (data->mustBeFreed) {
+ delete [] data->text;
+ delete [] data->ordr;
+ delete [] data->data;
+ }
+
+ data->mustBeFreed = 0;
+ data->text = data->ordr = data->data = 0;
+}
+
+void ScriptHelper::initScript(ScriptState *scriptStat, ScriptData *data) {
+ scriptStat->dataPtr = data;
+ scriptStat->ip = 0;
+ scriptStat->stack[60] = 0;
+ scriptStat->bp = 62;
+ scriptStat->sp = 60;
+}
+
+bool ScriptHelper::startScript(ScriptState *script, int function) {
+ if (!script->dataPtr) {
+ return false;
+ }
+ uint16 functionOffset = ((uint16*)script->dataPtr->ordr)[function];
+ if (functionOffset == (uint16)-1) {
+ return false;
+ }
+ script->ip = &script->dataPtr->data[functionOffset*2];
+ return true;
+}
+
+bool ScriptHelper::validScript(ScriptState *script) {
+ if (!script->ip || !script->dataPtr)
+ return false;
+ return true;
+}
+
+bool ScriptHelper::runScript(ScriptState *script) {
+ _parameter = 0;
+ _continue = true;
+
+ if (!script->ip) {
+ return false;
+ }
+
+ int16 code = READ_BE_UINT16(script->ip); script->ip += 2;
+ int16 opcode = (code >> 8) & 0x1F;
+
+ if (code & 0x8000) {
+ opcode = 0;
+ _parameter = code & 0x7FFF;
+ } else if (code & 0x4000) {
+ _parameter = (int8)(code);
+ } else if (code & 0x2000) {
+ _parameter = READ_BE_UINT16(script->ip); script->ip += 2;
+ } else {
+ _parameter = 0;
+ }
+
+ if (opcode > 18) {
+ error("Script unknown command: %d", opcode);
+ } else {
+ debug(5, "%s(%d)", _commands[opcode].desc, _parameter);
+ (this->*(_commands[opcode].proc))(script);
+ }
+
+ return _continue;
+}
+
+uint32 ScriptHelper::getFORMBlockSize(byte *&data) const {
+ static const uint32 chunkName = FORM_CHUNK;
+ if (READ_LE_UINT32(data) != chunkName) {
+ return (uint32)-1;
+ }
+ data += 4;
+ uint32 retValue = READ_BE_UINT32(data); data += 4;
+ return retValue;
+}
+
+uint32 ScriptHelper::getIFFBlockSize(byte *start, byte *&data, uint32 maxSize, const uint32 chunkName) const {
+ uint32 size = (uint32)-1;
+ bool special = false;
+
+ if (data == (start + maxSize)) {
+ data = start + 0x0C;
+ }
+ while (data < (start + maxSize)) {
+ uint32 chunk = READ_LE_UINT32(data); data += 4;
+ uint32 size_temp = READ_BE_UINT32(data); data += 4;
+ if (chunk != chunkName) {
+ if (special) {
+ data += (size_temp + 1) & 0xFFFFFFFE;
+ } else {
+ data = start + 0x0C;
+ special = true;
+ }
+ } else {
+ // kill our data
+ data = start;
+ size = size_temp;
+ break;
+ }
+ }
+ return size;
+}
+
+bool ScriptHelper::loadIFFBlock(byte *start, byte *&data, uint32 maxSize, const uint32 chunkName, byte *loadTo, uint32 ptrSize) const {
+ bool special = false;
+
+ if (data == (start + maxSize)) {
+ data = start + 0x0C;
+ }
+ while (data < (start + maxSize)) {
+ uint32 chunk = READ_LE_UINT32(data); data += 4;
+ uint32 chunkSize = READ_BE_UINT32(data); data += 4;
+ if (chunk != chunkName) {
+ if (special) {
+ data += (chunkSize + 1) & 0xFFFFFFFE;
+ } else {
+ data = start + 0x0C;
+ special = true;
+ }
+ } else {
+ uint32 loadSize = 0;
+ if (chunkSize < ptrSize)
+ loadSize = chunkSize;
+ else
+ loadSize = ptrSize;
+ memcpy(loadTo, data, loadSize);
+ chunkSize = (chunkSize + 1) & 0xFFFFFFFE;
+ if (chunkSize > loadSize) {
+ data += (chunkSize - loadSize);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+#pragma mark -
+#pragma mark - Command implementations
+#pragma mark -
+
+void ScriptHelper::c1_jmpTo(ScriptState* script) {
+ script->ip = script->dataPtr->data + (_parameter << 1);
+}
+
+void ScriptHelper::c1_setRetValue(ScriptState* script) {
+ script->retValue = _parameter;
+}
+
+void ScriptHelper::c1_pushRetOrPos(ScriptState* script) {
+ switch (_parameter) {
+ case 0:
+ script->stack[--script->sp] = script->retValue;
+ break;
+
+ case 1:
+ script->stack[--script->sp] = (script->ip - script->dataPtr->data) / 2 + 1;
+ script->stack[--script->sp] = script->bp;
+ script->bp = script->sp + 2;
+ break;
+
+ default:
+ _continue = false;
+ script->ip = 0;
+ break;
+ }
+}
+
+void ScriptHelper::c1_push(ScriptState* script) {
+ script->stack[--script->sp] = _parameter;
+}
+
+void ScriptHelper::c1_pushVar(ScriptState* script) {
+ script->stack[--script->sp] = script->variables[_parameter];
+}
+
+void ScriptHelper::c1_pushBPNeg(ScriptState* script) {
+ script->stack[--script->sp] = script->stack[(-(int32)(_parameter + 2)) + script->bp];
+}
+
+void ScriptHelper::c1_pushBPAdd(ScriptState* script) {
+ script->stack[--script->sp] = script->stack[(_parameter - 1) + script->bp];
+}
+
+void ScriptHelper::c1_popRetOrPos(ScriptState* script) {
+ switch (_parameter) {
+ case 0:
+ script->retValue = script->stack[script->sp++];
+ break;
+
+ case 1:
+ if (script->sp >= 60) {
+ _continue = false;
+ script->ip = 0;
+ } else {
+ script->bp = script->stack[script->sp++];
+ script->ip = script->dataPtr->data + (script->stack[script->sp++] << 1);
+ }
+ break;
+
+ default:
+ _continue = false;
+ script->ip = 0;
+ break;
+ }
+}
+
+void ScriptHelper::c1_popVar(ScriptState* script) {
+ script->variables[_parameter] = script->stack[script->sp++];
+}
+
+void ScriptHelper::c1_popBPNeg(ScriptState* script) {
+ script->stack[(-(int32)(_parameter + 2)) + script->bp] = script->stack[script->sp++];
+}
+
+void ScriptHelper::c1_popBPAdd(ScriptState* script) {
+ script->stack[(_parameter - 1) + script->bp] = script->stack[script->sp++];
+}
+
+void ScriptHelper::c1_addSP(ScriptState* script) {
+ script->sp += _parameter;
+}
+
+void ScriptHelper::c1_subSP(ScriptState* script) {
+ script->sp -= _parameter;
+}
+
+void ScriptHelper::c1_execOpcode(ScriptState* script) {
+ assert((uint8)_parameter < script->dataPtr->opcodeSize);
+ if (script->dataPtr->opcodes[(uint8)_parameter] == &KyraEngine::cmd_dummy)
+ debug("calling unimplemented opcode(0x%.02X)", (uint8)_parameter);
+ int val = (_vm->*script->dataPtr->opcodes[(uint8)_parameter])(script);
+ assert(script);
+ script->retValue = val;
+}
+
+void ScriptHelper::c1_ifNotJmp(ScriptState* script) {
+ if (!script->stack[script->sp++]) {
+ _parameter &= 0x7FFF;
+ script->ip = script->dataPtr->data + (_parameter << 1);
+ }
+}
+
+void ScriptHelper::c1_negate(ScriptState* script) {
+ int16 value = script->stack[script->sp];
+ switch (_parameter) {
+ case 0:
+ if (!value) {
+ script->stack[script->sp] = 1;
+ } else {
+ script->stack[script->sp] = 0;
+ }
+ break;
+
+ case 1:
+ script->stack[script->sp] = -value;
+ break;
+
+ case 2:
+ script->stack[script->sp] = ~value;
+ break;
+
+ default:
+ _continue = false;
+ break;
+ }
+}
+
+void ScriptHelper::c1_eval(ScriptState* script) {
+ int16 ret = 0;
+ bool error = false;
+
+ int16 val1 = script->stack[script->sp++];
+ int16 val2 = script->stack[script->sp++];
+
+ switch (_parameter) {
+ case 0:
+ if (!val2 || !val1) {
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+ break;
+
+ case 1:
+ if (val2 || val1) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 2:
+ if (val1 == val2) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 3:
+ if (val1 != val2) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 4:
+ if (val1 > val2) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 5:
+ if (val1 >= val2) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 6:
+ if (val1 < val2) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 7:
+ if (val1 <= val2) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 8:
+ ret = val1 + val2;
+ break;
+
+ case 9:
+ ret = val2 - val1;
+ break;
+
+ case 10:
+ ret = val1 * val2;
+ break;
+
+ case 11:
+ ret = val2 / val1;
+ break;
+
+ case 12:
+ ret = val2 >> val1;
+ break;
+
+ case 13:
+ ret = val2 << val1;
+ break;
+
+ case 14:
+ ret = val1 & val2;
+ break;
+
+ case 15:
+ ret = val1 | val2;
+ break;
+
+ case 16:
+ ret = val2 % val1;
+ break;
+
+ case 17:
+ ret = val1 ^ val2;
+ break;
+
+ default:
+ warning("Unknown evaluate func: %d", _parameter);
+ error = true;
+ break;
+ }
+
+ if (error) {
+ script->ip = 0;
+ _continue = false;
+ } else {
+ script->stack[--script->sp] = ret;
+ }
+}
+
+void ScriptHelper::c1_setRetAndJmp(ScriptState* script) {
+ if (script->sp >= 60) {
+ _continue = false;
+ script->ip = 0;
+ } else {
+ script->retValue = script->stack[script->sp++];
+ uint16 temp = script->stack[script->sp++];
+ script->stack[60] = 0;
+ script->ip = &script->dataPtr->data[temp*2];
+ }
+}
+} // end of namespace Kyra
diff --git a/engines/kyra/script.h b/engines/kyra/script.h
new file mode 100644
index 0000000000..28f5170422
--- /dev/null
+++ b/engines/kyra/script.h
@@ -0,0 +1,106 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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$
+ *
+ */
+
+#ifndef KYRASCRIPT_H
+#define KYRASCRIPT_H
+
+#include "kyra/kyra.h"
+
+namespace Kyra {
+struct ScriptData {
+ byte *text;
+ byte *data;
+ byte *ordr;
+ uint16 dataSize;
+ KyraEngine::OpcodeProc *opcodes;
+ int opcodeSize;
+ uint16 mustBeFreed;
+};
+
+struct ScriptState {
+ byte *ip;
+ ScriptData *dataPtr;
+ int16 retValue;
+ uint16 bp;
+ uint16 sp;
+ int16 variables[30];
+ int16 stack[61];
+};
+
+enum {
+ SCRIPT_INIT = 0
+};
+
+class ScriptHelper {
+public:
+ ScriptHelper(KyraEngine *vm);
+ virtual ~ScriptHelper();
+
+ bool loadScript(const char *filename, ScriptData *data, KyraEngine::OpcodeProc *opcodes, int opcodeSize, byte *specialPtr = 0);
+ void unloadScript(ScriptData *data);
+
+ void initScript(ScriptState *scriptState, ScriptData *data);
+ bool startScript(ScriptState *script, int function);
+
+ bool validScript(ScriptState *script);
+
+ bool runScript(ScriptState *script);
+protected:
+ uint32 getFORMBlockSize(byte *&data) const;
+ uint32 getIFFBlockSize(byte *start, byte *&data, uint32 maxSize, const uint32 chunk) const;
+ bool loadIFFBlock(byte *start, byte *&data, uint32 maxSize, const uint32 chunk, byte *loadTo, uint32 ptrSize) const;
+
+ KyraEngine *_vm;
+ int16 _parameter;
+ bool _continue;
+
+ typedef void (ScriptHelper::*CommandProc)(ScriptState*);
+ struct CommandEntry {
+ CommandProc proc;
+ const char* desc;
+ };
+
+ const CommandEntry *_commands;
+private:
+ void c1_jmpTo(ScriptState*);
+ void c1_setRetValue(ScriptState*);
+ void c1_pushRetOrPos(ScriptState*);
+ void c1_push(ScriptState*);
+ //void c1_push(); same as 03
+ void c1_pushVar(ScriptState*);
+ void c1_pushBPNeg(ScriptState*);
+ void c1_pushBPAdd(ScriptState*);
+ void c1_popRetOrPos(ScriptState*);
+ void c1_popVar(ScriptState*);
+ void c1_popBPNeg(ScriptState*);
+ void c1_popBPAdd(ScriptState*);
+ void c1_addSP(ScriptState*);
+ void c1_subSP(ScriptState*);
+ void c1_execOpcode(ScriptState*);
+ void c1_ifNotJmp(ScriptState*);
+ void c1_negate(ScriptState*);
+ void c1_eval(ScriptState*);
+ void c1_setRetAndJmp(ScriptState*);
+};
+} // end of namespace Kyra
+
+#endif
diff --git a/engines/kyra/script_v1.cpp b/engines/kyra/script_v1.cpp
new file mode 100644
index 0000000000..08ff4a6d9f
--- /dev/null
+++ b/engines/kyra/script_v1.cpp
@@ -0,0 +1,1713 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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 "kyra/kyra.h"
+#include "kyra/script.h"
+#include "kyra/screen.h"
+#include "kyra/sprites.h"
+#include "kyra/wsamovie.h"
+#include "kyra/animator.h"
+#include "kyra/text.h"
+#include "common/system.h"
+
+namespace Kyra {
+#define stackPos(x) script->stack[script->sp+x]
+#define stackPosString(x) (char*)&script->dataPtr->text[READ_BE_UINT16(&((uint16 *)script->dataPtr->text)[stackPos(x)])]
+
+int KyraEngine::cmd_magicInMouseItem(ScriptState *script) {
+ debug(3, "cmd_magicInMouseItem(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ magicInMouseItem(stackPos(0), stackPos(1), -1);
+ return 0;
+}
+
+int KyraEngine::cmd_characterSays(ScriptState *script) {
+ _skipFlag = false;
+ if (_features & GF_TALKIE) {
+ debug(3, "cmd_characterSays(0x%X) (%d, '%s', %d, %d)", script, stackPos(0), stackPosString(1), stackPos(2), stackPos(3));
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(stackPos(0));
+ characterSays(stackPosString(1), stackPos(2), stackPos(3));
+ } else {
+ debug(3, "cmd_characterSays(0x%X) ('%s', %d, %d)", script, stackPosString(0), stackPos(1), stackPos(2));
+ characterSays(stackPosString(0), stackPos(1), stackPos(2));
+ }
+
+ return 0;
+}
+
+int KyraEngine::cmd_pauseTicks(ScriptState *script) {
+ debug(3, "cmd_pauseTicks(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ if (stackPos(1)) {
+ warning("STUB: special cmd_pauseTicks");
+ // delete this after correct implementing
+ delayWithTicks(stackPos(0));
+ } else {
+ delayWithTicks(stackPos(0));
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_drawSceneAnimShape(ScriptState *script) {
+ debug(3, "cmd_drawSceneAnimShape(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ _screen->drawShape(stackPos(4), _sprites->_sceneShapes[stackPos(0)], stackPos(1), stackPos(2), 0, (stackPos(3) != 0) ? 1 : 0);
+ return 0;
+}
+
+int KyraEngine::cmd_queryGameFlag(ScriptState *script) {
+ debug(3, "cmd_queryGameFlag(0x%X) (0x%X)", script, stackPos(0));
+ return queryGameFlag(stackPos(0));
+}
+
+int KyraEngine::cmd_setGameFlag(ScriptState *script) {
+ debug(3, "cmd_setGameFlag(0x%X) (0x%X)", script, stackPos(0));
+ return setGameFlag(stackPos(0));
+}
+
+int KyraEngine::cmd_resetGameFlag(ScriptState *script) {
+ debug(3, "cmd_resetGameFlag(0x%X) (0x%X)", script, stackPos(0));
+ return resetGameFlag(stackPos(0));
+}
+
+int KyraEngine::cmd_runNPCScript(ScriptState *script) {
+ warning("STUB: cmd_runNPCScript");
+ return 0;
+}
+
+int KyraEngine::cmd_setSpecialExitList(ScriptState *script) {
+ debug(3, "cmd_setSpecialExitList(0x%X) (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9));
+
+ for (int i = 0; i < 10; ++i) {
+ _exitList[i] = stackPos(i);
+ }
+ _exitListPtr = _exitList;
+
+ return 0;
+}
+
+int KyraEngine::cmd_blockInWalkableRegion(ScriptState *script) {
+ debug(3, "cmd_blockInWalkableRegion(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ _screen->blockInRegion(stackPos(0), stackPos(1), stackPos(2)-stackPos(0)+1, stackPos(3)-stackPos(1)+1);
+ return 0;
+}
+
+int KyraEngine::cmd_blockOutWalkableRegion(ScriptState *script) {
+ debug(3, "cmd_blockOutWalkableRegion(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ _screen->blockOutRegion(stackPos(0), stackPos(1), stackPos(2)-stackPos(0)+1, stackPos(3)-stackPos(1)+1);
+ return 0;
+}
+
+int KyraEngine::cmd_walkPlayerToPoint(ScriptState *script) {
+ debug(3, "cmd_walkPlayerToPoint(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+
+ int normalTimers = stackPos(2);
+ if (!normalTimers) {
+ disableTimer(19);
+ disableTimer(14);
+ disableTimer(18);
+ }
+
+ int reinitScript = handleSceneChange(stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+
+ if (!normalTimers) {
+ enableTimer(19);
+ enableTimer(14);
+ enableTimer(18);
+ }
+
+ if (reinitScript) {
+ _scriptInterpreter->initScript(script, script->dataPtr);
+ }
+
+ if (_sceneChangeState) {
+ _sceneChangeState = 0;
+ return 1;
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_dropItemInScene(ScriptState *script) {
+ debug(3, "cmd_dropItemInScene(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ int item = stackPos(0);
+ int xpos = stackPos(1);
+ int ypos = stackPos(2);
+
+ byte freeItem = findFreeItemInScene(_currentCharacter->sceneId);
+ if (freeItem != 0xFF) {
+ int sceneId = _currentCharacter->sceneId;
+ Room *room = &_roomTable[sceneId];
+ room->itemsXPos[freeItem] = xpos;
+ room->itemsYPos[freeItem] = ypos;
+ room->itemsTable[freeItem] = item;
+
+ _animator->animAddGameItem(freeItem, sceneId);
+ _animator->updateAllObjectShapes();
+ } else {
+ if (item == 43) {
+ placeItemInGenericMapScene(item, 0);
+ } else {
+ placeItemInGenericMapScene(item, 1);
+ }
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_drawAnimShapeIntoScene(ScriptState *script) {
+ debug(3, "cmd_drawAnimShapeIntoScene(0x%X) (%d, %d, %d, %d)", stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ _screen->hideMouse();
+ _animator->restoreAllObjectBackgrounds();
+ int shape = stackPos(0);
+ int xpos = stackPos(1);
+ int ypos = stackPos(2);
+ int flags = (stackPos(3) != 0) ? 1 : 0;
+ _screen->drawShape(2, _sprites->_sceneShapes[shape], xpos, ypos, 0, flags);
+ _screen->drawShape(0, _sprites->_sceneShapes[shape], xpos, ypos, 0, flags);
+ _animator->flagAllObjectsForBkgdChange();
+ _animator->preserveAnyChangedBackgrounds();
+ _animator->flagAllObjectsForRefresh();
+ _animator->updateAllObjectShapes();
+ _screen->showMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_createMouseItem(ScriptState *script) {
+ debug(3, "cmd_createMouseItem(0x%X) (%d)", script, stackPos(0));
+ createMouseItem(stackPos(0));
+ return 0;
+}
+
+int KyraEngine::cmd_savePageToDisk(ScriptState *script) {
+ debug(3, "cmd_savePageToDisk(0x%X) ('%s', %d)", script, stackPosString(0), stackPos(1));
+ _screen->savePageToDisk(stackPosString(0), stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_sceneAnimOn(ScriptState *script) {
+ debug(3, "cmd_sceneAnimOn(0x%X) (%d)", script, stackPos(0));
+ _sprites->_anims[stackPos(0)].play = true;
+ return 0;
+}
+
+int KyraEngine::cmd_sceneAnimOff(ScriptState *script) {
+ debug(3, "cmd_sceneAnimOff(0x%X) (%d)", script, stackPos(0));
+ _sprites->_anims[stackPos(0)].play = false;
+ return 0;
+}
+
+int KyraEngine::cmd_getElapsedSeconds(ScriptState *script) {
+ debug(3, "cmd_getElapsedSeconds(0x%X) ()", script);
+ return _system->getMillis() / 1000;
+}
+
+int KyraEngine::cmd_mouseIsPointer(ScriptState *script) {
+ debug(3, "cmd_mouseIsPointer(0x%X) ()", script);
+ if (_itemInHand == -1) {
+ return 1;
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_destroyMouseItem(ScriptState *script) {
+ debug(3, "cmd_destroyMouseItem(0x%X) ()", script);
+ destroyMouseItem();
+ return 0;
+}
+
+int KyraEngine::cmd_runSceneAnimUntilDone(ScriptState *script) {
+ debug(3, "cmd_runSceneAnimUntilDone(0x%X) (%d)", script, stackPos(0));
+ _screen->hideMouse();
+ _animator->restoreAllObjectBackgrounds();
+ _sprites->_anims[stackPos(0)].play = true;
+ _animator->sprites()[stackPos(0)].active = 1;
+ _animator->flagAllObjectsForBkgdChange();
+ _animator->preserveAnyChangedBackgrounds();
+ while (_sprites->_anims[stackPos(0)].play) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ delay(10);
+ }
+ _animator->restoreAllObjectBackgrounds();
+ _screen->showMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_fadeSpecialPalette(ScriptState *script) {
+ debug(3, "cmd_fadeSpecialPalette(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ _screen->fadeSpecialPalette(stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ return 0;
+}
+
+int KyraEngine::cmd_playAdlibSound(ScriptState *script) {
+ debug(3, "cmd_playAdlibSound(0x%X) (%d)", script, stackPos(0));
+ snd_playSoundEffect(stackPos(0));
+ return 0;
+}
+
+int KyraEngine::cmd_playAdlibScore(ScriptState *script) {
+ debug(3, "cmd_playAdlibScore(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ snd_playWanderScoreViaMap(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_phaseInSameScene(ScriptState *script) {
+ debug(3, "cmd_phaseInSameScene(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ transcendScenes(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_setScenePhasingFlag(ScriptState *script) {
+ debug(3, "cmd_setScenePhasingFlag(0x%X) ()", script);
+ _scenePhasingFlag = 1;
+ return 1;
+}
+
+int KyraEngine::cmd_resetScenePhasingFlag(ScriptState *script) {
+ debug(3, "cmd_resetScenePhasingFlag(0x%X) ()", script);
+ _scenePhasingFlag = 0;
+ return 0;
+}
+
+int KyraEngine::cmd_queryScenePhasingFlag(ScriptState *script) {
+ debug(3, "cmd_queryScenePhasingFlag(0x%X) ()", script);
+ return _scenePhasingFlag;
+}
+
+int KyraEngine::cmd_sceneToDirection(ScriptState *script) {
+ debug(3, "cmd_sceneToDirection(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ assert(stackPos(0) < _roomTableSize);
+ Room *curRoom = &_roomTable[stackPos(0)];
+ uint16 returnValue = 0xFFFF;
+ switch (stackPos(1)) {
+ case 0:
+ returnValue = curRoom->northExit;
+ break;
+
+ case 2:
+ returnValue = curRoom->eastExit;
+ break;
+
+ case 4:
+ returnValue = curRoom->southExit;
+ break;
+
+ case 6:
+ returnValue = curRoom->westExit;
+ break;
+
+ default:
+ break;
+ }
+ if (returnValue == 0xFFFF)
+ return -1;
+ return returnValue;
+}
+
+int KyraEngine::cmd_setBirthstoneGem(ScriptState *script) {
+ debug(3, "cmd_setBirthstoneGem(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ int index = stackPos(0);
+ if (index < 4 && index >= 0) {
+ _birthstoneGemTable[index] = stackPos(1);
+ return 1;
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_placeItemInGenericMapScene(ScriptState *script) {
+ debug(3, "cmd_placeItemInGenericMapScene(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ placeItemInGenericMapScene(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_setBrandonStatusBit(ScriptState *script) {
+ debug(3, "cmd_setBrandonStatusBit(0x%X) (%d)", script, stackPos(0));
+ _brandonStatusBit |= stackPos(0);
+ return 0;
+}
+
+int KyraEngine::cmd_pauseSeconds(ScriptState *script) {
+ debug(3, "cmd_pauseSeconds(0x%X) (%d)", script, stackPos(0));
+ if (stackPos(0) > 0 && !_skipFlag)
+ delay(stackPos(0)*1000, true);
+ _skipFlag = false;
+ return 0;
+}
+
+int KyraEngine::cmd_getCharactersLocation(ScriptState *script) {
+ debug(3, "cmd_getCharactersLocation(0x%X) (%d)", script, stackPos(0));
+ return _characterList[stackPos(0)].sceneId;
+}
+
+int KyraEngine::cmd_runNPCSubscript(ScriptState *script) {
+ warning("STUB: cmd_runNPCSubscript");
+ return 0;
+}
+
+int KyraEngine::cmd_magicOutMouseItem(ScriptState *script) {
+ debug(3, "cmd_magicOutMouseItem(0x%X) (%d)", script, stackPos(0));
+ magicOutMouseItem(stackPos(0), -1);
+ return 0;
+}
+
+int KyraEngine::cmd_internalAnimOn(ScriptState *script) {
+ debug(3, "cmd_internalAnimOn(0x%X) (%d)", script, stackPos(0));
+ _animator->sprites()[stackPos(0)].active = 1;
+ return 0;
+}
+
+int KyraEngine::cmd_forceBrandonToNormal(ScriptState *script) {
+ debug(3, "cmd_forceBrandonToNormal(0x%X) ()", script);
+ checkAmuletAnimFlags();
+ return 0;
+}
+
+int KyraEngine::cmd_poisonDeathNow(ScriptState *script) {
+ debug(3, "cmd_poisonDeathNow(0x%X) ()", script);
+ seq_poisonDeathNow(1);
+ return 0;
+}
+
+int KyraEngine::cmd_setScaleMode(ScriptState *script) {
+ debug(3, "cmd_setScaleMode(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ int len = stackPos(0);
+ int setValue1 = stackPos(1);
+ int start2 = stackPos(2);
+ int setValue2 = stackPos(3);
+ for (int i = 0; i < len; ++i) {
+ _scaleTable[i] = setValue1;
+ }
+ int temp = setValue2 - setValue1;
+ int temp2 = start2 - len;
+ for (int i = len, offset = 0; i < start2; ++i, ++offset) {
+ _scaleTable[i] = (offset * temp) / temp2 + setValue1;
+ }
+ for (int i = start2; i < 145; ++i) {
+ _scaleTable[i] = setValue2;
+ }
+ _scaleMode = 1;
+ return _scaleMode;
+}
+
+int KyraEngine::cmd_openWSAFile(ScriptState *script) {
+ debug(3, "cmd_openWSAFile(0x%X) ('%s', %d, %d)", script, stackPosString(0), stackPos(1), stackPos(2));
+
+ char *filename = stackPosString(0);
+ int wsaIndex = stackPos(1);
+
+ _movieObjects[wsaIndex]->open(filename, (stackPos(2) != 0) ? 1 : 0, 0);
+ assert(_movieObjects[wsaIndex]->opened());
+
+ return 0;
+}
+
+int KyraEngine::cmd_closeWSAFile(ScriptState *script) {
+ debug(3, "cmd_closeWSAFile(0x%X) (%d)", script, stackPos(0));
+
+ int wsaIndex = stackPos(0);
+ if (_movieObjects[wsaIndex]) {
+ _movieObjects[wsaIndex]->close();
+ }
+
+ return 0;
+}
+
+int KyraEngine::cmd_runWSAFromBeginningToEnd(ScriptState *script) {
+ debug(3, "cmd_runWSAFromBeginningToEnd(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+
+ _screen->hideMouse();
+
+ bool running = true;
+
+ int xpos = stackPos(0);
+ int ypos = stackPos(1);
+ int waitTime = stackPos(2);
+ int wsaIndex = stackPos(3);
+ int worldUpdate = stackPos(4);
+ int wsaFrame = 0;
+
+ _movieObjects[wsaIndex]->_x = xpos;
+ _movieObjects[wsaIndex]->_y = ypos;
+ _movieObjects[wsaIndex]->_drawPage = 0;
+ while (running) {
+ _movieObjects[wsaIndex]->displayFrame(wsaFrame++);
+ _animator->_updateScreen = true;
+ if (wsaFrame >= _movieObjects[wsaIndex]->frames())
+ running = false;
+
+ uint32 continueTime = waitTime * _tickLength + _system->getMillis();
+ while (_system->getMillis() < continueTime) {
+ if (worldUpdate) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ } else {
+ _screen->updateScreen();
+ }
+ if (continueTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+
+ _screen->showMouse();
+
+ return 0;
+}
+
+int KyraEngine::cmd_displayWSAFrame(ScriptState *script) {
+ debug(3, "cmd_displayWSAFrame(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ int frame = stackPos(0);
+ int xpos = stackPos(1);
+ int ypos = stackPos(2);
+ int waitTime = stackPos(3);
+ int wsaIndex = stackPos(4);
+ _screen->hideMouse();
+ _movieObjects[wsaIndex]->_x = xpos;
+ _movieObjects[wsaIndex]->_y = ypos;
+ _movieObjects[wsaIndex]->_drawPage = 0;
+ _movieObjects[wsaIndex]->displayFrame(frame);
+ _animator->_updateScreen = true;
+ uint32 continueTime = waitTime * _tickLength + _system->getMillis();
+ while (_system->getMillis() < continueTime && !_skipFlag) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (continueTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ _screen->showMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_enterNewScene(ScriptState *script) {
+ debug(3, "cmd_enterNewScene(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ enterNewScene(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ return 0;
+}
+
+int KyraEngine::cmd_setSpecialEnterXAndY(ScriptState *script) {
+ debug(3, "cmd_setSpecialEnterXAndY(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ _brandonPosX = stackPos(0);
+ _brandonPosY = stackPos(1);
+ if (_brandonPosX + 1 == 0 && _brandonPosY + 1 == 0)
+ _currentCharacter->currentAnimFrame = 88;
+ return 0;
+}
+
+int KyraEngine::cmd_runWSAFrames(ScriptState *script) {
+ debug(3, "cmd_runWSAFrames(0x%X) (%d, %d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ int xpos = stackPos(0);
+ int ypos = stackPos(1);
+ int delayTime = stackPos(2);
+ int startFrame = stackPos(3);
+ int endFrame = stackPos(4);
+ int wsaIndex = stackPos(5);
+ _screen->hideMouse();
+ _movieObjects[wsaIndex]->_x = xpos;
+ _movieObjects[wsaIndex]->_y = ypos;
+ _movieObjects[wsaIndex]->_drawPage = 0;
+ for (; startFrame <= endFrame; ++startFrame) {
+ uint32 nextRun = _system->getMillis() + delayTime * _tickLength;
+ _movieObjects[wsaIndex]->displayFrame(startFrame);
+ _animator->_updateScreen = true;
+ while (_system->getMillis() < nextRun) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (nextRun - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+ _screen->showMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_popBrandonIntoScene(ScriptState *script) {
+ debug(3, "cmd_popBrandonIntoScene(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ int changeScaleMode = stackPos(3);
+ int xpos = (int16)(stackPos(0) & 0xFFFC);
+ int ypos = (int16)(stackPos(1) & 0xFFFE);
+ int facing = stackPos(2);
+ _currentCharacter->x1 = _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = _currentCharacter->y2 = ypos;
+ _currentCharacter->facing = facing;
+ _currentCharacter->currentAnimFrame = 7;
+ int xOffset = _defaultShapeTable[0].xOffset;
+ int yOffset = _defaultShapeTable[0].yOffset;
+ int width = _defaultShapeTable[0].w << 3;
+ int height = _defaultShapeTable[0].h;
+ AnimObject *curAnim = _animator->actors();
+
+ if (changeScaleMode) {
+ curAnim->x1 = _currentCharacter->x1;
+ curAnim->y1 = _currentCharacter->y1;
+ _animator->_brandonScaleY = _scaleTable[_currentCharacter->y1];
+ _animator->_brandonScaleX = _animator->_brandonScaleY;
+
+ int animWidth = _animator->fetchAnimWidth(curAnim->sceneAnimPtr, _animator->_brandonScaleX) >> 1;
+ int animHeight = _animator->fetchAnimHeight(curAnim->sceneAnimPtr, _animator->_brandonScaleY);
+
+ animWidth = (xOffset * animWidth) / width;
+ animHeight = (yOffset * animHeight) / height;
+
+ curAnim->x2 = curAnim->x1 += animWidth;
+ curAnim->y2 = curAnim->y1 += animHeight;
+ } else {
+ curAnim->x2 = curAnim->x1 = _currentCharacter->x1 + xOffset;
+ curAnim->y2 = curAnim->y1 = _currentCharacter->y1 + yOffset;
+ }
+
+ int scaleModeBackup = _scaleMode;
+ if (changeScaleMode) {
+ _scaleMode = 1;
+ }
+
+ _animator->animRefreshNPC(0);
+ _animator->preserveAllBackgrounds();
+ _animator->prepDrawAllObjects();
+ _animator->copyChangedObjectsForward(0);
+
+ _scaleMode = scaleModeBackup;
+
+ return 0;
+}
+
+int KyraEngine::cmd_restoreAllObjectBackgrounds(ScriptState *script) {
+ debug(3, "cmd_restoreAllObjectBackgrounds(0x%X) ()", script);
+ _animator->restoreAllObjectBackgrounds();
+ return 0;
+}
+
+int KyraEngine::cmd_setCustomPaletteRange(ScriptState *script) {
+ debug(3, "cmd_setCustomPaletteRange(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ uint8 *screenPal = _screen->_currentPalette;
+ memcpy(&screenPal[stackPos(1)*3], _specialPalettes[stackPos(0)], stackPos(2)*3);
+ _screen->setScreenPalette(screenPal);
+ return 0;
+}
+
+int KyraEngine::cmd_loadPageFromDisk(ScriptState *script) {
+ debug(3, "cmd_loadPageFromDisk(0x%X) ('%s', %d)", script, stackPosString(0), stackPos(1));
+ _screen->loadPageFromDisk(stackPosString(0), stackPos(1));
+ _animator->_updateScreen = true;
+ return 0;
+}
+
+int KyraEngine::cmd_customPrintTalkString(ScriptState *script) {
+ if (_features & GF_TALKIE) {
+ debug(3, "cmd_customPrintTalkString(0x%X) (%d, '%s', %d, %d, %d)", script, stackPos(0), stackPosString(1), stackPos(2), stackPos(3), stackPos(4) & 0xFF);
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(stackPos(0));
+ _skipFlag = false;
+ _text->printTalkTextMessage(stackPosString(1), stackPos(2), stackPos(3), stackPos(4) & 0xFF, 0, 2);
+ } else {
+ debug(3, "cmd_customPrintTalkString(0x%X) ('%s', %d, %d, %d)", script, stackPosString(0), stackPos(1), stackPos(2), stackPos(3) & 0xFF);
+ _skipFlag = false;
+ _text->printTalkTextMessage(stackPosString(0), stackPos(1), stackPos(2), stackPos(3) & 0xFF, 0, 2);
+ }
+ _screen->updateScreen();
+ return 0;
+}
+
+int KyraEngine::cmd_restoreCustomPrintBackground(ScriptState *script) {
+ debug(3, "cmd_restoreCustomPrintBackground(0x%X) ()", script);
+ _text->restoreTalkTextMessageBkgd(2, 0);
+ return 0;
+}
+
+int KyraEngine::cmd_hideMouse(ScriptState *script) {
+ debug(3, "cmd_hideMouse(0x%X) ()", script);
+ _screen->hideMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_showMouse(ScriptState *script) {
+ debug(3, "cmd_showMouse(0x%X) ()", script);
+ _screen->showMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_getCharacterX(ScriptState *script) {
+ debug(3, "cmd_getCharacterX(0x%X) (%d)", script, stackPos(0));
+ return _characterList[stackPos(0)].x1;
+}
+
+int KyraEngine::cmd_getCharacterY(ScriptState *script) {
+ debug(3, "cmd_getCharacterY(0x%X) (%d)", script, stackPos(0));
+ return _characterList[stackPos(0)].y1;
+}
+
+int KyraEngine::cmd_changeCharactersFacing(ScriptState *script) {
+ debug(3, "cmd_changeCharactersFacing(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ int character = stackPos(0);
+ int facing = stackPos(1);
+ int newAnimFrame = stackPos(2);
+
+ _animator->restoreAllObjectBackgrounds();
+ if (newAnimFrame != -1) {
+ _characterList[character].currentAnimFrame = newAnimFrame;
+ }
+ _characterList[character].facing = facing;
+ _animator->animRefreshNPC(character);
+ _animator->preserveAllBackgrounds();
+ _animator->prepDrawAllObjects();
+ _animator->copyChangedObjectsForward(0);
+
+ return 0;
+}
+
+int KyraEngine::cmd_copyWSARegion(ScriptState *script) {
+ debug(3, "cmd_copyWSARegion(0x%X) (%d, %d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ int xpos = stackPos(0);
+ int ypos = stackPos(1);
+ int width = stackPos(2);
+ int height = stackPos(3);
+ int srcPage = stackPos(4);
+ int dstPage = stackPos(5);
+ _screen->copyRegion(xpos, ypos, xpos, ypos, width, height, srcPage, dstPage);
+ _animator->_updateScreen = true;
+ return 0;
+}
+
+int KyraEngine::cmd_printText(ScriptState *script) {
+ debug(3, "cmd_printText(0x%X) ('%s', %d, %d, 0x%X, 0x%X)", script, stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ _screen->printText(stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ _screen->updateScreen();
+ return 0;
+}
+
+int KyraEngine::cmd_random(ScriptState *script) {
+ debug(3, "cmd_random(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ assert(stackPos(0) < stackPos(1));
+ return _rnd.getRandomNumberRng(stackPos(0), stackPos(1));
+}
+
+int KyraEngine::cmd_loadSoundFile(ScriptState *script) {
+ warning("STUB: cmd_loadSoundFile");
+ return 0;
+}
+
+int KyraEngine::cmd_displayWSAFrameOnHidPage(ScriptState *script) {
+ debug(3, "cmd_displayWSAFrameOnHidPage(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ int frame = stackPos(0);
+ int xpos = stackPos(1);
+ int ypos = stackPos(2);
+ int waitTime = stackPos(3);
+ int wsaIndex = stackPos(4);
+
+ _screen->hideMouse();
+ uint32 continueTime = waitTime * _tickLength + _system->getMillis();
+ _movieObjects[wsaIndex]->_x = xpos;
+ _movieObjects[wsaIndex]->_y = ypos;
+ _movieObjects[wsaIndex]->_drawPage = 2;
+ _movieObjects[wsaIndex]->displayFrame(frame);
+ _animator->_updateScreen = true;
+ while (_system->getMillis() < continueTime) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (continueTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ _screen->showMouse();
+
+ return 0;
+}
+
+int KyraEngine::cmd_displayWSASequentialFrames(ScriptState *script) {
+ debug(3, "cmd_displayWSASequentialFrames(0x%X) (%d, %d, %d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6));
+ int startFrame = stackPos(0);
+ int endFrame = stackPos(1);
+ int xpos = stackPos(2);
+ int ypos = stackPos(3);
+ int waitTime = stackPos(4);
+ int wsaIndex = stackPos(5);
+ int maxTime = stackPos(6);
+ if (maxTime - 1 <= 0)
+ maxTime = 1;
+
+ _movieObjects[wsaIndex]->_x = xpos;
+ _movieObjects[wsaIndex]->_y = ypos;
+ _movieObjects[wsaIndex]->_drawPage = 0;
+
+ int curTime = 0;
+ _screen->hideMouse();
+ while (curTime < maxTime) {
+ if (endFrame >= startFrame) {
+ int frame = startFrame;
+ while (endFrame >= frame) {
+ _movieObjects[wsaIndex]->displayFrame(frame);
+ _animator->_updateScreen = true;
+ uint32 continueTime = waitTime * _tickLength + _system->getMillis();
+ while (_system->getMillis() < continueTime && !_skipFlag) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (continueTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ ++frame;
+ }
+ } else {
+ int frame = startFrame;
+ while (endFrame <= frame) {
+ _movieObjects[wsaIndex]->displayFrame(frame);
+ _animator->_updateScreen = true;
+ uint32 continueTime = waitTime * _tickLength + _system->getMillis();
+ while (_system->getMillis() < continueTime && !_skipFlag) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (continueTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ --frame;
+ }
+ }
+ if (!_skipFlag)
+ break;
+ else
+ ++curTime;
+ }
+ _screen->showMouse();
+
+ return 0;
+}
+
+int KyraEngine::cmd_drawCharacterStanding(ScriptState *script) {
+ debug(3, "cmd_drawCharacterStanding(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ int character = stackPos(0);
+ int animFrame = stackPos(1);
+ int newFacing = stackPos(2);
+ int updateShapes = stackPos(3);
+ _characterList[character].currentAnimFrame = animFrame;
+ if (newFacing != -1) {
+ _characterList[character].facing = newFacing;
+ }
+ _animator->animRefreshNPC(character);
+ if (updateShapes) {
+ _animator->updateAllObjectShapes();
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_internalAnimOff(ScriptState *script) {
+ debug(3, "cmd_internalAnimOff(0x%X) (%d)", script, stackPos(0));
+ _animator->sprites()[stackPos(0)].active = 0;
+ return 0;
+}
+
+int KyraEngine::cmd_changeCharactersXAndY(ScriptState *script) {
+ debug(3, "cmd_changeCharactersXAndY(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ Character *ch = &_characterList[stackPos(0)];
+ int16 x = stackPos(1);
+ int16 y = stackPos(2);
+ if (x != -1 && y != -1) {
+ x &= 0xFFFC;
+ y &= 0xFFFE;
+ }
+ _animator->restoreAllObjectBackgrounds();
+ ch->x1 = ch->x2 = x;
+ ch->y1 = ch->y2 = y;
+ _animator->preserveAllBackgrounds();
+ return 0;
+}
+
+int KyraEngine::cmd_clearSceneAnimatorBeacon(ScriptState *script) {
+ debug(3, "cmd_clearSceneAnimatorBeacon(0x%X) ()", script);
+ _sprites->_sceneAnimatorBeaconFlag = 0;
+ return 0;
+}
+
+int KyraEngine::cmd_querySceneAnimatorBeacon(ScriptState *script) {
+ debug(3, "cmd_querySceneAnimatorBeacon(0x%X) ()", script);
+ return _sprites->_sceneAnimatorBeaconFlag;
+}
+
+int KyraEngine::cmd_refreshSceneAnimator(ScriptState *script) {
+ debug(3, "cmd_refreshSceneAnimator(0x%X) ()", script);
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ return 0;
+}
+
+int KyraEngine::cmd_placeItemInOffScene(ScriptState *script) {
+ debug(3, "cmd_placeItemInOffScene(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ int item = stackPos(0);
+ int xpos = stackPos(1);
+ int ypos = stackPos(2);
+ int sceneId = stackPos(3);
+
+ byte freeItem = findFreeItemInScene(sceneId);
+ if (freeItem != 0xFF) {
+ assert(sceneId < _roomTableSize);
+ Room *room = &_roomTable[sceneId];
+
+ room->itemsTable[freeItem] = item;
+ room->itemsXPos[freeItem] = xpos;
+ room->itemsYPos[freeItem] = ypos;
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_wipeDownMouseItem(ScriptState *script) {
+ debug(3, "cmd_wipeDownMouseItem(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ _screen->hideMouse();
+ wipeDownMouseItem(stackPos(1), stackPos(2));
+ destroyMouseItem();
+ _screen->showMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_placeCharacterInOtherScene(ScriptState *script) {
+ debug(3, "cmd_placeCharacterInOtherScene(0x%X) (%d, %d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ int id = stackPos(0);
+ int sceneId = stackPos(1);
+ int xpos = (int16)(stackPos(2) & 0xFFFC);
+ int ypos = (int16)(stackPos(3) & 0xFFFE);
+ int facing = stackPos(4);
+ int animFrame = stackPos(5);
+
+ _characterList[id].sceneId = sceneId;
+ _characterList[id].x1 = _characterList[id].x2 = xpos;
+ _characterList[id].y1 = _characterList[id].y2 = ypos;
+ _characterList[id].facing = facing;
+ _characterList[id].currentAnimFrame = animFrame;
+ return 0;
+}
+
+int KyraEngine::cmd_getKey(ScriptState *script) {
+ debug(3, "cmd_getKey(0x%X) ()", script);
+ waitForEvent();
+ return 0;
+}
+
+int KyraEngine::cmd_specificItemInInventory(ScriptState *script) {
+ warning("STUB: cmd_specificItemInInventory");
+ return 0;
+}
+
+int KyraEngine::cmd_popMobileNPCIntoScene(ScriptState *script) {
+ debug(3, "cmd_popMobileNPCIntoScene(0x%X) (%d, %d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), (int16)(stackPos(4) & 0xFFFC), (int8)(stackPos(5) & 0xFE));
+ int character = stackPos(0);
+ int sceneId = stackPos(1);
+ int animFrame = stackPos(2);
+ int facing = stackPos(3);
+ int16 xpos = (int16)(stackPos(4) & 0xFFFC);
+ int8 ypos = (int16)(stackPos(5) & 0xFFFE);
+ Character *curChar = &_characterList[character];
+
+ curChar->sceneId = sceneId;
+ curChar->currentAnimFrame = animFrame;
+ curChar->facing = facing;
+ curChar->x1 = curChar->x2 = xpos;
+ curChar->y1 = curChar->y2 = ypos;
+
+ _animator->animAddNPC(character);
+ _animator->updateAllObjectShapes();
+ return 0;
+}
+
+int KyraEngine::cmd_mobileCharacterInScene(ScriptState *script) {
+ warning("STUB: cmd_mobileCharacterInScene");
+ return 0;
+}
+
+int KyraEngine::cmd_hideMobileCharacter(ScriptState *script) {
+ warning("STUB: cmd_hideMobileCharacter");
+ return 0;
+}
+
+int KyraEngine::cmd_unhideMobileCharacter(ScriptState *script) {
+ warning("STUB: cmd_unhideMobileCharacter");
+ return 0;
+}
+
+int KyraEngine::cmd_setCharactersLocation(ScriptState *script) {
+ debug(3, "cmd_setCharactersLocation(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ Character *ch = &_characterList[stackPos(0)];
+ AnimObject *animObj = &_animator->actors()[stackPos(0)];
+ int newScene = stackPos(1);
+ if (_currentCharacter->sceneId == ch->sceneId) {
+ if (_currentCharacter->sceneId != newScene)
+ animObj->active = 0;
+ } else if (_currentCharacter->sceneId == newScene) {
+ if (_currentCharacter->sceneId != ch->sceneId)
+ animObj->active = 1;
+ }
+
+ ch->sceneId = stackPos(1);
+ return 0;
+}
+
+int KyraEngine::cmd_walkCharacterToPoint(ScriptState *script) {
+ debug(3, "cmd_walkCharacterToPoint(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ int character = stackPos(0);
+ int toX = stackPos(1);
+ int toY = stackPos(2);
+ _pathfinderFlag2 = 1;
+ uint32 nextFrame;
+ int findWayReturn = findWay(_characterList[character].x1, _characterList[character].y1, toX, toY, _movFacingTable, 150);
+ _pathfinderFlag2 = 0;
+ if (_lastFindWayRet < findWayReturn) {
+ _lastFindWayRet = findWayReturn;
+ }
+ if (findWayReturn == 0x7D00 || findWayReturn == 0) {
+ return 0;
+ }
+ int *curPos = _movFacingTable;
+ bool running = true;
+ while (running) {
+ bool forceContinue = false;
+ switch (*curPos) {
+ case 0:
+ _characterList[character].facing = 2;
+ break;
+
+ case 1:
+ _characterList[character].facing = 1;
+ break;
+
+ case 2:
+ _characterList[character].facing = 0;
+ break;
+
+ case 3:
+ _characterList[character].facing = 7;
+ break;
+
+ case 4:
+ _characterList[character].facing = 6;
+ break;
+
+ case 5:
+ _characterList[character].facing = 5;
+ break;
+
+ case 6:
+ _characterList[character].facing = 4;
+ break;
+
+ case 7:
+ _characterList[character].facing = 3;
+ break;
+
+ case 8:
+ running = 0;
+ break;
+
+ default:
+ ++curPos;
+ forceContinue = true;
+ break;
+ }
+
+ if (forceContinue || !running) {
+ continue;
+ }
+
+ setCharacterPosition(character, 0);
+ ++curPos;
+
+ nextFrame = getTimerDelay(5 + character) * _tickLength + _system->getMillis();
+ while (_system->getMillis() < nextFrame) {
+ _sprites->updateSceneAnims();
+ updateMousePointer();
+ updateGameTimers();
+ _animator->updateAllObjectShapes();
+ updateTextFade();
+ if ((nextFrame - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_specialEventDisplayBrynnsNote(ScriptState *script) {
+ debug(3, "cmd_specialEventDisplayBrynnsNote(0x%X) ()", script);
+ _screen->hideMouse();
+ _screen->savePageToDisk("HIDPAGE.TMP", 2);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+ if (_features & GF_TALKIE) {
+ if (_features & GF_ENGLISH) {
+ loadBitmap("NOTEENG.CPS", 3, 3, 0);
+ } else if (_features & GF_FRENCH) {
+ loadBitmap("NOTEFRE.CPS", 3, 3, 0);
+ } else if (_features & GF_GERMAN) {
+ loadBitmap("NOTEGER.CPS", 3, 3, 0);
+ }
+ } else {
+ loadBitmap("NOTE.CPS", 3, 3, 0);
+ }
+ _screen->copyRegion(63, 8, 63, 8, 194, 128, 2, 0);
+ _screen->updateScreen();
+ _screen->showMouse();
+ _screen->setFont(Screen::FID_6_FNT);
+ return 0;
+}
+
+int KyraEngine::cmd_specialEventRemoveBrynnsNote(ScriptState *script) {
+ debug(3, "cmd_specialEventRemoveBrynnsNote(0x%X) ()", script);
+ _screen->hideMouse();
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->loadPageFromDisk("HIDPAGE.TMP", 2);
+ _screen->updateScreen();
+ _screen->showMouse();
+ _screen->setFont(Screen::FID_8_FNT);
+ return 0;
+}
+
+int KyraEngine::cmd_setLogicPage(ScriptState *script) {
+ debug(3, "cmd_setLogicPage(0x%X) (%d)", script, stackPos(0));
+ _screen->_curPage = stackPos(0);
+ return stackPos(0);
+}
+
+int KyraEngine::cmd_fatPrint(ScriptState *script) {
+ debug(3, "cmd_fatPrint(0x%X) ('%s', %d, %d, %d, %d, %d)", script, stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ _text->printText(stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ return 0;
+}
+
+int KyraEngine::cmd_preserveAllObjectBackgrounds(ScriptState *script) {
+ debug(3, "cmd_preserveAllObjectBackgrounds(0x%X) ()", script);
+ _animator->preserveAllBackgrounds();
+ return 0;
+}
+
+int KyraEngine::cmd_updateSceneAnimations(ScriptState *script) {
+ debug(3, "cmd_updateSceneAnimations(0x%X) (%d)", script, stackPos(0));
+ if (stackPos(0)) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_sceneAnimationActive(ScriptState *script) {
+ debug(3, "cmd_sceneAnimationActive(0x%X) (%d)", script, stackPos(0));
+ return _sprites->_anims[stackPos(0)].play;
+}
+
+int KyraEngine::cmd_setCharactersMovementDelay(ScriptState *script) {
+ debug(3, "cmd_setCharactersMovementDelay(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ setTimerDelay(stackPos(0)+5, stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_getCharactersFacing(ScriptState *script) {
+ debug(3, "cmd_getCharactersFacing(0x%X) (%d)", script, stackPos(0));
+ return _characterList[stackPos(0)].facing;
+}
+
+int KyraEngine::cmd_bkgdScrollSceneAndMasksRight(ScriptState *script) {
+ debug(3, "cmd_bkgdScrollSceneAndMasksRight(0x%X) (%d)", script, stackPos(0));
+ _screen->copyBackgroundBlock(stackPos(0), 2, 0);
+ _screen->copyBackgroundBlock2(stackPos(0));
+ // update the whole screen
+ _screen->copyRegion(7, 7, 7, 7, 305, 129, 3, 0);
+ _screen->updateScreen();
+ return 0;
+}
+
+int KyraEngine::cmd_dispelMagicAnimation(ScriptState *script) {
+ debug(3, "cmd_dispelMagicAnimation(0x%X) ()", script);
+ seq_dispelMagicAnimation();
+ return 0;
+}
+
+int KyraEngine::cmd_findBrightestFireberry(ScriptState *script) {
+ debug(3, "cmd_findBrightestFireberry(0x%X) ()", script);
+ if (_currentCharacter->sceneId >= 187 && _currentCharacter->sceneId <= 198) {
+ return 29;
+ }
+ if (_currentCharacter->sceneId == 133 || _currentCharacter->sceneId == 137 ||
+ _currentCharacter->sceneId == 165 || _currentCharacter->sceneId == 173) {
+ return 29;
+ }
+ if (_itemInHand == 28)
+ return 28;
+ int brightestFireberry = 107;
+ if (_itemInHand >= 29 && _itemInHand <= 33)
+ brightestFireberry = _itemInHand;
+ for (int i = 0; i < 10; ++i) {
+ uint8 item = _currentCharacter->inventoryItems[i];
+ if (item == 0xFF)
+ continue;
+ if (item == 28)
+ return 28;
+ if (item >= 29 && item <= 33) {
+ if (item < brightestFireberry)
+ brightestFireberry = item;
+ }
+ }
+ assert(_currentCharacter->sceneId < _roomTableSize);
+ Room *curRoom = &_roomTable[_currentCharacter->sceneId];
+ for (int i = 0; i < 12; ++i) {
+ uint8 item = curRoom->itemsTable[i];
+ if (item == 0xFF)
+ continue;
+ if (item == 28)
+ return 28;
+ if (item >= 29 && item <= 33) {
+ if (item < brightestFireberry)
+ brightestFireberry = item;
+ }
+ }
+ if (brightestFireberry == 107)
+ return -1;
+ return brightestFireberry;
+}
+
+int KyraEngine::cmd_setFireberryGlowPalette(ScriptState *script) {
+ debug(3, "cmd_setFireberryGlowPalette(0x%X) (%d)", script, stackPos(0));
+ int palIndex = 0;
+ switch (stackPos(0)) {
+ case 0x1E:
+ palIndex = 9;
+ break;
+
+ case 0x1F:
+ palIndex = 10;
+ break;
+
+ case 0x20:
+ palIndex = 11;
+ break;
+
+ case 0x21:
+ case -1:
+ palIndex = 12;
+ break;
+
+ default:
+ palIndex = 8;
+ break;
+ }
+ if (_brandonStatusBit & 2) {
+ if (_currentCharacter->sceneId != 133 && _currentCharacter->sceneId != 137 &&
+ _currentCharacter->sceneId != 165 && _currentCharacter->sceneId != 173 &&
+ (_currentCharacter->sceneId < 187 || _currentCharacter->sceneId > 198)) {
+ palIndex = 14;
+ }
+ }
+ uint8 *palette = _specialPalettes[palIndex];
+ memcpy(&_screen->_currentPalette[684], palette, 44);
+ _screen->setScreenPalette(_screen->_currentPalette);
+ return 0;
+}
+
+int KyraEngine::cmd_setDeathHandlerFlag(ScriptState *script) {
+ debug(3, "cmd_setDeathHandlerFlag(0x%X) (%d)", script, stackPos(0));
+ _deathHandler = stackPos(0);
+ return 0;
+}
+
+int KyraEngine::cmd_drinkPotionAnimation(ScriptState *script) {
+ debug(3, "cmd_drinkPotionAnimation(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ seq_playDrinkPotionAnim(stackPos(0), stackPos(1), stackPos(2));
+ return 0;
+}
+
+int KyraEngine::cmd_makeAmuletAppear(ScriptState *script) {
+ debug(3, "cmd_makeAmuletAppear(0x%X) ()", script);
+ WSAMovieV1 amulet(this);
+ amulet.open("AMULET.WSA", 1, 0);
+ amulet._drawPage = 0;
+ amulet._x = 224;
+ amulet._y = 152;
+ if (amulet.opened()) {
+ assert(_amuleteAnim);
+ _screen->hideMouse();
+ snd_playSoundEffect(0x70);
+ uint32 nextTime = 0;
+ for (int i = 0; _amuleteAnim[i] != 0xFF; ++i) {
+ nextTime = _system->getMillis() + 5 * _tickLength;
+
+ uint8 code = _amuleteAnim[i];
+ if (code == 3 || code == 7) {
+ snd_playSoundEffect(0x71);
+ }
+
+ if (code == 5) {
+ snd_playSoundEffect(0x72);
+ }
+
+ if (code == 14) {
+ snd_playSoundEffect(0x73);
+ }
+
+
+ amulet.displayFrame(code);
+ _animator->_updateScreen = true;
+
+ while (_system->getMillis() < nextTime) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (nextTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+ _screen->showMouse();
+ }
+ setGameFlag(0x2D);
+ return 0;
+}
+
+int KyraEngine::cmd_drawItemShapeIntoScene(ScriptState *script) {
+ debug(3, "cmd_drawItemShapeIntoScene(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ int item = stackPos(0);
+ int x = stackPos(1);
+ int y = stackPos(2);
+ int flags = stackPos(3);
+ int onlyHidPage = stackPos(4);
+ if (flags)
+ flags = 1;
+ if (onlyHidPage) {
+ _screen->drawShape(2, _shapes[220+item], x, y, 0, flags);
+ } else {
+ _screen->hideMouse();
+ _animator->restoreAllObjectBackgrounds();
+ _screen->drawShape(2, _shapes[220+item], x, y, 0, flags);
+ _screen->drawShape(0, _shapes[220+item], x, y, 0, flags);
+ _animator->flagAllObjectsForBkgdChange();
+ _animator->flagAllObjectsForRefresh();
+ _animator->updateAllObjectShapes();
+ _screen->showMouse();
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_setCharactersCurrentFrame(ScriptState *script) {
+ debug(3, "cmd_setCharactersCurrentFrame(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ _characterList[stackPos(0)].currentAnimFrame = stackPos(1);
+ return 0;
+}
+
+int KyraEngine::cmd_waitForConfirmationMouseClick(ScriptState *script) {
+ debug(3, "cmd_waitForConfirmationMouseClick(0x%X) ()", script);
+ // if (mouseEnabled) {
+ while (!_mousePressFlag) {
+ updateMousePointer();
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ delay(10);
+ }
+
+ while (_mousePressFlag) {
+ updateMousePointer();
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ delay(10);
+ }
+ // }
+ // XXX processButtonList calls
+ script->variables[1] = _mouseX;
+ script->variables[2] = _mouseY;
+ return 0;
+}
+
+int KyraEngine::cmd_pageFlip(ScriptState *script) {
+ warning("STUB: cmd_pageFlip");
+ return 0;
+}
+
+int KyraEngine::cmd_setSceneFile(ScriptState *script) {
+ debug(3, "cmd_setSceneFile(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ setSceneFile(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_getItemInMarbleVase(ScriptState *script) {
+ debug(3, "cmd_getItemInMarbleVase(0x%X) ()", script);
+ return _marbleVaseItem;
+}
+
+int KyraEngine::cmd_setItemInMarbleVase(ScriptState *script) {
+ debug(3, "cmd_setItemInMarbleVase(0x%X) (%d)", script, stackPos(0));
+ _marbleVaseItem = stackPos(0);
+ return 0;
+}
+
+int KyraEngine::cmd_addItemToInventory(ScriptState *script) {
+ warning("STUB: cmd_addItemToInventory");
+ return 0;
+}
+
+int KyraEngine::cmd_intPrint(ScriptState *script) {
+ warning("STUB: cmd_intPrint");
+ return 0;
+}
+
+int KyraEngine::cmd_shakeScreen(ScriptState *script) {
+ warning("STUB: cmd_shakeScreen");
+ return 0;
+}
+
+int KyraEngine::cmd_createAmuletJewel(ScriptState *script) {
+ debug(3, "cmd_createAmuletJewel(0x%X) (%d)", script, stackPos(0));
+ seq_createAmuletJewel(stackPos(0), 0, 0, 0);
+ return 0;
+}
+
+int KyraEngine::cmd_setSceneAnimCurrXY(ScriptState *script) {
+ debug(3, "cmd_setSceneAnimCurrXY(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ _sprites->_anims[stackPos(0)].x = stackPos(1);
+ _sprites->_anims[stackPos(0)].y = stackPos(2);
+ return 0;
+}
+
+int KyraEngine::cmd_poisonBrandonAndRemaps(ScriptState *script) {
+ debug(3, "cmd_poisonBrandonAndRemaps(0x%X) ()", script);
+ setBrandonPoisonFlags(1);
+ return 0;
+}
+
+int KyraEngine::cmd_fillFlaskWithWater(ScriptState *script) {
+ debug(3, "cmd_fillFlaskWithWater(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ seq_fillFlaskWithWater(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_getCharactersMovementDelay(ScriptState *script) {
+ debug(3, "cmd_getCharactersMovementDelay(0x%X) (%d)", script, stackPos(0));
+ return getTimerDelay(stackPos(0)+5);
+}
+
+int KyraEngine::cmd_getBirthstoneGem(ScriptState *script) {
+ debug(3, "cmd_getBirthstoneGem(0x%X) (%d)", script, stackPos(0));
+ if (stackPos(0) < 4) {
+ return _birthstoneGemTable[stackPos(0)];
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_queryBrandonStatusBit(ScriptState *script) {
+ debug(3, "cmd_queryBrandonStatusBit(0x%X) (%d)", script, stackPos(0));
+ if (_brandonStatusBit & stackPos(0)) {
+ return 1;
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_playFluteAnimation(ScriptState *script) {
+ debug(3, "cmd_playFluteAnimation(0x%X) ()", script);
+ seq_playFluteAnimation();
+ return 0;
+}
+
+int KyraEngine::cmd_playWinterScrollSequence(ScriptState *script) {
+ debug(3, "cmd_playWinterScrollSequence(0x%X) (%d)", script, stackPos(0));
+ if (!stackPos(0)) {
+ seq_winterScroll2();
+ } else {
+ seq_winterScroll1();
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_getIdolGem(ScriptState *script) {
+ debug(3, "cmd_getIdolGem(0x%X) (%d)", script, stackPos(0));
+ return _idolGemsTable[stackPos(0)];;
+}
+
+int KyraEngine::cmd_setIdolGem(ScriptState *script) {
+ debug(3, "cmd_setIdolGem(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ _idolGemsTable[stackPos(0)] = stackPos(1);
+ return 0;
+}
+
+int KyraEngine::cmd_totalItemsInScene(ScriptState *script) {
+ debug(3, "cmd_totalItemsInScene(0x%X) (%d)", script, stackPos(0));
+ return countItemsInScene(stackPos(0));
+}
+
+int KyraEngine::cmd_restoreBrandonsMovementDelay(ScriptState *script) {
+ debug(3, "cmd_restoreBrandonsMovementDelay(0x%X) ()", script);
+ //TODO: Use movement set by menu, instead of 5.
+ setTimerDelay(5, 5);
+ return 0;
+}
+
+int KyraEngine::cmd_setMousePos(ScriptState *script) {
+ debug(3, "cmd_setMousePos(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ _system->warpMouse(stackPos(0), stackPos(1));
+ _mouseX = stackPos(0);
+ _mouseY = stackPos(1);
+ return 0;
+}
+
+int KyraEngine::cmd_getMouseState(ScriptState *script) {
+ debug(3, "cmd_getMouseState(0x%X) ()", script);
+ return _mouseState;
+}
+
+int KyraEngine::cmd_setEntranceMouseCursorTrack(ScriptState *script) {
+ debug(3, "cmd_setEntranceMouseCursorTrack(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ _entranceMouseCursorTracks[0] = stackPos(0);
+ _entranceMouseCursorTracks[1] = stackPos(1);
+ _entranceMouseCursorTracks[2] = stackPos(0) + stackPos(2) - 1;
+ _entranceMouseCursorTracks[3] = stackPos(1) + stackPos(3) - 1;
+ _entranceMouseCursorTracks[4] = stackPos(4);
+ return 0;
+}
+
+int KyraEngine::cmd_itemAppearsOnGround(ScriptState *script) {
+ debug(3, "cmd_itemAppearsOnGround(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ processItemDrop(_currentCharacter->sceneId, stackPos(0), stackPos(1), stackPos(2), 2, 0);
+ return 0;
+}
+
+int KyraEngine::cmd_setNoDrawShapesFlag(ScriptState *script) {
+ debug(3, "cmd_setNoDrawShapesFlag(0x%X) (%d)", script, stackPos(0));
+ _animator->_noDrawShapesFlag = stackPos(0);
+ return 0;
+}
+
+int KyraEngine::cmd_fadeEntirePalette(ScriptState *script) {
+ warning("STUB: cmd_fadeEntirePalette");
+ return 0;
+}
+
+int KyraEngine::cmd_itemOnGroundHere(ScriptState *script) {
+ debug(3, "cmd_itemOnGroundHere(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ assert(stackPos(0) < _roomTableSize);
+ Room *curRoom = &_roomTable[stackPos(0)];
+ for (int i = 0; i < 12; ++i) {
+ if (curRoom->itemsTable[i] == stackPos(1))
+ return 1;
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_queryCauldronState(ScriptState *script) {
+ debug(3, "cmd_queryCauldronState(0x%X) ()", script);
+ return _cauldronState;
+}
+
+int KyraEngine::cmd_setCauldronState(ScriptState *script) {
+ debug(3, "cmd_setCauldronState(0x%X) (%d)", script, stackPos(0));
+ _cauldronState = stackPos(0);
+ return _cauldronState;
+}
+
+int KyraEngine::cmd_queryCrystalState(ScriptState *script) {
+ debug(3, "cmd_queryCrystalState(0x%X) (%d)", script, stackPos(0));
+ if (!stackPos(0)) {
+ return _crystalState[0];
+ } else if (stackPos(0) == 1) {
+ return _crystalState[1];
+ }
+ return -1;
+}
+
+int KyraEngine::cmd_setCrystalState(ScriptState *script) {
+ debug(3, "cmd_setCrystalState(0x%X) (%d)", script, stackPos(0), stackPos(1));
+ if (!stackPos(0)) {
+ _crystalState[0] = stackPos(1);
+ } else if (stackPos(0) == 1) {
+ _crystalState[1] = stackPos(1);
+ }
+ return stackPos(1);
+}
+
+int KyraEngine::cmd_setPaletteRange(ScriptState *script) {
+ warning("STUB: cmd_setPaletteRange");
+ return 0;
+}
+
+int KyraEngine::cmd_shrinkBrandonDown(ScriptState *script) {
+ debug(3, "cmd_shrinkBrandonDown(0x%X) (%d)", script, stackPos(0));
+ int delayTime = stackPos(0);
+ checkAmuletAnimFlags();
+ int scaleValue = _scaleTable[_currentCharacter->y1];
+ int scale = 0;
+ if (_scaleMode) {
+ scale = scaleValue;
+ } else {
+ scale = 256;
+ }
+ int scaleModeBackUp = _scaleMode;
+ _scaleMode = 1;
+ int scaleEnd = scale >> 1;
+ for (; scaleEnd <= scale; --scale) {
+ _scaleTable[_currentCharacter->y1] = scale;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(1);
+ }
+ delayWithTicks(delayTime); // XXX
+ _scaleTable[_currentCharacter->y1] = scaleValue;
+ _scaleMode = scaleModeBackUp;
+ return 0;
+}
+
+int KyraEngine::cmd_growBrandonUp(ScriptState *script) {
+ debug(3, "cmd_growBrandonUp(0x%X) ()", script);
+ int scaleValue = _scaleTable[_currentCharacter->y1];
+ int scale = 0;
+ if (_scaleMode) {
+ scale = scaleValue;
+ } else {
+ scale = 256;
+ }
+ int scaleModeBackUp = _scaleMode;
+ _scaleMode = 1;
+ for (int curScale = scale >> 1; curScale <= scale; ++curScale) {
+ _scaleTable[_currentCharacter->y1] = curScale;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(1);
+ }
+ _scaleTable[_currentCharacter->y1] = scaleValue;
+ _scaleMode = scaleModeBackUp;
+ return 0;
+}
+
+int KyraEngine::cmd_setBrandonScaleXAndY(ScriptState *script) {
+ debug(3, "cmd_setBrandonScaleXAndY(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ _animator->_brandonScaleX = stackPos(0);
+ _animator->_brandonScaleY = stackPos(1);
+ return 0;
+}
+
+int KyraEngine::cmd_resetScaleMode(ScriptState *script) {
+ debug(3, "cmd_resetScaleMode(0x%X) ()", script);
+ _scaleMode = 0;
+ return 0;
+}
+
+int KyraEngine::cmd_getScaleDepthTableValue(ScriptState *script) {
+ debug(3, "cmd_getScaleDepthTableValue(0x%X) (%d)", script, stackPos(0));
+ assert(stackPos(0) < ARRAYSIZE(_scaleTable));
+ return _scaleTable[stackPos(0)];
+}
+
+int KyraEngine::cmd_setScaleDepthTableValue(ScriptState *script) {
+ debug(3, "cmd_setScaleDepthTableValue(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ assert(stackPos(0) < ARRAYSIZE(_scaleTable));
+ _scaleTable[stackPos(0)] = stackPos(1);
+ return stackPos(1);
+}
+
+int KyraEngine::cmd_message(ScriptState *script) {
+ if (_features & GF_TALKIE) {
+ debug(3, "cmd_message(0x%X) (%d, '%s', %d)", script, stackPos(0), stackPosString(1), stackPos(2));
+ drawSentenceCommand(stackPosString(1), stackPos(2));
+ } else {
+ debug(3, "cmd_message(0x%X) ('%s', %d)", script, stackPosString(0), stackPos(1));
+ drawSentenceCommand(stackPosString(0), stackPos(1));
+ }
+
+ return 0;
+}
+
+int KyraEngine::cmd_checkClickOnNPC(ScriptState *script) {
+ debug(3, "cmd_checkClickOnNPC(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ return checkForNPCScriptRun(stackPos(0), stackPos(1));
+}
+
+int KyraEngine::cmd_getFoyerItem(ScriptState *script) {
+ debug(3, "cmd_getFoyerItem(0x%X) (%d)", stackPos(0));
+ assert(stackPos(0) < ARRAYSIZE(_foyerItemTable));
+ return _foyerItemTable[stackPos(0)];
+}
+
+int KyraEngine::cmd_setFoyerItem(ScriptState *script) {
+ debug(3, "cmd_setFoyerItem(0x%X) (%d, %d)", stackPos(0), stackPos(1));
+ assert(stackPos(0) < ARRAYSIZE(_foyerItemTable));
+ _foyerItemTable[stackPos(0)] = stackPos(1);
+ return stackPos(1);
+}
+
+int KyraEngine::cmd_setNoItemDropRegion(ScriptState *script) {
+ debug(3, "cmd_setNoItemDropRegion(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ addToNoDropRects(stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ return 0;
+}
+
+int KyraEngine::cmd_walkMalcolmOn(ScriptState *script) {
+ debug(3, "cmd_walkMalcolmOn(0x%X) ()", script);
+ if (!_malcolmFlag)
+ _malcolmFlag = 1;
+ return 0;
+}
+
+int KyraEngine::cmd_passiveProtection(ScriptState *script) {
+ debug(3, "cmd_passiveProtection(0x%X) ()", script);
+ return 1;
+}
+
+int KyraEngine::cmd_setPlayingLoop(ScriptState *script) {
+ warning("STUB: cmd_setPlayingLoop");
+ return 0;
+}
+
+int KyraEngine::cmd_brandonToStoneSequence(ScriptState *script) {
+ warning("STUB: cmd_brandonToStoneSequence");
+ return 0;
+}
+
+int KyraEngine::cmd_brandonHealingSequence(ScriptState *script) {
+ debug(3, "cmd_brandonHealingSequence(0x%X) ()", script);
+ seq_brandonHealing();
+ return 0;
+}
+
+int KyraEngine::cmd_protectCommandLine(ScriptState *script) {
+ debug(3, "cmd_protectCommandLine(0x%X) (%d)", script, stackPos(0));
+ return stackPos(0);
+}
+
+int KyraEngine::cmd_pauseMusicSeconds(ScriptState *script) {
+ warning("STUB: cmd_pauseMusicSeconds");
+ return 0;
+}
+
+int KyraEngine::cmd_resetMaskRegion(ScriptState *script) {
+ warning("STUB: cmd_resetMaskRegion");
+ return 0;
+}
+
+int KyraEngine::cmd_setPaletteChangeFlag(ScriptState *script) {
+ debug(3, "cmd_setPaletteChangeFlag(0x%X) (%d)", script, stackPos(0));
+ _paletteChanged = stackPos(0);
+ return _paletteChanged;
+}
+
+int KyraEngine::cmd_fillRect(ScriptState *script) {
+ debug(3, "cmd_fillRect(0x%X) (%d, %d, %d, %d, %d, 0x%X)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ int videoPageBackup = _screen->_curPage;
+ _screen->_curPage = stackPos(0);
+ _screen->fillRect(stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ _screen->_curPage = videoPageBackup;
+ return 0;
+}
+
+int KyraEngine::cmd_vocUnload(ScriptState *script) {
+ debug(3, "cmd_vocUnload(0x%X) ()", script);
+ // this should unload all voc files (not needed)
+ return 0;
+}
+
+int KyraEngine::cmd_vocLoad(ScriptState *script) {
+ debug(3, "cmd_vocLoad(0x%X) (%d)", script, stackPos(0));
+ // this should load the specified voc file (not needed)
+ return 0;
+}
+
+int KyraEngine::cmd_dummy(ScriptState *script) {
+ debug(3, "cmd_dummy(0x%X) ()", script);
+ return 0;
+}
+
+} // end of namespace Kyra
diff --git a/engines/kyra/seqplayer.cpp b/engines/kyra/seqplayer.cpp
new file mode 100644
index 0000000000..fab2228c8a
--- /dev/null
+++ b/engines/kyra/seqplayer.cpp
@@ -0,0 +1,640 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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 "common/system.h"
+
+#include "base/engine.h"
+
+#include "kyra/kyra.h"
+#include "kyra/resource.h"
+#include "kyra/screen.h"
+#include "kyra/sound.h"
+#include "kyra/wsamovie.h"
+#include "kyra/text.h"
+
+#include "kyra/seqplayer.h"
+
+#define SEQOP(n, x) { n, &SeqPlayer::x, #x }
+
+namespace Kyra {
+
+SeqPlayer::SeqPlayer(KyraEngine* vm, OSystem* system) {
+ _vm = vm;
+ _system = system;
+
+ _screen = vm->screen();
+ _sound = vm->sound();
+ _res = vm->resource();
+
+ _copyViewOffs = false;
+ _specialBuffer = 0;
+
+ for (int i = 0; i < ARRAYSIZE(_handShapes); ++i)
+ _handShapes[i] = 0;
+}
+
+SeqPlayer::~SeqPlayer() {
+ freeHandShapes();
+
+ for (int i = 0; i < ARRAYSIZE(_seqMovies); ++i) {
+ _seqMovies[i].movie->close();
+ delete _seqMovies[i].movie;
+ _seqMovies[i].movie = 0;
+ }
+}
+
+uint8 *SeqPlayer::setPanPages(int pageNum, int shape) {
+ debug(9, "SeqPlayer::setPanPages(%d, %d)", pageNum, shape);
+ uint8 *panPage = 0;
+ const uint8 *data = _screen->getPagePtr(pageNum);
+ uint16 numShapes = READ_LE_UINT16(data);
+ if (shape < numShapes) {
+ uint32 offs = 0;
+ if (_vm->features() & GF_TALKIE) {
+ offs = READ_LE_UINT32(data + 2 + shape * 4);
+ } else {
+ offs = READ_LE_UINT16(data + 2 + shape * 2);
+ }
+ if (offs != 0) {
+ data += offs;
+ uint16 sz = READ_LE_UINT16(data + 6);
+ panPage = (uint8 *)malloc(sz);
+ if (panPage) {
+ memcpy(panPage, data, sz);
+ }
+ }
+ }
+ return panPage;
+}
+
+void SeqPlayer::makeHandShapes() {
+ debug(9, "SeqPlayer::makeHandShapes()");
+ _vm->loadBitmap("WRITING.CPS", 3, 3, 0);
+ for (int i = 0; i < ARRAYSIZE(_handShapes); ++i) {
+ if (_handShapes[i])
+ free(_handShapes[i]);
+ _handShapes[i] = setPanPages(3, i);
+ }
+}
+
+void SeqPlayer::freeHandShapes() {
+ debug(9, "SeqPlayer::freeHandShapes()");
+ for (int i = 0; i < ARRAYSIZE(_handShapes); ++i) {
+ free(_handShapes[i]);
+ _handShapes[i] = 0;
+ }
+}
+
+void SeqPlayer::s1_wsaOpen() {
+ uint8 wsaObj = *_seqData++;
+ assert(wsaObj < ARRAYSIZE(_seqMovies));
+ uint8 offscreenDecode = *_seqData++;
+ _seqWsaCurDecodePage = _seqMovies[wsaObj].page = (offscreenDecode == 0) ? 0 : 3;
+ if (!_seqMovies[wsaObj].movie)
+ _seqMovies[wsaObj].movie = _vm->createWSAMovie();
+ _seqMovies[wsaObj].movie->_drawPage = _seqMovies[wsaObj].page;
+ _seqMovies[wsaObj].movie->open(_vm->seqWSATable()[wsaObj], offscreenDecode, 0);
+ _seqMovies[wsaObj].frame = 0;
+ _seqMovies[wsaObj].numFrames = _seqMovies[wsaObj].movie->frames() - 1;
+}
+
+void SeqPlayer::s1_wsaClose() {
+ uint8 wsaObj = *_seqData++;
+ assert(wsaObj < ARRAYSIZE(_seqMovies));
+ if (_seqMovies[wsaObj].movie) {
+ _seqMovies[wsaObj].movie->close();
+ }
+}
+
+void SeqPlayer::s1_wsaPlayFrame() {
+ uint8 wsaObj = *_seqData++;
+ assert(wsaObj < ARRAYSIZE(_seqMovies));
+ int16 frame = (int8)*_seqData++;
+ _seqMovies[wsaObj].pos.x = READ_LE_UINT16(_seqData); _seqData += 2;
+ _seqMovies[wsaObj].pos.y = *_seqData++;
+ assert(_seqMovies[wsaObj].movie);
+ _seqMovies[wsaObj].movie->_x = _seqMovies[wsaObj].pos.x;
+ _seqMovies[wsaObj].movie->_y = _seqMovies[wsaObj].pos.y;
+ _seqMovies[wsaObj].movie->displayFrame(frame);
+ _seqMovies[wsaObj].frame = frame;
+}
+
+void SeqPlayer::s1_wsaPlayNextFrame() {
+ uint8 wsaObj = *_seqData++;
+ assert(wsaObj < ARRAYSIZE(_seqMovies));
+ int16 frame = ++_seqMovies[wsaObj].frame;
+ if (frame > _seqMovies[wsaObj].numFrames) {
+ frame = 0;
+ _seqMovies[wsaObj].frame = 0;
+ }
+ _seqMovies[wsaObj].movie->displayFrame(frame);
+}
+
+void SeqPlayer::s1_wsaPlayPrevFrame() {
+ uint8 wsaObj = *_seqData++;
+ assert(wsaObj < ARRAYSIZE(_seqMovies));
+ int16 frame = --_seqMovies[wsaObj].frame;
+ if (frame < 0) {
+ frame = _seqMovies[wsaObj].numFrames;
+ _seqMovies[wsaObj].frame = frame;
+ } else {
+ _seqMovies[wsaObj].movie->displayFrame(frame);
+ }
+}
+
+void SeqPlayer::s1_drawShape() {
+ uint8 shapeNum = *_seqData++;
+ int x = READ_LE_UINT16(_seqData); _seqData += 2;
+ int y = *_seqData++;
+ _screen->drawShape(2, _handShapes[shapeNum], x, y, 0, 0, 0);
+}
+
+void SeqPlayer::s1_waitTicks() {
+ uint16 ticks = READ_LE_UINT16(_seqData); _seqData += 2;
+ _vm->delay(ticks * _vm->tickLength());
+}
+
+void SeqPlayer::s1_copyWaitTicks() {
+ s1_copyView();
+ s1_waitTicks();
+}
+
+void SeqPlayer::s1_shuffleScreen() {
+ _screen->shuffleScreen(0, 16, 320, 128, 2, 0, 0, false);
+ _screen->_curPage = 2;
+ if (_specialBuffer)
+ _screen->copyCurPageBlock(0, 16, 40, 128, _specialBuffer);
+ _screen->_curPage = 0;
+}
+
+void SeqPlayer::s1_copyView() {
+ int y = 128;
+ if (!_copyViewOffs) {
+ y -= 8;
+ }
+ if (_specialBuffer && !_copyViewOffs) {
+ _screen->copyToPage0(16, y, 3, _specialBuffer);
+ } else {
+ _screen->copyRegion(0, 16, 0, 16, 320, y, 2, 0);
+ }
+}
+
+void SeqPlayer::s1_loopInit() {
+ uint8 seqLoop = *_seqData++;
+ if (seqLoop < ARRAYSIZE(_seqLoopTable)) {
+ _seqLoopTable[seqLoop].ptr = _seqData;
+ } else {
+ _seqQuitFlag = true;
+ }
+}
+
+void SeqPlayer::s1_loopInc() {
+ uint8 seqLoop = *_seqData++;
+ uint16 seqLoopCount = READ_LE_UINT16(_seqData); _seqData += 2;
+ if (_seqLoopTable[seqLoop].count == 0xFFFF) {
+ _seqLoopTable[seqLoop].count = seqLoopCount - 1;
+ _seqData = _seqLoopTable[seqLoop].ptr;
+ } else if (_seqLoopTable[seqLoop].count == 0) {
+ _seqLoopTable[seqLoop].count = 0xFFFF;
+ _seqLoopTable[seqLoop].ptr = 0;
+ } else {
+ --_seqLoopTable[seqLoop].count;
+ _seqData = _seqLoopTable[seqLoop].ptr;
+ }
+}
+
+void SeqPlayer::s1_skip() {
+ uint8 a = *_seqData++;
+ warning("STUB: s1_skip(%d)\n", a);
+}
+
+void SeqPlayer::s1_loadPalette() {
+ uint8 colNum = *_seqData++;
+ uint32 fileSize;
+ uint8 *srcData;
+ srcData = _res->fileData(_vm->seqCOLTable()[colNum], &fileSize);
+ memcpy(_screen->_currentPalette, srcData, fileSize);
+ delete[] srcData;
+}
+
+void SeqPlayer::s1_loadBitmap() {
+ uint8 cpsNum = *_seqData++;
+ _vm->loadBitmap(_vm->seqCPSTable()[cpsNum], 3, 3, 0);
+}
+
+void SeqPlayer::s1_fadeToBlack() {
+ _screen->fadeToBlack();
+}
+
+void SeqPlayer::s1_printText() {
+ static const uint8 colorMap[] = { 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0 };
+ uint8 txt = *_seqData++;
+ _screen->fillRect(0, 180, 319, 195, 12);
+ _screen->setTextColorMap(colorMap);
+ if (!_seqDisplayTextFlag) {
+ const char *str = _vm->seqTextsTable()[txt];
+ int x = (Screen::SCREEN_W - _screen->getTextWidth(str)) / 2;
+ _screen->printText(str, x, 180, 0xF, 0xC);
+ } else {
+ _seqDisplayedTextTimer = _system->getMillis() + 1000 / ((_vm->features() & GF_FRENCH) ? 120 : 60);
+ _seqDisplayedText = txt;
+ _seqDisplayedChar = 0;
+ const char *str = _vm->seqTextsTable()[_seqDisplayedText];
+ _seqDisplayedTextX = (Screen::SCREEN_W - _screen->getTextWidth(str)) / 2;
+ }
+}
+
+void SeqPlayer::s1_printTalkText() {
+ uint8 txt = *_seqData++;
+ int x = READ_LE_UINT16(_seqData); _seqData += 2;
+ int y = *_seqData++;
+ uint8 fillColor = *_seqData++;
+ int b;
+ if (_seqTalkTextPrinted && !_seqTalkTextRestored) {
+ if (_seqWsaCurDecodePage != 0 && !_specialBuffer) {
+ b = 2;
+ } else {
+ b = 0;
+ }
+ _vm->text()->restoreTalkTextMessageBkgd(2, b);
+ }
+ _seqTalkTextPrinted = true;
+ _seqTalkTextRestored = false;
+ if (_seqWsaCurDecodePage != 0 && !_specialBuffer) {
+ b = 2;
+ } else {
+ b = 0;
+ }
+ _vm->text()->printTalkTextMessage(_vm->seqTextsTable()[txt], x, y, fillColor, b, 2);
+}
+
+void SeqPlayer::s1_restoreTalkText() {
+ if (_seqTalkTextPrinted && !_seqTalkTextRestored) {
+ int b;
+ if (_seqWsaCurDecodePage != 0 && !_specialBuffer) {
+ b = 2;
+ } else {
+ b = 0;
+ }
+ _vm->text()->restoreTalkTextMessageBkgd(2, b);
+ _seqTalkTextRestored = true;
+ }
+}
+
+void SeqPlayer::s1_clearCurrentScreen() {
+ _screen->fillRect(10, 180, 319, 196, 0xC);
+}
+
+void SeqPlayer::s1_break() {
+ // Do nothing
+}
+
+void SeqPlayer::s1_fadeFromBlack() {
+ _screen->fadeFromBlack();
+}
+
+void SeqPlayer::s1_copyRegion() {
+ uint8 srcPage = *_seqData++;
+ uint8 dstPage = *_seqData++;
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, srcPage, dstPage);
+}
+
+void SeqPlayer::s1_copyRegionSpecial() {
+ static const uint8 colorMap[] = { 0, 0, 0, 0, 0, 12, 12, 0, 0, 0, 0, 0 };
+ const char *copyStr = 0;
+ if (_vm->features() & GF_FLOPPY || _vm->features() & GF_DEMO) {
+ copyStr = "Copyright (c) 1992 Westwood Studios";
+ } else if (_vm->features() & GF_TALKIE) {
+ copyStr = "Copyright (c) 1992,1993 Westwood Studios";
+ }
+
+ uint8 so = *_seqData++;
+ switch (so) {
+ case 0:
+ _screen->copyRegion(0, 0, 0, 47, 320, 77, 2, 0);
+ break;
+ case 1:
+ _screen->copyRegion(0, 0, 0, 47, 320, 56, 2, 0);
+ break;
+ case 2:
+ _screen->copyRegion(107, 72, 107, 72, 43, 87, 2, 0);
+ _screen->copyRegion(130, 159, 130, 159, 35, 17, 2, 0);
+ _screen->copyRegion(165, 105, 165, 105, 32, 9, 2, 0);
+ _screen->copyRegion(206, 83, 206, 83, 94, 93, 2, 0);
+ break;
+ case 3:
+ _screen->copyRegion(152, 56, 152, 56, 48, 48, 2, 0);
+ break;
+ case 4: {
+ _screen->_charWidth = -2;
+ const int x = (Screen::SCREEN_W - _screen->getTextWidth(copyStr)) / 2;
+ const int y = 179;
+ _screen->setTextColorMap(colorMap);
+ _screen->printText(copyStr, x + 1, y + 1, 0xB, 0xC);
+ _screen->printText(copyStr, x, y, 0xF, 0xC);
+ } break;
+ case 5:
+ _screen->_curPage = 2;
+ break;
+ default:
+ error("Invalid subopcode %d for s1_copyRegionSpecial", so);
+ break;
+ }
+}
+
+void SeqPlayer::s1_fillRect() {
+ int x1 = READ_LE_UINT16(_seqData); _seqData += 2;
+ int y1 = *_seqData++;
+ int x2 = READ_LE_UINT16(_seqData); _seqData += 2;
+ int y2 = *_seqData++;
+ uint8 color = *_seqData++;
+ uint8 page = *_seqData++;
+ _screen->fillRect(x1, y1, x2, y2, color, page);
+}
+
+void SeqPlayer::s1_playEffect() {
+ uint8 track = *_seqData++;
+ _vm->delay(3 * _vm->tickLength());
+ _sound->playSoundEffect(track);
+}
+
+void SeqPlayer::s1_playTrack() {
+ uint8 msg = *_seqData++;
+/*
+ // we do not have audio cd support for now
+ if (_vm->features() & GF_AUDIOCD) {
+ switch (msg) {
+ case 0:
+ // nothing to do here...
+ break;
+ case 1:
+ _sound->beginFadeOut();
+ break;
+ case 56:
+ _vm->snd_playTheme(KyraEngine::MUSIC_INTRO, 3);
+ break;
+ case 57:
+ _vm->snd_playTheme(KyraEngine::MUSIC_INTRO, 4);
+ break;
+ case 58:
+ _vm->snd_playTheme(KyraEngine::MUSIC_INTRO, 5);
+ break;
+ default:
+ warning("Unknown seq. message: %.02d", msg);
+ break;
+ }
+ } else {*/
+ if (msg == 0) {
+ // nothing to do here...
+ } else if (msg == 1) {
+ _sound->beginFadeOut();
+ } else {
+ _sound->playTrack(msg);
+ }
+// }
+}
+
+void SeqPlayer::s1_allocTempBuffer() {
+ if (_vm->features() & GF_DEMO) {
+ _seqQuitFlag = true;
+ } else {
+ if (!_specialBuffer && !_copyViewOffs) {
+ _specialBuffer = new uint8[40960];
+ assert(_specialBuffer);
+ int page = _screen->_curPage;
+ _screen->_curPage = 0;
+ _screen->copyCurPageBlock(0, 0, 320, 128, _specialBuffer);
+ _screen->_curPage = page;
+ }
+ }
+}
+
+void SeqPlayer::s1_textDisplayEnable() {
+ _seqDisplayTextFlag = true;
+}
+
+void SeqPlayer::s1_textDisplayDisable() {
+ _seqDisplayTextFlag = false;
+}
+
+void SeqPlayer::s1_endOfScript() {
+ _seqQuitFlag = true;
+}
+
+void SeqPlayer::s1_loadIntroVRM() {
+ _res->loadPakFile("INTRO.VRM");
+}
+
+void SeqPlayer::s1_playVocFile() {
+ _vm->snd_voiceWaitForFinish(false);
+ uint8 a = *_seqData++;
+ _vm->snd_playVoiceFile(a);
+}
+
+void SeqPlayer::s1_miscUnk3() {
+ warning("STUB: s1_miscUnk3");
+}
+
+void SeqPlayer::s1_prefetchVocFile() {
+ *_seqData++;
+ // we do not have to prefetch the vocfiles on modern systems
+}
+
+bool SeqPlayer::playSequence(const uint8 *seqData, bool skipSeq) {
+ debug(9, "SeqPlayer::seq_playSequence(0x%X, %d)", seqData, skipSeq);
+ assert(seqData);
+
+ static SeqEntry floppySeqProcs[] = {
+ // 0x00
+ SEQOP(3, s1_wsaOpen),
+ SEQOP(2, s1_wsaClose),
+ SEQOP(6, s1_wsaPlayFrame),
+ SEQOP(2, s1_wsaPlayNextFrame),
+ // 0x04
+ SEQOP(2, s1_wsaPlayPrevFrame),
+ SEQOP(5, s1_drawShape),
+ SEQOP(3, s1_waitTicks),
+ SEQOP(3, s1_copyWaitTicks),
+ // 0x08
+ SEQOP(1, s1_shuffleScreen),
+ SEQOP(1, s1_copyView),
+ SEQOP(2, s1_loopInit),
+ SEQOP(4, s1_loopInc),
+ // 0x0C
+ SEQOP(2, s1_loadPalette),
+ SEQOP(2, s1_loadBitmap),
+ SEQOP(1, s1_fadeToBlack),
+ SEQOP(2, s1_printText),
+ // 0x10
+ SEQOP(6, s1_printTalkText),
+ SEQOP(1, s1_restoreTalkText),
+ SEQOP(1, s1_clearCurrentScreen),
+ SEQOP(1, s1_break),
+ // 0x14
+ SEQOP(1, s1_fadeFromBlack),
+ SEQOP(3, s1_copyRegion),
+ SEQOP(2, s1_copyRegionSpecial),
+ SEQOP(9, s1_fillRect),
+ // 0x18
+ SEQOP(2, s1_playEffect),
+ SEQOP(2, s1_playTrack),
+ SEQOP(1, s1_allocTempBuffer),
+ SEQOP(1, s1_textDisplayEnable),
+ // 0x1C
+ SEQOP(1, s1_textDisplayDisable),
+ SEQOP(1, s1_endOfScript)
+ };
+
+ static SeqEntry cdromSeqProcs[] = {
+ // 0x00
+ SEQOP(3, s1_wsaOpen),
+ SEQOP(2, s1_wsaClose),
+ SEQOP(6, s1_wsaPlayFrame),
+ SEQOP(2, s1_wsaPlayNextFrame),
+ // 0x04
+ SEQOP(2, s1_wsaPlayPrevFrame),
+ SEQOP(5, s1_drawShape),
+ SEQOP(3, s1_waitTicks),
+ SEQOP(3, s1_waitTicks),
+ // 0x08
+ SEQOP(3, s1_copyWaitTicks),
+ SEQOP(1, s1_shuffleScreen),
+ SEQOP(1, s1_copyView),
+ SEQOP(2, s1_loopInit),
+ // 0x0C
+ SEQOP(4, s1_loopInc),
+ SEQOP(4, s1_loopInc),
+ SEQOP(2, s1_skip),
+ SEQOP(2, s1_loadPalette),
+ // 0x10
+ SEQOP(2, s1_loadBitmap),
+ SEQOP(1, s1_fadeToBlack),
+ SEQOP(2, s1_printText),
+ SEQOP(6, s1_printTalkText),
+ // 0x14
+ SEQOP(1, s1_restoreTalkText),
+ SEQOP(1, s1_clearCurrentScreen),
+ SEQOP(1, s1_break),
+ SEQOP(1, s1_fadeFromBlack),
+ // 0x18
+ SEQOP(3, s1_copyRegion),
+ SEQOP(2, s1_copyRegionSpecial),
+ SEQOP(9, s1_fillRect),
+ SEQOP(2, s1_playEffect),
+ // 0x1C
+ SEQOP(2, s1_playTrack),
+ SEQOP(1, s1_allocTempBuffer),
+ SEQOP(1, s1_textDisplayEnable),
+ SEQOP(1, s1_textDisplayDisable),
+ // 0x20
+ SEQOP(1, s1_endOfScript),
+ SEQOP(1, s1_loadIntroVRM),
+ SEQOP(2, s1_playVocFile),
+ SEQOP(1, s1_miscUnk3),
+ // 0x24
+ SEQOP(2, s1_prefetchVocFile)
+ };
+
+ const SeqEntry* commands;
+ int numCommands;
+
+ if (_vm->features() & GF_FLOPPY || _vm->features() & GF_DEMO) {
+ commands = floppySeqProcs;
+ numCommands = ARRAYSIZE(floppySeqProcs);
+ } else if (_vm->features() & GF_TALKIE) {
+ commands = cdromSeqProcs;
+ numCommands = ARRAYSIZE(cdromSeqProcs);
+ } else {
+ error("No commandlist found");
+ }
+
+ bool seqSkippedFlag = false;
+
+ _seqData = seqData;
+
+ _seqDisplayedTextTimer = 0xFFFFFFFF;
+ _seqDisplayTextFlag = false;
+ _seqDisplayedTextX = 0;
+ _seqDisplayedText = 0;
+ _seqDisplayedChar = 0;
+ _seqTalkTextRestored = false;
+ _seqTalkTextPrinted = false;
+
+ _seqQuitFlag = false;
+ _seqWsaCurDecodePage = 0;
+
+ for (int i = 0; i < 20; ++i) {
+ _seqLoopTable[i].ptr = 0;
+ _seqLoopTable[i].count = 0xFFFF;
+ }
+
+ memset(_seqMovies, 0, sizeof(_seqMovies));
+
+ _screen->_curPage = 0;
+ while (!_seqQuitFlag) {
+ if (skipSeq && _vm->seq_skipSequence()) {
+ while (1) {
+ uint8 code = *_seqData;
+ if (commands[code].proc == &SeqPlayer::s1_endOfScript || commands[code].proc == &SeqPlayer::s1_break) {
+ break;
+ }
+ _seqData += commands[code].len;
+ }
+ skipSeq = false;
+ seqSkippedFlag = true;
+ }
+ // used in Kallak writing intro
+ if (_seqDisplayTextFlag && _seqDisplayedTextTimer != 0xFFFFFFFF) {
+ if (_seqDisplayedTextTimer < _system->getMillis()) {
+ char charStr[2];
+ charStr[0] = _vm->seqTextsTable()[_seqDisplayedText][_seqDisplayedChar];
+ charStr[1] = '\0';
+ _screen->printText(charStr, _seqDisplayedTextX, 180, 0xF, 0xC);
+ _seqDisplayedTextX += _screen->getCharWidth(charStr[0]);
+ ++_seqDisplayedChar;
+ if (_vm->seqTextsTable()[_seqDisplayedText][_seqDisplayedChar] == '\0') {
+ _seqDisplayedTextTimer = 0xFFFFFFFF;
+ } else {
+ _seqDisplayedTextTimer = _system->getMillis() + 1000 / ((_vm->features() & GF_FRENCH) ? 120 : 60);
+ }
+ }
+ }
+
+ uint8 seqCode = *_seqData++;
+ if (seqCode < numCommands) {
+ SeqProc currentProc = commands[seqCode].proc;
+ debug(5, "seqCode = %d (%s)", seqCode, commands[seqCode].desc);
+ (this->*currentProc)();
+ } else {
+ error("Invalid sequence opcode %d", seqCode);
+ }
+
+ _screen->updateScreen();
+ }
+ delete [] _specialBuffer;
+ _specialBuffer = 0;
+ return seqSkippedFlag;
+}
+
+
+} // End of namespace Kyra
diff --git a/engines/kyra/seqplayer.h b/engines/kyra/seqplayer.h
new file mode 100644
index 0000000000..ad747238c5
--- /dev/null
+++ b/engines/kyra/seqplayer.h
@@ -0,0 +1,123 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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$
+ *
+ */
+
+#ifndef KYRASEQPLAYER_H
+#define KYRASEQPLAYER_H
+
+namespace Kyra {
+
+class SeqPlayer {
+public:
+ SeqPlayer(KyraEngine* vm, OSystem* system);
+ ~SeqPlayer();
+
+ void setCopyViewOffs(bool offs) {
+ _copyViewOffs = offs;
+ }
+
+ void makeHandShapes();
+ void freeHandShapes();
+
+ bool playSequence(const uint8 *seqData, bool skipSeq);
+
+ uint8 *setPanPages(int pageNum, int shape);
+
+protected:
+ KyraEngine *_vm;
+ OSystem *_system;
+ Screen *_screen;
+ Sound *_sound;
+ Resource *_res;
+
+ uint8 *_handShapes[3];
+ bool _copyViewOffs;
+
+ typedef void (SeqPlayer::*SeqProc)();
+ struct SeqEntry {
+ uint8 len;
+ SeqProc proc;
+ const char* desc;
+ };
+
+ // the sequence procs
+ void s1_wsaOpen();
+ void s1_wsaClose();
+ void s1_wsaPlayFrame();
+ void s1_wsaPlayNextFrame();
+ void s1_wsaPlayPrevFrame();
+ void s1_drawShape();
+ void s1_waitTicks();
+ void s1_copyWaitTicks();
+ void s1_shuffleScreen();
+ void s1_copyView();
+ void s1_loopInit();
+ void s1_loopInc();
+ void s1_skip();
+ void s1_loadPalette();
+ void s1_loadBitmap();
+ void s1_fadeToBlack();
+ void s1_printText();
+ void s1_printTalkText();
+ void s1_restoreTalkText();
+ void s1_clearCurrentScreen();
+ void s1_break();
+ void s1_fadeFromBlack();
+ void s1_copyRegion();
+ void s1_copyRegionSpecial();
+ void s1_fillRect();
+ void s1_playEffect();
+ void s1_playTrack();
+ void s1_allocTempBuffer();
+ void s1_textDisplayEnable();
+ void s1_textDisplayDisable();
+ void s1_endOfScript();
+ void s1_loadIntroVRM();
+ void s1_playVocFile();
+ void s1_miscUnk3();
+ void s1_prefetchVocFile();
+
+ struct SeqMovie {
+ Movie *movie;
+ int32 page;
+ int16 frame;
+ int16 numFrames;
+ Common::Point pos;
+ };
+
+ const uint8 *_seqData;
+ uint8 *_specialBuffer;
+ SeqMovie _seqMovies[12];
+ SeqLoop _seqLoopTable[20];
+ uint16 _seqWsaCurDecodePage;
+ uint32 _seqDisplayedTextTimer;
+ bool _seqDisplayTextFlag;
+ uint8 _seqDisplayedText;
+ uint8 _seqDisplayedChar;
+ uint16 _seqDisplayedTextX;
+ bool _seqTalkTextPrinted;
+ bool _seqTalkTextRestored;
+ bool _seqQuitFlag;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/sequences_v1.cpp b/engines/kyra/sequences_v1.cpp
new file mode 100644
index 0000000000..8b60196763
--- /dev/null
+++ b/engines/kyra/sequences_v1.cpp
@@ -0,0 +1,1630 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-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 "kyra/kyra.h"
+#include "kyra/seqplayer.h"
+#include "kyra/screen.h"
+#include "kyra/resource.h"
+#include "kyra/sound.h"
+#include "kyra/sprites.h"
+#include "kyra/wsamovie.h"
+#include "kyra/animator.h"
+#include "kyra/text.h"
+
+#include "common/system.h"
+#include "common/savefile.h"
+
+namespace Kyra {
+
+void KyraEngine::seq_demo() {
+ debug(9, "KyraEngine::seq_demo()");
+
+ snd_playTheme(MUSIC_INTRO, 2);
+
+ loadBitmap("START.CPS", 7, 7, _screen->_currentPalette);
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 6, 0);
+ _system->copyRectToScreen(_screen->getPagePtr(0), 320, 0, 0, 320, 200);
+ _screen->fadeFromBlack();
+ delay(60 * _tickLength);
+ _screen->fadeToBlack();
+
+ _screen->clearPage(0);
+ loadBitmap("TOP.CPS", 7, 7, NULL);
+ loadBitmap("BOTTOM.CPS", 5, 5, _screen->_currentPalette);
+ _screen->copyRegion(0, 91, 0, 8, 320, 103, 6, 0);
+ _screen->copyRegion(0, 0, 0, 111, 320, 64, 6, 0);
+ _system->copyRectToScreen(_screen->getPagePtr(0), 320, 0, 0, 320, 200);
+ _screen->fadeFromBlack();
+
+ _seq->playSequence(_seq_WestwoodLogo, true);
+ delay(60 * _tickLength);
+ _seq->playSequence(_seq_KyrandiaLogo, true);
+
+ _screen->fadeToBlack();
+ _screen->clearPage(2);
+ _screen->clearPage(0);
+
+ _seq->playSequence(_seq_Demo1, true);
+
+ _screen->clearPage(0);
+ _seq->playSequence(_seq_Demo2, true);
+
+ _screen->clearPage(0);
+ _seq->playSequence(_seq_Demo3, true);
+
+ _screen->clearPage(0);
+ _seq->playSequence(_seq_Demo4, true);
+
+ _screen->clearPage(0);
+ loadBitmap("FINAL.CPS", 7, 7, _screen->_currentPalette);
+ _screen->_curPage = 0;
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 6, 0);
+ _system->copyRectToScreen(_screen->getPagePtr(0), 320, 0, 0, 320, 200);
+ _screen->fadeFromBlack();
+ delay(60 * _tickLength);
+ _screen->fadeToBlack();
+ _sound->stopMusic();
+}
+
+void KyraEngine::seq_intro() {
+ debug(9, "KyraEngine::seq_intro()");
+
+ if (_features & GF_TALKIE) {
+ _res->loadPakFile("INTRO.VRM");
+ }
+
+ static const IntroProc introProcTable[] = {
+ &KyraEngine::seq_introLogos,
+ &KyraEngine::seq_introStory,
+ &KyraEngine::seq_introMalcolmTree,
+ &KyraEngine::seq_introKallakWriting,
+ &KyraEngine::seq_introKallakMalcolm
+ };
+
+ Common::InSaveFile *in;
+ if ((in = _saveFileMan->openForLoading(getSavegameFilename(0)))) {
+ delete in;
+ _skipIntroFlag = true;
+ } else
+ _skipIntroFlag = false;
+
+ _seq->setCopyViewOffs(true);
+ _screen->setFont(Screen::FID_8_FNT);
+ snd_playTheme(MUSIC_INTRO, 2);
+ snd_setSoundEffectFile(MUSIC_INTRO);
+ _text->setTalkCoords(144);
+ for (int i = 0; i < ARRAYSIZE(introProcTable) && !seq_skipSequence(); ++i) {
+ (this->*introProcTable[i])();
+ }
+ _text->setTalkCoords(136);
+ delay(30 * _tickLength);
+ _seq->setCopyViewOffs(false);
+ _sound->stopMusic();
+ if (_features & GF_TALKIE) {
+ _res->unloadPakFile("INTRO.VRM");
+ }
+ res_unloadResources(RES_INTRO | RES_OUTRO);
+}
+
+void KyraEngine::seq_introLogos() {
+ debug(9, "KyraEngine::seq_introLogos()");
+ _screen->clearPage(0);
+ loadBitmap("TOP.CPS", 7, 7, NULL);
+ loadBitmap("BOTTOM.CPS", 5, 5, _screen->_currentPalette);
+ _screen->_curPage = 0;
+ _screen->copyRegion(0, 91, 0, 8, 320, 103, 6, 0);
+ _screen->copyRegion(0, 0, 0, 111, 320, 64, 6, 0);
+ _system->copyRectToScreen(_screen->getPagePtr(0), 320, 0, 0, 320, 200);
+ _screen->fadeFromBlack();
+
+ if (_seq->playSequence(_seq_WestwoodLogo, _skipFlag)) {
+ _screen->fadeToBlack();
+ _screen->clearPage(0);
+ return;
+ }
+ delay(60 * _tickLength);
+ if (_seq->playSequence(_seq_KyrandiaLogo, _skipFlag) && !seq_skipSequence()) {
+ _screen->fadeToBlack();
+ _screen->clearPage(0);
+ return;
+ }
+ _screen->fillRect(0, 179, 319, 199, 0);
+
+ int y1 = 8;
+ int h1 = 175;
+ int y2 = 176;
+ int h2 = 0;
+ int32 start, now;
+ int wait;
+ _screen->copyRegion(0, 91, 0, 8, 320, 103, 6, 2);
+ _screen->copyRegion(0, 0, 0, 111, 320, 64, 6, 2);
+ do {
+ start = (int32)_system->getMillis();
+ if (h1 > 0) {
+ _screen->copyRegion(0, y1, 0, 8, 320, h1, 2, 0);
+ }
+ ++y1;
+ --h1;
+ if (h2 > 0) {
+ _screen->copyRegion(0, 64, 0, y2, 320, h2, 4, 0);
+ }
+ --y2;
+ ++h2;
+ _screen->updateScreen();
+ now = (int32)_system->getMillis();
+ wait = _tickLength - (now - start);
+ if (wait > 0) {
+ delay(wait);
+ }
+ } while (y2 >= 64);
+
+ _seq->playSequence(_seq_Forest, true);
+}
+
+void KyraEngine::seq_introStory() {
+ debug(9, "KyraEngine::seq_introStory()");
+ _screen->clearPage(3);
+ _screen->clearPage(0);
+ if (_features & GF_TALKIE) {
+ return;
+ } else if (_features & GF_ENGLISH) {
+ loadBitmap("TEXT.CPS", 3, 3, 0);
+ } else if (_features & GF_GERMAN) {
+ loadBitmap("TEXT_GER.CPS", 3, 3, 0);
+ } else if (_features & GF_FRENCH) {
+ loadBitmap("TEXT_FRE.CPS", 3, 3, 0);
+ } else if (_features & GF_SPANISH) {
+ loadBitmap("TEXT_SPA.CPS", 3, 3, 0);
+ } else {
+ warning("no story graphics file found");
+ }
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 3, 0);
+ _screen->updateScreen();
+ debug("skipFlag %i, %i", _skipFlag, _tickLength);
+ delay(360 * _tickLength);
+}
+
+void KyraEngine::seq_introMalcolmTree() {
+ debug(9, "KyraEngine::seq_introMalcolmTree()");
+ _screen->_curPage = 0;
+ _screen->clearPage(3);
+ _seq->playSequence(_seq_MalcolmTree, true);
+}
+
+void KyraEngine::seq_introKallakWriting() {
+ debug(9, "KyraEngine::seq_introKallakWriting()");
+ _seq->makeHandShapes();
+ _screen->setAnimBlockPtr(5060);
+ _screen->_charWidth = -2;
+ _screen->clearPage(3);
+ _seq->playSequence(_seq_KallakWriting, true);
+}
+
+void KyraEngine::seq_introKallakMalcolm() {
+ debug(9, "KyraEngine::seq_introKallakMalcolm()");
+ _screen->clearPage(3);
+ _seq->playSequence(_seq_KallakMalcolm, true);
+}
+
+void KyraEngine::seq_createAmuletJewel(int jewel, int page, int noSound, int drawOnly) {
+ debug(9, "seq_createAmuletJewel(%d, %d, %d, %d)", jewel, page, noSound, drawOnly);
+ static const uint16 specialJewelTable[] = {
+ 0x167, 0x162, 0x15D, 0x158, 0x153, 0xFFFF
+ };
+ static const uint16 specialJewelTable1[] = {
+ 0x14F, 0x154, 0x159, 0x15E, 0x163, 0xFFFF
+ };
+ static const uint16 specialJewelTable2[] = {
+ 0x150, 0x155, 0x15A, 0x15F, 0x164, 0xFFFF
+ };
+ static const uint16 specialJewelTable3[] = {
+ 0x151, 0x156, 0x15B, 0x160, 0x165, 0xFFFF
+ };
+ static const uint16 specialJewelTable4[] = {
+ 0x152, 0x157, 0x15C, 0x161, 0x166, 0xFFFF
+ };
+ if (!noSound)
+ snd_playSoundEffect(0x5F);
+ _screen->hideMouse();
+ if (!drawOnly) {
+ for (int i = 0; specialJewelTable[i] != 0xFFFF; ++i) {
+ _screen->drawShape(page, _shapes[4+specialJewelTable[i]], _amuletX2[jewel], _amuletY2[jewel], 0, 0);
+ _screen->updateScreen();
+ delayWithTicks(3);
+ }
+
+ const uint16 *opcodes = 0;
+ switch (jewel - 1) {
+ case 0:
+ opcodes = specialJewelTable1;
+ break;
+
+ case 1:
+ opcodes = specialJewelTable2;
+ break;
+
+ case 2:
+ opcodes = specialJewelTable3;
+ break;
+
+ case 3:
+ opcodes = specialJewelTable4;
+ break;
+ }
+
+ if (opcodes) {
+ for (int i = 0; opcodes[i] != 0xFFFF; ++i) {
+ _screen->drawShape(page, _shapes[4+opcodes[i]], _amuletX2[jewel], _amuletY2[jewel], 0, 0);
+ _screen->updateScreen();
+ delayWithTicks(3);
+ }
+ }
+ }
+ _screen->drawShape(page, _shapes[327+jewel], _amuletX2[jewel], _amuletY2[jewel], 0, 0);
+ _screen->updateScreen();
+ _screen->showMouse();
+ setGameFlag(0x55+jewel);
+}
+
+void KyraEngine::seq_brandonHealing() {
+ debug(9, "seq_brandonHealing()");
+ if (!(_deathHandler & 8))
+ return;
+ if (_currentCharacter->sceneId == 210) {
+ if (_beadStateVar == 4 || _beadStateVar == 6)
+ return;
+ }
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ assert(_healingShapeTable);
+ setupShapes123(_healingShapeTable, 22, 0);
+ _animator->setBrandonAnimSeqSize(3, 48);
+ snd_playSoundEffect(0x53);
+ for (int i = 123; i <= 144; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+ for (int i = 125; i >= 123; --i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_brandonHealing2() {
+ debug(9, "seq_brandonHealing2()");
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ assert(_healingShape2Table);
+ setupShapes123(_healingShape2Table, 30, 0);
+ resetBrandonPoisonFlags();
+ _animator->setBrandonAnimSeqSize(3, 48);
+ snd_playSoundEffect(0x50);
+ for (int i = 123; i <= 152; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+ assert(_poisonGone);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2010);
+ }
+ characterSays(_poisonGone[0], 0, -2);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2011);
+ }
+ characterSays(_poisonGone[1], 0, -2);
+}
+
+void KyraEngine::seq_poisonDeathNow(int now) {
+ debug(9, "seq_poisonDeathNow(%d)", now);
+ if (!(_brandonStatusBit & 1))
+ return;
+ ++_poisonDeathCounter;
+ if (now)
+ _poisonDeathCounter = 2;
+ if (_poisonDeathCounter >= 2) {
+ snd_playWanderScoreViaMap(1, 1);
+ assert(_thePoison);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(7000);
+ }
+ characterSays(_thePoison[0], 0, -2);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(7001);
+ }
+ characterSays(_thePoison[1], 0, -2);
+ seq_poisonDeathNowAnim();
+ _deathHandler = 3;
+ } else {
+ assert(_thePoison);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(7002);
+ }
+ characterSays(_thePoison[2], 0, -2);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(7004);
+ }
+ characterSays(_thePoison[3], 0, -2);
+ }
+}
+
+void KyraEngine::seq_poisonDeathNowAnim() {
+ debug(9, "seq_poisonDeathNowAnim()");
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ assert(_posionDeathShapeTable);
+ setupShapes123(_posionDeathShapeTable, 20, 0);
+ _animator->setBrandonAnimSeqSize(8, 48);
+
+ _currentCharacter->currentAnimFrame = 124;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(30);
+
+ _currentCharacter->currentAnimFrame = 123;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(30);
+
+ for (int i = 125; i <= 139; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ delayWithTicks(60);
+
+ for (int i = 140; i <= 142; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ delayWithTicks(60);
+
+ _animator->resetBrandonAnimSeqSize();
+ freeShapes123();
+ _animator->restoreAllObjectBackgrounds();
+ _currentCharacter->x1 = _currentCharacter->x2 = -1;
+ _currentCharacter->y1 = _currentCharacter->y2 = -1;
+ _animator->preserveAllBackgrounds();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_playFluteAnimation() {
+ debug(9, "seq_playFluteAnimation()");
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ setupShapes123(_fluteAnimShapeTable, 36, 0);
+ _animator->setBrandonAnimSeqSize(3, 75);
+ for (int i = 123; i <= 130; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(2);
+ }
+
+ int delayTime = 0, soundType = 0;
+ if (queryGameFlag(0x85)) {
+ snd_playSoundEffect(0x63);
+ delayTime = 9;
+ soundType = 3;
+ } else if (!queryGameFlag(0x86)) {
+ snd_playSoundEffect(0x61);
+ delayTime = 2;
+ soundType = 1;
+ setGameFlag(0x86);
+ } else {
+ snd_playSoundEffect(0x62);
+ delayTime = 2;
+ soundType = 2;
+ }
+
+ for (int i = 131; i <= 158; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(delayTime);
+ }
+
+ for (int i = 126; i >= 123; --i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(delayTime);
+ }
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+
+ if (soundType == 1) {
+ assert(_fluteString);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(1000);
+ }
+ characterSays(_fluteString[0], 0, -2);
+ } else if (soundType == 2) {
+ assert(_fluteString);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(1001);
+ }
+ characterSays(_fluteString[1], 0, -2);
+ }
+}
+
+void KyraEngine::seq_winterScroll1() {
+ debug(9, "seq_winterScroll1()");
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ assert(_winterScrollTable);
+ assert(_winterScroll1Table);
+ assert(_winterScroll2Table);
+ setupShapes123(_winterScrollTable, 7, 0);
+ _animator->setBrandonAnimSeqSize(5, 66);
+
+ for (int i = 123; i <= 129; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ freeShapes123();
+ snd_playSoundEffect(0x20);
+
+ uint8 numFrames, midpoint;
+ if (_features & GF_TALKIE) {
+ numFrames = 18;
+ midpoint = 136;
+ } else {
+ numFrames = 35;
+ midpoint = 147;
+ }
+ setupShapes123(_winterScroll1Table, numFrames, 0);
+ for (int i = 123; i < midpoint; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ if (_currentCharacter->sceneId == 41 && !queryGameFlag(0xA2)) {
+ snd_playSoundEffect(0x20);
+ _sprites->_anims[0].play = false;
+ _animator->sprites()[0].active = 0;
+ _sprites->_anims[1].play = true;
+ _animator->sprites()[1].active = 1;
+ setGameFlag(0xA2);
+ }
+
+ for (int i = midpoint; i < 123 + numFrames; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ if (_currentCharacter->sceneId == 117 && !queryGameFlag(0xB3)) {
+ for (int i = 0; i <= 7; ++i) {
+ _sprites->_anims[i].play = false;
+ _animator->sprites()[i].active = 0;
+ }
+ uint8 tmpPal[768];
+ memcpy(tmpPal, _screen->_currentPalette, 768);
+ memcpy(&tmpPal[684], palTable2()[0], 60);
+ _screen->fadePalette(tmpPal, 72);
+ memcpy(&_screen->_currentPalette[684], palTable2()[0], 60);
+ _screen->setScreenPalette(_screen->_currentPalette);
+ setGameFlag(0xB3);
+ } else {
+ delayWithTicks(120);
+ }
+
+ freeShapes123();
+ setupShapes123(_winterScroll2Table, 4, 0);
+
+ for (int i = 123; i <= 126; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_winterScroll2() {
+ debug(9, "seq_winterScroll2()");
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ assert(_winterScrollTable);
+ setupShapes123(_winterScrollTable, 7, 0);
+ _animator->setBrandonAnimSeqSize(5, 66);
+
+ for (int i = 123; i <= 128; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ delayWithTicks(120);
+
+ for (int i = 127; i >= 123; --i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_makeBrandonInv() {
+ debug(9, "seq_makeBrandonInv()");
+ if (_deathHandler == 8)
+ return;
+
+ if (_currentCharacter->sceneId == 210) {
+ if (_beadStateVar == 4 || _beadStateVar == 6)
+ return;
+ }
+
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ _brandonStatusBit |= 0x20;
+ setTimerCountdown(18, 2700);
+ _brandonStatusBit |= 0x40;
+ snd_playSoundEffect(0x77);
+ _brandonInvFlag = 0;
+ while (_brandonInvFlag <= 0x100) {
+ _animator->animRefreshNPC(0);
+ delayWithTicks(10);
+ _brandonInvFlag += 0x10;
+ }
+ _brandonStatusBit &= 0xFFBF;
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_makeBrandonNormal() {
+ debug(9, "seq_makeBrandonNormal()");
+ _screen->hideMouse();
+ _brandonStatusBit |= 0x40;
+ snd_playSoundEffect(0x77);
+ _brandonInvFlag = 0x100;
+ while (_brandonInvFlag >= 0) {
+ _animator->animRefreshNPC(0);
+ delayWithTicks(10);
+ _brandonInvFlag -= 0x10;
+ }
+ _brandonInvFlag = 0;
+ _brandonStatusBit &= 0xFF9F;
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_makeBrandonNormal2() {
+ debug(9, "seq_makeBrandonNormal2()");
+ _screen->hideMouse();
+ assert(_brandonToWispTable);
+ setupShapes123(_brandonToWispTable, 26, 0);
+ _animator->setBrandonAnimSeqSize(5, 48);
+ _brandonStatusBit &= 0xFFFD;
+ snd_playSoundEffect(0x6C);
+ for (int i = 138; i >= 123; --i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+ _animator->setBrandonAnimSeqSize(4, 48);
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ if (_currentCharacter->sceneId >= 229 && _currentCharacter->sceneId <= 245) {
+ _screen->fadeSpecialPalette(31, 234, 13, 4);
+ } else if (_currentCharacter->sceneId >= 118 && _currentCharacter->sceneId <= 186) {
+ _screen->fadeSpecialPalette(14, 228, 15, 4);
+ }
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_makeBrandonWisp() {
+ debug(9, "seq_makeBrandonWisp()");
+ if (_deathHandler == 8)
+ return;
+
+ if (_currentCharacter->sceneId == 210) {
+ if (_beadStateVar == 4 || _beadStateVar == 6)
+ return;
+ }
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ assert(_brandonToWispTable);
+ setupShapes123(_brandonToWispTable, 26, 0);
+ _animator->setBrandonAnimSeqSize(5, 48);
+ snd_playSoundEffect(0x6C);
+ for (int i = 123; i <= 138; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+ _brandonStatusBit |= 2;
+ if (_currentCharacter->sceneId >= 109 && _currentCharacter->sceneId <= 198) {
+ setTimerCountdown(14, 18000);
+ } else {
+ setTimerCountdown(14, 7200);
+ }
+ _animator->_brandonDrawFrame = 113;
+ _brandonStatusBit0x02Flag = 1;
+ _currentCharacter->currentAnimFrame = 113;
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+ if (_currentCharacter->sceneId >= 229 && _currentCharacter->sceneId <= 245) {
+ _screen->fadeSpecialPalette(30, 234, 13, 4);
+ } else if (_currentCharacter->sceneId >= 118 && _currentCharacter->sceneId <= 186) {
+ _screen->fadeSpecialPalette(14, 228, 15, 4);
+ }
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_dispelMagicAnimation() {
+ debug(9, "seq_dispelMagicAnimation()");
+ if (_deathHandler == 8)
+ return;
+ if (_currentCharacter->sceneId == 210) {
+ if (_beadStateVar == 4 || _beadStateVar == 6)
+ return;
+ }
+ _screen->hideMouse();
+ if (_currentCharacter->sceneId == 210 && _currentCharacter->sceneId < 160)
+ _currentCharacter->facing = 3;
+ if (_malcolmFlag == 7 && _beadStateVar == 3) {
+ _beadStateVar = 6;
+ _unkEndSeqVar5 = 2;
+ _malcolmFlag = 10;
+ }
+ checkAmuletAnimFlags();
+ setGameFlag(0xEE);
+ assert(_magicAnimationTable);
+ setupShapes123(_magicAnimationTable, 5, 0);
+ _animator->setBrandonAnimSeqSize(8, 49);
+ snd_playSoundEffect(0x15);
+ for (int i = 123; i <= 127; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ delayWithTicks(120);
+
+ for (int i = 127; i >= 123; --i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(10);
+ }
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_fillFlaskWithWater(int item, int type) {
+ debug(9, "seq_fillFlaskWithWater(%d, %d)", item, type);
+ int newItem = -1;
+ static const uint8 flaskTable1[] = { 0x46, 0x48, 0x4A, 0x4C };
+ static const uint8 flaskTable2[] = { 0x47, 0x49, 0x4B, 0x4D };
+
+ if (item >= 60 && item <= 77) {
+ assert(_flaskFull);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(8006);
+ }
+ characterSays(_flaskFull[0], 0, -2);
+ } else if (item == 78) {
+ assert(type >= 0 && type < ARRAYSIZE(flaskTable1));
+ newItem = flaskTable1[type];
+ } else if (item == 79) {
+ assert(type >= 0 && type < ARRAYSIZE(flaskTable2));
+ newItem = flaskTable2[type];
+ }
+
+ if (newItem == -1)
+ return;
+
+ _screen->hideMouse();
+ setMouseItem(newItem);
+ _screen->showMouse();
+ _itemInHand = newItem;
+ assert(_fullFlask);
+ assert(type < _fullFlask_Size && type >= 0);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ static const uint16 voiceEntries[] = {
+ 0x1F40, 0x1F41, 0x1F42, 0x1F45
+ };
+ assert(type < ARRAYSIZE(voiceEntries));
+ snd_playVoiceFile(voiceEntries[type]);
+ }
+ characterSays(_fullFlask[type], 0, -2);
+}
+
+void KyraEngine::seq_playDrinkPotionAnim(int unk1, int unk2, int flags) {
+ debug(9, "KyraEngine::seq_playDrinkPotionAnim(%d, %d, %d)", unk1, unk2, flags);
+ // XXX
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ _currentCharacter->facing = 5;
+ _animator->animRefreshNPC(0);
+ assert(_drinkAnimationTable);
+ setupShapes123(_drinkAnimationTable, 9, flags);
+ _animator->setBrandonAnimSeqSize(5, 54);
+
+ for (int i = 123; i <= 131; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(5);
+ }
+ snd_playSoundEffect(0x34);
+ for (int i = 0; i < 2; ++i) {
+ _currentCharacter->currentAnimFrame = 130;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(7);
+ _currentCharacter->currentAnimFrame = 131;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(7);
+ }
+
+ if (unk2) {
+ // XXX
+ }
+
+ for (int i = 131; i >= 123; --i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(5);
+ }
+
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+}
+
+int KyraEngine::seq_playEnd() {
+ debug(9, "KyraEngine::seq_playEnd()");
+ if (_endSequenceSkipFlag) {
+ return 0;
+ }
+ if (_deathHandler == 8) {
+ return 0;
+ }
+ _screen->_curPage = 2;
+ if (_endSequenceNeedLoading) {
+ snd_playWanderScoreViaMap(50, 1);
+ setupPanPages();
+ _finalA = new WSAMovieV1(this);
+ assert(_finalA);
+ _finalA->open("finala.wsa", 1, 0);
+ _finalB = new WSAMovieV1(this);
+ assert(_finalB);
+ _finalB->open("finalb.wsa", 1, 0);
+ _finalC = new WSAMovieV1(this);
+ assert(_finalC);
+ _endSequenceNeedLoading = 0;
+ _finalC->open("finalc.wsa", 1, 0);
+ _screen->_curPage = 0;
+ _beadStateVar = 0;
+ _malcolmFlag = 0;
+ // wired stuff with _unkEndSeqVar2 which needs timer handling
+ _screen->copyRegion(312, 0, 312, 0, 8, 136, 0, 2);
+ }
+ if (handleMalcolmFlag()) {
+ _beadStateVar = 0;
+ _malcolmFlag = 12;
+ handleMalcolmFlag();
+ handleBeadState();
+ closeFinalWsa();
+ if (_deathHandler == 8) {
+ _screen->_curPage = 0;
+ checkAmuletAnimFlags();
+ seq_brandonToStone();
+ delay(60 * _tickLength);
+ return 1;
+ } else {
+ _endSequenceSkipFlag = 1;
+ if (_text->printed()) {
+ _text->restoreTalkTextMessageBkgd(2, 0);
+ }
+ _screen->_curPage = 0;
+ _screen->hideMouse();
+ _screen->fadeSpecialPalette(32, 228, 20, 60);
+ delay(60 * _tickLength);
+ loadBitmap("GEMHEAL.CPS", 3, 3, _screen->_currentPalette);
+ _screen->setScreenPalette(_screen->_currentPalette);
+ _screen->shuffleScreen(8, 8, 304, 128, 2, 0, 1, 0);
+ uint32 nextTime = _system->getMillis() + 120 * _tickLength;
+ _finalA = new WSAMovieV1(this);
+ assert(_finalA);
+ _finalA->open("finald.wsa", 1, 0);
+ _finalA->_x = _finalA->_y = 8;
+ _finalA->_drawPage = 0;
+ while (_system->getMillis() < nextTime) {}
+ snd_playSoundEffect(0x40);
+ for (int i = 0; i < 22; ++i) {
+ while (_system->getMillis() < nextTime) {}
+ if (i == 4) {
+ snd_playSoundEffect(0x3E);
+ } else if (i == 20) {
+ snd_playSoundEffect(0x0E);
+ }
+ nextTime = _system->getMillis() + 8 * _tickLength;
+ _finalA->displayFrame(i);
+ _screen->updateScreen();
+ }
+ delete _finalA;
+ _finalA = 0;
+ seq_playEnding();
+ return 1;
+ }
+ } else {
+ handleBeadState();
+ _screen->bitBlitRects();
+ _screen->updateScreen();
+ _screen->_curPage = 0;
+ }
+ return 0;
+}
+
+void KyraEngine::seq_brandonToStone() {
+ debug(9, "KyraEngine::seq_brandonToStone()");
+ _screen->hideMouse();
+ assert(_brandonStoneTable);
+ setupShapes123(_brandonStoneTable, 14, 0);
+ _animator->setBrandonAnimSeqSize(5, 51);
+ for (int i = 123; i <= 136; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+ _animator->resetBrandonAnimSeqSize();
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_playEnding() {
+ debug(9, "KyraEngine::seq_playEnding()");
+ _screen->hideMouse();
+ res_unloadResources(RES_INGAME);
+ res_loadResources(RES_OUTRO);
+ loadBitmap("REUNION.CPS", 3, 3, _screen->_currentPalette);
+ _screen->copyRegion(8, 8, 8, 8, 304, 128, 2, 0);
+ _screen->_curPage = 0;
+ // XXX
+ assert(_homeString);
+ drawSentenceCommand(_homeString[0], 179);
+ _screen->_curPage = 0;
+ _screen->fadeToBlack();
+ _seq->playSequence(_seq_Reunion, false);
+ _screen->fadeToBlack();
+ _screen->showMouse();
+ seq_playCredits();
+}
+
+void KyraEngine::seq_playCredits() {
+ debug(9, "KyraEngine::seq_playCredits()");
+ static const uint8 colorMap[] = { 0, 0, 0xC, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ _screen->hideMouse();
+ uint32 sz = 0;
+ if (_features & GF_FLOPPY) {
+ _screen->loadFont(Screen::FID_CRED6_FNT, _res->fileData("CREDIT6.FNT", &sz));
+ _screen->loadFont(Screen::FID_CRED8_FNT, _res->fileData("CREDIT8.FNT", &sz));
+ }
+ loadBitmap("CHALET.CPS", 2, 2, _screen->_currentPalette);
+ _screen->setScreenPalette(_screen->_currentPalette);
+ _screen->setCurPage(0);
+ _screen->clearCurPage();
+ _screen->copyRegion(8, 8, 8, 8, 304, 128, 2, 0);
+ _screen->setTextColorMap(colorMap);
+ _screen->_charWidth = -1;
+ snd_playWanderScoreViaMap(53, 1);
+ // delete
+ _screen->updateScreen();
+ // XXX
+ delay(120 * _tickLength); // wait until user presses escape normally
+ _screen->fadeToBlack();
+ _screen->clearCurPage();
+ _screen->showMouse();
+}
+
+bool KyraEngine::seq_skipSequence() const {
+ debug(9, "KyraEngine::seq_skipSequence()");
+ return _quitFlag || _abortIntroFlag;
+}
+
+int KyraEngine::handleMalcolmFlag() {
+ debug(9, "KyraEngine::handleMalcolmFlag()");
+ static uint16 frame = 0;
+ static uint32 timer1 = 0;
+ static uint32 timer2 = 0;
+
+ switch (_malcolmFlag) {
+ case 1:
+ frame = 0;
+ _malcolmFlag = 2;
+ timer2 = 0;
+ case 2:
+ if (_system->getMillis() >= timer2) {
+ _finalA->_x = 8;
+ _finalA->_y = 46;
+ _finalA->_drawPage = 0;
+ _finalA->displayFrame(frame);
+ _screen->updateScreen();
+ timer2 = _system->getMillis() + 8 * _tickLength;
+ ++frame;
+ if (frame > 13) {
+ _malcolmFlag = 3;
+ timer1 = _system->getMillis() + 180 * _tickLength;
+ }
+ }
+ break;
+
+ case 3:
+ if (_system->getMillis() < timer1) {
+ if (_system->getMillis() >= timer2) {
+ frame = _rnd.getRandomNumberRng(14, 17);
+ _finalA->_x = 8;
+ _finalA->_y = 46;
+ _finalA->_drawPage = 0;
+ _finalA->displayFrame(frame);
+ _screen->updateScreen();
+ timer2 = _system->getMillis() + 8 * _tickLength;
+ }
+ } else {
+ _malcolmFlag = 4;
+ frame = 18;
+ }
+ break;
+
+ case 4:
+ if (_system->getMillis() >= timer2) {
+ _finalA->_x = 8;
+ _finalA->_y = 46;
+ _finalA->_drawPage = 0;
+ _finalA->displayFrame(frame);
+ _screen->updateScreen();
+ timer2 = _system->getMillis() + 8 * _tickLength;
+ ++frame;
+ if (frame > 25) {
+ frame = 26;
+ _malcolmFlag = 5;
+ _beadStateVar = 1;
+ }
+ }
+ break;
+
+ case 5:
+ if (_system->getMillis() >= timer2) {
+ _finalA->_x = 8;
+ _finalA->_y = 46;
+ _finalA->_drawPage = 0;
+ _finalA->displayFrame(frame);
+ _screen->updateScreen();
+ timer2 = _system->getMillis() + 8 * _tickLength;
+ ++frame;
+ if (frame > 31) {
+ frame = 32;
+ _malcolmFlag = 6;
+ }
+ }
+ break;
+
+ case 6:
+ if (_unkEndSeqVar4) {
+ if (frame <= 33 && _system->getMillis() >= timer2) {
+ _finalA->_x = 8;
+ _finalA->_y = 46;
+ _finalA->_drawPage = 0;
+ _finalA->displayFrame(frame);
+ _screen->updateScreen();
+ timer2 = _system->getMillis() + 8 * _tickLength;
+ ++frame;
+ if (frame > 33) {
+ _malcolmFlag = 7;
+ frame = 32;
+ _unkEndSeqVar5 = 0;
+ }
+ }
+ }
+ break;
+
+ case 7:
+ if (_unkEndSeqVar5 == 1) {
+ _malcolmFlag = 8;
+ frame = 34;
+ } else if (_unkEndSeqVar5 == 2) {
+ _malcolmFlag = 3;
+ timer1 = _system->getMillis() + 180 * _tickLength;
+ }
+ break;
+
+ case 8:
+ if (_system->getMillis() >= timer2) {
+ _finalA->_x = 8;
+ _finalA->_y = 46;
+ _finalA->_drawPage = 0;
+ _finalA->displayFrame(frame);
+ _screen->updateScreen();
+ timer2 = _system->getMillis() + 8 * _tickLength;
+ ++frame;
+ if (frame > 37) {
+ _malcolmFlag = 0;
+ _deathHandler = 8;
+ return 1;
+ }
+ }
+ break;
+
+ case 9:
+ snd_playSoundEffect(12);
+ snd_playSoundEffect(12);
+ _finalC->_x = 16;
+ _finalC->_y = 50;
+ _finalC->_drawPage = 0;
+ for (int i = 0; i < 18; ++i) {
+ timer2 = _system->getMillis() + 4 * _tickLength;
+ _finalC->displayFrame(i);
+ _screen->updateScreen();
+ while (_system->getMillis() < timer2) {}
+ }
+ snd_playWanderScoreViaMap(51, 1);
+ delay(60*_tickLength);
+ _malcolmFlag = 0;
+ return 1;
+ break;
+
+ case 10:
+ if (!_beadStateVar) {
+ handleBeadState();
+ _screen->bitBlitRects();
+ assert(_veryClever);
+ _text->printTalkTextMessage(_veryClever[0], 60, 31, 5, 0, 2);
+ timer2 = _system->getMillis() + 180 * _tickLength;
+ _malcolmFlag = 11;
+ }
+ break;
+
+ case 11:
+ if (_system->getMillis() >= timer2) {
+ _text->restoreTalkTextMessageBkgd(2, 0);
+ _malcolmFlag = 3;
+ timer1 = _system->getMillis() + 180 * _tickLength;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int KyraEngine::handleBeadState() {
+ debug(9, "KyraEngine::handleBeadState()");
+ static uint32 timer1 = 0;
+ static uint32 timer2 = 0;
+ static BeadState beadState1 = { -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static BeadState beadState2 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ static const int table1[] = {
+ -1, -2, -4, -5, -6, -7, -6, -5,
+ -4, -2, -1, 0, 1, 2, 4, 5,
+ 6, 7, 6, 5, 4, 2, 1, 0, 0
+ };
+ static const int table2[] = {
+ 0, 0, 1, 1, 2, 2, 3, 3,
+ 4, 4, 5, 5, 5, 5, 4, 4,
+ 3, 3, 2, 2, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ switch (_beadStateVar) {
+ case 0:
+ if (beadState1.x != -1 && _endSequenceBackUpRect) {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ } else {
+ beadState1.x = -1;
+ beadState1.tableIndex = 0;
+ timer1 = 0;
+ timer2 = 0;
+ _lastDisplayedPanPage = 0;
+ return 1;
+ }
+
+ case 1:
+ if (beadState1.x != -1) {
+ if (_endSequenceBackUpRect) {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ }
+ beadState1.x = -1;
+ beadState1.tableIndex = 0;
+ }
+ _beadStateVar = 2;
+ break;
+
+ case 2:
+ if (_system->getMillis() >= timer1) {
+ int x = 0, y = 0;
+ timer1 = _system->getMillis() + 4 * _tickLength;
+ if (beadState1.x == -1) {
+ assert(_panPagesTable);
+ beadState1.width2 = _animator->fetchAnimWidth(_panPagesTable[19], 256);
+ beadState1.width = ((beadState1.width2 + 7) >> 3) + 1;
+ beadState1.height = _animator->fetchAnimHeight(_panPagesTable[19], 256);
+ if (!_endSequenceBackUpRect) {
+ _endSequenceBackUpRect = new uint8[(beadState1.width * beadState1.height) << 3];
+ assert(_endSequenceBackUpRect);
+ memset(_endSequenceBackUpRect, 0, ((beadState1.width * beadState1.height) << 3) * sizeof(uint8));
+ }
+ x = beadState1.x = 60;
+ y = beadState1.y = 40;
+ initBeadState(x, y, x, 25, 8, &beadState2);
+ } else {
+ if (processBead(beadState1.x, beadState1.y, x, y, &beadState2)) {
+ _beadStateVar = 3;
+ timer2 = _system->getMillis() + 240 * _tickLength;
+ _unkEndSeqVar4 = 0;
+ beadState1.dstX = beadState1.x;
+ beadState1.dstY = beadState1.y;
+ return 0;
+ } else {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ beadState1.x = x;
+ beadState1.y = y;
+ }
+ }
+ _screen->copyCurPageBlock(x >> 3, y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->drawShape(2, _panPagesTable[_lastDisplayedPanPage++], x, y, 0, 0);
+ if (_lastDisplayedPanPage > 17)
+ _lastDisplayedPanPage = 0;
+ _screen->addBitBlitRect(x, y, beadState1.width2, beadState1.height);
+ }
+ break;
+
+ case 3:
+ if (_system->getMillis() >= timer1) {
+ timer1 = _system->getMillis() + 4 * _tickLength;
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ beadState1.x = beadState1.dstX + table1[beadState1.tableIndex];
+ beadState1.y = beadState1.dstY + table2[beadState1.tableIndex];
+ _screen->copyCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->drawShape(2, _panPagesTable[_lastDisplayedPanPage++], beadState1.x, beadState1.y, 0, 0);
+ if (_lastDisplayedPanPage >= 17) {
+ _lastDisplayedPanPage = 0;
+ }
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ ++beadState1.tableIndex;
+ if (beadState1.tableIndex > 24) {
+ beadState1.tableIndex = 0;
+ _unkEndSeqVar4 = 1;
+ }
+ if (_system->getMillis() > timer2 && _malcolmFlag == 7 && !_unkAmuletVar && !_text->printed()) {
+ snd_playSoundEffect(0x0B);
+ if (_currentCharacter->x1 > 233 && _currentCharacter->x1 < 305 && _currentCharacter->y1 > 85 && _currentCharacter->y1 < 105 &&
+ (_brandonStatusBit & 0x20)) {
+ beadState1.unk8 = 290;
+ beadState1.unk9 = 40;
+ _beadStateVar = 5;
+ } else {
+ _beadStateVar = 4;
+ beadState1.unk8 = _currentCharacter->x1 - 4;
+ beadState1.unk9 = _currentCharacter->y1 - 30;
+ }
+
+ if (_text->printed()) {
+ _text->restoreTalkTextMessageBkgd(2, 0);
+ }
+ initBeadState(beadState1.x, beadState1.y, beadState1.unk8, beadState1.unk9, 6, &beadState2);
+ _lastDisplayedPanPage = 18;
+ }
+ }
+ break;
+
+ case 4:
+ if (_system->getMillis() >= timer1) {
+ int x = 0, y = 0;
+ timer1 = _system->getMillis();
+ if (processBead(beadState1.x, beadState1.y, x, y, &beadState2)) {
+ if (_brandonStatusBit & 20) {
+ _unkEndSeqVar5 = 2;
+ _beadStateVar = 6;
+ } else {
+ snd_playWanderScoreViaMap(52, 1);
+ snd_playSoundEffect(0x0C);
+ _unkEndSeqVar5 = 1;
+ _beadStateVar = 0;
+ }
+ } else {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ beadState1.x = x;
+ beadState1.y = y;
+ _screen->copyCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->drawShape(2, _panPagesTable[_lastDisplayedPanPage++], x, y, 0, 0);
+ if (_lastDisplayedPanPage > 17) {
+ _lastDisplayedPanPage = 0;
+ }
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ }
+ }
+ break;
+
+ case 5:
+ if (_system->getMillis() >= timer1) {
+ timer1 = _system->getMillis();
+ int x = 0, y = 0;
+ if (processBead(beadState1.x, beadState1.y, x, y, &beadState2)) {
+ if (beadState1.dstX == 290) {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ uint32 nextRun = 0;
+ _finalB->_x = 224;
+ _finalB->_y = 8;
+ _finalB->_drawPage = 0;
+ for (int i = 0; i < 8; ++i) {
+ nextRun = _system->getMillis() + _tickLength;
+ _finalB->displayFrame(i);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextRun) {}
+ }
+ snd_playSoundEffect(0x0D);
+ for (int i = 7; i >= 0; --i) {
+ nextRun = _system->getMillis() + _tickLength;
+ _finalB->displayFrame(i);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextRun) {}
+ }
+ initBeadState(beadState1.x, beadState1.y, 63, 60, 6, &beadState2);
+ } else {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ beadState1.x = -1;
+ beadState1.tableIndex = 0;
+ _beadStateVar = 0;
+ _malcolmFlag = 9;
+ }
+ } else {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ beadState1.x = x;
+ beadState1.y = y;
+ _screen->copyCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->drawShape(2, _panPagesTable[_lastDisplayedPanPage++], x, y, 0, 0);
+ if (_lastDisplayedPanPage > 17) {
+ _lastDisplayedPanPage = 0;
+ }
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ }
+ }
+ break;
+
+ case 6:
+ _screen->drawShape(2, _panPagesTable[19], beadState1.x, beadState1.y, 0, 0);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ _beadStateVar = 0;
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+void KyraEngine::initBeadState(int x, int y, int x2, int y2, int unk, BeadState *ptr) {
+ debug(9, "KyraEngine::initBeadState(%d, %d, %d, %d, %d, 0x%X)", x, y, x2, y2, unk, ptr);
+ ptr->unk9 = unk;
+ int xDiff = x2 - x;
+ int yDiff = y2 - y;
+ int unk1 = 0, unk2 = 0;
+ if (xDiff > 0) {
+ unk1 = 1;
+ } else if (xDiff == 0) {
+ unk1 = 0;
+ } else {
+ unk1 = -1;
+ }
+
+ if (yDiff > 0) {
+ unk2 = 1;
+ } else if (yDiff == 0) {
+ unk2 = 0;
+ } else {
+ unk2 = -1;
+ }
+
+ xDiff = abs(xDiff);
+ yDiff = abs(yDiff);
+
+ ptr->y = 0;
+ ptr->x = 0;
+ ptr->width = xDiff;
+ ptr->height = yDiff;
+ ptr->dstX = x2;
+ ptr->dstY = y2;
+ ptr->width2 = unk1;
+ ptr->unk8 = unk2;
+}
+
+int KyraEngine::processBead(int x, int y, int &x2, int &y2, BeadState *ptr) {
+ debug(9, "KyraEngine::processBead(%d, %d, 0x%X, 0x%X, 0x%X)", x, y, &x2, &y2, ptr);
+ if (x == ptr->dstX && y == ptr->dstY) {
+ return 1;
+ }
+
+ int xPos = x, yPos = y;
+ if (ptr->width >= ptr->height) {
+ for (int i = 0; i < ptr->unk9; ++i) {
+ ptr->y += ptr->height;
+ if (ptr->y >= ptr->width) {
+ ptr->y -= ptr->width;
+ yPos += ptr->unk8;
+ }
+ xPos += ptr->width2;
+ }
+ } else {
+ for (int i = 0; i < ptr->unk9; ++i) {
+ ptr->x += ptr->width;
+ if (ptr->x >= ptr->height) {
+ ptr->x -= ptr->height;
+ xPos += ptr->width2;
+ }
+ yPos += ptr->unk8;
+ }
+ }
+
+ int temp = abs(x - ptr->dstX);
+ if (ptr->unk9 > temp) {
+ xPos = ptr->dstX;
+ }
+ temp = abs(y - ptr->dstY);
+ if (ptr->unk9 > temp) {
+ yPos = ptr->dstY;
+ }
+ x2 = xPos;
+ y2 = yPos;
+ return 0;
+}
+
+void KyraEngine::setupPanPages() {
+ debug(9, "KyraEngine::setupPanPages()");
+ loadBitmap("bead.cps", 3, 3, 0);
+ for (int i = 0; i <= 19; ++i) {
+ _panPagesTable[i] = _seq->setPanPages(3, i);
+ }
+}
+
+void KyraEngine::freePanPages() {
+ debug(9, "KyraEngine::freePanPages()");
+ delete _endSequenceBackUpRect;
+ _endSequenceBackUpRect = 0;
+ for (int i = 0; i <= 19; ++i) {
+ free(_panPagesTable[i]);
+ _panPagesTable[i] = NULL;
+ }
+}
+
+void KyraEngine::closeFinalWsa() {
+ debug(9, "KyraEngine::closeFinalWsa()");
+ delete _finalA;
+ _finalA = 0;
+ delete _finalB;
+ _finalB = 0;
+ delete _finalC;
+ _finalC = 0;
+ freePanPages();
+ _endSequenceNeedLoading = 1;
+}
+
+void KyraEngine::updateKyragemFading() {
+ static const uint8 kyraGemPalette[0x28] = {
+ 0x3F, 0x3B, 0x38, 0x34, 0x32, 0x2F, 0x2C, 0x29, 0x25, 0x22,
+ 0x1F, 0x1C, 0x19, 0x16, 0x12, 0x0F, 0x0C, 0x0A, 0x06, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ if (_system->getMillis() < _kyragemFadingState.timerCount)
+ return;
+
+ _kyragemFadingState.timerCount = _system->getMillis() + 4 * _tickLength;
+ int palPos = 684;
+ for (int i = 0; i < 20; ++i) {
+ _screen->_currentPalette[palPos++] = kyraGemPalette[i + _kyragemFadingState.rOffset];
+ _screen->_currentPalette[palPos++] = kyraGemPalette[i + _kyragemFadingState.gOffset];
+ _screen->_currentPalette[palPos++] = kyraGemPalette[i + _kyragemFadingState.bOffset];
+ }
+ _screen->setScreenPalette(_screen->_currentPalette);
+ _animator->_updateScreen = true;
+ switch (_kyragemFadingState.nextOperation) {
+ case 0:
+ --_kyragemFadingState.bOffset;
+ if (_kyragemFadingState.bOffset >= 1)
+ return;
+ _kyragemFadingState.nextOperation = 1;
+ break;
+
+ case 1:
+ ++_kyragemFadingState.rOffset;
+ if (_kyragemFadingState.rOffset < 19)
+ return;
+ _kyragemFadingState.nextOperation = 2;
+ break;
+
+ case 2:
+ --_kyragemFadingState.gOffset;
+ if (_kyragemFadingState.gOffset >= 1)
+ return;
+ _kyragemFadingState.nextOperation = 3;
+ break;
+
+ case 3:
+ ++_kyragemFadingState.bOffset;
+ if (_kyragemFadingState.bOffset < 19)
+ return;
+ _kyragemFadingState.nextOperation = 4;
+ break;
+
+ case 4:
+ --_kyragemFadingState.rOffset;
+ if (_kyragemFadingState.rOffset >= 1)
+ return;
+ _kyragemFadingState.nextOperation = 5;
+ break;
+
+ case 5:
+ ++_kyragemFadingState.gOffset;
+ if (_kyragemFadingState.gOffset < 19)
+ return;
+ _kyragemFadingState.nextOperation = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ _kyragemFadingState.timerCount = _system->getMillis() + 120 * _tickLength;
+}
+
+void KyraEngine::drawJewelPress(int jewel, int drawSpecial) {
+ debug(9, "KyraEngine::drawJewelPress(%d, %d)", jewel, drawSpecial);
+ _screen->hideMouse();
+ int shape = 0;
+ if (drawSpecial) {
+ shape = 0x14E;
+ } else {
+ shape = jewel + 0x149;
+ }
+ snd_playSoundEffect(0x45);
+ _screen->drawShape(0, _shapes[4+shape], _amuletX2[jewel], _amuletY2[jewel], 0, 0);
+ _screen->updateScreen();
+ delayWithTicks(2);
+ if (drawSpecial) {
+ shape = 0x148;
+ } else {
+ shape = jewel + 0x143;
+ }
+ _screen->drawShape(0, _shapes[4+shape], _amuletX2[jewel], _amuletY2[jewel], 0, 0);
+ _screen->updateScreen();
+ _screen->showMouse();
+}
+
+void KyraEngine::drawJewelsFadeOutStart() {
+ debug(9, "KyraEngine::drawJewelsFadeOutStart()");
+ static const uint16 jewelTable1[] = { 0x164, 0x15F, 0x15A, 0x155, 0x150, 0xFFFF };
+ static const uint16 jewelTable2[] = { 0x163, 0x15E, 0x159, 0x154, 0x14F, 0xFFFF };
+ static const uint16 jewelTable3[] = { 0x166, 0x160, 0x15C, 0x157, 0x152, 0xFFFF };
+ static const uint16 jewelTable4[] = { 0x165, 0x161, 0x15B, 0x156, 0x151, 0xFFFF };
+ for (int i = 0; jewelTable1[i] != 0xFFFF; ++i) {
+ if (queryGameFlag(0x57)) {
+ _screen->drawShape(0, _shapes[4+jewelTable1[i]], _amuletX2[2], _amuletY2[2], 0, 0);
+ }
+ if (queryGameFlag(0x59)) {
+ _screen->drawShape(0, _shapes[4+jewelTable3[i]], _amuletX2[4], _amuletY2[4], 0, 0);
+ }
+ if (queryGameFlag(0x56)) {
+ _screen->drawShape(0, _shapes[4+jewelTable2[i]], _amuletX2[1], _amuletY2[1], 0, 0);
+ }
+ if (queryGameFlag(0x58)) {
+ _screen->drawShape(0, _shapes[4+jewelTable4[i]], _amuletX2[3], _amuletY2[3], 0, 0);
+ }
+ _screen->updateScreen();
+ delayWithTicks(3);
+ }
+}
+
+void KyraEngine::drawJewelsFadeOutEnd(int jewel) {
+ debug(9, "KyraEngine::drawJewelsFadeOutEnd(%d)", jewel);
+ static const uint16 jewelTable[] = { 0x153, 0x158, 0x15D, 0x162, 0x148, 0xFFFF };
+ int newDelay = 0;
+ switch (jewel-1) {
+ case 2:
+ if (_currentCharacter->sceneId >= 109 && _currentCharacter->sceneId <= 198) {
+ newDelay = 18900;
+ } else {
+ newDelay = 8100;
+ }
+ break;
+
+ default:
+ newDelay = 3600;
+ break;
+ }
+ setGameFlag(0xF1);
+ setTimerCountdown(19, newDelay);
+ _screen->hideMouse();
+ for (int i = 0; jewelTable[i] != 0xFFFF; ++i) {
+ uint16 shape = jewelTable[i];
+ if (queryGameFlag(0x57)) {
+ _screen->drawShape(0, _shapes[4+shape], _amuletX2[2], _amuletY2[2], 0, 0);
+ }
+ if (queryGameFlag(0x59)) {
+ _screen->drawShape(0, _shapes[4+shape], _amuletX2[4], _amuletY2[4], 0, 0);
+ }
+ if (queryGameFlag(0x56)) {
+ _screen->drawShape(0, _shapes[4+shape], _amuletX2[1], _amuletY2[1], 0, 0);
+ }
+ if (queryGameFlag(0x58)) {
+ _screen->drawShape(0, _shapes[4+shape], _amuletX2[3], _amuletY2[3], 0, 0);
+ }
+ _screen->updateScreen();
+ delayWithTicks(3);
+ }
+ _screen->showMouse();
+}
+
+} // end of namespace Kyra
diff --git a/engines/kyra/sound.cpp b/engines/kyra/sound.cpp
new file mode 100644
index 0000000000..48e59a5d15
--- /dev/null
+++ b/engines/kyra/sound.cpp
@@ -0,0 +1,477 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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 "common/system.h"
+#include "kyra/resource.h"
+#include "kyra/sound.h"
+
+#include "sound/mixer.h"
+#include "sound/voc.h"
+#include "sound/audiostream.h"
+
+#include "sound/mp3.h"
+#include "sound/vorbis.h"
+#include "sound/flac.h"
+
+namespace Kyra {
+
+SoundPC::SoundPC(MidiDriver *driver, Audio::Mixer *mixer, KyraEngine *engine) : Sound() {
+ _engine = engine;
+ _driver = driver;
+ _passThrough = false;
+ _eventFromMusic = false;
+ _fadeMusicOut = _sfxIsPlaying = false;
+ _fadeStartTime = 0;
+ _isPlaying = _isLooping = _nativeMT32 = false;
+ _soundEffect = _parser = 0;
+ _soundEffectSource = _parserSource = 0;
+
+ memset(_channel, 0, sizeof(MidiChannel*) * 32);
+ memset(_channelVolume, 50, sizeof(uint8) * 16);
+ _channelVolume[10] = 100;
+ for (int i = 0; i < 16; ++i) {
+ _virChannel[i] = i;
+ }
+ _volume = 0;
+
+ int ret = open();
+ if (ret != MERR_ALREADY_OPEN && ret != 0) {
+ error("couldn't open midi driver");
+ }
+
+ _currentVocFile = 0;
+ _mixer = mixer;
+}
+
+SoundPC::~SoundPC() {
+ _driver->setTimerCallback(NULL, NULL);
+ close();
+}
+
+void SoundPC::setVolume(int volume) {
+ if (volume < 0)
+ volume = 0;
+ else if (volume > 255)
+ volume = 255;
+
+ if (_volume == volume)
+ return;
+
+ _volume = volume;
+ for (int i = 0; i < 32; ++i) {
+ if (_channel[i]) {
+ if (i >= 16) {
+ _channel[i]->volume(_channelVolume[i - 16] * _volume / 255);
+ } else {
+ _channel[i]->volume(_channelVolume[i] * _volume / 255);
+ }
+ }
+ }
+}
+
+int SoundPC::open() {
+ // Don't ever call open without first setting the output driver!
+ if (!_driver)
+ return 255;
+
+ int ret = _driver->open();
+ if (ret)
+ return ret;
+
+ _driver->setTimerCallback(this, &onTimer);
+ return 0;
+}
+
+void SoundPC::close() {
+ if (_driver)
+ _driver->close();
+ _driver = 0;
+}
+
+void SoundPC::send(uint32 b) {
+ if (_passThrough) {
+ if ((b & 0xFFF0) == 0x007BB0)
+ return;
+ _driver->send(b);
+ return;
+ }
+
+ uint8 channel = (byte)(b & 0x0F);
+ if (((b & 0xFFF0) == 0x6FB0 || (b & 0xFFF0) == 0x6EB0) && channel != 9) {
+ if (_virChannel[channel] == channel) {
+ _virChannel[channel] = channel + 16;
+ if (!_channel[_virChannel[channel]])
+ _channel[_virChannel[channel]] = _driver->allocateChannel();
+ if (_channel[_virChannel[channel]])
+ _channel[_virChannel[channel]]->volume(_channelVolume[channel] * _volume / 255);
+ }
+ return;
+ }
+
+ if ((b & 0xFFF0) == 0x07B0) {
+ // Adjust volume changes by master volume
+ uint8 volume = (uint8)((b >> 16) & 0x7F);
+ _channelVolume[channel] = volume;
+ volume = volume * _volume / 255;
+ b = (b & 0xFF00FFFF) | (volume << 16);
+ } else if ((b & 0xF0) == 0xC0 && !_nativeMT32) {
+ b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
+ } else if ((b & 0xFFF0) == 0x007BB0) {
+ //Only respond to All Notes Off if this channel
+ //has currently been allocated
+ if (!_channel[channel])
+ return;
+ }
+
+ if (!_channel[_virChannel[channel]]) {
+ _channel[_virChannel[channel]] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
+ if (_channel[_virChannel[channel]])
+ _channel[_virChannel[channel]]->volume(_channelVolume[channel] * _volume / 255);
+ }
+ if (_channel[_virChannel[channel]])
+ _channel[_virChannel[channel]]->send(b);
+}
+
+void SoundPC::metaEvent(byte type, byte *data, uint16 length) {
+ switch (type) {
+ case 0x2F: // End of Track
+ if (_eventFromMusic) {
+ if (!_isLooping) {
+ _isPlaying = false;
+ }
+ // remap all channels
+ for (int i = 0; i < 16; ++i) {
+ _virChannel[i] = i;
+ }
+ } else {
+ _sfxIsPlaying = false;
+ }
+ break;
+ default:
+ _driver->metaEvent(type, data, length);
+ break;
+ }
+}
+
+void SoundPC::playMusic(const char *file) {
+ uint32 size;
+ uint8 *data = (_engine->resource())->fileData(file, &size);
+
+ if (!data) {
+ warning("couldn't load '%s'", file);
+ return;
+ }
+
+ playMusic(data, size);
+}
+
+void SoundPC::playMusic(uint8 *data, uint32 size) {
+ stopMusic();
+
+ _parserSource = data;
+ _parser = MidiParser::createParser_XMIDI();
+ assert(_parser);
+
+ if (!_parser->loadMusic(data, size)) {
+ warning("Error reading track!");
+ delete _parser;
+ _parser = 0;
+ return;
+ }
+
+ _parser->setTrack(0);
+ _parser->setMidiDriver(this);
+ _parser->setTimerRate(getBaseTempo());
+ _parser->property(MidiParser::mpAutoLoop, false);
+}
+
+void SoundPC::loadSoundEffectFile(const char *file) {
+ uint32 size;
+ uint8 *data = (_engine->resource())->fileData(file, &size);
+
+ if (!data) {
+ warning("couldn't load '%s'", file);
+ return;
+ }
+
+ loadSoundEffectFile(data, size);
+}
+
+void SoundPC::loadSoundEffectFile(uint8 *data, uint32 size) {
+ stopSoundEffect();
+
+ _soundEffectSource = data;
+ _soundEffect = MidiParser::createParser_XMIDI();
+ assert(_soundEffect);
+
+ if (!_soundEffect->loadMusic(data, size)) {
+ warning("Error reading track!");
+ delete _parser;
+ _parser = 0;
+ return;
+ }
+
+ _soundEffect->setTrack(0);
+ _soundEffect->setMidiDriver(this);
+ _soundEffect->setTimerRate(getBaseTempo());
+ _soundEffect->property(MidiParser::mpAutoLoop, false);
+}
+
+void SoundPC::stopMusic() {
+ _isLooping = false;
+ _isPlaying = false;
+ if (_parser) {
+ _parser->unloadMusic();
+ delete _parser;
+ _parser = 0;
+ delete [] _parserSource;
+ _parserSource = 0;
+
+ _fadeStartTime = 0;
+ _fadeMusicOut = false;
+ setVolume(255);
+ }
+}
+
+void SoundPC::stopSoundEffect() {
+ _sfxIsPlaying = false;
+ if (_soundEffect) {
+ _soundEffect->unloadMusic();
+ delete _soundEffect;
+ _soundEffect = 0;
+ delete [] _soundEffectSource;
+ _soundEffectSource = 0;
+ }
+}
+
+void SoundPC::onTimer(void *refCon) {
+ SoundPC *music = (SoundPC *)refCon;
+
+ // this should be set to the fadeToBlack value
+ static const uint32 musicFadeTime = 2 * 1000;
+
+ if (music->_fadeMusicOut && music->_fadeStartTime + musicFadeTime > music->_engine->_system->getMillis()) {
+ byte volume = (byte)((musicFadeTime - (music->_engine->_system->getMillis() - music->_fadeStartTime)) * 255 / musicFadeTime);
+ music->setVolume(volume);
+ } else if(music->_fadeStartTime) {
+ music->setVolume(255);
+ music->_fadeStartTime = 0;
+ music->_fadeMusicOut = false;
+ music->_isLooping = false;
+ music->_isPlaying = false;
+
+ music->_eventFromMusic = true;
+ // from sound/midiparser.cpp
+ for (int i = 0; i < 128; ++i) {
+ for (int j = 0; j < 16; ++j) {
+ music->send(0x80 | j | i << 8);
+ }
+ }
+ for (int i = 0; i < 16; ++i) {
+ music->send(0x007BB0 | i);
+ }
+ }
+
+ if (music->_isPlaying) {
+ if (music->_parser) {
+ music->_eventFromMusic = true;
+ music->_parser->onTimer();
+ }
+ }
+
+ if (music->_sfxIsPlaying) {
+ if (music->_soundEffect) {
+ music->_eventFromMusic = false;
+ music->_soundEffect->onTimer();
+ }
+ }
+}
+
+void SoundPC::playTrack(uint8 track, bool loop) {
+ if (_parser) {
+ _isPlaying = true;
+ _isLooping = loop;
+ _parser->setTrack(track);
+ _parser->jumpToTick(0);
+ _parser->setTempo(1);
+ _parser->property(MidiParser::mpAutoLoop, loop);
+ }
+}
+
+void SoundPC::playSoundEffect(uint8 track) {
+ if (_soundEffect) {
+ _sfxIsPlaying = true;
+ _soundEffect->setTrack(track);
+ _soundEffect->jumpToTick(0);
+ _soundEffect->property(MidiParser::mpAutoLoop, false);
+ }
+}
+
+void SoundPC::beginFadeOut() {
+ // this should be something like fade out...
+ _fadeMusicOut = true;
+ _fadeStartTime = _engine->_system->getMillis();
+}
+
+void SoundPC::voicePlay(const char *file) {
+ uint32 fileSize = 0;
+ byte *fileData = 0;
+ bool found = false;
+ char filenamebuffer[25];
+
+ for (int i = 0; _supportedCodes[i].fileext; ++i) {
+ strcpy(filenamebuffer, file);
+ strcat(filenamebuffer, _supportedCodes[i].fileext);
+
+ _engine->resource()->fileHandle(filenamebuffer, &fileSize, _compressHandle);
+ if (!_compressHandle.isOpen())
+ continue;
+
+ _currentVocFile = _supportedCodes[i].streamFunc(&_compressHandle, fileSize);
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ strcpy(filenamebuffer, file);
+ strcat(filenamebuffer, ".VOC");
+
+ fileData = _engine->resource()->fileData(filenamebuffer, &fileSize);
+ if (!fileData)
+ return;
+
+ Common::MemoryReadStream vocStream(fileData, fileSize);
+ _mixer->stopHandle(_vocHandle);
+ _currentVocFile = makeVOCStream(vocStream);
+ }
+
+ if (_currentVocFile)
+ _mixer->playInputStream(Audio::Mixer::kSpeechSoundType, &_vocHandle, _currentVocFile);
+ delete [] fileData;
+ fileSize = 0;
+}
+
+bool SoundPC::voiceIsPlaying() {
+ return _mixer->isSoundHandleActive(_vocHandle);
+}
+
+void KyraEngine::snd_playTheme(int file, int track) {
+ debug(9, "KyraEngine::snd_playTheme(%d)", file);
+ assert(file < _xmidiFilesCount);
+ _curMusicTheme = _newMusicTheme = file;
+ _sound->playMusic(_xmidiFiles[file]);
+ _sound->playTrack(track, false);
+}
+
+void KyraEngine::snd_setSoundEffectFile(int file) {
+ debug(9, "KyraEngine::snd_setSoundEffectFile(%d)", file);
+ assert(file < _xmidiFilesCount);
+ _sound->loadSoundEffectFile(_xmidiFiles[file]);
+}
+
+void KyraEngine::snd_playSoundEffect(int track) {
+ debug(9, "KyraEngine::snd_playSoundEffect(%d)", track);
+ if (track == 49) {
+ snd_playWanderScoreViaMap(56, 1);
+ } else {
+ _sound->playSoundEffect(track);
+ }
+}
+
+void KyraEngine::snd_playWanderScoreViaMap(int command, int restart) {
+ debug(9, "KyraEngine::snd_playWanderScoreViaMap(%d, %d)", command, restart);
+ static const int8 soundTable[] = {
+ -1, 0, -1, 1, 0, 3, 0, 2,
+ 0, 4, 1, 2, 1, 3, 1, 4,
+ 1, 0x5C, 1, 6, 1, 7, 2, 2,
+ 2, 3, 2, 4, 2, 5, 2, 6,
+ 2, 7, 3, 3, 3, 4, 1, 8,
+ 1, 9, 4, 2, 4, 3, 4, 4,
+ 4, 5, 4, 6, 4, 7, 4, 8,
+ 1, 0x0B, 1, 0x0C, 1, 0x0E, 1, 0x0D,
+ 4, 9, 5, 0x0C, 6, 2, 6, 6,
+ 6, 7, 6, 8, 6, 9, 6, 3,
+ 6, 4, 6, 5, 7, 2, 7, 3,
+ 7, 4, 7, 5, 7, 6, 7, 7,
+ 7, 8, 7, 9, 8, 2, 8, 3,
+ 8, 4, 8, 5, 6, 0x0B, 5, 0x0B
+ };
+ //if (!_disableSound) {
+ // XXX
+ //}
+ assert(command*2+1 < ARRAYSIZE(soundTable));
+ if (_curMusicTheme != soundTable[command*2]+1) {
+ if (soundTable[command*2] != -1) {
+ snd_playTheme(soundTable[command*2]+1);
+ }
+ }
+
+ if (restart)
+ _lastMusicCommand = -1;
+
+ if (command != 1) {
+ if (_lastMusicCommand != command) {
+ _lastMusicCommand = command;
+ _sound->playTrack(soundTable[command*2+1], true);
+ }
+ } else {
+ _lastMusicCommand = 1;
+ _sound->beginFadeOut();
+ }
+}
+
+void KyraEngine::snd_playVoiceFile(int id) {
+ debug(9, "KyraEngine::snd_playVoiceFile(%d)", id);
+ char vocFile[9];
+ assert(id >= 0 && id < 9999);
+ sprintf(vocFile, "%03d", id);
+ _sound->voicePlay(vocFile);
+}
+
+void KyraEngine::snd_voiceWaitForFinish(bool ingame) {
+ debug(9, "KyraEngine::snd_voiceWaitForFinish(%d)", ingame);
+ while (_sound->voiceIsPlaying() && !_skipFlag) {
+ if (ingame) {
+ delay(10, true);
+ } else {
+ _system->delayMillis(10);
+ }
+ }
+}
+
+// static res
+
+const SoundPC::SpeechCodecs SoundPC::_supportedCodes[] = {
+#ifdef USE_MAD
+ { ".VO3", makeMP3Stream },
+#endif // USE_MAD
+#ifdef USE_VORBIS
+ { ".VOG", makeVorbisStream },
+#endif // USE_VORBIS
+#ifdef USE_FLAC
+ { ".VOF", makeFlacStream },
+#endif // USE_FLAC
+ { 0, 0 }
+};
+
+} // end of namespace Kyra
diff --git a/engines/kyra/sound.h b/engines/kyra/sound.h
new file mode 100644
index 0000000000..7af201cd9f
--- /dev/null
+++ b/engines/kyra/sound.h
@@ -0,0 +1,155 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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$
+ *
+ */
+
+#ifndef SOUND_H
+#define SOUND_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "sound/mididrv.h"
+#include "sound/midiparser.h"
+#include "kyra/kyra.h"
+
+class AudioStream;
+
+namespace Audio {
+class Mixer;
+class SoundHandle;
+} // end of namespace Audio
+
+namespace Kyra {
+
+class Sound {
+public:
+ Sound() {}
+ virtual ~Sound() {}
+
+ virtual void setVolume(int volume) = 0;
+ virtual int getVolume() = 0;
+
+ virtual void playMusic(const char *file) = 0;
+ virtual void playMusic(uint8 *data, uint32 size) = 0;
+ virtual void stopMusic() = 0;
+
+ virtual void playTrack(uint8 track, bool looping = true) = 0;
+ virtual void haltTrack() = 0;
+ virtual void startTrack() = 0;
+
+ virtual void loadSoundEffectFile(const char *file) = 0;
+ virtual void loadSoundEffectFile(uint8 *data, uint32 size) = 0;
+ virtual void stopSoundEffect() = 0;
+
+ virtual void playSoundEffect(uint8 track) = 0;
+
+ virtual void beginFadeOut() = 0;
+ virtual bool fadeOut() = 0;
+
+ virtual void voicePlay(const char *file) = 0;
+ virtual void voiceUnload() = 0;
+ virtual bool voiceIsPlaying() = 0;
+};
+
+class SoundPC : public MidiDriver, public Sound {
+public:
+
+ SoundPC(MidiDriver *driver, Audio::Mixer *mixer, KyraEngine *engine);
+ ~SoundPC();
+
+ void setVolume(int volume);
+ int getVolume() { return _volume; }
+
+ void hasNativeMT32(bool nativeMT32) { _nativeMT32 = nativeMT32; }
+ bool isMT32() { return _nativeMT32; }
+
+ void playMusic(const char *file);
+ void playMusic(uint8 *data, uint32 size);
+ void stopMusic();
+
+ void playTrack(uint8 track, bool looping);
+ void haltTrack() { _isPlaying = false; }
+ void startTrack() { _isPlaying = true; }
+ void setPassThrough(bool b) { _passThrough = b; }
+
+ void loadSoundEffectFile(const char *file);
+ void loadSoundEffectFile(uint8 *data, uint32 size);
+ void stopSoundEffect();
+
+ void playSoundEffect(uint8 track);
+
+ void beginFadeOut();
+ bool fadeOut() { return _fadeMusicOut; }
+
+ void voicePlay(const char *file);
+ void voiceUnload() {};
+ bool voiceIsPlaying();
+
+ //MidiDriver interface implementation
+ int open();
+ void close();
+ void send(uint32 b);
+ void metaEvent(byte type, byte *data, uint16 length);
+
+ void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { }
+ uint32 getBaseTempo(void) { return _driver ? _driver->getBaseTempo() : 0; }
+
+ //Channel allocation functions
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+
+protected:
+
+ static void onTimer(void *data);
+
+ MidiChannel *_channel[32];
+ int _virChannel[16];
+ uint8 _channelVolume[16];
+ MidiDriver *_driver;
+ bool _nativeMT32;
+ bool _passThrough;
+ uint8 _volume;
+ bool _isPlaying;
+ bool _sfxIsPlaying;
+ uint32 _fadeStartTime;
+ bool _fadeMusicOut;
+ bool _isLooping;
+ bool _eventFromMusic;
+ MidiParser *_parser;
+ byte *_parserSource;
+ MidiParser *_soundEffect;
+ byte *_soundEffectSource;
+ KyraEngine *_engine;
+
+ Audio::Mixer *_mixer;
+ AudioStream *_currentVocFile;
+ Audio::SoundHandle _vocHandle;
+ Common::File _compressHandle;
+
+ struct SpeechCodecs {
+ const char *fileext;
+ AudioStream *(*streamFunc)(Common::File*, uint32);
+ };
+
+ static const SpeechCodecs _supportedCodes[];
+};
+} // end of namespace Kyra
+
+#endif
diff --git a/engines/kyra/sprites.cpp b/engines/kyra/sprites.cpp
new file mode 100644
index 0000000000..d1f9d00fd6
--- /dev/null
+++ b/engines/kyra/sprites.cpp
@@ -0,0 +1,569 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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 "common/stream.h"
+#include "common/util.h"
+#include "common/system.h"
+#include "kyra/screen.h"
+#include "kyra/kyra.h"
+#include "kyra/sprites.h"
+#include "kyra/resource.h"
+#include "kyra/animator.h"
+
+namespace Kyra {
+
+Sprites::Sprites(KyraEngine *engine, OSystem *system) {
+ _engine = engine;
+ _res = engine->resource();
+ _screen = engine->screen();
+ _system = system;
+ _dat = 0;
+ memset(_anims, 0, sizeof(_anims));
+ memset(_sceneShapes, 0, sizeof(_sceneShapes));
+ _animDelay = 16;
+ _spriteDefStart = 0;
+ memset(_drawLayerTable, 0, sizeof(_drawLayerTable));
+ _sceneAnimatorBeaconFlag = 0;
+}
+
+Sprites::~Sprites() {
+ delete[] _dat;
+ freeSceneShapes();
+ for (int i = 0; i < MAX_NUM_ANIMS; i++) {
+ if (_anims[i].background)
+ free(_anims[i].background);
+ }
+}
+
+void Sprites::setupSceneAnims() {
+ debug(9, "Sprites::setupSceneAnims()");
+ uint8 *data;
+
+ for (int i = 0; i < MAX_NUM_ANIMS; i++) {
+ if (_anims[i].background) {
+ free(_anims[i].background);
+ _anims[i].background = 0;
+ }
+
+ if (_anims[i].script != 0) {
+ data = _anims[i].script;
+
+ assert( READ_LE_UINT16(data) == 0xFF86 );
+ data += 4;
+
+ _anims[i].disable = READ_LE_UINT16(data) != 0;
+ data += 4;
+ _anims[i].unk2 = READ_LE_UINT16(data);
+ data += 4;
+
+ if (_engine->_northExitHeight > READ_LE_UINT16(data))
+ _anims[i].drawY = _engine->_northExitHeight;
+ else
+ _anims[i].drawY = READ_LE_UINT16(data);
+ data += 4;
+
+ //sceneUnk2[i] = READ_LE_UINT16(data);
+ data += 4;
+
+ _anims[i].x = READ_LE_UINT16(data);
+ data += 4;
+ _anims[i].y = READ_LE_UINT16(data);
+ data += 4;
+ _anims[i].width = *(data) - 1;
+ data += 4;
+ _anims[i].height = *(data);
+ data += 4;
+ _anims[i].sprite = READ_LE_UINT16(data);
+ data += 4;
+ _anims[i].flipX = READ_LE_UINT16(data) != 0;
+ data += 4;
+ _anims[i].width2 = *(data);
+ data += 4;
+ _anims[i].height2 = *(data);
+ data += 4;
+ _anims[i].unk1 = READ_LE_UINT16(data) != 0;
+ data += 4;
+ _anims[i].play = READ_LE_UINT16(data) != 0;
+ data += 2;
+
+ _anims[i].script = data;
+
+ int bkgdWidth = _anims[i].width << 3;
+ int bkgdHeight = _anims[i].height;
+
+ if (_anims[i].width2)
+ bkgdWidth += _anims[i].width2 << 3;
+
+ if (_anims[i].height2)
+ bkgdHeight += _anims[i].height2;
+
+ _anims[i].background = (uint8 *)malloc(_screen->getRectSize(bkgdWidth + 1, bkgdHeight));
+ memset(_anims[i].background, 0, _screen->getRectSize(bkgdWidth + 1, bkgdHeight));
+
+ assert(_anims[i].background);
+ }
+ }
+}
+
+void Sprites::updateSceneAnims() {
+ debug(9, "Sprites::updateSceneAnims()");
+ uint32 currTime = _system->getMillis();
+ uint8 *data;
+ bool endLoop;
+ uint16 rndNr;
+ uint16 anim;
+ uint16 sound;
+
+ for (int i = 0; i < MAX_NUM_ANIMS; i++) {
+ if (_anims[i].script == 0 || !_anims[i].play || _anims[i].nextRun != 0 && _anims[i].nextRun > currTime)
+ continue;
+
+ if (_anims[i].reentry == 0) {
+ data = _anims[i].script;
+ if (READ_LE_UINT16(data) == 0xFF8B)
+ continue;
+ } else {
+ data = _anims[i].reentry;
+ _anims[i].reentry = 0;
+ }
+
+ endLoop = false;
+ while (READ_LE_UINT16(data) != 0xFF87 && !endLoop) {
+ assert((data - _anims[i].script) < _anims[i].length);
+ switch (READ_LE_UINT16(data)) {
+ case 0xFF88:
+ data += 2;
+ debug(6, "func: Set sprite image.");
+ debug(6, "Sprite index %i", READ_LE_UINT16(data));
+ _anims[i].sprite = READ_LE_UINT16(data);
+ data += 2;
+ //debug(6, "Unused %i", READ_LE_UINT16(data));
+ data += 2;
+ debug(6, "X %i", READ_LE_UINT16(data));
+ _anims[i].x = READ_LE_UINT16(data);
+ data += 2;
+ debug(6, "Y %i", READ_LE_UINT16(data));
+ _anims[i].y = READ_LE_UINT16(data);
+ data += 2;
+ _anims[i].flipX = false;
+ refreshSceneAnimObject(i, _anims[i].sprite, _anims[i].x, _anims[i].y, _anims[i].flipX, _anims[i].unk1 != 0);
+ break;
+ case 0xFF8D:
+ data += 2;
+ debug(6, "func: Set sprite image, flipped.");
+ debug(6, "Sprite index %i", READ_LE_UINT16(data));
+ _anims[i].sprite = READ_LE_UINT16(data);
+ data += 2;
+ //debug(9, "Unused %i", READ_LE_UINT16(data));
+ data += 2;
+ debug(6, "X %i", READ_LE_UINT16(data));
+ _anims[i].x = READ_LE_UINT16(data);
+ data += 2;
+ debug(6, "Y %i", READ_LE_UINT16(data));
+ _anims[i].y = READ_LE_UINT16(data);
+ data += 2;
+ _anims[i].flipX = true;
+ refreshSceneAnimObject(i, _anims[i].sprite, _anims[i].x, _anims[i].y, _anims[i].flipX, _anims[i].unk1 != 0);
+ break;
+ case 0xFF8A:
+ data += 2;
+ debug(6, "func: Set time to wait");
+ debug(6, "Time %i", READ_LE_UINT16(data));
+ _anims[i].nextRun = _system->getMillis() + READ_LE_UINT16(data) * _animDelay;
+ data += 2;
+ break;
+ case 0xFFB3:
+ data += 2;
+ debug(6, "func: Set time to wait to random value");
+ rndNr = READ_LE_UINT16(data) + _rnd.getRandomNumber( READ_LE_UINT16(data) + 2);
+ debug(6, "Minimum time %i", READ_LE_UINT16(data));
+ data += 2;
+ debug(6, "Maximum time %i", READ_LE_UINT16(data));
+ data += 2;
+ _anims[i].nextRun = _system->getMillis() + rndNr * _animDelay;
+ break;
+ case 0xFF8C:
+ data += 2;
+ debug(6, "func: Wait until wait time has elapsed");
+ _anims[i].reentry = data;
+ endLoop = true;
+ //assert( _anims[i].nextRun > _system->getMillis());
+ break;
+ case 0xFF99:
+ data += 2;
+ debug(1, "func: Set value of unknown animation property to 1");
+ _anims[i].unk1 = 1;
+ break;
+ case 0xFF9A:
+ data += 2;
+ debug(1, "func: Set value of unknown animation property to 0");
+ _anims[i].unk1 = 0;
+ break;
+ case 0xFF97:
+ data += 2;
+ debug(6, "func: Set default X coordinate of sprite");
+ debug(6, "X %i", READ_LE_UINT16(data));
+ _anims[i].x = READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFF98:
+ data += 2;
+ debug(6, "func: Set default Y coordinate of sprite");
+ debug(6, "Y %i", READ_LE_UINT16(data));
+ _anims[i].y = READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFF8B:
+ debug(6, "func: Jump to start of script section");
+ //data = scriptStart;
+ _anims[i].nextRun = _system->getMillis();
+ endLoop = true;
+ break;
+ case 0xFF8E:
+ data += 2;
+ debug(6, "func: Begin for () loop");
+ debug(6, "Iterations: %i", READ_LE_UINT16(data));
+ _anims[i].loopsLeft = READ_LE_UINT16(data);
+ data += 2;
+ _anims[i].loopStart = data;
+ break;
+ case 0xFF8F:
+ data += 2;
+ debug(6, "func: End for () loop");
+ if (_anims[i].loopsLeft > 0) {
+ _anims[i].loopsLeft--;
+ data = _anims[i].loopStart;
+ }
+ break;
+ case 0xFF90:
+ data += 2;
+ debug(6, "func: Set sprite image using default X and Y");
+ debug(6, "Sprite index %i", READ_LE_UINT16(data));
+ _anims[i].sprite = READ_LE_UINT16(data);
+ _anims[i].flipX = false;
+ data += 2;
+ refreshSceneAnimObject(i, _anims[i].sprite, _anims[i].x, _anims[i].y, _anims[i].flipX, _anims[i].unk1 != 0);
+ break;
+ case 0xFF91:
+ data += 2;
+ debug(6, "func: Set sprite image using default X and Y, flipped.");
+ debug(6, "Sprite index %i", READ_LE_UINT16(data));
+ _anims[i].sprite = READ_LE_UINT16(data);
+ _anims[i].flipX = true;
+ data += 2;
+ refreshSceneAnimObject(i, _anims[i].sprite, _anims[i].x, _anims[i].y, _anims[i].flipX, _anims[i].unk1 != 0);
+ break;
+ case 0xFF92:
+ data += 2;
+ debug(6, "func: Increase value of default X-coordinate");
+ debug(6, "Increment %i", READ_LE_UINT16(data));
+ _anims[i].x += READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFF93:
+ data += 2;
+ debug(6, "func: Increase value of default Y-coordinate");
+ debug(6, "Increment %i", READ_LE_UINT16(data));
+ _anims[i].y += READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFF94:
+ data += 2;
+ debug(6, "func: Decrease value of default X-coordinate");
+ debug(6, "Decrement %i", READ_LE_UINT16(data));
+ _anims[i].x -= READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFF95:
+ data += 2;
+ debug(6, "func: Decrease value of default Y-coordinate");
+ debug(6, "Decrement %i", READ_LE_UINT16(data));
+ _anims[i].y -= READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFF96:
+ data += 2;
+ debug(9, "func: Stop animation");
+ debug(9, "Animation index %i", READ_LE_UINT16(data));
+ anim = READ_LE_UINT16(data);
+ data += 2;
+ _anims[anim].play = false;
+ _anims[anim].sprite = -1;
+ break;
+/* case 0xFF97:
+ data += 2;
+ debug(1, "func: Set value of animation property 34h to 0");
+ break;*/
+ case 0xFFAD:
+ data += 2;
+ debug(6, "func: Set Brandon's X coordinate");
+ debug(6, "X %i", READ_LE_UINT16(data));
+ _engine->currentCharacter()->x1 = READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFFAE:
+ data += 2;
+ debug(6, "func: Set Brandon's Y coordinate");
+ debug(6, "Y %i", READ_LE_UINT16(data));
+ _engine->currentCharacter()->y1 = READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFFAF:
+ data += 2;
+ debug(6, "func: Set Brandon's sprite");
+ debug(6, "Sprite %i", READ_LE_UINT16(data));
+ _engine->currentCharacter()->currentAnimFrame = READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFFAA:
+ data += 2;
+ debug(1, "TODO func: Reset Brandon's sprite");
+ break;
+ case 0xFFAB:
+ data += 2;
+ debug(6, "func: Update Brandon's sprite");
+ _engine->animator()->animRefreshNPC(0);
+ _engine->animator()->flagAllObjectsForRefresh();
+ _engine->animator()->updateAllObjectShapes();
+ break;
+ case 0xFFB0:
+ data += 2;
+ debug(6, "func: Play sound");
+ debug(6, "Sound index %i", READ_LE_UINT16(data));
+ _engine->snd_playSoundEffect(READ_LE_UINT16(data));
+ data += 2;
+ break;
+ case 0xFFB1:
+ data += 2;
+ _sceneAnimatorBeaconFlag = 1;
+ break;
+ case 0xFFB2:
+ data += 2;
+ _sceneAnimatorBeaconFlag = 0;
+ break;
+ case 0xFFB4:
+ data += 2;
+ debug(6, "func: Play (at random) a certain sound at a certain percentage of time");
+ debug(6, "Sound index %i", READ_LE_UINT16(data));
+ sound = READ_LE_UINT16(data);
+ data += 2;
+ debug(6, "Percentage %i", READ_LE_UINT16(data));
+ rndNr = _rnd.getRandomNumber(100);
+ if (rndNr <= READ_LE_UINT16(data))
+ _engine->snd_playSoundEffect(sound);
+ data += 2;
+ break;
+ case 0xFFA7:
+ data += 2;
+ _anims[READ_LE_UINT16(data)].play = 1;
+ data += 2;
+ break;
+ default:
+ debug(1, "Unsupported anim command %X in script %i", READ_LE_UINT16(data), i);
+ //endLoop = true;
+ data += 1;
+ break;
+ }
+ }
+
+ if (READ_LE_UINT16(data) == 0xFF87)
+ _anims[i].play = false;
+ }
+}
+
+void Sprites::loadDAT(const char *filename, SceneExits &exits) {
+ debug(9, "Sprites::loadDat('%s')", filename);
+ uint32 fileSize;
+
+ delete[] _dat;
+ _spriteDefStart = 0;
+
+ _dat = _res->fileData(filename, &fileSize);
+
+ memset(_anims, 0, sizeof(_anims));
+ uint8 nextAnim = 0;
+
+ assert(fileSize > 0x6D);
+
+ memcpy(_drawLayerTable, (_dat + 0x0D), 8);
+ _engine->_northExitHeight = READ_LE_UINT16(_dat + 0x15);
+ if (_engine->_northExitHeight & 1)
+ _engine->_northExitHeight += 1;
+ // XXX
+ memcpy(_screen->_currentPalette + 744 - 60, _dat + 0x17, 60);
+ uint8 *data = _dat + 0x6B;
+
+ uint16 length = READ_LE_UINT16(data);
+ data += 2;
+
+ if (length > 2) {
+ assert( length < fileSize);
+ uint8 *animstart;
+ uint8 *start = data;
+
+ while (1) {
+ if (((uint16)(data - _dat) >= fileSize) || (data - start) >= length)
+ break;
+
+ if (READ_LE_UINT16(data) == 0xFF83) {
+ //debug(1, "Body section end.");
+ data += 2;
+ break;
+ }
+
+ switch (READ_LE_UINT16(data)) {
+ case 0xFF81:
+ data += 2;
+ //debug(1, "Body section start");
+ break;
+ case 0xFF82:
+ data += 2;
+ //debug(1, "Unknown 0xFF82 section");
+ break;
+ case 0xFF84:
+ data += 2;
+ _spriteDefStart = data;
+ while (READ_LE_UINT16(data) != 0xFF85) {
+ data += 2;
+ }
+ data += 2;
+ break;
+ case 0xFF86:
+ assert(nextAnim < MAX_NUM_ANIMS);
+ _anims[nextAnim].script = data;
+ _anims[nextAnim].sprite = -1;
+ _anims[nextAnim].play = true;
+ animstart = data;
+ data += 2;
+ while (READ_LE_UINT16(data) != 0xFF87) {
+ assert((uint16)(data - _dat) < fileSize);
+ data += 2;
+ }
+ _anims[nextAnim].length = data - animstart;
+ //debug(1, "Found an anim script of length %i!", _anims[nextAnim].length);
+ nextAnim++;
+ data += 2;
+ break;
+ default:
+ debug(1, "Unknown code in DAT file: %x", READ_LE_UINT16(data));
+ data += 2;
+ break;
+ }
+ }
+ } else {
+ data += 2;
+ }
+
+ assert(fileSize - (data - _dat) == 0xC);
+
+ exits.northXPos = READ_LE_UINT16(data) & 0xFFFC; data += 2;
+ exits.northYPos = *data++ & 0xFFFE;
+ exits.eastXPos = READ_LE_UINT16(data) & 0xFFFC; data += 2;
+ exits.eastYPos = *data++ & 0xFFFE;
+ exits.southXPos = READ_LE_UINT16(data) & 0xFFFC; data += 2;
+ exits.southYPos = *data++ & 0xFFFE;
+ exits.westXPos = READ_LE_UINT16(data) & 0xFFFC; data += 2;
+ exits.westYPos = *data++ & 0xFFFE;
+}
+
+void Sprites::freeSceneShapes() {
+ debug(9, "Sprites::freeSceneShapes()");
+ for (int i = 0; i < ARRAYSIZE(_sceneShapes); i++ ) {
+ free(_sceneShapes[i]);
+ _sceneShapes[i] = 0;
+ }
+}
+
+void Sprites::loadSceneShapes() {
+ debug(9, "Sprites::loadSceneShapes()");
+ uint8 *data = _spriteDefStart;
+ int spriteNum, x, y, width, height;
+
+ freeSceneShapes();
+ memset( _sceneShapes, 0, sizeof(_sceneShapes));
+
+ if (_spriteDefStart == 0)
+ return;
+
+ int bakPage = _screen->_curPage;
+ _screen->_curPage = 3;
+
+ while (READ_LE_UINT16(data) != 0xFF85) {
+ spriteNum = READ_LE_UINT16(data);
+ assert(spriteNum < ARRAYSIZE(_sceneShapes));
+ data += 2;
+ x = READ_LE_UINT16(data) * 8;
+ data += 2;
+ y = READ_LE_UINT16(data);
+ data += 2;
+ width = READ_LE_UINT16(data) * 8;
+ data += 2;
+ height = READ_LE_UINT16(data);
+ data += 2;
+ _sceneShapes[spriteNum] = _screen->encodeShape(x, y, width, height, 2);
+ debug(9, "Sprite %i is at (%i, %i), width %i, height %i", spriteNum, x, y, width, height);
+ }
+ _screen->_curPage = bakPage;
+}
+
+void Sprites::refreshSceneAnimObject(uint8 animNum, uint8 shapeNum, uint16 x, uint16 y, bool flipX, bool unkFlag) {
+ debug(9, "Sprites::refreshSceneAnimObject(%i, %i, %i, %i, %i, %i", animNum, shapeNum, x, y, flipX, unkFlag);
+ AnimObject &anim = _engine->animator()->sprites()[animNum];
+ anim.refreshFlag = 1;
+ anim.bkgdChangeFlag = 1;
+
+ if (unkFlag)
+ anim.flags |= 0x0200;
+ else
+ anim.flags &= 0xFD00;
+
+ if (flipX)
+ anim.flags |= 1;
+ else
+ anim.flags &= 0xFE;
+
+ anim.sceneAnimPtr = _sceneShapes[shapeNum];
+ anim.animFrameNumber = -1;
+ anim.x1 = x;
+ anim.y1 = y;
+}
+
+int Sprites::getDrawLayer(int y) {
+ debug(9, "getDrawLayer(%d)", y);
+ uint8 returnValue = 0;
+ for (int i = 0; i < ARRAYSIZE(_drawLayerTable); ++i) {
+ uint8 temp = _drawLayerTable[i];
+ if (temp) {
+ if (temp <= y) {
+ returnValue = i;
+ }
+ }
+ }
+ if (returnValue <= 0) {
+ returnValue = 1;
+ } else if (returnValue >= 7) {
+ returnValue = 6;
+ }
+ return returnValue;
+}
+} // end of namespace Kyra
diff --git a/engines/kyra/sprites.h b/engines/kyra/sprites.h
new file mode 100644
index 0000000000..b1ce9ae8e7
--- /dev/null
+++ b/engines/kyra/sprites.h
@@ -0,0 +1,95 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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$
+ *
+ */
+
+#ifndef KYRASPRITES_H
+#define KYRASPRITES_H
+
+namespace Kyra {
+
+#define MAX_NUM_ANIMS 11
+
+struct Sprite {
+ uint16 x;
+ uint16 y;
+ uint16 width;
+ uint16 height;
+};
+
+struct Anim {
+ uint8 *script;
+ uint16 length;
+ int16 x;
+ int16 y;
+ bool flipX;
+ int8 sprite;
+ uint8 *loopStart;
+ uint16 loopsLeft;
+ uint8 *reentry;
+ uint32 nextRun;
+ bool play;
+ uint16 width;
+ uint16 height;
+ uint16 width2;
+ uint16 height2;
+ uint16 unk1;
+ uint16 drawY;
+ uint16 unk2;
+ uint8 *background;
+ bool disable;
+};
+
+class Sprites {
+public:
+
+ Sprites(KyraEngine *engine, OSystem *system);
+ ~Sprites();
+
+ void updateSceneAnims();
+ void setupSceneAnims();
+ void loadDAT(const char *filename, SceneExits &exits);
+ void loadSceneShapes();
+
+ Anim _anims[MAX_NUM_ANIMS];
+ uint8 *_sceneShapes[50];
+
+ void refreshSceneAnimObject(uint8 animNum, uint8 shapeNum, uint16 x, uint16 y, bool flipX, bool unkFlag);
+
+ int getDrawLayer(int y);
+
+ int _sceneAnimatorBeaconFlag;
+protected:
+ void freeSceneShapes();
+
+ KyraEngine *_engine;
+ Resource *_res;
+ OSystem *_system;
+ Screen *_screen;
+ uint8 *_dat;
+ Common::RandomSource _rnd;
+ uint8 _animDelay;
+ uint8 *_spriteDefStart;
+ uint8 _drawLayerTable[8];
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp
new file mode 100644
index 0000000000..66e10358c3
--- /dev/null
+++ b/engines/kyra/staticres.cpp
@@ -0,0 +1,978 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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 "kyra/kyra.h"
+#include "kyra/screen.h"
+#include "kyra/resource.h"
+
+namespace Kyra {
+
+#define RESFILE_VERSION 11
+
+#define GAME_FLAGS (GF_FLOPPY | GF_TALKIE | GF_DEMO | GF_AUDIOCD)
+#define LANGUAGE_FLAGS (GF_ENGLISH | GF_FRENCH | GF_GERMAN | GF_SPANISH | GF_LNGUNK)
+
+byte *getFile(PAKFile &res, const char *filename) {
+ uint32 size = 0;
+ size = res.getFileSize(filename);
+ if (!size)
+ return 0;
+ return res.getFile(filename);
+}
+
+struct LanguageTypes {
+ uint32 flags;
+ const char *ext;
+};
+
+static LanguageTypes languages[] = {
+ { GF_ENGLISH, "ENG" }, // this is the default language
+ { GF_FRENCH, "FRE" },
+ { GF_GERMAN, "GER" },
+ { GF_SPANISH, "SPA" },
+ { 0, 0 }
+};
+
+void KyraEngine::res_loadResources(int type) {
+ debug(9, "res_loadResources(%d)", type);
+ PAKFile resFile("KYRA.DAT");
+ if (!resFile.isValid() || !resFile.isOpen()) {
+ error("couldn't open Kyrandia resource file ('KYRA.DAT') make sure you got one file for your version");
+ }
+
+ uint32 version = 0;
+ uint32 gameID = 0;
+ uint32 featuresValue = 0;
+ bool loadNativeLanguage = true;
+
+ byte *temp = 0;
+
+ if (_features & GF_TALKIE) {
+ temp = getFile(resFile, "INDEX.CD");
+ } else if (_features & GF_DEMO) {
+ temp = getFile(resFile, "INDEX.DEM");
+ } else {
+ temp = getFile(resFile, "INDEX");
+ }
+ if (!temp) {
+ error("no matching INDEX file found");
+ }
+
+ version = READ_BE_UINT32(temp);
+ gameID = READ_BE_UINT32((temp+4));
+ featuresValue = READ_BE_UINT32((temp+8));
+
+ delete [] temp;
+ temp = 0;
+
+ if (version < RESFILE_VERSION) {
+ error("invalid KYRA.DAT file version (%d, required %d)", version, RESFILE_VERSION);
+ }
+ if (gameID != _game) {
+ error("invalid game id (%d)", gameID);
+ }
+ if ((featuresValue & GAME_FLAGS) != (_features & GAME_FLAGS)) {
+ error("your data file has a different game flags (0x%.08X has the data and your version has 0x%.08X)", (featuresValue & GAME_FLAGS), (_features & GAME_FLAGS));
+ }
+
+ if (!((featuresValue & LANGUAGE_FLAGS) & (_features & LANGUAGE_FLAGS))) {
+ char buffer[240];
+ sprintf(buffer, "your data file has support for:");
+ if (featuresValue & GF_ENGLISH) {
+ sprintf(buffer + strlen(buffer), " English");
+ }
+ if (featuresValue & GF_FRENCH) {
+ sprintf(buffer + strlen(buffer), " French");
+ }
+ if (featuresValue & GF_GERMAN) {
+ sprintf(buffer + strlen(buffer), " German");
+ }
+ if (featuresValue & GF_SPANISH) {
+ sprintf(buffer + strlen(buffer), " Spanish");
+ }
+ sprintf(buffer + strlen(buffer), " but not your language (");
+ if (_features & GF_ENGLISH) {
+ sprintf(buffer + strlen(buffer), "English");
+ } else if (_features & GF_FRENCH) {
+ sprintf(buffer + strlen(buffer), "French");
+ } else if (_features & GF_GERMAN) {
+ sprintf(buffer + strlen(buffer), "German");
+ } else if (_features & GF_SPANISH) {
+ sprintf(buffer + strlen(buffer), "Spanish");
+ } else {
+ sprintf(buffer + strlen(buffer), "unknown");
+ }
+ sprintf(buffer + strlen(buffer), ")");
+ warning(buffer);
+ loadNativeLanguage = false;
+ }
+
+#define getFileEx(x, y) \
+ if (_features & GF_TALKIE) { \
+ temp = getFile(x, y ".CD"); \
+ } else if (_features & GF_DEMO) { \
+ temp = getFile(x, y ".DEM"); \
+ } else { \
+ temp = getFile(x, y); \
+ }
+#define loadRawFile(x, y, z) \
+ getFileEx(x, y) \
+ if (temp) { \
+ z = temp; \
+ temp = 0; \
+ }
+#define loadTable(x, y, z, a) \
+ getFileEx(x, y) \
+ if (temp) { \
+ res_loadTable(temp, z, a); \
+ delete [] temp; \
+ temp = 0; \
+ }
+#define loadRooms(x, y, z, a) \
+ getFileEx(x, y) \
+ if (temp) { \
+ res_loadRoomTable(temp, z, a); \
+ delete [] temp; \
+ temp = 0; \
+ }
+#define loadShapes(x, y, z, a) \
+ getFileEx(x, y) \
+ if (temp) { \
+ res_loadShapeTable(temp, z, a); \
+ delete [] temp; \
+ temp = 0; \
+ }
+
+
+ if ((type & RES_INTRO) || (type & RES_OUTRO) || type == RES_ALL) {
+ loadRawFile(resFile, "FOREST.SEQ", _seq_Forest);
+ loadRawFile(resFile, "KALLAK-WRITING.SEQ", _seq_KallakWriting);
+ loadRawFile(resFile, "KYRANDIA-LOGO.SEQ", _seq_KyrandiaLogo);
+ loadRawFile(resFile, "KALLAK-MALCOLM.SEQ", _seq_KallakMalcolm);
+ loadRawFile(resFile, "MALCOLM-TREE.SEQ", _seq_MalcolmTree);
+ loadRawFile(resFile, "WESTWOOD-LOGO.SEQ", _seq_WestwoodLogo);
+ loadRawFile(resFile, "DEMO1.SEQ", _seq_Demo1);
+ loadRawFile(resFile, "DEMO2.SEQ", _seq_Demo2);
+ loadRawFile(resFile, "DEMO3.SEQ", _seq_Demo3);
+ loadRawFile(resFile, "DEMO4.SEQ", _seq_Demo4);
+
+ loadTable(resFile, "INTRO-CPS.TXT", (byte***)&_seq_CPSTable, &_seq_CPSTable_Size);
+ loadTable(resFile, "INTRO-COL.TXT", (byte***)&_seq_COLTable, &_seq_COLTable_Size);
+ loadTable(resFile, "INTRO-WSA.TXT", (byte***)&_seq_WSATable, &_seq_WSATable_Size);
+
+ res_loadLangTable("INTRO-STRINGS.", &resFile, (byte***)&_seq_textsTable, &_seq_textsTable_Size, loadNativeLanguage);
+
+ loadRawFile(resFile, "REUNION.SEQ", _seq_Reunion);
+
+ res_loadLangTable("HOME.", &resFile, (byte***)&_homeString, &_homeString_Size, loadNativeLanguage);
+ }
+
+ if ((type & RES_INGAME) || type == RES_ALL) {
+ loadTable(resFile, "ROOM-FILENAMES.TXT", (byte***)&_roomFilenameTable, &_roomFilenameTableSize);
+ loadRooms(resFile, "ROOM-TABLE.ROOM", &_roomTable, &_roomTableSize);
+
+ loadTable(resFile, "CHAR-IMAGE.TXT", (byte***)&_characterImageTable, &_characterImageTableSize);
+
+ loadShapes(resFile, "SHAPES-DEFAULT.SHP", &_defaultShapeTable, &_defaultShapeTableSize);
+
+ res_loadLangTable("ITEMLIST.", &resFile, (byte***)&_itemList, &_itemList_Size, loadNativeLanguage);
+ res_loadLangTable("TAKEN.", &resFile, (byte***)&_takenList, &_takenList_Size, loadNativeLanguage);
+ res_loadLangTable("PLACED.", &resFile, (byte***)&_placedList, &_placedList_Size, loadNativeLanguage);
+ res_loadLangTable("DROPPED.", &resFile, (byte***)&_droppedList, &_droppedList_Size, loadNativeLanguage);
+ res_loadLangTable("NODROP.", &resFile, (byte***)&_noDropList, &_noDropList_Size, loadNativeLanguage);
+
+ loadRawFile(resFile, "AMULETEANIM.SEQ", _amuleteAnim);
+
+ for (int i = 1; i <= 33; ++i) {
+ char buffer[32];
+ sprintf(buffer, "PALTABLE%d.PAL", i);
+ if (_features & GF_TALKIE) {
+ strcat(buffer, ".CD");
+ } else if (_features & GF_DEMO) {
+ strcat(buffer, ".DEM");
+ }
+ temp = getFile(resFile, buffer);
+ if (temp) {
+ _specialPalettes[i-1] = temp;
+ temp = 0;
+ }
+ }
+
+ res_loadLangTable("PUTDOWN.", &resFile, (byte***)&_putDownFirst, &_putDownFirst_Size, loadNativeLanguage);
+ res_loadLangTable("WAITAMUL.", &resFile, (byte***)&_waitForAmulet, &_waitForAmulet_Size, loadNativeLanguage);
+ res_loadLangTable("BLACKJEWEL.", &resFile, (byte***)&_blackJewel, &_blackJewel_Size, loadNativeLanguage);
+ res_loadLangTable("POISONGONE.", &resFile, (byte***)&_poisonGone, &_poisonGone_Size, loadNativeLanguage);
+ res_loadLangTable("HEALINGTIP.", &resFile, (byte***)&_healingTip, &_healingTip_Size, loadNativeLanguage);
+
+ loadShapes(resFile, "HEALING.SHP", &_healingShapeTable, &_healingShapeTableSize);
+ loadShapes(resFile, "HEALING2.SHP", &_healingShape2Table, &_healingShape2TableSize);
+
+ res_loadLangTable("THEPOISON.", &resFile, (byte***)&_thePoison, &_thePoison_Size, loadNativeLanguage);
+ res_loadLangTable("FLUTE.", &resFile, (byte***)&_fluteString, &_fluteString_Size, loadNativeLanguage);
+
+ loadShapes(resFile, "POISONDEATH.SHP", &_posionDeathShapeTable, &_posionDeathShapeTableSize);
+ loadShapes(resFile, "FLUTE.SHP", &_fluteAnimShapeTable, &_fluteAnimShapeTableSize);
+
+ loadShapes(resFile, "WINTER1.SHP", &_winterScrollTable, &_winterScrollTableSize);
+ loadShapes(resFile, "WINTER2.SHP", &_winterScroll1Table, &_winterScroll1TableSize);
+ loadShapes(resFile, "WINTER3.SHP", &_winterScroll2Table, &_winterScroll2TableSize);
+ loadShapes(resFile, "DRINK.SHP", &_drinkAnimationTable, &_drinkAnimationTableSize);
+ loadShapes(resFile, "WISP.SHP", &_brandonToWispTable, &_brandonToWispTableSize);
+ loadShapes(resFile, "MAGICANIM.SHP", &_magicAnimationTable, &_magicAnimationTableSize);
+ loadShapes(resFile, "BRANSTONE.SHP", &_brandonStoneTable, &_brandonStoneTableSize);
+
+ res_loadLangTable("WISPJEWEL.", &resFile, (byte***)&_wispJewelStrings, &_wispJewelStrings_Size, loadNativeLanguage);
+ res_loadLangTable("MAGICJEWEL.", &resFile, (byte***)&_magicJewelString, &_magicJewelString_Size, loadNativeLanguage);
+
+ res_loadLangTable("FLASKFULL.", &resFile, (byte***)&_flaskFull, &_fullFlask_Size, loadNativeLanguage);
+ res_loadLangTable("FULLFLASK.", &resFile, (byte***)&_fullFlask, &_fullFlask_Size, loadNativeLanguage);
+
+ res_loadLangTable("VERYCLEVER.", &resFile, (byte***)&_veryClever, &_veryClever_Size, loadNativeLanguage);
+ }
+
+#undef loadRooms
+#undef loadTable
+#undef loadRawFile
+#undef getFileEx
+}
+
+void KyraEngine::res_unloadResources(int type) {
+ debug(9, "res_unloadResources(%d)", type);
+ if ((type & RES_INTRO) || (type & RES_OUTRO) || type & RES_ALL) {
+ res_freeLangTable(&_seq_WSATable, &_seq_WSATable_Size);
+ res_freeLangTable(&_seq_CPSTable, &_seq_CPSTable_Size);
+ res_freeLangTable(&_seq_COLTable, &_seq_COLTable_Size);
+ res_freeLangTable(&_seq_textsTable, &_seq_textsTable_Size);
+
+ delete [] _seq_Forest; _seq_Forest = 0;
+ delete [] _seq_KallakWriting; _seq_KallakWriting = 0;
+ delete [] _seq_KyrandiaLogo; _seq_KyrandiaLogo = 0;
+ delete [] _seq_KallakMalcolm; _seq_KallakMalcolm = 0;
+ delete [] _seq_MalcolmTree; _seq_MalcolmTree = 0;
+ delete [] _seq_WestwoodLogo; _seq_WestwoodLogo = 0;
+ delete [] _seq_Demo1; _seq_Demo1 = 0;
+ delete [] _seq_Demo2; _seq_Demo2 = 0;
+ delete [] _seq_Demo3; _seq_Demo3 = 0;
+ delete [] _seq_Demo4; _seq_Demo4 = 0;
+
+ delete [] _seq_Reunion; _seq_Reunion = 0;
+ res_freeLangTable(&_homeString, &_homeString_Size);
+ }
+
+ if ((type & RES_INGAME) || type & RES_ALL) {
+ res_freeLangTable(&_roomFilenameTable, &_roomFilenameTableSize);
+
+ delete [] _roomTable; _roomTable = 0;
+ _roomTableSize = 0;
+
+ res_freeLangTable(&_characterImageTable, &_characterImageTableSize);
+
+ delete [] _defaultShapeTable;
+ _defaultShapeTable = 0;
+ _defaultShapeTableSize = 0;
+
+ res_freeLangTable(&_itemList, &_itemList_Size);
+ res_freeLangTable(&_takenList, &_takenList_Size);
+ res_freeLangTable(&_placedList, &_placedList_Size);
+ res_freeLangTable(&_droppedList, &_droppedList_Size);
+ res_freeLangTable(&_noDropList, &_noDropList_Size);
+
+ delete [] _amuleteAnim;
+ _amuleteAnim = 0;
+
+ for (int i = 0; i < 33; ++i) {
+ delete [] _specialPalettes[i];
+ _specialPalettes[i] = 0;
+ }
+
+ res_freeLangTable(&_putDownFirst, &_putDownFirst_Size);
+ res_freeLangTable(&_waitForAmulet, &_waitForAmulet_Size);
+ res_freeLangTable(&_blackJewel, &_blackJewel_Size);
+ res_freeLangTable(&_poisonGone, &_poisonGone_Size);
+ res_freeLangTable(&_healingTip, &_healingTip_Size);
+
+ delete [] _healingShapeTable;
+ _healingShapeTable = 0;
+ _healingShapeTableSize = 0;
+
+ delete [] _healingShape2Table;
+ _healingShape2Table = 0;
+ _healingShape2TableSize = 0;
+
+ res_freeLangTable(&_thePoison, &_thePoison_Size);
+ res_freeLangTable(&_fluteString, &_fluteString_Size);
+
+ delete [] _posionDeathShapeTable;
+ _posionDeathShapeTable = 0;
+ _posionDeathShapeTableSize = 0;
+
+ delete [] _fluteAnimShapeTable;
+ _fluteAnimShapeTable = 0;
+ _fluteAnimShapeTableSize = 0;
+
+ delete [] _winterScrollTable;
+ _winterScrollTable = 0;
+ _winterScrollTableSize = 0;
+
+ delete [] _winterScroll1Table;
+ _winterScroll1Table = 0;
+ _winterScroll1TableSize = 0;
+
+ delete [] _winterScroll2Table;
+ _winterScroll2Table = 0;
+ _winterScroll2TableSize = 0;
+
+ delete [] _drinkAnimationTable;
+ _drinkAnimationTable = 0;
+ _drinkAnimationTableSize = 0;
+
+ delete [] _brandonToWispTable;
+ _brandonToWispTable = 0;
+ _brandonToWispTableSize = 0;
+
+ delete [] _magicAnimationTable;
+ _magicAnimationTable = 0;
+ _magicAnimationTableSize = 0;
+
+ delete [] _brandonStoneTable;
+ _brandonStoneTable = 0;
+ _brandonStoneTableSize = 0;
+
+ res_freeLangTable(&_flaskFull, &_flaskFull_Size);
+ res_freeLangTable(&_fullFlask, &_fullFlask_Size);
+
+ res_freeLangTable(&_veryClever, &_veryClever_Size);
+ }
+}
+
+void KyraEngine::res_loadLangTable(const char *filename, PAKFile *res, byte ***loadTo, int *size, bool nativ) {
+ char file[36];
+ for (int i = 0; languages[i].ext; ++i) {
+ if (languages[i].flags != (_features & LANGUAGE_FLAGS) && nativ) {
+ continue;
+ }
+
+ strcpy(file, filename);
+ strcat(file, languages[i].ext);
+ if (_features & GF_TALKIE) {
+ strcat(file, ".CD");
+ } else if (_features & GF_DEMO) {
+ strcat(file, ".DEM");
+ }
+ byte *temp = getFile(*res, file);
+ if (temp) {
+ res_loadTable(temp, loadTo, size);
+ delete [] temp;
+ temp = 0;
+ } else {
+ if (!nativ)
+ continue;
+ }
+ break;
+ }
+}
+
+void KyraEngine::res_loadTable(const byte *src, byte ***loadTo, int *size) {
+ uint32 count = READ_BE_UINT32(src); src += 4;
+ *size = count;
+ *loadTo = new byte*[count];
+
+ const char *curPos = (const char*)src;
+ for (uint32 i = 0; i < count; ++i) {
+ int strLen = strlen(curPos);
+ (*loadTo)[i] = new byte[strLen+1];
+ memcpy((*loadTo)[i], curPos, strLen+1);
+ curPos += strLen+1;
+ }
+}
+
+void KyraEngine::res_loadRoomTable(const byte *src, Room **loadTo, int *size) {
+ uint32 count = READ_BE_UINT32(src); src += 4;
+ *size = count;
+ *loadTo = new Room[count];
+
+ for (uint32 i = 0; i < count; ++i) {
+ (*loadTo)[i].nameIndex = *src++;
+ (*loadTo)[i].northExit = READ_BE_UINT16(src); src += 2;
+ (*loadTo)[i].eastExit = READ_BE_UINT16(src); src += 2;
+ (*loadTo)[i].southExit = READ_BE_UINT16(src); src += 2;
+ (*loadTo)[i].westExit = READ_BE_UINT16(src); src += 2;
+ memset(&(*loadTo)[i].itemsTable[0], 0xFF, sizeof(byte)*6);
+ memset(&(*loadTo)[i].itemsTable[6], 0, sizeof(byte)*6);
+ memset((*loadTo)[i].itemsXPos, 0, sizeof(uint16)*12);
+ memset((*loadTo)[i].itemsYPos, 0, sizeof(uint8)*12);
+ memset((*loadTo)[i].needInit, 0, sizeof((*loadTo)[i].needInit));
+ }
+}
+
+void KyraEngine::res_loadShapeTable(const byte *src, Shape **loadTo, int *size) {
+ uint32 count = READ_BE_UINT32(src); src += 4;
+ *size = count;
+ *loadTo = new Shape[count];
+
+ for (uint32 i = 0; i < count; ++i) {
+ (*loadTo)[i].imageIndex = *src++;
+ (*loadTo)[i].x = *src++;
+ (*loadTo)[i].y = *src++;
+ (*loadTo)[i].w = *src++;
+ (*loadTo)[i].h = *src++;
+ (*loadTo)[i].xOffset = *src++;
+ (*loadTo)[i].yOffset = *src++;
+ }
+}
+
+void KyraEngine::res_freeLangTable(char ***string, int *size) {
+ if (!string || !size)
+ return;
+ if (!*size || !*string)
+ return;
+ for (int i = 0; i < *size; ++i) {
+ delete [] (*string)[i];
+ }
+ delete [] *string;
+ size = 0;
+ *string = 0;
+}
+
+void KyraEngine::loadMouseShapes() {
+ loadBitmap("MOUSE.CPS", 3, 3, 0);
+ _screen->_curPage = 2;
+ _shapes[4] = _screen->encodeShape(0, 0, 8, 10, 0);
+ _shapes[5] = _screen->encodeShape(0, 0x17, 0x20, 7, 0);
+ _shapes[6] = _screen->encodeShape(0x50, 0x12, 0x10, 9, 0);
+ _shapes[7] = _screen->encodeShape(0x60, 0x12, 0x10, 11, 0);
+ _shapes[8] = _screen->encodeShape(0x70, 0x12, 0x10, 9, 0);
+ _shapes[9] = _screen->encodeShape(0x80, 0x12, 0x10, 11, 0);
+ _shapes[10] = _screen->encodeShape(0x90, 0x12, 0x10, 10, 0);
+ _shapes[364] = _screen->encodeShape(0x28, 0, 0x10, 13, 0);
+ _screen->setMouseCursor(1, 1, 0);
+ _screen->setMouseCursor(1, 1, _shapes[4]);
+ _screen->setShapePages(5, 3);
+}
+
+void KyraEngine::loadCharacterShapes() {
+ int curImage = 0xFF;
+ int videoPage = _screen->_curPage;
+ _screen->_curPage = 2;
+ for (int i = 0; i < 115; ++i) {
+ assert(i < _defaultShapeTableSize);
+ Shape *shape = &_defaultShapeTable[i];
+ if (shape->imageIndex == 0xFF) {
+ _shapes[i+7+4] = 0;
+ continue;
+ }
+ if (shape->imageIndex != curImage) {
+ assert(shape->imageIndex < _characterImageTableSize);
+ loadBitmap(_characterImageTable[shape->imageIndex], 3, 3, 0);
+ curImage = shape->imageIndex;
+ }
+ _shapes[i+7+4] = _screen->encodeShape(shape->x<<3, shape->y, shape->w<<3, shape->h, 1);
+ }
+ _screen->_curPage = videoPage;
+}
+
+void KyraEngine::loadSpecialEffectShapes() {
+ loadBitmap("EFFECTS.CPS", 3, 3, 0);
+ _screen->_curPage = 2;
+
+ int currShape;
+ for (currShape = 173; currShape < 183; currShape++)
+ _shapes[4 + currShape] = _screen->encodeShape((currShape-173) * 24, 0, 24, 24, 1);
+
+ for (currShape = 183; currShape < 190; currShape++)
+ _shapes[4 + currShape] = _screen->encodeShape((currShape-183) * 24, 24, 24, 24, 1);
+
+ for (currShape = 190; currShape < 201; currShape++)
+ _shapes[4 + currShape] = _screen->encodeShape((currShape-190) * 24, 48, 24, 24, 1);
+
+ for (currShape = 201; currShape < 206; currShape++)
+ _shapes[4 + currShape] = _screen->encodeShape((currShape-201) * 16, 106, 16, 16, 1);
+}
+
+void KyraEngine::loadItems() {
+ int shape;
+
+ loadBitmap("JEWELS3.CPS", 3, 3, 0);
+ _screen->_curPage = 2;
+
+ _shapes[327] = 0;
+
+ for (shape = 1; shape < 6; shape++ )
+ _shapes[327 + shape] = _screen->encodeShape((shape - 1) * 32, 0, 32, 17, 0);
+
+ for (shape = 330; shape <= 334; shape++)
+ _shapes[4 + shape] = _screen->encodeShape((shape-330) * 32, 102, 32, 17, 0);
+
+ for (shape = 335; shape <= 339; shape++)
+ _shapes[4 + shape] = _screen->encodeShape((shape-335) * 32, 17, 32, 17, 0);
+
+ for (shape = 340; shape <= 344; shape++)
+ _shapes[4 + shape] = _screen->encodeShape((shape-340) * 32, 34, 32, 17, 0);
+
+ for (shape = 345; shape <= 349; shape++)
+ _shapes[4 + shape] = _screen->encodeShape((shape-345) * 32, 51, 32, 17, 0);
+
+ for (shape = 350; shape <= 354; shape++)
+ _shapes[4 + shape] = _screen->encodeShape((shape-350) * 32, 68, 32, 17, 0);
+
+ for (shape = 355; shape <= 359; shape++)
+ _shapes[4 + shape] = _screen->encodeShape((shape-355) * 32, 85, 32, 17, 0);
+
+
+ loadBitmap("ITEMS.CPS", 3, 3, 0);
+ _screen->_curPage = 2;
+
+ for (int i = 0; i < 107; i++) {
+ shape = findDuplicateItemShape(i);
+
+ if (shape != -1)
+ _shapes[220 + i] = _shapes[220 + shape];
+ else
+ _shapes[220 + i] = _screen->encodeShape( (i % 20) * 16, i/20 * 16, 16, 16, 0);
+ }
+
+ uint32 size;
+ uint8 *fileData = _res->fileData("_ITEM_HT.DAT", &size);
+ assert(fileData);
+
+ for (int i = 0; i < 107; i++) {
+ _itemTable[i].height = fileData[i];
+ _itemTable[i].unk1 = _itemTable[i].unk2 = 0;
+ }
+
+ delete[] fileData;
+}
+
+void KyraEngine::loadButtonShapes() {
+ loadBitmap("BUTTONS2.CPS", 3, 3, 0);
+ _screen->_curPage = 2;
+ _scrollUpButton.process0PtrShape = _screen->encodeShape(0, 0, 24, 14, 1);
+ _scrollUpButton.process1PtrShape = _screen->encodeShape(24, 0, 24, 14, 1);
+ _scrollUpButton.process2PtrShape = _screen->encodeShape(48, 0, 24, 14, 1);
+ _scrollDownButton.process0PtrShape = _screen->encodeShape(0, 15, 24, 14, 1);
+ _scrollDownButton.process1PtrShape = _screen->encodeShape(24, 15, 24, 14, 1);
+ _scrollDownButton.process2PtrShape = _screen->encodeShape(48, 15, 24, 14, 1);
+ _screen->_curPage = 0;
+}
+
+void KyraEngine::loadMainScreen(int page) {
+ if ((_features & GF_ENGLISH) && (_features & GF_TALKIE))
+ loadBitmap("MAIN_ENG.CPS", page, page, 0);
+ else if(_features & GF_FRENCH)
+ loadBitmap("MAIN_FRE.CPS", page, page, 0);
+ else if(_features & GF_GERMAN)
+ loadBitmap("MAIN_GER.CPS", page, page, 0);
+ else if ((_features & GF_ENGLISH) && (_features & GF_FLOPPY))
+ loadBitmap("MAIN15.CPS", page, page, 0);
+ else if (_features & GF_SPANISH)
+ loadBitmap("MAIN_SPA.CPS", page, page, 0);
+ else
+ warning("no main graphics file found");
+
+ uint8 *_pageSrc = _screen->getPagePtr(page);
+ uint8 *_pageDst = _screen->getPagePtr(0);
+ memcpy(_pageDst, _pageSrc, 320*200);
+}
+
+const ScreenDim Screen::_screenDimTable[] = {
+ { 0x00, 0x00, 0x28, 0xC8, 0x0F, 0x0C, 0x00, 0x00 },
+ { 0x08, 0x48, 0x18, 0x38, 0x0F, 0x0C, 0x00, 0x00 },
+ { 0x01, 0x08, 0x26, 0x80, 0x0F, 0x0C, 0x00, 0x00 },
+ { 0x00, 0xC2, 0x28, 0x06, 0x0F, 0x0C, 0x00, 0x00 },
+ { 0x00, 0x90, 0x28, 0x38, 0x04, 0x0C, 0x00, 0x00 },
+ { 0x01, 0x94, 0x26, 0x30, 0x04, 0x1B, 0x00, 0x00 },
+ { 0x00, 0x90, 0x28, 0x38, 0x0F, 0x0D, 0x00, 0x00 },
+ { 0x01, 0x96, 0x26, 0x32, 0x0F, 0x0D, 0x00, 0x00 },
+ { 0x00, 0x00, 0x28, 0x88, 0x0F, 0x0C, 0x00, 0x00 },
+ { 0x01, 0x20, 0x26, 0x80, 0x0F, 0x0C, 0x00, 0x00 },
+ { 0x03, 0x28, 0x22, 0x46, 0x0F, 0x0D, 0x00, 0x00 }
+};
+
+const int Screen::_screenDimTableCount = ARRAYSIZE(_screenDimTable);
+
+// CD Version *could* use an different opcodeTable
+#define Opcode(x) &KyraEngine::x
+KyraEngine::OpcodeProc KyraEngine::_opcodeTable[] = {
+ // 0x00
+ Opcode(cmd_magicInMouseItem),
+ Opcode(cmd_characterSays),
+ Opcode(cmd_pauseTicks),
+ Opcode(cmd_drawSceneAnimShape),
+ // 0x04
+ Opcode(cmd_queryGameFlag),
+ Opcode(cmd_setGameFlag),
+ Opcode(cmd_resetGameFlag),
+ Opcode(cmd_runNPCScript),
+ // 0x08
+ Opcode(cmd_setSpecialExitList),
+ Opcode(cmd_blockInWalkableRegion),
+ Opcode(cmd_blockOutWalkableRegion),
+ Opcode(cmd_walkPlayerToPoint),
+ // 0x0c
+ Opcode(cmd_dropItemInScene),
+ Opcode(cmd_drawAnimShapeIntoScene),
+ Opcode(cmd_createMouseItem),
+ Opcode(cmd_savePageToDisk),
+ // 0x10
+ Opcode(cmd_sceneAnimOn),
+ Opcode(cmd_sceneAnimOff),
+ Opcode(cmd_getElapsedSeconds),
+ Opcode(cmd_mouseIsPointer),
+ // 0x14
+ Opcode(cmd_destroyMouseItem),
+ Opcode(cmd_runSceneAnimUntilDone),
+ Opcode(cmd_fadeSpecialPalette),
+ Opcode(cmd_playAdlibSound),
+ // 0x18
+ Opcode(cmd_playAdlibScore),
+ Opcode(cmd_phaseInSameScene),
+ Opcode(cmd_setScenePhasingFlag),
+ Opcode(cmd_resetScenePhasingFlag),
+ // 0x1c
+ Opcode(cmd_queryScenePhasingFlag),
+ Opcode(cmd_sceneToDirection),
+ Opcode(cmd_setBirthstoneGem),
+ Opcode(cmd_placeItemInGenericMapScene),
+ // 0x20
+ Opcode(cmd_setBrandonStatusBit),
+ Opcode(cmd_pauseSeconds),
+ Opcode(cmd_getCharactersLocation),
+ Opcode(cmd_runNPCSubscript),
+ // 0x24
+ Opcode(cmd_magicOutMouseItem),
+ Opcode(cmd_internalAnimOn),
+ Opcode(cmd_forceBrandonToNormal),
+ Opcode(cmd_poisonDeathNow),
+ // 0x28
+ Opcode(cmd_setScaleMode),
+ Opcode(cmd_openWSAFile),
+ Opcode(cmd_closeWSAFile),
+ Opcode(cmd_runWSAFromBeginningToEnd),
+ // 0x2c
+ Opcode(cmd_displayWSAFrame),
+ Opcode(cmd_enterNewScene),
+ Opcode(cmd_setSpecialEnterXAndY),
+ Opcode(cmd_runWSAFrames),
+ // 0x30
+ Opcode(cmd_popBrandonIntoScene),
+ Opcode(cmd_restoreAllObjectBackgrounds),
+ Opcode(cmd_setCustomPaletteRange),
+ Opcode(cmd_loadPageFromDisk),
+ // 0x34
+ Opcode(cmd_customPrintTalkString),
+ Opcode(cmd_restoreCustomPrintBackground),
+ Opcode(cmd_hideMouse),
+ Opcode(cmd_showMouse),
+ // 0x38
+ Opcode(cmd_getCharacterX),
+ Opcode(cmd_getCharacterY),
+ Opcode(cmd_changeCharactersFacing),
+ Opcode(cmd_copyWSARegion),
+ // 0x3c
+ Opcode(cmd_printText),
+ Opcode(cmd_random),
+ Opcode(cmd_loadSoundFile),
+ Opcode(cmd_displayWSAFrameOnHidPage),
+ // 0x40
+ Opcode(cmd_displayWSASequentialFrames),
+ Opcode(cmd_drawCharacterStanding),
+ Opcode(cmd_internalAnimOff),
+ Opcode(cmd_changeCharactersXAndY),
+ // 0x44
+ Opcode(cmd_clearSceneAnimatorBeacon),
+ Opcode(cmd_querySceneAnimatorBeacon),
+ Opcode(cmd_refreshSceneAnimator),
+ Opcode(cmd_placeItemInOffScene),
+ // 0x48
+ Opcode(cmd_wipeDownMouseItem),
+ Opcode(cmd_placeCharacterInOtherScene),
+ Opcode(cmd_getKey),
+ Opcode(cmd_specificItemInInventory),
+ // 0x4c
+ Opcode(cmd_popMobileNPCIntoScene),
+ Opcode(cmd_mobileCharacterInScene),
+ Opcode(cmd_hideMobileCharacter),
+ Opcode(cmd_unhideMobileCharacter),
+ // 0x50
+ Opcode(cmd_setCharactersLocation),
+ Opcode(cmd_walkCharacterToPoint),
+ Opcode(cmd_specialEventDisplayBrynnsNote),
+ Opcode(cmd_specialEventRemoveBrynnsNote),
+ // 0x54
+ Opcode(cmd_setLogicPage),
+ Opcode(cmd_fatPrint),
+ Opcode(cmd_preserveAllObjectBackgrounds),
+ Opcode(cmd_updateSceneAnimations),
+ // 0x58
+ Opcode(cmd_sceneAnimationActive),
+ Opcode(cmd_setCharactersMovementDelay),
+ Opcode(cmd_getCharactersFacing),
+ Opcode(cmd_bkgdScrollSceneAndMasksRight),
+ // 0x5c
+ Opcode(cmd_dispelMagicAnimation),
+ Opcode(cmd_findBrightestFireberry),
+ Opcode(cmd_setFireberryGlowPalette),
+ Opcode(cmd_setDeathHandlerFlag),
+ // 0x60
+ Opcode(cmd_drinkPotionAnimation),
+ Opcode(cmd_makeAmuletAppear),
+ Opcode(cmd_drawItemShapeIntoScene),
+ Opcode(cmd_setCharactersCurrentFrame),
+ // 0x64
+ Opcode(cmd_waitForConfirmationMouseClick),
+ Opcode(cmd_pageFlip),
+ Opcode(cmd_setSceneFile),
+ Opcode(cmd_getItemInMarbleVase),
+ // 0x68
+ Opcode(cmd_setItemInMarbleVase),
+ Opcode(cmd_addItemToInventory),
+ Opcode(cmd_intPrint),
+ Opcode(cmd_shakeScreen),
+ // 0x6c
+ Opcode(cmd_createAmuletJewel),
+ Opcode(cmd_setSceneAnimCurrXY),
+ Opcode(cmd_poisonBrandonAndRemaps),
+ Opcode(cmd_fillFlaskWithWater),
+ // 0x70
+ Opcode(cmd_getCharactersMovementDelay),
+ Opcode(cmd_getBirthstoneGem),
+ Opcode(cmd_queryBrandonStatusBit),
+ Opcode(cmd_playFluteAnimation),
+ // 0x74
+ Opcode(cmd_playWinterScrollSequence),
+ Opcode(cmd_getIdolGem),
+ Opcode(cmd_setIdolGem),
+ Opcode(cmd_totalItemsInScene),
+ // 0x78
+ Opcode(cmd_restoreBrandonsMovementDelay),
+ Opcode(cmd_setMousePos),
+ Opcode(cmd_getMouseState),
+ Opcode(cmd_setEntranceMouseCursorTrack),
+ // 0x7c
+ Opcode(cmd_itemAppearsOnGround),
+ Opcode(cmd_setNoDrawShapesFlag),
+ Opcode(cmd_fadeEntirePalette),
+ Opcode(cmd_itemOnGroundHere),
+ // 0x80
+ Opcode(cmd_queryCauldronState),
+ Opcode(cmd_setCauldronState),
+ Opcode(cmd_queryCrystalState),
+ Opcode(cmd_setCrystalState),
+ // 0x84
+ Opcode(cmd_setPaletteRange),
+ Opcode(cmd_shrinkBrandonDown),
+ Opcode(cmd_growBrandonUp),
+ Opcode(cmd_setBrandonScaleXAndY),
+ // 0x88
+ Opcode(cmd_resetScaleMode),
+ Opcode(cmd_getScaleDepthTableValue),
+ Opcode(cmd_setScaleDepthTableValue),
+ Opcode(cmd_message),
+ // 0x8c
+ Opcode(cmd_checkClickOnNPC),
+ Opcode(cmd_getFoyerItem),
+ Opcode(cmd_setFoyerItem),
+ Opcode(cmd_setNoItemDropRegion),
+ // 0x90
+ Opcode(cmd_walkMalcolmOn),
+ Opcode(cmd_passiveProtection),
+ Opcode(cmd_setPlayingLoop),
+ Opcode(cmd_brandonToStoneSequence),
+ // 0x94
+ Opcode(cmd_brandonHealingSequence),
+ Opcode(cmd_protectCommandLine),
+ Opcode(cmd_pauseMusicSeconds),
+ Opcode(cmd_resetMaskRegion),
+ // 0x98
+ Opcode(cmd_setPaletteChangeFlag),
+ Opcode(cmd_fillRect),
+ Opcode(cmd_vocUnload),
+ Opcode(cmd_vocLoad),
+ Opcode(cmd_dummy)
+};
+#undef Opcode
+
+const int KyraEngine::_opcodeTableSize = ARRAYSIZE(_opcodeTable);
+
+const char *KyraEngine::_xmidiFiles[] = {
+ "INTRO.XMI",
+ "KYRA1A.XMI",
+ "KYRA1B.XMI",
+ "KYRA2A.XMI",
+ "KYRA3A.XMI",
+ "KYRA4A.XMI",
+ "KYRA4B.XMI",
+ "KYRA5A.XMI",
+ "KYRA5B.XMI",
+ "KYRAMISC.XMI"
+};
+
+const int KyraEngine::_xmidiFilesCount = ARRAYSIZE(_xmidiFiles);
+
+const int8 KyraEngine::_charXPosTable[] = {
+ 0, 4, 4, 4, 0, -4, -4, -4
+};
+
+const int8 KyraEngine::_addXPosTable[] = {
+ 4, 4, 0, -4, -4, -4, 0, 4
+};
+
+const int8 KyraEngine::_charYPosTable[] = {
+ -2, -2, 0, 2, 2, 2, 0, -2
+};
+
+const int8 KyraEngine::_addYPosTable[] = {
+ 0, -2, -2, -2, 0, 2, 2, 2
+};
+
+const uint16 KyraEngine::_itemPosX[] = {
+ 95, 115, 135, 155, 175, 95, 115, 135, 155, 175
+};
+
+const uint8 KyraEngine::_itemPosY[] = {
+ 160, 160, 160, 160, 160, 181, 181, 181, 181, 181
+};
+
+Button KyraEngine::_buttonData[] = {
+ { 0, 0x02, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x05D, 0x9E, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x01, /*XXX,*/1, 1, 1, /*XXX,*/ 0x0487, 0, 0, 0, 0, 0, 0, 0, 0x009, 0xA4, 0x36, 0x1E, /*XXX,*/ 0, &KyraEngine::buttonMenuCallback/*, XXX*/ },
+ { 0, 0x03, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x071, 0x9E, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x04, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x085, 0x9E, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x05, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x099, 0x9E, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x06, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x0AD, 0x9E, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x07, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x05D, 0xB3, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x08, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x071, 0xB3, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x09, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x085, 0xB3, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x0A, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x099, 0xB3, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x0B, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x0AD, 0xB3, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x15, /*XXX,*/1, 1, 1, /*XXX,*/ 0x0487, 0, 0, 0, 0, 0, 0, 0, 0x0FD, 0x9C, 0x1A, 0x12, /*XXX,*/ 0, &KyraEngine::buttonAmuletCallback/*, XXX*/ },
+ { 0, 0x16, /*XXX,*/1, 1, 1, /*XXX,*/ 0x0487, 0, 0, 0, 0, 0, 0, 0, 0x0E7, 0xAA, 0x1A, 0x12, /*XXX,*/ 0, &KyraEngine::buttonAmuletCallback/*, XXX*/ },
+ { 0, 0x17, /*XXX,*/1, 1, 1, /*XXX,*/ 0x0487, 0, 0, 0, 0, 0, 0, 0, 0x0FD, 0xB5, 0x1A, 0x12, /*XXX,*/ 0, &KyraEngine::buttonAmuletCallback/*, XXX*/ },
+ { 0, 0x18, /*XXX,*/1, 1, 1, /*XXX,*/ 0x0487, 0, 0, 0, 0, 0, 0, 0, 0x113, 0xAA, 0x1A, 0x12, /*XXX,*/ 0, &KyraEngine::buttonAmuletCallback/*, XXX*/ }
+};
+
+Button *KyraEngine::_buttonDataListPtr[] = {
+ &_buttonData[1],
+ &_buttonData[2],
+ &_buttonData[3],
+ &_buttonData[4],
+ &_buttonData[5],
+ &_buttonData[6],
+ &_buttonData[7],
+ &_buttonData[8],
+ &_buttonData[9],
+ &_buttonData[10],
+ &_buttonData[11],
+ &_buttonData[12],
+ &_buttonData[13],
+ &_buttonData[14],
+ 0
+};
+
+Button KyraEngine::_scrollUpButton = {0, 0x12, 1, 1, 1, 0x483, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x18, 0x0f, 0, 0};
+Button KyraEngine::_scrollDownButton = {0, 0x13, 1, 1, 1, 0x483, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x18, 0x0f, 0, 0};
+
+
+
+Button KyraEngine::_menuButtonData[] = {
+ { 0, 0x0c, /*XXX,*/1, 1, 1, /*XXX,*/ 0x487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*XXX,*/ 0, 0 /*, XXX*/ },
+ { 0, 0x0d, /*XXX,*/1, 1, 1, /*XXX,*/ 0x487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*XXX,*/ 0, 0 /*, XXX*/ },
+ { 0, 0x0e, /*XXX,*/1, 1, 1, /*XXX,*/ 0x487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*XXX,*/ 0, 0 /*, XXX*/ },
+ { 0, 0x0f, /*XXX,*/1, 1, 1, /*XXX,*/ 0x487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*XXX,*/ 0, 0 /*, XXX*/ },
+ { 0, 0x10, /*XXX,*/1, 1, 1, /*XXX,*/ 0x487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*XXX,*/ 0, 0 /*, XXX*/ },
+ { 0, 0x11, /*XXX,*/1, 1, 1, /*XXX,*/ 0x487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*XXX,*/ 0, 0 /*, XXX*/ }
+};
+
+Menu KyraEngine::_menu[] = {
+ { -1, -1, 208, 136, 248, 249, 250, "The Legend of Kyrandia", 251, -1, 8, 0, 5, -1, -1, -1, -1,
+ {
+ {1, 0, 0, "Load a Game", -1, -1, 30, 148, 15, 252, 253, 24, 0,
+ 248, 249, 250, &KyraEngine::gui_loadGameMenu, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "Save this Game", -1, -1, 47, 148, 15, 252, 253, 24, 0,
+ 248, 249, 250, &KyraEngine::gui_saveGameMenu, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "Game Controls", -1, -1, 64, 148, 15, 252, 253, 24, 0,
+ 248, 249, 250, /*&menu_gameControls*/ 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "Quit playing", -1, -1, 81, 148, 15, 252, 253, 24, 0,
+ 248, 249, 250, &KyraEngine::gui_quitPlaying, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "Resume game", 86, 0, 110, 92, 15, 252, 253, -1, 255,
+ 248, 249, 250, &KyraEngine::gui_resumeGame, -1, 0, 0, 0, 0, 0}
+ }
+ },
+ { -1, -1, 288, 56, 248, 249, 250, 0, 254,-1, 8, 0, 2, -1, -1, -1, -1,
+ {
+ {1, 0, 0, "Yes", 24, 0, 30, 72, 15, 252, 253, -1, 255,
+ 248, 249, 250, &KyraEngine::gui_quitConfirmYes, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "No", 192, 0, 30, 72, 15, 252, 253, -1, 255,
+ 248, 249, 250, &KyraEngine::gui_quitConfirmNo, -1, 0, 0, 0, 0, 0}
+ }
+ },
+ { -1, -1, 288, 160, 248, 249, 250, 0, 251, -1, 8, 0, 6, 132, 22, 132, 124,
+ {
+ {1, 0, 0, 0, -1, 255, 39, 256, 15, 252, 253, 5, 0,
+ 248, 249, 250, 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, 0, -1, 255, 56, 256, 15, 252, 253, 5, 0,
+ 248, 249, 250, 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, 0, -1, 255, 73, 256, 15, 252, 253, 5, 0,
+ 248, 249, 250, 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, 0, -1, 255, 90, 256, 15, 252, 253, 5, 0,
+ 248, 249, 250, 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, 0, -1, 255, 107, 256, 15, 252, 253, 5, 0,
+ 248, 249, 250, 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "Cancel", 184, 0, 134, 88, 15, 252, 253, -1, 255,
+ 248, 249, 250, &KyraEngine::gui_cancelSubMenu, -1, 0, 0, 0, 0, 0},
+ }
+ },
+ { -1, -1, 288, 67, 248, 249, 250, "Enter a description of your saved game:", 251, -1, 8, 0, 3, -1, -1, -1, -1,
+ {
+ {1, 0, 0, "Save", 24, 0, 44, 72, 15, 252, 253, -1, 255,
+ 248, 249, 250, &KyraEngine::gui_savegameConfirm, -1, 0, 0, 0, 0, 0},
+ // {1, 0, 0, "Cancel", 110, 0, 44, 72, 15, 252, 253, -1, 255,
+ // 248, 249, 250, /*&menu_cancelconfirmsave*/ 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "Cancel", 192, 0, 44, 72, 15, 252, 253, -1, 255,
+ 248, 249, 250, &KyraEngine::gui_cancelSubMenu, -1, 0, 0, 0, 0, 0}
+ }
+ }
+};
+
+const uint8 KyraEngine::_magicMouseItemStartFrame[] = {
+ 0xAD, 0xB7, 0xBE, 0x00
+};
+
+const uint8 KyraEngine::_magicMouseItemEndFrame[] = {
+ 0xB1, 0xB9, 0xC2, 0x00
+};
+
+const uint8 KyraEngine::_magicMouseItemStartFrame2[] = {
+ 0xB2, 0xBA, 0xC3, 0x00
+};
+
+const uint8 KyraEngine::_magicMouseItemEndFrame2[] = {
+ 0xB6, 0xBD, 0xC8, 0x00
+};
+
+const uint16 KyraEngine::_amuletX[] = { 231, 275, 253, 253 };
+const uint16 KyraEngine::_amuletY[] = { 170, 170, 159, 181 };
+
+const uint16 KyraEngine::_amuletX2[] = { 0x000, 0x0FD, 0x0E7, 0x0FD, 0x113, 0x000 };
+const uint16 KyraEngine::_amuletY2[] = { 0x000, 0x09F, 0x0AA, 0x0B5, 0x0AA, 0x000 };
+} // End of namespace Kyra
diff --git a/engines/kyra/text.cpp b/engines/kyra/text.cpp
new file mode 100644
index 0000000000..2877687682
--- /dev/null
+++ b/engines/kyra/text.cpp
@@ -0,0 +1,574 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-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 "kyra/kyra.h"
+#include "kyra/screen.h"
+#include "kyra/text.h"
+#include "kyra/animator.h"
+#include "kyra/sprites.h"
+
+#include "common/system.h"
+
+namespace Kyra {
+
+void KyraEngine::waitForChatToFinish(int16 chatDuration, char *chatStr, uint8 charNum) {
+ debug(9, "KyraEngine::waitForChatToFinish(%i, %s, %i)", chatDuration, chatStr, charNum);
+ bool hasUpdatedNPCs = false;
+ bool runLoop = true;
+ uint8 currPage;
+ OSystem::Event event;
+ int16 delayTime;
+
+ //while( towns_isEscKeyPressed() )
+ //towns_getKey();
+
+ uint32 timeToEnd = strlen(chatStr) * 8 * _tickLength + _system->getMillis();
+
+ if (chatDuration != -1 ) {
+ switch (_configTalkspeed) {
+ case 0: chatDuration *= 2;
+ break;
+ case 2: chatDuration /= 4;
+ break;
+ case 3: chatDuration = -1;
+ }
+ }
+
+ if (chatDuration != -1)
+ chatDuration *= _tickLength;
+
+ disableTimer(14);
+ disableTimer(18);
+ disableTimer(19);
+
+ uint32 timeAtStart = _system->getMillis();
+ uint32 loopStart;
+ while (runLoop) {
+ loopStart = _system->getMillis();
+ if (_currentCharacter->sceneId == 210)
+ if (seq_playEnd())
+ break;
+
+ if (_system->getMillis() > timeToEnd && !hasUpdatedNPCs) {
+ hasUpdatedNPCs = true;
+ disableTimer(15);
+ _currHeadShape = 4;
+ _animator->animRefreshNPC(0);
+ _animator->animRefreshNPC(_talkingCharNum);
+
+ if (_charSayUnk2 != -1) {
+ _animator->sprites()[_charSayUnk2].active = 0;
+ _sprites->_anims[_charSayUnk2].play = false;
+ _charSayUnk2 = -1;
+ }
+ }
+
+ updateGameTimers();
+ _sprites->updateSceneAnims();
+ _animator->restoreAllObjectBackgrounds();
+ _animator->preserveAnyChangedBackgrounds();
+ _animator->prepDrawAllObjects();
+
+ currPage = _screen->_curPage;
+ _screen->_curPage = 2;
+ _text->printCharacterText(chatStr, charNum, _characterList[charNum].x1);
+ _animator->_updateScreen = true;
+ _screen->_curPage = currPage;
+
+ _animator->copyChangedObjectsForward(0);
+ updateTextFade();
+
+ if ((chatDuration < (int16)(_system->getMillis() - timeAtStart)) && chatDuration != -1)
+ break;
+
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ if (event.kbd.keycode == '.')
+ _skipFlag = true;
+ break;
+ case OSystem::EVENT_QUIT:
+ quitGame();
+ case OSystem::EVENT_LBUTTONDOWN:
+ runLoop = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (_skipFlag)
+ runLoop = false;
+
+ delayTime = (loopStart + _gameSpeed) - _system->getMillis();
+ if (delayTime > 0)
+ _system->delayMillis(delayTime);
+ }
+
+ enableTimer(14);
+ enableTimer(15);
+ enableTimer(18);
+ enableTimer(19);
+ //clearKyrandiaButtonIO();
+}
+
+void KyraEngine::endCharacterChat(int8 charNum, int16 convoInitialized) {
+ _charSayUnk3 = -1;
+
+ if (charNum > 4 && charNum < 11) {
+ //TODO: weird _game_inventory stuff here
+ warning("STUB: endCharacterChat() for high charnums");
+ }
+
+ if (convoInitialized != 0) {
+ _talkingCharNum = -1;
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+ }
+}
+
+void KyraEngine::restoreChatPartnerAnimFrame(int8 charNum) {
+ _talkingCharNum = -1;
+
+ if (charNum > 0 && charNum < 5) {
+ _characterList[charNum].currentAnimFrame = _currentChatPartnerBackupFrame;
+ _animator->animRefreshNPC(charNum);
+ }
+
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+}
+
+void KyraEngine::backupChatPartnerAnimFrame(int8 charNum) {
+ _talkingCharNum = 0;
+
+ if (charNum < 5 && charNum > 0)
+ _currentChatPartnerBackupFrame = _characterList[charNum].currentAnimFrame;
+
+ if (_scaleMode != 0)
+ _currentCharacter->currentAnimFrame = 7;
+ else
+ _currentCharacter->currentAnimFrame = _currentCharAnimFrame;
+
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+}
+
+int8 KyraEngine::getChatPartnerNum() {
+ uint8 sceneTable[] = {0x2, 0x5, 0x2D, 0x7, 0x1B, 0x8, 0x22, 0x9, 0x30, 0x0A};
+ int pos = 0;
+ int partner = -1;
+
+ for (int i = 1; i < 6; i++) {
+ if (_currentCharacter->sceneId == sceneTable[pos]) {
+ partner = sceneTable[pos+1];
+ break;
+ }
+ pos += 2;
+ }
+
+ for (int i = 1; i < 5; i++) {
+ if (_characterList[i].sceneId == _currentCharacter->sceneId) {
+ partner = i;
+ break;
+ }
+ }
+ return partner;
+}
+
+int KyraEngine::initCharacterChat(int8 charNum) {
+ if (_talkingCharNum == -1) {
+ _talkingCharNum = 0;
+
+ if (_scaleMode != 0)
+ _currentCharacter->currentAnimFrame = 7;
+ else
+ _currentCharacter->currentAnimFrame = 16;
+
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+ }
+
+ _charSayUnk2 = -1;
+ _animator->flagAllObjectsForBkgdChange();
+ _animator->restoreAllObjectBackgrounds();
+
+ if (charNum > 4 && charNum < 11) {
+ // TODO: Fill in weird _game_inventory stuff here
+ warning("STUB: initCharacterChat() for high charnums");
+ }
+
+ _animator->flagAllObjectsForRefresh();
+ _animator->flagAllObjectsForBkgdChange();
+ _animator->preserveAnyChangedBackgrounds();
+ _charSayUnk3 = charNum;
+
+ return 1;
+}
+
+void KyraEngine::characterSays(char *chatStr, int8 charNum, int8 chatDuration) {
+ debug(9, "KyraEngine::characterSays('%s', %i, %d)", chatStr, charNum, chatDuration);
+ uint8 startAnimFrames[] = { 0x10, 0x32, 0x56, 0x0, 0x0, 0x0 };
+
+ uint16 chatTicks;
+ int16 convoInitialized;
+ int8 chatPartnerNum;
+
+ if (_currentCharacter->sceneId == 210)
+ return;
+
+ convoInitialized = initCharacterChat(charNum);
+ chatPartnerNum = getChatPartnerNum();
+
+ if (chatPartnerNum != -1 && chatPartnerNum < 5)
+ backupChatPartnerAnimFrame(chatPartnerNum);
+
+ if (charNum < 5) {
+ _characterList[charNum].currentAnimFrame = startAnimFrames[charNum];
+ _charSayUnk3 = charNum;
+ _talkingCharNum = charNum;
+ _animator->animRefreshNPC(charNum);
+ }
+
+ char *processedString = _text->preprocessString(chatStr);
+ int lineNum = _text->buildMessageSubstrings(processedString);
+
+ int16 yPos = _characterList[charNum].y1;
+ yPos -= _scaleTable[charNum] * _characterList[charNum].height;
+ yPos -= 8;
+ yPos -= lineNum * 10;
+
+ if (yPos < 11)
+ yPos = 11;
+
+ if (yPos > 100)
+ yPos = 100;
+
+ _text->_talkMessageY = yPos;
+ _text->_talkMessageH = lineNum * 10;
+ _animator->restoreAllObjectBackgrounds();
+
+ _screen->copyRegion(12, _text->_talkMessageY, 12, 136, 296, _text->_talkMessageH, 2, 2);
+ _screen->hideMouse();
+
+ _text->printCharacterText(processedString, charNum, _characterList[charNum].x1);
+ _screen->showMouse();
+
+ if (chatDuration == -2)
+ chatTicks = strlen(processedString) * 9;
+ else
+ chatTicks = chatDuration;
+
+ waitForChatToFinish(chatTicks, chatStr, charNum);
+
+ _animator->restoreAllObjectBackgrounds();
+
+ _screen->copyRegion(12, 136, 12, _text->_talkMessageY, 296, _text->_talkMessageH, 2, 2);
+ _animator->preserveAllBackgrounds();
+ _animator->prepDrawAllObjects();
+ _screen->hideMouse();
+
+ _screen->copyRegion(12, _text->_talkMessageY, 12, _text->_talkMessageY, 296, _text->_talkMessageH, 2, 0);
+ _screen->showMouse();
+ _animator->flagAllObjectsForRefresh();
+ _animator->copyChangedObjectsForward(0);
+
+ if (chatPartnerNum != -1 && chatPartnerNum < 5)
+ restoreChatPartnerAnimFrame(chatPartnerNum);
+
+ endCharacterChat(charNum, convoInitialized);
+}
+
+void KyraEngine::drawSentenceCommand(char *sentence, int color) {
+ debug(9, "KyraEngine::drawSentenceCommand('%s', %i)", sentence, color);
+ _screen->hideMouse();
+ _screen->fillRect(8, 143, 311, 152, 12);
+
+ if (_startSentencePalIndex != color || _fadeText != false) {
+ _currSentenceColor[0] = _screen->_currentPalette[765] = _screen->_currentPalette[color*3];
+ _currSentenceColor[1] = _screen->_currentPalette[766] = _screen->_currentPalette[color*3+1];
+ _currSentenceColor[2] = _screen->_currentPalette[767] = _screen->_currentPalette[color*3+2];
+
+ _screen->setScreenPalette(_screen->_currentPalette);
+ _startSentencePalIndex = 0;
+ }
+
+ _text->printText(sentence, 8, 143, 0xFF, 12, 0);
+ _screen->showMouse();
+ setTextFadeTimerCountdown(15);
+ _fadeText = false;
+}
+
+void KyraEngine::updateSentenceCommand(char *str1, char *str2, int color) {
+ debug(9, "KyraEngine::updateSentenceCommand('%s', '%s', %i)", str1, str2, color);
+ char sentenceCommand[500];
+ strncpy(sentenceCommand, str1, 500);
+ if (str2)
+ strncat(sentenceCommand, str2, 500 - strlen(sentenceCommand));
+
+ drawSentenceCommand(sentenceCommand, color);
+ _screen->updateScreen();
+}
+
+void KyraEngine::updateTextFade() {
+ debug(9, "KyraEngine::updateTextFade()");
+ if (!_fadeText)
+ return;
+
+ bool finished = false;
+ for (int i = 0; i < 3; i++)
+ if (_currSentenceColor[i] > 4)
+ _currSentenceColor[i] -= 4;
+ else
+ if (_currSentenceColor[i]) {
+ _currSentenceColor[i] = 0;
+ finished = true;
+ }
+
+ _screen->_currentPalette[765] = _currSentenceColor[0];
+ _screen->_currentPalette[766] = _currSentenceColor[1];
+ _screen->_currentPalette[767] = _currSentenceColor[2];
+ _screen->setScreenPalette(_screen->_currentPalette);
+
+ if (finished) {
+ _fadeText = false;
+ _startSentencePalIndex = -1;
+ }
+}
+
+TextDisplayer::TextDisplayer(Screen *screen) {
+ _screen = screen;
+
+ _talkCoords.y = 0x88;
+ _talkCoords.x = 0;
+ _talkCoords.w = 0;
+ _talkMessageY = 0xC;
+ _talkMessageH = 0;
+ _talkMessagePrinted = false;
+}
+
+void TextDisplayer::setTalkCoords(uint16 y) {
+ debug(9, "TextDisplayer::setTalkCoords(%d)", y);
+ _talkCoords.y = y;
+}
+
+int TextDisplayer::getCenterStringX(const char *str, int x1, int x2) {
+ debug(9, "TextDisplayer::getCenterStringX('%s', %d, %d)", str, x1, x2);
+ _screen->_charWidth = -2;
+ Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
+ int strWidth = _screen->getTextWidth(str);
+ _screen->setFont(curFont);
+ _screen->_charWidth = 0;
+ int w = x2 - x1 + 1;
+ return x1 + (w - strWidth) / 2;
+}
+
+int TextDisplayer::getCharLength(const char *str, int len) {
+ debug(9, "TextDisplayer::getCharLength('%s', %d)", str, len);
+ int charsCount = 0;
+ if (*str) {
+ _screen->_charWidth = -2;
+ Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
+ int i = 0;
+ while (i <= len && *str) {
+ i += _screen->getCharWidth(*str++);
+ ++charsCount;
+ }
+ _screen->setFont(curFont);
+ _screen->_charWidth = 0;
+ }
+ return charsCount;
+}
+
+int TextDisplayer::dropCRIntoString(char *str, int offs) {
+ debug(9, "TextDisplayer::dropCRIntoString('%s', %d)", str, offs);
+ int pos = 0;
+ str += offs;
+ while (*str) {
+ if (*str == ' ') {
+ *str = '\r';
+ return pos;
+ }
+ ++str;
+ ++pos;
+ }
+ return 0;
+}
+
+char *TextDisplayer::preprocessString(const char *str) {
+ debug(9, "TextDisplayer::preprocessString('%s')", str);
+ assert(strlen(str) < sizeof(_talkBuffer) - 1);
+ strcpy(_talkBuffer, str);
+ char *p = _talkBuffer;
+ while (*p) {
+ if (*p == '\r') {
+ return _talkBuffer;
+ }
+ ++p;
+ }
+ p = _talkBuffer;
+ Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
+ _screen->_charWidth = -2;
+ int textWidth = _screen->getTextWidth(p);
+ _screen->_charWidth = 0;
+ if (textWidth > 176) {
+ if (textWidth > 352) {
+ int count = getCharLength(p, textWidth / 3);
+ int offs = dropCRIntoString(p, count);
+ p += count + offs;
+ _screen->_charWidth = -2;
+ textWidth = _screen->getTextWidth(p);
+ _screen->_charWidth = 0;
+ count = getCharLength(p, textWidth / 2);
+ dropCRIntoString(p, count);
+ } else {
+ int count = getCharLength(p, textWidth / 2);
+ dropCRIntoString(p, count);
+ }
+ }
+ _screen->setFont(curFont);
+ return _talkBuffer;
+}
+
+int TextDisplayer::buildMessageSubstrings(const char *str) {
+ debug(9, "TextDisplayer::buildMessageSubstrings('%s')", str);
+ int currentLine = 0;
+ int pos = 0;
+ while (*str) {
+ if (*str == '\r') {
+ assert(currentLine < TALK_SUBSTRING_NUM);
+ _talkSubstrings[currentLine * TALK_SUBSTRING_LEN + pos] = '\0';
+ ++currentLine;
+ pos = 0;
+ } else {
+ _talkSubstrings[currentLine * TALK_SUBSTRING_LEN + pos] = *str;
+ ++pos;
+ if (pos > TALK_SUBSTRING_LEN - 2) {
+ pos = TALK_SUBSTRING_LEN - 2;
+ }
+ }
+ ++str;
+ }
+ _talkSubstrings[currentLine * TALK_SUBSTRING_LEN + pos] = '\0';
+ return currentLine + 1;
+}
+
+int TextDisplayer::getWidestLineWidth(int linesCount) {
+ debug(9, "TextDisplayer::getWidestLineWidth(%d)", linesCount);
+ int maxWidth = 0;
+ Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
+ _screen->_charWidth = -2;
+ for (int l = 0; l < linesCount; ++l) {
+ int w = _screen->getTextWidth(&_talkSubstrings[l * TALK_SUBSTRING_LEN]);
+ if (maxWidth < w) {
+ maxWidth = w;
+ }
+ }
+ _screen->setFont(curFont);
+ _screen->_charWidth = 0;
+ return maxWidth;
+}
+
+void TextDisplayer::calcWidestLineBounds(int &x1, int &x2, int w, int cx) {
+ debug(9, "TextDisplayer::calcWidestLineBounds(%d, %d)", w, cx);
+ x1 = cx - w / 2;
+ if (x1 + w >= Screen::SCREEN_W - 12) {
+ x1 = Screen::SCREEN_W - 12 - w - 1;
+ } else if (x1 < 12) {
+ x1 = 12;
+ }
+ x2 = x1 + w + 1;
+}
+
+void TextDisplayer::restoreTalkTextMessageBkgd(int srcPage, int dstPage) {
+ debug(9, "TextDisplayer::restoreTalkTextMessageBkgd(%d, %d)", srcPage, dstPage);
+ if (_talkMessagePrinted) {
+ _talkMessagePrinted = false;
+ _screen->copyRegion(_talkCoords.x, _talkCoords.y, _talkCoords.x, _talkMessageY, _talkCoords.w, _talkMessageH, srcPage, dstPage);
+ }
+}
+
+void TextDisplayer::printTalkTextMessage(const char *text, int x, int y, uint8 color, int srcPage, int dstPage) {
+ debug(9, "TextDisplayer::printTalkTextMessage('%s', %d, %d, %d, %d, %d)", text, x, y, color, srcPage, dstPage);
+ char *str = preprocessString(text);
+ int lineCount = buildMessageSubstrings(str);
+ int top = y - lineCount * 10;
+ if (top < 0) {
+ top = 0;
+ }
+ _talkMessageY = top;
+ _talkMessageH = lineCount * 10;
+ int w = getWidestLineWidth(lineCount);
+ int x1, x2;
+ calcWidestLineBounds(x1, x2, w, x);
+ _talkCoords.x = x1;
+ _talkCoords.w = w + 2;
+ _screen->copyRegion(_talkCoords.x, _talkMessageY, _talkCoords.x, _talkCoords.y, _talkCoords.w, _talkMessageH, srcPage, dstPage);
+ int curPage = _screen->_curPage;
+ _screen->_curPage = srcPage;
+ for (int i = 0; i < lineCount; ++i) {
+ top = i * 10 + _talkMessageY;
+ char *msg = &_talkSubstrings[i * TALK_SUBSTRING_LEN];
+ int left = getCenterStringX(msg, x1, x2);
+ printText(msg, left, top, color, 0xC, 0);
+ }
+ _screen->_curPage = curPage;
+ _talkMessagePrinted = true;
+}
+
+void TextDisplayer::printText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2) {
+ debug(9, "TextDisplayer::printText('%s', %d, %d, %d, %d, %d)", str, x, y, c0, c1, c2);
+ uint8 colorMap[] = { 0, 15, 12, 12 };
+ colorMap[3] = c1;
+ _screen->setTextColor(colorMap, 0, 3);
+ Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
+ _screen->_charWidth = -2;
+ _screen->printText(str, x, y, c0, c2);
+ _screen->_charWidth = 0;
+ _screen->setFont(curFont);
+}
+
+void TextDisplayer::printCharacterText(char *text, int8 charNum, int charX) {
+ debug(9, "TextDisplayer::printCharacterText('%s', %d, %d)", text, charNum, charX);
+ uint8 colorTable[] = {0x0F, 0x9, 0x0C9, 0x80, 0x5, 0x81, 0x0E, 0xD8, 0x55, 0x3A, 0x3a};
+ int top, left, x1, x2, w, x;
+ char *msg;
+
+ uint8 color = colorTable[charNum];
+ text = preprocessString(text);
+ int lineCount = buildMessageSubstrings(text);
+ w = getWidestLineWidth(lineCount);
+ x = charX;
+ calcWidestLineBounds(x1, x2, w, x);
+
+ for (int i = 0; i < lineCount; ++i) {
+ top = i * 10 + _talkMessageY;
+ msg = &_talkSubstrings[i * TALK_SUBSTRING_LEN];
+ left = getCenterStringX(msg, x1, x2);
+ printText(msg, left, top, color, 0xC, 0);
+ }
+}
+} // end of namespace Kyra
diff --git a/engines/kyra/text.h b/engines/kyra/text.h
new file mode 100644
index 0000000000..f8f7c975a6
--- /dev/null
+++ b/engines/kyra/text.h
@@ -0,0 +1,69 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-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$
+ *
+ */
+
+#ifndef KYRATEXT_H
+#define KYRATEXT_H
+
+namespace Kyra {
+class Screen;
+
+class TextDisplayer {
+ struct TalkCoords {
+ uint16 y, x, w;
+ };
+
+ enum {
+ TALK_SUBSTRING_LEN = 80,
+ TALK_SUBSTRING_NUM = 3
+ };
+public:
+ TextDisplayer(Screen *screen);
+ ~TextDisplayer() {}
+
+ void setTalkCoords(uint16 y);
+ int getCenterStringX(const char *str, int x1, int x2);
+ int getCharLength(const char *str, int len);
+ int dropCRIntoString(char *str, int offs);
+ char *preprocessString(const char *str);
+ int buildMessageSubstrings(const char *str);
+ int getWidestLineWidth(int linesCount);
+ void calcWidestLineBounds(int &x1, int &x2, int w, int cx);
+ void restoreTalkTextMessageBkgd(int srcPage, int dstPage);
+ void printTalkTextMessage(const char *text, int x, int y, uint8 color, int srcPage, int dstPage);
+ void printText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2);
+ void printCharacterText(char *text, int8 charNum, int charX);
+
+ uint16 _talkMessageY;
+ uint16 _talkMessageH;
+ bool printed() const { return _talkMessagePrinted; }
+private:
+ Screen *_screen;
+
+ char _talkBuffer[300];
+ char _talkSubstrings[TALK_SUBSTRING_LEN * TALK_SUBSTRING_NUM];
+ TalkCoords _talkCoords;
+ bool _talkMessagePrinted;
+};
+} // end of namespace Kyra
+
+#endif
+
diff --git a/engines/kyra/timer.cpp b/engines/kyra/timer.cpp
new file mode 100644
index 0000000000..7abfd9fcbc
--- /dev/null
+++ b/engines/kyra/timer.cpp
@@ -0,0 +1,280 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-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 "kyra/kyra.h"
+#include "kyra/screen.h"
+#include "kyra/animator.h"
+
+#include "common/system.h"
+
+namespace Kyra {
+void KyraEngine::setupTimers() {
+ debug(9, "setupTimers()");
+ memset(_timers, 0, sizeof(_timers));
+
+ for (int i = 0; i < 34; i++)
+ _timers[i].active = 1;
+
+ _timers[0].func = _timers[1].func = _timers[2].func = _timers[3].func = _timers[4].func = 0; //Unused.
+ _timers[5].func = _timers[6].func = _timers[7].func = _timers[8].func = _timers[9].func = 0; //_nullsub51;
+ _timers[10].func = _timers[11].func = _timers[12].func = _timers[13].func = 0; //_nullsub50;
+ _timers[14].func = &KyraEngine::timerCheckAnimFlag2; //_nullsub52;
+ _timers[15].func = &KyraEngine::timerUpdateHeadAnims; //_nullsub48;
+ _timers[16].func = &KyraEngine::timerSetFlags1; //_nullsub47;
+ _timers[17].func = 0; //sub_15120;
+ _timers[18].func = &KyraEngine::timerCheckAnimFlag1; //_nullsub53;
+ _timers[19].func = &KyraEngine::timerRedrawAmulet; //_nullsub54;
+ _timers[20].func = 0; //offset _timerDummy1
+ _timers[21].func = 0; //sub_1517C;
+ _timers[22].func = 0; //offset _timerDummy2
+ _timers[23].func = 0; //offset _timerDummy3,
+ _timers[24].func = 0; //_nullsub45;
+ _timers[25].func = 0; //offset _timerDummy4
+ _timers[26].func = 0; //_nullsub46;
+ _timers[27].func = 0; //offset _timerDummy5,
+ _timers[28].func = 0; //offset _timerDummy6
+ _timers[29].func = 0; //offset _timerDummy7,
+ _timers[30].func = 0; //offset _timerDummy8,
+ _timers[31].func = &KyraEngine::timerFadeText; //sub_151F8;
+ _timers[32].func = &KyraEngine::updateAnimFlag1; //_nullsub61;
+ _timers[33].func = &KyraEngine::updateAnimFlag2; //_nullsub62;
+
+ _timers[0].countdown = _timers[1].countdown = _timers[2].countdown = _timers[3].countdown = _timers[4].countdown = -1;
+ _timers[5].countdown = 5;
+ _timers[6].countdown = 7;
+ _timers[7].countdown = 8;
+ _timers[8].countdown = 9;
+ _timers[9].countdown = 7;
+ _timers[10].countdown = _timers[11].countdown = _timers[12].countdown = _timers[13].countdown = 420;
+ _timers[14].countdown = 600;
+ _timers[15].countdown = 11;
+ _timers[16].countdown = _timers[17].countdown = 7200;
+ _timers[18].countdown = _timers[19].countdown = 600;
+ _timers[20].countdown = 7200;
+ _timers[21].countdown = 18000;
+ _timers[22].countdown = 7200;
+ _timers[23].countdown = _timers[24].countdown = _timers[25].countdown = _timers[26].countdown = _timers[27].countdown = 10800;
+ _timers[28].countdown = 21600;
+ _timers[29].countdown = 7200;
+ _timers[30].countdown = 10800;
+ _timers[31].countdown = -1;
+ _timers[32].countdown = 9;
+ _timers[33].countdown = 3;
+}
+
+void KyraEngine::updateGameTimers() {
+ debug(9, "updateGameTimers()");
+
+ if (_system->getMillis() < _timerNextRun)
+ return;
+
+ _timerNextRun += 99999;
+
+ for (int i = 0; i < 34; i++) {
+ if (_timers[i].active && _timers[i].countdown > -1) {
+ if (_timers[i].nextRun <=_system->getMillis()) {
+ if (i > 4 && _timers[i].func)
+ (*this.*_timers[i].func)(i);
+
+ _timers[i].nextRun = _system->getMillis() + _timers[i].countdown * _tickLength;
+ }
+ }
+ if (_timers[i].nextRun < _timerNextRun)
+ _timerNextRun = _timers[i].nextRun;
+ }
+}
+
+void KyraEngine::clearNextEventTickCount() {
+ debug(9, "clearNextEventTickCount()");
+ _timerNextRun = 0;
+}
+
+void KyraEngine::setTimerDelay(uint8 timer, int32 countdown) {
+ debug(9, "setTimerDelay(%i, %d)", timer, countdown);
+ _timers[timer].countdown = countdown;
+}
+
+int16 KyraEngine::getTimerDelay(uint8 timer) {
+ debug(9, "getTimerDelay(%i)", timer);
+ return _timers[timer].countdown;
+}
+
+void KyraEngine::setTimerCountdown(uint8 timer, int32 countdown) {
+ debug(9, "setTimerCountdown(%i, %i)", timer, countdown);
+ _timers[timer].countdown = countdown;
+ _timers[timer].nextRun = _system->getMillis() + countdown * _tickLength;
+
+ uint32 nextRun = _system->getMillis() + countdown * _tickLength;
+ if (nextRun < _timerNextRun)
+ _timerNextRun = nextRun;
+}
+
+void KyraEngine::enableTimer(uint8 timer) {
+ debug(9, "enableTimer(%i)", timer);
+ _timers[timer].active = 1;
+}
+
+void KyraEngine::disableTimer(uint8 timer) {
+ debug(9, "disableTimer(%i)", timer);
+ _timers[timer].active = 0;
+}
+
+void KyraEngine::timerUpdateHeadAnims(int timerNum) {
+ debug(9, "timerUpdateHeadAnims(%i)", timerNum);
+ static int8 currentFrame = 0;
+ static const int8 frameTable[] = {4, 5, 4, 5, 4, 5, 0, 1, 4, 5,
+ 4, 4, 6, 4, 8, 1, 9, 4, -1};
+
+ if (_talkingCharNum < 0)
+ return;
+
+ _currHeadShape = frameTable[currentFrame];
+ currentFrame++;
+
+ if (frameTable[currentFrame] == -1)
+ currentFrame = 0;
+
+ _animator->animRefreshNPC(0);
+ _animator->animRefreshNPC(_talkingCharNum);
+}
+
+void KyraEngine::timerSetFlags1(int timerNum) {
+ debug(9, "timerSetFlags(%i)", timerNum);
+ if (_currentCharacter->sceneId == 0x1C)
+ return;
+
+ int rndNr = _rnd.getRandomNumberRng(0, 3);
+
+ for (int i = 0; i < 4; i++) {
+ if (!queryGameFlag(rndNr + 17)) {
+ setGameFlag(rndNr + 17);
+ break;
+ } else {
+ rndNr++;
+ if (rndNr > 3)
+ rndNr = 0;
+ }
+ }
+}
+
+void KyraEngine::timerFadeText(int timerNum) {
+ debug(9, "timerFadeText(%i)", timerNum);
+ _fadeText = true;
+}
+
+void KyraEngine::updateAnimFlag1(int timerNum) {
+ debug(9, "updateAnimFlag1(%d)", timerNum);
+ if (_brandonStatusBit & 2) {
+ _brandonStatusBit0x02Flag = 1;
+ }
+}
+
+void KyraEngine::updateAnimFlag2(int timerNum) {
+ debug(9, "updateAnimFlag2(%d)", timerNum);
+ if (_brandonStatusBit & 0x20) {
+ _brandonStatusBit0x20Flag = 1;
+ }
+}
+
+void KyraEngine::setTextFadeTimerCountdown(int16 countdown) {
+ debug(9, "setTextFadeTimerCountdown(%i)", countdown);
+ //if (countdown == -1)
+ //countdown = 32000;
+
+ setTimerCountdown(31, countdown*60);
+}
+
+void KyraEngine::timerSetFlags2(int timerNum) {
+ debug(9, "timerSetFlags2(%i)", timerNum);
+ if (!((uint32*)(_flagsTable+0x2D))[timerNum])
+ ((uint32*)(_flagsTable+0x2D))[timerNum] = 1;
+}
+
+void KyraEngine::timerCheckAnimFlag1(int timerNum) {
+ debug(9, "timerCheckAnimFlag1(%i)", timerNum);
+ if (_brandonStatusBit & 0x20) {
+ checkAmuletAnimFlags();
+ setTimerCountdown(18, -1);
+ }
+}
+
+void KyraEngine::timerCheckAnimFlag2(int timerNum) {
+ debug(9, "timerCheckAnimFlag1(%i)", timerNum);
+ if (_brandonStatusBit & 0x2) {
+ checkAmuletAnimFlags();
+ setTimerCountdown(14, -1);
+ }
+}
+
+void KyraEngine::checkAmuletAnimFlags() {
+ debug(9, "checkSpecialAnimFlags()");
+ if (_brandonStatusBit & 2) {
+ seq_makeBrandonNormal2();
+ setTimerCountdown(19, 300);
+ }
+
+ if (_brandonStatusBit & 0x20) {
+ seq_makeBrandonNormal();
+ setTimerCountdown(19, 300);
+ }
+}
+
+void KyraEngine::timerRedrawAmulet(int timerNum) {
+ debug(9, "timerRedrawAmulet(%i)", timerNum);
+ if (queryGameFlag(0xF1)) {
+ drawAmulet();
+ setTimerCountdown(19, -1);
+ }
+}
+
+void KyraEngine::drawAmulet() {
+ debug(9, "drawAmulet()");
+ static const int16 amuletTable1[] = {0x167, 0x162, 0x15D, 0x158, 0x153, 0x150, 0x155, 0x15A, 0x15F, 0x164, 0x145, -1};
+ static const int16 amuletTable3[] = {0x167, 0x162, 0x15D, 0x158, 0x153, 0x14F, 0x154, 0x159, 0x15E, 0x163, 0x144, -1};
+ static const int16 amuletTable2[] = {0x167, 0x162, 0x15D, 0x158, 0x153, 0x152, 0x157, 0x15C, 0x161, 0x166, 0x147, -1};
+ static const int16 amuletTable4[] = {0x167, 0x162, 0x15D, 0x158, 0x153, 0x151, 0x156, 0x15B, 0x160, 0x165, 0x146, -1};
+
+ resetGameFlag(0xF1);
+ _screen->hideMouse();
+
+ int i = 0;
+ while (amuletTable1[i] != -1) {
+ if (queryGameFlag(87))
+ _screen->drawShape(0, _shapes[4+amuletTable1[i]], _amuletX[0], _amuletY[0], 0, 0);
+
+ if (queryGameFlag(89))
+ _screen->drawShape(0, _shapes[4+amuletTable2[i]], _amuletX[1], _amuletY[1], 0, 0);
+
+ if (queryGameFlag(86))
+ _screen->drawShape(0, _shapes[4+amuletTable3[i]], _amuletX[2], _amuletY[2], 0, 0);
+
+ if (queryGameFlag(88))
+ _screen->drawShape(0, _shapes[4+amuletTable4[i]], _amuletX[3], _amuletY[3], 0, 0);
+
+ _screen->updateScreen();
+ delayWithTicks(3);
+ i++;
+ }
+ _screen->showMouse();
+}
+} // end of namespace Kyra
+
diff --git a/engines/kyra/wsamovie.cpp b/engines/kyra/wsamovie.cpp
new file mode 100644
index 0000000000..17fe3f8cb2
--- /dev/null
+++ b/engines/kyra/wsamovie.cpp
@@ -0,0 +1,206 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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 "kyra/kyra.h"
+#include "kyra/screen.h"
+#include "kyra/wsamovie.h"
+
+
+namespace Kyra {
+WSAMovieV1::WSAMovieV1(KyraEngine *vm) : Movie(vm) {}
+WSAMovieV1::~WSAMovieV1() { close(); }
+
+void WSAMovieV1::open(const char *filename, int offscreenDecode, uint8 *palBuf) {
+ debug(9, "WSAMovieV1::open('%s', %d, 0x%X)", filename, offscreenDecode, palBuf);
+ close();
+
+ uint32 flags = 0;
+ uint32 fileSize;
+ uint8 *p = _vm->resource()->fileData(filename, &fileSize);
+ if (!p)
+ return;
+
+ const uint8 *wsaData = p;
+ _numFrames = READ_LE_UINT16(wsaData); wsaData += 2;
+ _width = READ_LE_UINT16(wsaData); wsaData += 2;
+ _height = READ_LE_UINT16(wsaData); wsaData += 2;
+ _deltaBufferSize = READ_LE_UINT16(wsaData); wsaData += 2;
+ _offscreenBuffer = NULL;
+ _flags = 0;
+ if (_vm->features() & GF_TALKIE) {
+ flags = READ_LE_UINT16(wsaData); wsaData += 2;
+ }
+
+ uint32 offsPal = 0;
+ if (flags & 1) {
+ offsPal = 0x300;
+ _flags |= WF_HAS_PALETTE;
+ if (palBuf) {
+ memcpy(palBuf, wsaData + (_numFrames + 2) * 4, 0x300);
+ }
+ }
+
+ if (offscreenDecode) {
+ _flags |= WF_OFFSCREEN_DECODE;
+ const int offscreenBufferSize = _width * _height;
+ _offscreenBuffer = new uint8[offscreenBufferSize];
+ memset(_offscreenBuffer, 0, offscreenBufferSize);
+ }
+
+ if (_numFrames & 0x8000) {
+ warning("Unhandled wsa flags 0x80");
+ _flags |= 0x80;
+ _numFrames &= 0x7FFF;
+ }
+ _currentFrame = _numFrames;
+
+ _deltaBuffer = new uint8[_deltaBufferSize];
+ memset(_deltaBuffer, 0, _deltaBufferSize);
+
+ // read frame offsets
+ _frameOffsTable = new uint32[_numFrames + 2];
+ _frameOffsTable[0] = 0;
+ uint32 frameDataOffs = READ_LE_UINT32(wsaData); wsaData += 4;
+ bool firstFrame = true;
+ if (frameDataOffs == 0) {
+ firstFrame = false;
+ frameDataOffs = READ_LE_UINT32(wsaData);
+ _flags |= WF_NO_FIRST_FRAME;
+ }
+ for (int i = 1; i < _numFrames + 2; ++i) {
+ _frameOffsTable[i] = READ_LE_UINT32(wsaData) - frameDataOffs;
+ wsaData += 4;
+ }
+
+ // skip palette
+ wsaData += offsPal;
+
+ // read frame data
+ const int frameDataSize = p + fileSize - wsaData;
+ _frameData = new uint8[frameDataSize];
+ memcpy(_frameData, wsaData, frameDataSize);
+
+ // decode first frame
+ if (firstFrame) {
+ Screen::decodeFrame4(_frameData, _deltaBuffer, _deltaBufferSize);
+ }
+
+ delete [] p;
+ _opened = true;
+}
+
+void WSAMovieV1::close() {
+ debug(9, "WSAMovieV1::close()");
+ if (_opened) {
+ delete [] _deltaBuffer;
+ delete [] _offscreenBuffer;
+ delete [] _frameOffsTable;
+ delete [] _frameData;
+ _opened = false;
+ }
+}
+
+void WSAMovieV1::displayFrame(int frameNum) {
+ debug(9, "WSAMovieV1::displayFrame(%d)", frameNum);
+ if (frameNum >= _numFrames || !_opened)
+ return;
+
+ uint8 *dst;
+ if (_flags & WF_OFFSCREEN_DECODE) {
+ dst = _offscreenBuffer;
+ } else {
+ dst = _vm->screen()->getPagePtr(_drawPage) + _y * Screen::SCREEN_W + _x;
+ }
+
+ if (_currentFrame == _numFrames) {
+ if (!(_flags & WF_NO_FIRST_FRAME)) {
+ if (_flags & WF_OFFSCREEN_DECODE) {
+ Screen::decodeFrameDelta(dst, _deltaBuffer);
+ } else {
+ Screen::decodeFrameDeltaPage(dst, _deltaBuffer, _width, 1);
+ }
+ }
+ _currentFrame = 0;
+ }
+
+ // try to reduce the number of needed frame operations
+ int diffCount = ABS(_currentFrame - frameNum);
+ int frameStep = 1;
+ int frameCount;
+ if (_currentFrame < frameNum) {
+ frameCount = _numFrames - frameNum + _currentFrame;
+ if (diffCount > frameCount) {
+ frameStep = -1;
+ } else {
+ frameCount = diffCount;
+ }
+ } else {
+ frameCount = _numFrames - _currentFrame + frameNum;
+ if (frameCount >= diffCount) {
+ frameStep = -1;
+ frameCount = diffCount;
+ }
+ }
+
+ // process
+ if (frameStep > 0) {
+ uint16 cf = _currentFrame;
+ while (frameCount--) {
+ cf += frameStep;
+ processFrame(cf, dst);
+ if (cf == _numFrames) {
+ cf = 0;
+ }
+ }
+ } else {
+ uint16 cf = _currentFrame;
+ while (frameCount--) {
+ if (cf == 0) {
+ cf = _numFrames;
+ }
+ processFrame(cf, dst);
+ cf += frameStep;
+ }
+ }
+
+ // display
+ _currentFrame = frameNum;
+ if (_flags & WF_OFFSCREEN_DECODE) {
+ _vm->screen()->copyBlockToPage(_drawPage, _x, _y, _width, _height, _offscreenBuffer);
+ }
+}
+
+void WSAMovieV1::processFrame(int frameNum, uint8 *dst) {
+ debug(9, "WSAMovieV1::processFrame(%d, 0x%X)", frameNum, dst);
+ if (!_opened)
+ return;
+ assert(frameNum <= _numFrames);
+ const uint8 *src = _frameData + _frameOffsTable[frameNum];
+ Screen::decodeFrame4(src, _deltaBuffer, _deltaBufferSize);
+ if (_flags & WF_OFFSCREEN_DECODE) {
+ Screen::decodeFrameDelta(dst, _deltaBuffer);
+ } else {
+ Screen::decodeFrameDeltaPage(dst, _deltaBuffer, _width, 0);
+ }
+}
+} // end of namespace Kyra
diff --git a/engines/kyra/wsamovie.h b/engines/kyra/wsamovie.h
new file mode 100644
index 0000000000..fe3927f175
--- /dev/null
+++ b/engines/kyra/wsamovie.h
@@ -0,0 +1,85 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-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$
+ *
+ */
+
+#ifndef WSAMOVIES_H
+#define WSAMOVIES_H
+
+#include "kyra/resource.h"
+
+namespace Kyra {
+class KyraEngine;
+
+class Movie {
+public:
+ Movie(KyraEngine *vm) : _x(-1), _y(-1), _drawPage(-1), _vm(vm), _opened(false) {}
+ virtual ~Movie() {}
+
+ virtual bool opened() { return _opened; }
+
+ virtual void open(const char *filename, int offscreen, uint8 *palette) = 0;
+ virtual void close() = 0;
+
+ virtual int frames() = 0;
+
+ virtual void displayFrame(int frameNum) = 0;
+
+ int _x, _y;
+ int _drawPage;
+protected:
+ KyraEngine *_vm;
+ bool _opened;
+};
+
+class WSAMovieV1 : public Movie {
+public:
+ WSAMovieV1(KyraEngine *vm);
+ virtual ~WSAMovieV1();
+
+ virtual void open(const char *filename, int offscreen, uint8 *palette);
+ virtual void close();
+
+ virtual int frames() { return _opened ? _numFrames : -1; }
+
+ virtual void displayFrame(int frameNum);
+protected:
+ virtual void processFrame(int frameNum, uint8 *dst);
+
+ enum WSAFlags {
+ WF_OFFSCREEN_DECODE = 0x10,
+ WF_NO_FIRST_FRAME = 0x40,
+ WF_HAS_PALETTE = 0x100
+ };
+
+ uint16 _currentFrame;
+ uint16 _numFrames;
+ uint16 _width;
+ uint16 _height;
+ uint16 _flags;
+ uint8 *_deltaBuffer;
+ uint32 _deltaBufferSize;
+ uint8 *_offscreenBuffer;
+ uint32 *_frameOffsTable;
+ uint8 *_frameData;
+};
+} // end of namespace Kyra
+
+#endif