diff options
-rw-r--r-- | kyra/kyra.cpp | 1509 | ||||
-rw-r--r-- | kyra/kyra.h | 355 | ||||
-rw-r--r-- | kyra/screen.cpp | 417 | ||||
-rw-r--r-- | kyra/screen.h | 13 | ||||
-rw-r--r-- | kyra/script.cpp | 4 | ||||
-rw-r--r-- | kyra/script.h | 7 | ||||
-rw-r--r-- | kyra/script_v1.cpp | 955 | ||||
-rw-r--r-- | kyra/sprites.cpp | 85 | ||||
-rw-r--r-- | kyra/sprites.h | 11 | ||||
-rw-r--r-- | kyra/staticres.cpp | 308 |
10 files changed, 3445 insertions, 219 deletions
diff --git a/kyra/kyra.cpp b/kyra/kyra.cpp index eb48efa8c6..3d45754063 100644 --- a/kyra/kyra.cpp +++ b/kyra/kyra.cpp @@ -45,8 +45,6 @@ #include "kyra/sprites.h" #include "kyra/wsamovie.h" -#define TEST_SPRITES 1 - using namespace Kyra; enum { @@ -269,6 +267,44 @@ int KyraEngine::init(GameDetector &detector) { assert(_sprites); _seq = new SeqPlayer(this, _system); assert(_seq); + + _currentCharacter = 0; + _characterList = new Character[11]; + assert(_characterList); + for (int i = 0; i < 11; ++i) { + memset(&_characterList[i], 0, sizeof(Character)); + } + _characterList[0].sceneId = 5; + _characterList[0].height = 48; + _characterList[0].facing = 3; + _characterList[0].currentAnimFrame = 7; + + _objectQueue = 0; + _animStates = new AnimObject[31]; + assert(_animStates); + _charactersAnimState = &_animStates[0]; + _animObjects = &_animStates[5]; + _unkAnimsBuffer = &_animStates[16]; + + _scriptInterpreter = new ScriptHelper(this); + assert(_scriptInterpreter); + + _npcScriptData = new ScriptData; + memset(_npcScriptData, 0, sizeof(ScriptData)); + assert(_npcScriptData); + _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)); + + memset(_shapes, 0, sizeof(_shapes)); + memset(_wsaObjects, 0, sizeof(_wsaObjects)); _fastMode = false; _talkCoords.y = 0x88; @@ -280,6 +316,16 @@ int KyraEngine::init(GameDetector &detector) { _mouseX = _mouseY = 0; _needMouseUpdate = true; + + _brandonPosX = _brandonPosY = -1; + _brandonDrawFrame = 113; + + memset(_itemTable, 0, sizeof(_itemTable)); + memset(_exitList, 0xFFFF, sizeof(_exitList)); + _exitListPtr = 0; + + _movFacingTable = new int[150]; + assert(_movFacingTable); return 0; } @@ -290,9 +336,31 @@ KyraEngine::~KyraEngine() { delete _res; delete _midi; delete _seq; + delete _scriptInterpreter; + + delete _npcScriptData; + delete _scriptMain; + + delete _scriptClickData; + delete _scriptClick; + + delete [] _animStates; + delete [] _characterList; - for (int i = 0; i < ARRAYSIZE(_itemShapes); ++i) { - free(_itemShapes[i]); + delete [] _movFacingTable; + + for (int i = 0; i < ARRAYSIZE(_shapes); ++i) { + if (_shapes[i] != 0) { + free(_shapes[i]); + 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]); } } @@ -330,14 +398,72 @@ void KyraEngine::startup() { _screen->setTextColorMap(colorMap); // _screen->setFont(Screen::FID_6_FNT); _screen->setAnimBlockPtr(3750); - memset(_itemShapes, 0, sizeof(_itemShapes)); + memset(_sceneAnimTable, 0, sizeof(_sceneAnimTable)); loadMouseShapes(); + _currentCharacter = &_characterList[0]; + for (int i = 1; i < 5; ++i) + setCharacterDefaultFrame(i); + for (int i = 5; i <= 10; ++i) + setCharactersPositions(i); + setCharactersHeight(); + resetBrandonPosionFlags(); + _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] = (byte*)malloc(_screen->getRectSize(3, 24)); + memset(_shapes[0], 0, _screen->getRectSize(3, 24)); + _shapes[1] = (byte*)malloc(_screen->getRectSize(4, 32)); + memset(_shapes[1], 0, _screen->getRectSize(4, 32)); + _shapes[2] = (byte*)malloc(_screen->getRectSize(8, 69)); + memset(_shapes[2], 0, _screen->getRectSize(8, 69)); + _shapes[3] = (byte*)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] = 0; + _roomTable[i].itemsYPos[item] = 0; + _roomTable[i].unkField3[item] = 0; + } + } + loadCharacterShapes(); + loadSpecialEffectShapes(); + loadSpecialEffectShapes(); + loadItems(); + loadMainScreen(); + loadPalette("PALETTE.COL", _screen->_currentPalette); + _screen->setScreenPalette(_screen->_currentPalette); + // XXX + _screen->showMouse(); + _screen->hideMouse(); + 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); + enterNewScene(_currentCharacter->sceneId, _currentCharacter->facing, 0, 0, 1); _gameSpeed = 50; memset(_flagsTable, 0, sizeof(_flagsTable)); - - setupRooms(); - // XXX } void KyraEngine::delay(uint32 amount) { @@ -349,8 +475,10 @@ void KyraEngine::delay(uint32 amount) { case OSystem::EVENT_KEYDOWN: if (event.kbd.keycode == 'q' || event.kbd.keycode == 27) { _quitFlag = true; - } else if (event.kbd.keycode == ' ') { - loadRoom((++_currentRoom) % MAX_NUM_ROOMS); + } else { + ++_currentRoom; + if (_currentRoom > ARRAYSIZE(_shapes)) + _currentRoom = 3; } break; case OSystem::EVENT_MOUSEMOVE: @@ -358,12 +486,6 @@ void KyraEngine::delay(uint32 amount) { _mouseY = event.mouse.y; _needMouseUpdate = true; break; - case OSystem::EVENT_LBUTTONDOWN: - loadRoom((++_currentRoom) % MAX_NUM_ROOMS); - break; - case OSystem::EVENT_RBUTTONDOWN: - loadRoom((--_currentRoom) % MAX_NUM_ROOMS); - break; case OSystem::EVENT_QUIT: _quitFlag = true; break; @@ -377,89 +499,13 @@ void KyraEngine::delay(uint32 amount) { } while (_system->getMillis() < start + amount); } -void KyraEngine::drawRoom() { - //_screen->clearPage(0); - _screen->copyRegion(0, 0, 0, 0, 320, 200, 10, 0); - _screen->copyRegion(4, 4, 4, 4, 308, 132, 14, 0); - _sprites->doAnims(); - _sprites->drawSprites(14, 0); -} - -void KyraEngine::setupRooms() { - // XXX - // Just a few sample rooms, most with sprite anims. - memset(_rooms, 0, sizeof(_rooms)); - _rooms[0].filename = "gemcut"; - _rooms[1].filename = "arch"; - _rooms[2].filename = "sorrow"; - _rooms[3].filename = "emcav"; - _rooms[4].filename = "extpot"; - _rooms[5].filename = "spell"; - _rooms[6].filename = "song"; - _rooms[7].filename = "belroom"; - _rooms[8].filename = "kyragem"; - _rooms[9].filename = "lephole"; - _rooms[10].filename = "sickwil"; - _rooms[11].filename = "temple"; -} - -void KyraEngine::loadRoom(uint16 roomID) { - debug(9, "KyraEngine::loadRoom(%i)", roomID); - char buf[12]; - - loadPalette("palette.col", _screen->_currentPalette); - - //loadPalette(_rooms[roomID].palFilename, _screen->_currentPalette); - //_screen->setScreenPalette(_screen->_currentPalette); - - _screen->clearPage(14); - _screen->clearPage(0); - _screen->clearPage(10); - - // Loading GUI bitmap - if ((_features & GF_ENGLISH) && (_features & GF_TALKIE)) - loadBitmap("MAIN_ENG.CPS", 10, 10, 0); - else if(_features & GF_FRENCH) - loadBitmap("MAIN_FRE.CPS", 10, 10, 0); - else if(_features & GF_GERMAN) - loadBitmap("MAIN_GER.CPS", 10, 10, 0); - else if ((_features & GF_ENGLISH) && (_features & GF_FLOPPY)) - loadBitmap("MAIN15.CPS", 10, 10, 0); - else if (_features & GF_SPANISH) - loadBitmap("MAIN_SPA.CPS", 10, 10, 0); - else - warning("no main graphics file found"); - - // Loading main room background - strncpy(buf, _rooms[roomID].filename, 8); - strcat(buf, ".cps"); - loadBitmap( buf, 14, 14, 0); - - // Loading the room mask - strncpy(buf, _rooms[roomID].filename, 8); - strcat(buf, ".msc"); - loadBitmap( buf, 12, 12, 0); - - // Loading room data - strncpy(buf, _rooms[roomID].filename, 8); - strcat(buf, ".dat"); - _sprites->loadDAT(buf); - - drawRoom(); - _screen->showMouse(); - _needMouseUpdate = true; -} - void KyraEngine::mainLoop() { debug(9, "KyraEngine::mainLoop()"); -#ifdef TEST_SPRITES - _currentRoom = 0; - loadRoom(_currentRoom); + _currentRoom = 11; while (!_quitFlag) { int32 frameTime = (int32)_system->getMillis(); - drawRoom(); if (_needMouseUpdate) { _screen->hideMouse(); _screen->showMouse(); @@ -469,7 +515,6 @@ void KyraEngine::mainLoop() { delay((frameTime + _gameSpeed) - _system->getMillis()); } -#endif } void KyraEngine::loadPalette(const char *filename, uint8 *palData) { @@ -784,7 +829,7 @@ void KyraEngine::seq_intro() { &KyraEngine::seq_introKallakWriting, &KyraEngine::seq_introKallakMalcolm }; - _skipIntroFlag = true; // only true if user already played the game once + _skipIntroFlag = true; // only true if user already saved the game once _seq->setCopyViewOffs(true); _screen->setFont(Screen::FID_8_FNT); snd_playTheme(MUSIC_INTRO, 2); @@ -903,6 +948,7 @@ bool KyraEngine::seq_skipSequence() const { void KyraEngine::snd_playTheme(int file, int track) { debug(9, "KyraEngine::snd_playTheme(%d)", file); assert(file < _xmidiFilesCount); + _curMusicTheme = _newMusicTheme = file; _midi->playMusic(_xmidiFiles[file]); _midi->playTrack(track, false); } @@ -958,17 +1004,1272 @@ void KyraEngine::snd_haltTrack() { void KyraEngine::loadMouseShapes() { loadBitmap("MOUSE.CPS", 3, 3, 0); _screen->_curPage = 2; - _itemShapes[4] = _screen->encodeShape(0, 0, 8, 10, 0); - _itemShapes[5] = _screen->encodeShape(0, 0x17, 0x20, 7, 0); - _itemShapes[6] = _screen->encodeShape(0x50, 0x12, 0x10, 9, 0); - _itemShapes[7] = _screen->encodeShape(0x60, 0x12, 0x10, 11, 0); - _itemShapes[8] = _screen->encodeShape(0x70, 0x12, 0x10, 9, 0); - _itemShapes[9] = _screen->encodeShape(0x80, 0x12, 0x10, 11, 0); - _itemShapes[10] = _screen->encodeShape(0x90, 0x12, 0x10, 10, 0); - _itemShapes[364] = _screen->encodeShape(0x28, 0, 0x10, 13, 0); + _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, _itemShapes[4]); + _screen->setMouseCursor(1, 1, _shapes[4]); _screen->setShapePages(3, 5); } +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+4] = 0; + continue; + } + if (shape->imageIndex != curImage) { + assert(shape->imageIndex < _characterImageTableSize); + loadBitmap(_characterImageTable[shape->imageIndex], 3, 3, 0); + curImage = shape->imageIndex; + } + _shapes[i+4] = _screen->encodeShape(shape->x, shape->y, shape->w, 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); +} + +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::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::loadMainScreen() { + if ((_features & GF_ENGLISH) && (_features & GF_TALKIE)) + loadBitmap("MAIN_ENG.CPS", 3, 3, 0); + else if(_features & GF_FRENCH) + loadBitmap("MAIN_FRE.CPS", 3, 3, 0); + else if(_features & GF_GERMAN) + loadBitmap("MAIN_GER.CPS", 3, 3, 0); + else if ((_features & GF_ENGLISH) && (_features & GF_FLOPPY)) + loadBitmap("MAIN15.CPS", 3, 3, 0); + else if (_features & GF_SPANISH) + loadBitmap("MAIN_SPA.CPS", 3, 3, 0); + else + warning("no main graphics file found"); + + uint8 *_page3 = _screen->getPagePtr(3); + uint8 *_page0 = _screen->getPagePtr(0); + memcpy(_page0, _page3, 320*200); +} + +void KyraEngine::setCharactersInDefaultScene() { + static 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; + uint32 *curTable = defaultSceneTable[i-1]; + cur->sceneId = curTable[0]; + if (cur->sceneId == _currentCharacter->sceneId) { + ++cur->field_20; + cur->sceneId = curTable[cur->field_20]; + } + cur->field_23 = curTable[cur->field_20+1]; + } +} + +void KyraEngine::setCharacterDefaultFrame(int character) { + static uint16 initFrameTable[] = { + 0x07, 0x29, 0x4D, 0x00, 0x00 + }; + assert(character < ARRAYSIZE(initFrameTable)); + Character *edit = &_characterList[character]; + edit->sceneId = 0xFFFF; + edit->facing = 0; + edit->currentAnimFrame = initFrameTable[character]; + edit->unk6 = 1; +} + +void KyraEngine::setCharactersPositions(int character) { + static int16 initXPosTable[] = { + 0x3200, 0x0024, 0x2230, 0x2F00, 0x0020, 0x002B, + 0x00CA, 0x00F0, 0x0082, 0x00A2, 0x0042, 0x6767, + 0x5A60 + }; + static int8 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]; +} + +void KyraEngine::setCharactersHeight() { + static int8 initHeightTable[] = { + 0x30, 0x28, 0x30, 0x2F, 0x38, + 0x2C, 0x2A, 0x2F, 0x26, 0x23, + 0x28 + }; + for (int i = 0; i < 11; ++i) { + _characterList[i].height = initHeightTable[i]; + } +} + +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::enterNewScene(int sceneId, int facing, int unk1, int unk2, int brandonAlive) { + int unkVar1 = 1; + _screen->hideMouse(); + if (_currentCharacter->sceneId == 7 && sceneId == 24) { + _newMusicTheme = 2; + } else if (_currentCharacter->sceneId == 25 && sceneId == 109) { + _newMusicTheme = 3; + } else if (_currentCharacter->sceneId == 120 && sceneId == 37) { + _newMusicTheme = 4; + } else if (_currentCharacter->sceneId == 52 && sceneId == 199) { + _newMusicTheme = 5; + } else if (_currentCharacter->sceneId == 37 && sceneId == 120) { + _newMusicTheme = 3; + } else if (_currentCharacter->sceneId == 109 && sceneId == 25) { + _newMusicTheme = 2; + } else if (_currentCharacter->sceneId == 24 && sceneId == 7) { + _newMusicTheme = 1; + } + 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, 2, xpos, ypos); + } + + for (int i = 0; i < 10; ++i) { + wsa_close(_wsaObjects[i]); + _wsaObjects[i] = 0; + } + + 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 < _roomFilenameTableSize); + + assert(sceneId < _roomTableSize); + Room *currentRoom = &_roomTable[sceneId]; + + assert(_currentCharacter->sceneId < _roomTableSize); + int tableId = _roomTable[_currentCharacter->sceneId].nameIndex; + assert(tableId < _roomFilenameTableSize); + char datFileNameBuffer[32]; + strcpy(datFileNameBuffer, _roomFilenameTable[tableId]); + strcat(datFileNameBuffer, ".DAT"); + _sprites->loadDAT(datFileNameBuffer); + + loadSceneMSC(); + + _walkBlockNorth = currentRoom->northExit; + _walkBlockEast = currentRoom->eastExit; + _walkBlockSouth = currentRoom->southExit; + _walkBlockWest = currentRoom->westExit; + + if (_walkBlockNorth == 0xFFFF) { + blockOutRegion(0, 0, 320, (_northExitHeight & 0xFF)+3); + } + if (_walkBlockEast == 0xFFFF) { + blockOutRegion(312, 0, 8, 139); + } + if (_walkBlockSouth == 0xFFFF) { + blockOutRegion(0, 135, 320, 8); + } + if (_walkBlockWest == 0xFFFF) { + blockOutRegion(0, 0, 8, 139); + } + + if (!brandonAlive) { + // XXX + } + + startSceneScript(brandonAlive); + // XXX setupSceneItems + initSceneData(facing, unk2, brandonAlive); + + // XXX setTextFadeTimerCountdown + _scriptClick->variables[3] = 1; + + _screen->showMouse(); + if (!brandonAlive) { + // XXX seq_poisionDeathNow + } +} + +void KyraEngine::moveCharacterToPos(int character, int facing, int xpos, int ypos) { + debug(9, "moveCharacterToPos(%d, %d, %d, %d)", character, facing, xpos, ypos); + Character *ch = &_characterList[character]; + _screen->hideMouse(); + xpos &= 0xFFFC; + ypos &= 0xFFFE; + switch (facing) { + case 0: + if (ypos < ch->y1) { + setCharacterPositionWithUpdate(character); + } + break; + + case 2: + if (ch->x1 < xpos) { + setCharacterPositionWithUpdate(character); + } + break; + + case 4: + if (ypos > ch->y1) { + setCharacterPositionWithUpdate(character); + } + break; + + case 6: + if (ch->x1 > xpos) { + setCharacterPositionWithUpdate(character); + } + break; + + default: + break; + } + _screen->showMouse(); +} + +void KyraEngine::setCharacterPositionWithUpdate(int character) { + debug(9, "setCharacterPositionWithUpdate(%d)", character); + // XXX game_updateAnimsFlags + setCharacterPosition(character, 0); + updateAllObjectShapes(); + // XXX processPalette + if (_currentCharacter->sceneId == 210) { + // XXX game_updateKyragemFading + } +} + +int KyraEngine::setCharacterPosition(int character, uint8 *unk1) { + debug(9, "setCharacterPosition(%d)", character); + static bool firstRun = true; + if (character == 0) { + if (firstRun) { + firstRun = false; + _currentCharacter->x1 += _charXPosTable[character]; + _currentCharacter->y1 += _charYPosTable[character]; + setCharacterPositionHelper(0, unk1); + return 1; + } + } else { + _characterList[character].x1 += _charXPosTable[character]; + _characterList[character].y1 += _charYPosTable[character]; + if (_characterList[character].sceneId == _currentCharacter->sceneId) { + setCharacterPositionHelper(character, 0); + } + } + return 0; +} + +void KyraEngine::setCharacterPositionHelper(int character, uint8 *unk1) { + Character *ch = &_characterList[character]; + int facing = ch->facing; + if (unk1) { + // XXX + } + + static uint8 facingIsZero[8]; + static uint8 facingIsFour[8]; + + 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; + } + } + + if (resetTables) { + facingIsZero[character] = 0; + facingIsFour[character] = 0; + } + } + + static 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; + } + + animRefreshNPC(character); +} + +int KyraEngine::getOppositeFacingDirection(int 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"); + loadBitmap(fileNameBuffer, 3, 5, 0); +} + +// maybe move this two functions to Screen +void KyraEngine::blockInRegion(int x, int y, int width, int height) { + debug(9, "blockInRegion(%d, %d, %d, %d, %d)", x, y, width, height); + assert(_screen->_shapePages[0]); + byte *toPtr = _screen->_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 KyraEngine::blockOutRegion(int x, int y, int width, int height) { + debug(9, "blockOutRegion(%d, %d, %d, %d, %d)", x, y, width, height); + assert(_screen->_shapePages[0]); + byte *toPtr = _screen->_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 KyraEngine::startSceneScript(int brandonAlive) { + debug(9, "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(); + // TODO: check there it is done normally + _screen->setScreenPalette(_screen->_currentPalette); + _screen->copyRegion(4, 4, 4, 4, 308, 132, 3, 0); + + _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, "initSceneData(%d, %d, %d)", facing, unk1, brandonAlive); + + int xpos2 = 0; + int setFacing = 1; + + int xpos = 0, ypos = 0; + + if (_brandonPosX == -1 && _brandonPosY == -1) { + switch (facing) { + 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 ((_northExitHeight & 0xFF) >= ypos) { + ypos = (_northExitHeight & 0xFF); + } + if (xpos >= 308) { + xpos = 304; + } + if ((_northExitHeight >> 8) - 2 <= ypos) { + ypos = (_northExitHeight >> 8) - 4; + } + if (xpos <= 12) { + xpos = 16; + } + } + + if (_brandonPosX > -1) { + xpos = _brandonPosX; + } + if (_brandonPosY > -1) { + ypos = _brandonPosY; + } + + int 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; + break; + + case 3: + _currentCharacter->x1 = xpos; + _currentCharacter->x2 = xpos; + _currentCharacter->y1 = ypos; + _currentCharacter->y2 = ypos; + facing = 2; + xpos2 = 204; + ypos2 = 94; + break; + + case 26: + _currentCharacter->x1 = xpos; + _currentCharacter->x2 = xpos; + _currentCharacter->y1 = ypos; + _currentCharacter->y2 = ypos; + facing = 4; + xpos2 = 192; + ypos2 = 128; + break; + + case 44: + _currentCharacter->x1 = xpos; + _currentCharacter->x2 = xpos; + _currentCharacter->y1 = ypos; + _currentCharacter->y2 = ypos; + facing = 6; + xpos2 = 156; + ypos2 = 96; + break; + + case 37: + _currentCharacter->x1 = xpos; + _currentCharacter->x2 = xpos; + _currentCharacter->y1 = ypos; + _currentCharacter->y2 = ypos; + facing = 2; + xpos2 = 148; + ypos2 = 114; + break; + + default: + break; + } + + setFacing = 0; + unk1 = 1; + } + + _brandonPosX = _brandonPosY = -1; + + if (unk1 != 0 && setFacing != 0) { + xpos2 = xpos; + switch (facing) { + case 0: + ypos = 142; + break; + + case 2: + xpos = -16; + break; + + case 4: + ypos = (_northExitHeight & 0xFF) - 4; + break; + + case 6: + xpos = 335; + break; + + default: + break; + } + } + + xpos2 &= 0xFFFC; + ypos2 &= 0xFE; + xpos &= 0xFFFC; + ypos &= 0xFFFE; + _currentCharacter->facing = facing; + _currentCharacter->x1 = xpos; + _currentCharacter->x2 = xpos; + _currentCharacter->y1 = ypos; + _currentCharacter->y2 = ypos; + + initSceneObjectList(brandonAlive); + + if (unk1 != 0 && brandonAlive == 0) { + moveCharacterToPos(0, facing, xpos2, ypos2); + } + + // XXX _mousePointerFlag + _scriptClick->variables[4] = -1; + _scriptClick->variables[7] = brandonAlive; + _scriptInterpreter->startScript(_scriptClick, 3); + while (_scriptInterpreter->validScript(_scriptClick)) { + _scriptInterpreter->runScript(_scriptClick); + } +} + +void KyraEngine::resetBrandonPosionFlags() { + _brandonStatusBit = 0; + for (int i = 0; i < 256; ++i) { + _unkBrandonPoisonFlags[i] = i; + } +} + +void KyraEngine::initAnimStateList() { + _animStates[0].index = 0; + _animStates[0].active = 1; + _animStates[0].flags = 0x800; + _animStates[0].background = _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 = _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 = _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 KyraEngine::initSceneObjectList(int brandonAlive) { + warning("STUB: initSceneObjectList"); +} + +#pragma mark - +#pragma mark - Item handling +#pragma mark - + +void KyraEngine::addToNoDropRects(int x, int y, int w, int h) { + debug(9, "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, "clearNoDropRects()"); + memset(_noDropRects, -1, sizeof(_noDropRects)); +} + +byte KyraEngine::findFreeItemInScene(int scene) { + debug(9, "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) { + assert(_currentCharacter->sceneId < _roomTableSize); + uint8 *itemsTable = _roomTable[_currentCharacter->sceneId].itemsTable; + uint16 *xposOffset = _roomTable[_currentCharacter->sceneId].itemsXPos; + 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 - 8; + int xpos2 = *xposOffset + 10; + if (x > xpos && x < xpos2) { + assert(*itemsTable < ARRAYSIZE(_itemTable)); + int itemHeight = _itemTable[*itemsTable].height; + int ypos = *yposOffset; + int ypos2 = ypos - itemHeight - 3; + + if (y < ypos2 && (ypos+3) > y) { + if (highestYPos <= ypos) { + returnValue = i; + highestYPos = ypos; + } + } + } + } + xposOffset += 2; + yposOffset += 1; + itemsTable += 1; + } + + return returnValue; +} + +void KyraEngine::placeItemInGenericMapScene(int item, int index) { + debug(9, "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) { + warning("placeItemInGenericMapScene: placing an item is NOT implemented"); + //if (!sub_B010(room, item, -1, -1, 2, 0)) + // continue; + break; + } + } +} + +#pragma mark - +#pragma mark - Animation specific code +#pragma mark - + +void KyraEngine::restoreAllObjectBackgrounds() { + AnimObject *curObject = _objectQueue; + _screen->_curPage = 2; + + while (curObject) { + // XXX + if (curObject->active) { + preserveOrRestoreBackground(curObject, true); + curObject->x2 = curObject->x1; + curObject->y2 = curObject->y1; + } + curObject = curObject->nextAnimObject; + } + + _screen->_curPage = 0; +} + +void KyraEngine::preserveAnyChangedBackgrounds() { + AnimObject *curObject = _objectQueue; + _screen->_curPage = 2; + + while (curObject) { + // XXX + if (curObject->active && curObject->bkgdChangeFlag) { + preserveOrRestoreBackground(curObject, false); + curObject->bkgdChangeFlag = 0; + } + curObject = curObject->nextAnimObject; + } + + _screen->_curPage = 0; +} + +void KyraEngine::preserveOrRestoreBackground(AnimObject *obj, bool restore) { + int x, y, width = obj->width, 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->copyCurPageBlock(x >> 3, y, width, height, obj->background); + } else { + _screen->copyBlockToPage(_screen->_curPage, x, y, width, height, obj->background); + } +} + +void KyraEngine::prepDrawAllObjects() { + AnimObject *curObject = _objectQueue; + int drawPage = 2; + int flagUnk1 = 0, flagUnk2 = 0, flagUnk3 = 0; + // XXX + if (_brandonStatusBit & 0x20) + flagUnk1 = 0x200; + if (_brandonStatusBit & 0x40) + flagUnk2 = 0x4000; + + while (curObject) { + if (curObject->active) { + int xpos = curObject->x1; + int ypos = curObject->y1; + + int temp = 0; /*si*/ + if (curObject->flags & 0x800) { + temp = 7; + } else { + // XXX + temp = 0; + } + + // XXX + if (!true) { + // XXX + } + + curObject->flags |= 0x800; + if (curObject->index == 0) { + flagUnk3 = 0x100; + + if (flagUnk1 & 0x200 || flagUnk2 & 0x4000) { + flagUnk3 = 0; + } + + if (_brandonStatusBit & 2) { + curObject->flags &= 0xFFFFFFFE; + } + + if (!_scaleMode) { + if (flagUnk3 & 0x100) { + _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x100, (uint8*)_unkBrandonPoisonFlags, 1, temp); + } else if (flagUnk3 & 0x4000) { + // XXX + int hackVar = 0; + _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x4000, hackVar, 0); + } else { + _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1, temp); + } + } else { + if (flagUnk3 & 0x100) { + _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x104, (uint8*)_unkBrandonPoisonFlags, 1, temp, _brandonScaleX, _brandonScaleY); + } else if (flagUnk3 & 0x4000) { + // XXX + int hackVar = 0; + _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x4004, 0, temp, hackVar, _brandonScaleX, _brandonScaleY); + } else { + _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x4, temp, _brandonScaleX, _brandonScaleY); + } + } + } else { + if (curObject->index >= 16 && curObject->index <= 27) { + // XXX + _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | 4, temp, (int)_scaleTable[curObject->drawY]); + } else { + _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags, temp); + } + } + } + curObject = curObject->nextAnimObject; + } +} + +void KyraEngine::copyChangedObjectsForward(int 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; + ypos = curObject->y1; + width = curObject->width; + height = curObject->height; + + _screen->copyRegion(xpos, ypos, xpos, ypos, width, height, 2, 0); + curObject->refreshFlag = 0; + } + } + curObject = curObject->nextAnimObject; + } +} + +void KyraEngine::updateAllObjectShapes() { + restoreAllObjectBackgrounds(); + preserveAnyChangedBackgrounds(); + prepDrawAllObjects(); + copyChangedObjectsForward(0); + + _screen->updateScreen(); +} + +void KyraEngine::animRefreshNPC(int character) { + AnimObject *animObj = &_charactersAnimState[character]; + Character *ch = &_characterList[character]; + + animObj->refreshFlag = 1; + animObj->bkgdChangeFlag = 1; + int facing = ch->facing; + if (facing >= 1 && facing <= 3) { + ch->inventoryItems[7] |= 1; + } else if (facing >= 5 && facing <= 7) { + ch->inventoryItems[7] &= 0xFE; + } + + animObj->drawY = ch->y1; + animObj->sceneAnimPtr = _shapes[4+ch->currentAnimFrame]; + animObj->animFrameNumber = ch->currentAnimFrame; + // XXX + if (character == 0) { + if (_brandonStatusBit & 10) { + animObj->animFrameNumber = 88; + ch->currentAnimFrame = 88; + } + if (_brandonStatusBit & 2) { + animObj->animFrameNumber = _brandonDrawFrame; + ch->currentAnimFrame = _brandonDrawFrame; + animObj->sceneAnimPtr = _shapes[4+_brandonDrawFrame]; + // XXX + } + } + + int xOffset = _defaultShapeTable[ch->currentAnimFrame].xOffset; + int yOffset = _defaultShapeTable[ch->currentAnimFrame].yOffset; + + if (_scaleMode) { + animObj->x1 = ch->x1; + animObj->y1 = ch->y1; + + _brandonScaleX = _scaleTable[ch->y1]; + _brandonScaleY = _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; + + _objectQueue = objectRemoveQueue(_objectQueue, animObj); + if (_objectQueue) { + + } else { + _objectQueue = objectAddHead(_objectQueue, animObj); + } +} + +#pragma mark - +#pragma mark - Queue handling +#pragma mark - + +AnimObject *KyraEngine::objectRemoveQueue(AnimObject *queue, AnimObject *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 (!prev) { + return 0; + } else { + prev->nextAnimObject = 0; + } + } else { + if (cur == rem) { + prev->nextAnimObject = rem->nextAnimObject; + } + } + + return queue; +} + +AnimObject *KyraEngine::objectAddHead(AnimObject *queue, AnimObject *head) { + head->nextAnimObject = queue; + return head; +} + +AnimObject *KyraEngine::objectQueue(AnimObject *queue, AnimObject *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 0; +} } // End of namespace Kyra diff --git a/kyra/kyra.h b/kyra/kyra.h index ead9cbe771..1ba9aa5e18 100644 --- a/kyra/kyra.h +++ b/kyra/kyra.h @@ -31,8 +31,6 @@ class AudioStream; namespace Kyra { -#define MAX_NUM_ROOMS 12 - enum { GF_FLOPPY = 1 << 0, GF_TALKIE = 1 << 1, @@ -58,39 +56,80 @@ struct Character { uint32 unk6; uint8 inventoryItems[10]; int16 x1, y1, x2, y2; + uint16 field_20; + uint16 field_23; }; struct Shape { - uint8 unk0; - uint8 unk1; - uint8 imageNum; + uint8 imageIndex; + int8 xOffset, yOffset; uint8 x, y, w, h; }; struct Room { -// uint8 id; + uint8 nameIndex; uint16 northExit; uint16 eastExit; uint16 southExit; uint16 westExit; uint8 itemsTable[12]; - const char *filename; + uint16 itemsXPos[12]; + uint8 itemsYPos[12]; + uint32 unkField3[12]; // maybe pointer to shape of the item }; -struct Cursor { +struct AnimObject { + uint8 index; + uint32 active; + uint32 refreshFlag; + uint32 bkgdChangeFlag; + uint32 flags; + int16 drawY; + uint8 *sceneAnimPtr; + uint16 animFrameNumber; + uint8 *background; + uint16 rectSize; + int16 x1, y1; + int16 x2, y2; + uint16 width; + uint16 height; + uint16 width2; + uint16 height2; + AnimObject *nextAnimObject; +}; + +struct Rect { int x, y; - int w, h; + int x2, y2; }; struct TalkCoords { uint16 y, x, w; }; +struct Item { + uint8 unk1; + uint8 height; + uint8 unk2; + uint8 unk3; +}; + struct SeqLoop { const uint8 *ptr; uint16 count; }; +struct SceneExits { + int16 NorthXPos; + int8 NorthYPos; + int16 EastXPos; + int8 EastYPos; + int16 SouthXPos; + int8 SouthYPos; + int16 WestXPos; + int8 WestYPos; +}; + struct WSAMovieV1; class MusicPlayer; @@ -99,6 +138,9 @@ class Resource; class PAKFile; class Screen; class Sprites; +struct ScriptState; +struct ScriptData; +class ScriptHelper; class KyraEngine : public Engine { friend class MusicPlayer; @@ -124,10 +166,15 @@ public: uint8 game() const { return _game; } uint32 features() const { return _features; } + SceneExits sceneExits() const { return _sceneExits; } + // ugly hack used by the dat loader + SceneExits &sceneExits() { return _sceneExits; } Common::RandomSource _rnd; + int16 _northExitHeight; typedef void (KyraEngine::*IntroProc)(); + typedef int (KyraEngine::*OpcodeProc)(ScriptState *script); const char **seqWSATable() { return (const char **)_seq_WSATable; } const char **seqCPSTable() { return (const char **)_seq_CPSTable; } @@ -155,6 +202,163 @@ public: 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_Poison_Brandon_And_Remaps(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); protected: @@ -172,6 +376,42 @@ protected: int getWidestLineWidth(int linesCount); void calcWidestLineBounds(int &x1, int &x2, int w, int cx); void printText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2); + void setCharacterDefaultFrame(int character); + void setCharactersPositions(int character); + void setCharactersHeight(); + 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 moveCharacterToPos(int character, int facing, int xpos, int ypos); + void setCharacterPositionWithUpdate(int character); + int setCharacterPosition(int character, uint8 *unk1); + void setCharacterPositionHelper(int character, uint8 *unk1); + int getOppositeFacingDirection(int dir); + void loadSceneMSC(); + void blockInRegion(int x, int y, int width, int height); + void blockOutRegion(int x, int y, int width, int height); + void startSceneScript(int brandonAlive); + 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 restoreAllObjectBackgrounds(); + void preserveAnyChangedBackgrounds(); + void preserveOrRestoreBackground(AnimObject *obj, bool restore); + void prepDrawAllObjects(); + void copyChangedObjectsForward(int refreshFlag); + void updateAllObjectShapes(); + void animRefreshNPC(int character); + int findDuplicateItemShape(int shape); + + AnimObject *objectRemoveQueue(AnimObject *queue, AnimObject *rem); + AnimObject *objectAddHead(AnimObject *queue, AnimObject *head); + AnimObject *objectQueue(AnimObject *queue, AnimObject *add); void seq_demo(); void seq_intro(); @@ -188,22 +428,32 @@ protected: void snd_setSoundEffectFile(int file); void snd_playSoundEffect(int track); + static OpcodeProc _opcodeTable[]; + static const int _opcodeTableSize; + enum { RES_ALL = 0, - RES_INTRO = (1 << 0) + RES_INTRO = (1 << 0), + RES_INGAME = (1 << 1) }; 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 loadRoom(uint16 roomID); - void drawRoom(); + void res_loadRoomTable(const byte *src, Room **loadTo, int *size); + void res_loadShapeTable(const byte *src, Shape **loadTo, int *size); + void delay(uint32 millis); void loadPalette(const char *filename, uint8 *palData); void loadMouseShapes(); - void setupRooms(); + void loadCharacterShapes(); + void loadSpecialEffectShapes(); + void loadItems(); + void loadMainScreen(); + void setCharactersInDefaultScene(); + void resetBrandonPosionFlags(); + void initAnimStateList(); uint8 _game; bool _fastMode; @@ -216,14 +466,60 @@ protected: uint16 _talkMessageY; uint16 _talkMessageH; bool _talkMessagePrinted; - uint8 _flagsTable[51]; - uint8 *_itemShapes[377]; + uint8 _flagsTable[53]; + uint8 *_shapes[377]; uint16 _gameSpeed; uint32 _features; int _mouseX, _mouseY; bool _needMouseUpdate; - + + WSAMovieV1 *_wsaObjects[10]; + uint16 _entranceMouseCursorTracks[8]; + uint16 _walkBlockNorth; + uint16 _walkBlockEast; + uint16 _walkBlockSouth; + uint16 _walkBlockWest; + + int32 _scaleMode; + uint16 _scaleTable[145]; + + Rect _noDropRects[11]; + + uint16 _birthstoneGemTable[4]; + uint8 _idolGemsTable[3]; + + uint16 _marbleVaseItem; + + uint16 _brandonStatusBit; + uint8 _unkBrandonPoisonFlags[256]; // this seem not to be posion flags, it is used for drawing once + int _brandonPosX; + int _brandonPosY; + int16 _brandonScaleX; + int16 _brandonScaleY; + int _brandonDrawFrame; + + int8 *_sceneAnimTable[50]; + + Item _itemTable[145]; + + uint16 *_exitListPtr; + uint16 _exitList[11]; + SceneExits _sceneExits; uint16 _currentRoom; + uint8 *_maskBuffer; + + int _movUnkVar1; + int _lastFindWayRet; + int *_movFacingTable; + + AnimObject *_objectQueue; + AnimObject *_animStates; + AnimObject *_charactersAnimState; + AnimObject *_animObjects; + AnimObject *_unkAnimsBuffer; + + int _curMusicTheme; + int _newMusicTheme; AudioStream *_currentVocFile; Audio::SoundHandle _vocHandle; @@ -232,7 +528,16 @@ protected: MusicPlayer *_midi; SeqPlayer *_seq; Sprites *_sprites; - Room _rooms[MAX_NUM_ROOMS]; + ScriptHelper *_scriptInterpreter; + + ScriptState *_scriptMain; + ScriptData *_npcScriptData; + + ScriptState *_scriptClick; // TODO: rename to a better name + ScriptData *_scriptClickData; + + Character *_characterList; + Character *_currentCharacter; uint8 *_seq_Forest; uint8 *_seq_KallakWriting; @@ -255,8 +560,22 @@ protected: int _seq_COLTable_Size; int _seq_textsTable_Size; + char **_characterImageTable; + int _characterImageTableSize; + + Shape *_defaultShapeTable; + int _defaultShapeTableSize; + + Room *_roomTable; + int _roomTableSize; + char **_roomFilenameTable; + int _roomFilenameTableSize; + static const char *_xmidiFiles[]; static const int _xmidiFilesCount; + + static const int8 _charXPosTable[]; + static const int8 _charYPosTable[]; }; } // End of namespace Kyra diff --git a/kyra/screen.cpp b/kyra/screen.cpp index f99786211b..691f1fad6b 100644 --- a/kyra/screen.cpp +++ b/kyra/screen.cpp @@ -45,6 +45,12 @@ Screen::Screen(KyraEngine *vm, OSystem *system) 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; @@ -78,6 +84,9 @@ Screen::~Screen() { free(_animBlockPtr); free(_mouseShape); free(_mouseRect); + for (int i = 0; i < 3; ++i) { + free(_palettes[i]); + } } void Screen::updateScreen() { @@ -314,7 +323,7 @@ void Screen::copyCurPageBlock(int x, int y, int h, int w, uint8 *dst) { } const uint8 *src = getPagePtr(_curPage) + y * SCREEN_W + x * 8; while (h--) { - memcpy(dst, src, w * 8); + memcpy(dst, src, w); dst += SCREEN_W; src += SCREEN_H; } @@ -559,47 +568,67 @@ void Screen::setScreenDim(int dim) { // XXX } -void Screen::drawShapePlotPixelCallback1(uint8 *dst, uint8 color) { - debug(9, "Screen::drawShapePlotPixelCallback1(0x%X, %d)", dst, color); - *dst = color; -} - -void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, int *flagsTable, bool itemShape) { - debug(9, "Screen::drawShape(%d, %d, %d, %d, %d, %d)", pageNum, x, y, sd, flags, itemShape); +// TODO: implement the other callbacks and implement all of this function +void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...) { + debug(9, "Screen::drawShape(%d, %d, %d, %d, %d, ...)", pageNum, x, y, sd, flags); assert(shapeData); + 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 var_30 = 0; + uint8 *table2 = 0; + uint8 *table3 = 0; + uint8 *table4 = 0; + if (flags & 0x8000) { - warning("unhandled (flags & 0x8000) in Screen::drawShape()"); + table2 = va_arg(args, uint8*); } if (flags & 0x100) { - warning("unhandled (flags & 0x100) in Screen::drawShape()"); + table = va_arg(args, uint8*); + tableLoopCount = va_arg(args, int); + if (!tableLoopCount) + flags &= 0xFFFFFEFF; } if (flags & 0x1000) { - warning("unhandled (flags & 0x1000) in Screen::drawShape()"); + table3 = va_arg(args, uint8*); + table4 = va_arg(args, uint8*); } if (flags & 0x200) { - warning("unhandled (flags & 0x200) in Screen::drawShape()"); + drawShapeVar1 += 1; + drawShapeVar1 &= 7; + drawShapeVar3 = drawShapeVar2[drawShapeVar1]; + drawShapeVar4 = 0; + drawShapeVar5 = 256; } if (flags & 0x4000) { - warning("unhandled (flags & 0x4000) in Screen::drawShape()"); + drawShapeVar5 = va_arg(args, int); } if (flags & 0x800) { - warning("unhandled (flags & 0x800) in Screen::drawShape()"); + var_30 = va_arg(args, int); } int scale_w, scale_h; if (flags & DSF_SCALE) { - scale_w = *flagsTable++; - scale_h = *flagsTable++; + scale_w = va_arg(args, int); + scale_h = va_arg(args, int); } else { scale_w = 0x100; scale_h = 0x100; } int ppc = (flags >> 8) & 0x3F; - assert(ppc < _drawShapePlotPixelCount); - DrawShapePlotPixelCallback plotPixel = _drawShapePlotPixelTable[ppc]; const uint8 *src = shapeData; - if (_vm->features() & GF_TALKIE && !itemShape) { + if (_vm->features() & GF_TALKIE) { src += 2; } uint16 shapeFlags = READ_LE_UINT16(src); src += 2; @@ -607,12 +636,14 @@ void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int 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; } @@ -640,6 +671,7 @@ void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int } if (!_decodeShapeBuffer) { _decodeShapeBufferSize = 0; + va_end(args); return; } memset(_decodeShapeBuffer, 0, _decodeShapeBufferSize); @@ -650,7 +682,15 @@ void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int while (count > 0) { uint8 code = *src++; if (code != 0) { - *decodedShapeFrame++ = code; + // this is guessed + if (shapeFlags & 1) { + const uint8 *colorTable = shapeData + 10; + if (_vm->features() & GF_TALKIE) + colorTable += 2; + *decodedShapeFrame++ = colorTable[code]; + } else { + *decodedShapeFrame++ = code; + } --count; } else { code = *src++; @@ -732,18 +772,294 @@ void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int j = -j; } for (x = x1; x < x2; ++x) { - int i = scaleXTable[x]; + int xpos = scaleXTable[x]; if (flags & DSF_X_FLIPPED) { - i = -i; + xpos = -xpos; } - uint8 color = shapeBuffer[j * shapeWidth + i]; + uint8 color = shapeBuffer[j * shapeWidth + xpos]; if (color != 0) { - (this->*plotPixel)(dst, color); + 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 - shapeBuffer; + uint8 pixel = *(_shapePages[0] + offset); + pixel &= 0x7F; + pixel &= 0x87; + if (var_30 < pixel) { + color = *(_shapePages[1] + offset); + } + } break; + + case 9: { + int offset = dst - shapeBuffer; + uint8 pixel = *(_shapePages[0] + offset); + pixel &= 0x7F; + pixel &= 0x87; + if (var_30 < pixel) { + color = *(_shapePages[1] + offset); + } else { + for (int i = 0; i < tableLoopCount; ++i) { + color = table[color]; + } + } + } break; + + case 10: { + int offset = dst - shapeBuffer; + uint8 pixel = *(_shapePages[0] + offset); + pixel &= 0x7F; + pixel &= 0x87; + if (var_30 < 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 - shapeBuffer; + uint8 pixel = *(_shapePages[0] + offset); + pixel &= 0x7F; + pixel &= 0x87; + if (var_30 < pixel) { + color = *(_shapePages[1] + offset); + } else { + color = *dst; + for (int i = 0; i < tableLoopCount; ++i) { + color = table[color]; + } + } + } break; + + case 12: { + int offset = dst - shapeBuffer; + uint8 pixel = *(_shapePages[0] + offset); + pixel &= 0x7F; + pixel &= 0x87; + if (var_30 < pixel) { + color = *(_shapePages[1] + offset); + } else { + color = table2[color]; + } + } break; + + case 13: { + int offset = dst - shapeBuffer; + uint8 pixel = *(_shapePages[0] + offset); + pixel &= 0x7F; + pixel &= 0x87; + if (var_30 < 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 - shapeBuffer; + uint8 pixel = *(_shapePages[0] + offset); + pixel &= 0x7F; + pixel &= 0x87; + if (var_30 < 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 - shapeBuffer; + uint8 pixel = *(_shapePages[0] + offset); + pixel &= 0x7F; + pixel &= 0x87; + if (var_30 < 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) { @@ -994,10 +1310,17 @@ uint8 *Screen::encodeShape(int x, int y, int w, int h, int flags) { static uint8 table[274]; int tableIndex = 0; - uint8 *newShape = (uint8*)malloc(shapeSize+16); + uint8 *newShape = NULL; + if (_vm->features() & GF_TALKIE) { + newShape = (uint8*)malloc(shapeSize+16); + } else { + newShape = (uint8*)malloc(shapeSize+18); + } 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; @@ -1026,6 +1349,7 @@ uint8 *Screen::encodeShape(int x, int y, int w, int h, int flags) { table[0x100+tableIndex] = value; table[value] = tableIndex; ++tableIndex; + value = tableIndex; } } else { value = table[value]; @@ -1063,11 +1387,15 @@ uint8 *Screen::encodeShape(int x, int y, int w, int h, int flags) { 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; } @@ -1097,6 +1425,8 @@ uint8 *Screen::encodeShape(int x, int y, int w, int h, int flags) { if (flags & 1) { dst = newShape + 10; + if (_vm->features() & GF_TALKIE) + dst += 2; src = &table[0x100]; memcpy(dst, src, sizeof(uint8)*16); } @@ -1281,6 +1611,9 @@ byte *Screen::setMouseCursor(int x, int y, byte *shape) { restoreMouseRect(); + if (_vm->features() & GF_TALKIE) + shape += 2; + int mouseRectSize = getRectSize((READ_LE_UINT16(shape + 3) >> 3) + 2, shape[5]); if (_mouseRectSize < mouseRectSize) { free(_mouseRect); @@ -1302,6 +1635,8 @@ byte *Screen::setMouseCursor(int x, int y, byte *shape) { byte *dst = _mouseShape; byte *src = shape; + if (_vm->features() & GF_TALKIE) + dst += 2; if (!(READ_LE_UINT16(shape) & 2)) { uint16 newFlags = 0; @@ -1338,13 +1673,10 @@ void Screen::restoreMouseRect() { // if disableMouse // return - // if mouseUnk == 0 { - if (_mouseDrawWidth && _mouseRect) { - copyScreenFromRect(_mouseDrawX, _mouseDrawY, _mouseDrawWidth, _mouseDrawHeight, _mouseRect); - } - _mouseDrawWidth = 0; - // } - // ++mouseUnk + if (_mouseDrawWidth && _mouseRect) { + copyScreenFromRect(_mouseDrawX, _mouseDrawY, _mouseDrawWidth, _mouseDrawHeight, _mouseRect); + } + _mouseDrawWidth = 0; } void Screen::copyMouseToScreen() { @@ -1352,11 +1684,6 @@ void Screen::copyMouseToScreen() { // if disableMouse // return - // if mouseUnk == 0 - // return - // --mouseUnk - // if mouseUnk != 0 - // return int width = _mouseWidth; int height = _mouseHeight; int xpos = _vm->mouseX() - _mouseXOffset; @@ -1396,14 +1723,14 @@ void Screen::copyMouseToScreen() { copyScreenToRect(_mouseDrawX, _mouseDrawY, width, height, _mouseRect); } - drawShape(0, _mouseShape, xpos, ypos, 0, 0, 0, true); + drawShape(0, _mouseShape, xpos, ypos, 0, 0, 0); } void Screen::copyScreenFromRect(int x, int y, int w, int h, uint8 *ptr) { debug(9, "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]; + uint8 *dst = &_pagePtrs[0][y * SCREEN_W + x]; for (int i = 0; i < h; ++i) { memcpy(dst, src, w); src += w; @@ -1415,7 +1742,7 @@ void Screen::copyScreenToRect(int x, int y, int w, int h, uint8 *ptr) { debug(9, "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; + uint8 *dst = ptr; for (int i = 0; i < h; ++i) { memcpy(dst, src, w); dst += w; @@ -1423,4 +1750,14 @@ void Screen::copyScreenToRect(int x, int y, int w, int h, uint8 *ptr) { } } +uint8 *Screen::getPalette(int num) { + debug(9, "getPalette(%d)", num); + assert(num >= 0 && num < 4); + if (num == 0) { + return _screenPalette; + } + + return _palettes[num-1]; +} + } // End of namespace Kyra diff --git a/kyra/screen.h b/kyra/screen.h index 2642ba782a..6cae20df88 100644 --- a/kyra/screen.h +++ b/kyra/screen.h @@ -23,6 +23,7 @@ #define KYRASCREEN_H #include "common/util.h" +#include <stdarg.h> class OSystem; @@ -110,26 +111,26 @@ public: 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, int *flagsTable, bool itemShape=false); + 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); 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); byte *setMouseCursor(int x, int y, byte *shape); + uint8 *getPalette(int num); int _charWidth; int _charOffset; int _curPage; uint8 *_currentPalette; - - typedef void (Screen::*DrawShapePlotPixelCallback)(uint8 *dst, uint8 c); + uint8 *_shapePages[2]; private: int16 encodeShapeAndCalculateSize(uint8 *from, uint8 *to, int size); @@ -139,8 +140,8 @@ private: void copyScreenToRect(int x, int y, int w, int h, uint8 *ptr); uint8 *_pagePtrs[16]; - uint8 *_shapePages[2]; uint8 *_screenPalette; + uint8 *_palettes[3]; const ScreenDim *_curDim; FontId _currentFont; Font _fonts[FID_NUM]; @@ -164,8 +165,6 @@ private: static const ScreenDim _screenDimTable[]; static const int _screenDimTableCount; - static const DrawShapePlotPixelCallback _drawShapePlotPixelTable[]; - static const int _drawShapePlotPixelCount; }; } // End of namespace Kyra diff --git a/kyra/script.cpp b/kyra/script.cpp index 5520079b71..c30e25208b 100644 --- a/kyra/script.cpp +++ b/kyra/script.cpp @@ -69,7 +69,7 @@ ScriptHelper::ScriptHelper(KyraEngine *vm) : _vm(vm) { ScriptHelper::~ScriptHelper() { } -bool ScriptHelper::loadScript(const char *filename, ScriptData *scriptData, byte *specialPtr) { +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; @@ -147,6 +147,8 @@ bool ScriptHelper::loadScript(const char *filename, ScriptData *scriptData, byte return false; } scriptData->dataSize = chunkSize / 2; + scriptData->opcodes = opcodes; + scriptData->opcodeSize = opcodeSize; delete [] data; return true; diff --git a/kyra/script.h b/kyra/script.h index 9140e3b7a4..1469ee6ef6 100644 --- a/kyra/script.h +++ b/kyra/script.h @@ -30,7 +30,8 @@ struct ScriptData { byte *data; byte *ordr; uint16 dataSize; - /*command table ptr (uint32)*/ + KyraEngine::OpcodeProc *opcodes; + int opcodeSize; uint16 mustBeFreed; }; @@ -53,10 +54,10 @@ public: ScriptHelper(KyraEngine *vm); virtual ~ScriptHelper(); - bool loadScript(const char *filename, ScriptData *data, byte *specialPtr = 0); + bool loadScript(const char *filename, ScriptData *data, KyraEngine::OpcodeProc *opcodes, int opcodeSize, byte *specialPtr = 0); void unloadScript(ScriptData *data); - void initScript(ScriptState *scriptStat, ScriptData *data); + void initScript(ScriptState *scriptState, ScriptData *data); bool startScript(ScriptState *script, int function); bool validScript(ScriptState *script); diff --git a/kyra/script_v1.cpp b/kyra/script_v1.cpp index 326de04d2c..b61aa144d8 100644 --- a/kyra/script_v1.cpp +++ b/kyra/script_v1.cpp @@ -22,6 +22,9 @@ #include "common/stdafx.h" #include "kyra/kyra.h" #include "kyra/script.h" +#include "kyra/screen.h" +#include "kyra/sprites.h" +#include "common/system.h" namespace Kyra { @@ -112,9 +115,10 @@ void ScriptHelper::c1_subSP() { } void ScriptHelper::c1_execOpcode() { - warning("c1_execOpcode STUB"); - // return 0 zero for now - _curScript->retValue = 0; + assert((int)_parameter < _curScript->dataPtr->opcodeSize); + if (_curScript->dataPtr->opcodes[_parameter] == &KyraEngine::cmd_dummy) + debug("calling unimplemented opcode(0x%.02X)", _parameter); + _curScript->retValue = (_vm->*_curScript->dataPtr->opcodes[_parameter])(_curScript); } void ScriptHelper::c1_ifNotJmp() { @@ -287,4 +291,949 @@ void ScriptHelper::c1_setRetAndJmp() { } } +#pragma mark - +#pragma mark - Opcode implementations +#pragma mark - + +#define stackPos(x) script->stack[script->sp+x] + +int KyraEngine::cmd_magicInMouseItem(ScriptState *script) { + warning("STUB: cmd_magicInMouseItem"); + return 0; +} + +int KyraEngine::cmd_characterSays(ScriptState *script) { + warning("STUB: cmd_characterSays"); + return 0; +} + +int KyraEngine::cmd_pauseTicks(ScriptState *script) { + warning("STUB: cmd_pauseTicks"); + return 0; +} + +int KyraEngine::cmd_drawSceneAnimShape(ScriptState *script) { + debug(9, "cmd_drawSceneAnimShape(0x%X)", script); + _screen->drawShape( stackPos(4), _sprites->getSceneShape(stackPos(0)), stackPos(1), stackPos(2), 0, stackPos(3) ); + return 0; +} + +int KyraEngine::cmd_queryGameFlag(ScriptState *script) { + debug(9, "cmd_queryGameFlag(0x%X)", script); + return queryGameFlag(stackPos(0)); +} + +int KyraEngine::cmd_setGameFlag(ScriptState *script) { + debug(9, "cmd_setGameFlag(0x%X)", script); + return setGameFlag(stackPos(0)); +} + +int KyraEngine::cmd_resetGameFlag(ScriptState *script) { + debug(9, "cmd_resetGameFlag(0x%X)", script); + return resetGameFlag(stackPos(0)); +} + +int KyraEngine::cmd_runNPCScript(ScriptState *script) { + warning("STUB: cmd_runNPCScript"); + return 0; +} + +int KyraEngine::cmd_setSpecialExitList(ScriptState *script) { + debug(9, "cmd_setSpecialExitList(0x%X)", script); + + for (int i = 0; i < 10; ++i) { + _exitList[i] = stackPos(i); + } + _exitListPtr = _exitList; + + return 0; +} + +int KyraEngine::cmd_blockInWalkableRegion(ScriptState *script) { + debug(9, "cmd_blockInWalkableRegion(0x%X)", script); + blockOutRegion(stackPos(0), stackPos(1), stackPos(2)-stackPos(0)+1, stackPos(3)-stackPos(1)+1); + return 0; +} + +int KyraEngine::cmd_blockOutWalkableRegion(ScriptState *script) { + debug(9, "cmd_blockOutWalkableRegion(0x%X)", script); + blockOutRegion(stackPos(0), stackPos(1), stackPos(2)-stackPos(0)+1, stackPos(3)-stackPos(1)+1); + return 0; +} + +int KyraEngine::cmd_walkPlayerToPoint(ScriptState *script) { + warning("STUB: cmd_walkPlayerToPoint"); + return 0; +} + +int KyraEngine::cmd_dropItemInScene(ScriptState *script) { + debug(9, "cmd_dropItemInScene(0x%X)", script); + 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; + + warning("PARTIALLY IMPLEMENTED: cmd_dropItemInScene"); + // XXX animAddGameItem + // XXX updateAllObjectShapes + } else { + if (item == 43) { + placeItemInGenericMapScene(item, 0); + } else { + placeItemInGenericMapScene(item, 1); + } + } + return 0; +} + +int KyraEngine::cmd_drawAnimShapeIntoScene(ScriptState *script) { + warning("STUB: cmd_drawAnimShapeIntoScene"); + return 0; +} + +int KyraEngine::cmd_createMouseItem(ScriptState *script) { + warning("STUB: cmd_createMouseItem"); + return 0; +} + +int KyraEngine::cmd_savePageToDisk(ScriptState *script) { + warning("STUB: cmd_savePageToDisk"); + return 0; +} + +int KyraEngine::cmd_sceneAnimOn(ScriptState *script) { + debug(9, "cmd_sceneAnimOn(0x%X)", script); + _sprites->enableAnim(stackPos(0)); + return 0; +} + +int KyraEngine::cmd_sceneAnimOff(ScriptState *script) { + debug(9, "cmd_sceneAnimOff(0x%X)", script); + _sprites->disableAnim(stackPos(0)); + return 0; +} + +int KyraEngine::cmd_getElapsedSeconds(ScriptState *script) { + warning("STUB: cmd_getElapsedSeconds"); + return 0; +} + +int KyraEngine::cmd_mouseIsPointer(ScriptState *script) { + warning("STUB: cmd_mouseIsPointer"); + return 0; +} + +int KyraEngine::cmd_destroyMouseItem(ScriptState *script) { + warning("STUB: cmd_destroyMouseItem"); + return 0; +} + +int KyraEngine::cmd_runSceneAnimUntilDone(ScriptState *script) { + warning("STUB: cmd_runSceneAnimUntilDone"); + return 0; +} + +int KyraEngine::cmd_fadeSpecialPalette(ScriptState *script) { + warning("STUB: cmd_fadeSpecialPalette"); + return 0; +} + +int KyraEngine::cmd_playAdlibSound(ScriptState *script) { + warning("STUB: cmd_playAdlibSound"); + return 0; +} + +int KyraEngine::cmd_playAdlibScore(ScriptState *script) { + warning("STUB: cmd_playAdlibScore"); + return 0; +} + +int KyraEngine::cmd_phaseInSameScene(ScriptState *script) { + warning("STUB: cmd_phaseInSameScene"); + return 0; +} + +int KyraEngine::cmd_setScenePhasingFlag(ScriptState *script) { + warning("STUB: cmd_setScenePhasingFlag"); + return 0; +} + +int KyraEngine::cmd_resetScenePhasingFlag(ScriptState *script) { + warning("STUB: cmd_resetScenePhasingFlag"); + return 0; +} + +int KyraEngine::cmd_queryScenePhasingFlag(ScriptState *script) { + warning("STUB: cmd_queryScenePhasingFlag"); + return 0; +} + +int KyraEngine::cmd_sceneToDirection(ScriptState *script) { + warning("STUB: cmd_sceneToDirection"); + return 0; +} + +int KyraEngine::cmd_setBirthstoneGem(ScriptState *script) { + debug(9, "cmd_setBirthstoneGem(0x%X)", script); + int index = stackPos(0); + if (index < 4 && index >= 0) { + _birthstoneGemTable[index] = stackPos(1); + return 1; + } + return 0; +} + +int KyraEngine::cmd_placeItemInGenericMapScene(ScriptState *script) { + debug(9, "cmd_placeItemInGenericMapScene(0x%X)", script); + placeItemInGenericMapScene(stackPos(0), stackPos(1)); + return 0; +} + +int KyraEngine::cmd_setBrandonStatusBit(ScriptState *script) { + warning("STUB: cmd_setBrandonStatusBit"); + return 0; +} + +int KyraEngine::cmd_pauseSeconds(ScriptState *script) { + debug(9, "cmd_pauseSeconds(0x%X)", script); + _system->delayMillis(stackPos(0)*1000); + return 0; +} + +int KyraEngine::cmd_getCharactersLocation(ScriptState *script) { + warning("STUB: cmd_getCharactersLocation"); + return 0; +} + +int KyraEngine::cmd_runNPCSubscript(ScriptState *script) { + warning("STUB: cmd_runNPCSubscript"); + return 0; +} + +int KyraEngine::cmd_magicOutMouseItem(ScriptState *script) { + warning("STUB: cmd_magicOutMouseItem"); + return 0; +} + +int KyraEngine::cmd_internalAnimOn(ScriptState *script) { + warning("STUB: cmd_internalAnimOn"); + return 0; +} + +int KyraEngine::cmd_forceBrandonToNormal(ScriptState *script) { + warning("STUB: cmd_forceBrandonToNormal"); + return 0; +} + +int KyraEngine::cmd_poisonDeathNow(ScriptState *script) { + warning("STUB: cmd_poisonDeathNow"); + return 0; +} + +int KyraEngine::cmd_setScaleMode(ScriptState *script) { + warning("STUB: cmd_setScaleMode"); + return 0; +} + +int KyraEngine::cmd_openWSAFile(ScriptState *script) { + debug(9, "cmd_openWSAFile(0x%X)", script); + + int wsaIndex = stackPos(0); + uint16 offset = READ_BE_UINT16(&script->dataPtr->text[wsaIndex]); + char *filename = (char*)&script->dataPtr->text[offset]; + + wsaIndex = stackPos(1); + // stackPos(2) is NOT used whyever + int offscreenDecode = 0; + if (!stackPos(3)) { + offscreenDecode = 1; + } else { + offscreenDecode = 0; + } + + _wsaObjects[wsaIndex] = wsa_open(filename, offscreenDecode, 0); + assert(_wsaObjects[wsaIndex]); + + return 0; +} + +int KyraEngine::cmd_closeWSAFile(ScriptState *script) { + debug(9, "cmd_closeWSAFile(0x%X)", script); + + int wsaIndex = stackPos(0); + if (_wsaObjects[wsaIndex]) { + wsa_close(_wsaObjects[wsaIndex]); + _wsaObjects[wsaIndex] = 0; + } + + return 0; +} + +int KyraEngine::cmd_runWSAFromBeginningToEnd(ScriptState *script) { + debug(9, "cmd_runWSAFromBeginningToEnd(0x%X)", script); + + _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; + + while (running) { + wsa_play(_wsaObjects[wsaIndex], wsaFrame++, xpos, ypos, 0); + if (wsaFrame >= wsa_getNumFrames(_wsaObjects[wsaIndex])) + running = false; + + waitTicks(waitTime); + _screen->updateScreen(); + if (worldUpdate) { + updateAllObjectShapes(); + // XXX + } + } + + _screen->showMouse(); + + return 0; +} + +int KyraEngine::cmd_displayWSAFrame(ScriptState *script) { + warning("STUB: cmd_displayWSAFrame"); + return 0; +} + +int KyraEngine::cmd_enterNewScene(ScriptState *script) { + debug(9, "cmd_enterNewScene(0x%X)", script); + enterNewScene(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + return 0; +} + +int KyraEngine::cmd_setSpecialEnterXAndY(ScriptState *script) { + debug(9, "cmd_setSpecialEnterXAndY(0x%X)", script); + _brandonPosX = stackPos(0); + _brandonPosY = stackPos(1); + if (_brandonPosX + 1 == 0 && _brandonPosY + 1 == 0) + _currentCharacter->currentAnimFrame = 88; + return 0; +} + +int KyraEngine::cmd_runWSAFrames(ScriptState *script) { + warning("STUB: cmd_runWSAFrames"); + return 0; +} + +int KyraEngine::cmd_popBrandonIntoScene(ScriptState *script) { + warning("STUB: cmd_popBrandonIntoScene"); + return 0; +} + +int KyraEngine::cmd_restoreAllObjectBackgrounds(ScriptState *script) { + warning("STUB: cmd_restoreAllObjectBackgrounds"); + return 0; +} + +int KyraEngine::cmd_setCustomPaletteRange(ScriptState *script) { + warning("STUB: cmd_setCustomPaletteRange"); + return 0; +} + +int KyraEngine::cmd_loadPageFromDisk(ScriptState *script) { + warning("STUB: cmd_loadPageFromDisk"); + return 0; +} + +int KyraEngine::cmd_customPrintTalkString(ScriptState *script) { + warning("STUB: cmd_customPrintTalkString"); + return 0; +} + +int KyraEngine::cmd_restoreCustomPrintBackground(ScriptState *script) { + warning("STUB: cmd_restoreCustomPrintBackground"); + return 0; +} + +int KyraEngine::cmd_hideMouse(ScriptState *script) { + debug(9, "cmd_hideMouse(0x%X)", script); + _screen->hideMouse(); + return 0; +} + +int KyraEngine::cmd_showMouse(ScriptState *script) { + debug(9, "cmd_showMouse(0x%X)", script); + _screen->showMouse(); + return 0; +} + +int KyraEngine::cmd_getCharacterX(ScriptState *script) { + debug(9, "cmd_getCharacterX(0x%X)", script); + return _characterList[stackPos(0)].x1; +} + +int KyraEngine::cmd_getCharacterY(ScriptState *script) { + debug(9, "cmd_getCharacterY(0x%X)", script); + return _characterList[stackPos(0)].y1; +} + +int KyraEngine::cmd_changeCharactersFacing(ScriptState *script) { + warning("STUB: cmd_changeCharactersFacing"); + return 0; +} + +int KyraEngine::cmd_CopyWSARegion(ScriptState *script) { + warning("STUB: cmd_CopyWSARegion"); + return 0; +} + +int KyraEngine::cmd_printText(ScriptState *script) { + warning("STUB: cmd_printText"); + return 0; +} + +int KyraEngine::cmd_random(ScriptState *script) { + debug(9, "cmd_random(0x%X)", script); + 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) { + warning("STUB: cmd_displayWSAFrameOnHidPage"); + return 0; +} + +int KyraEngine::cmd_displayWSASequentialFrames(ScriptState *script) { + warning("STUB: cmd_displayWSASequentialFrames"); + return 0; +} + +int KyraEngine::cmd_drawCharacterStanding(ScriptState *script) { + debug(9, "cmd_drawCharacterStanding(0x%X)", script); + // XXX + 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; + } + animRefreshNPC(character); + if (updateShapes) { + updateAllObjectShapes(); + } + return 0; +} + +int KyraEngine::cmd_internalAnimOff(ScriptState *script) { + warning("STUB: cmd_internalAnimOff"); + return 0; +} + +int KyraEngine::cmd_changeCharactersXAndY(ScriptState *script) { + warning("STUB: cmd_changeCharactersXAndY"); + return 0; +} + +int KyraEngine::cmd_clearSceneAnimatorBeacon(ScriptState *script) { + warning("STUB: cmd_clearSceneAnimatorBeacon"); + return 0; +} + +int KyraEngine::cmd_querySceneAnimatorBeacon(ScriptState *script) { + warning("STUB: cmd_querySceneAnimatorBeacon"); + return 0; +} + +int KyraEngine::cmd_refreshSceneAnimator(ScriptState *script) { + warning("STUB: cmd_refreshSceneAnimator"); + return 0; +} + +int KyraEngine::cmd_placeItemInOffScene(ScriptState *script) { + debug(9, "cmd_placeItemInOffScene(0x%X)", script); + 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) { + warning("STUB: cmd_wipeDownMouseItem"); + return 0; +} + +int KyraEngine::cmd_placeCharacterInOtherScene(ScriptState *script) { + debug(9, "cmd_placeCharacterInOtherScene(0x%X)", script); + int id = stackPos(0); + int sceneId = stackPos(1); + int xpos = stackPos(2) & 0xFFFC; + int ypos = stackPos(3) & 0xFE; + 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) { + warning("STUB: cmd_getKey"); + return 0; +} + +int KyraEngine::cmd_specificItemInInventory(ScriptState *script) { + warning("STUB: cmd_specificItemInInventory"); + return 0; +} + +int KyraEngine::cmd_popMobileNPCIntoScene(ScriptState *script) { + warning("STUB: cmd_popMobileNPCIntoScene"); + 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) { + warning("STUB: cmd_setCharactersLocation"); + return 0; +} + +int KyraEngine::cmd_walkCharacterToPoint(ScriptState *script) { + warning("STUB: cmd_walkCharacterToPoint"); +// debug(9, "cmd_walkCharacterToPoint(0x%X)", script); +// int character = stackPos(0); +// int toX = stackPos(1); +// int toY = stackPos(2); +// _movUnkVar1 = 1; +// int findWayReturn = findWay(_characterList[character].x1, _characterList[character].y1, toX, toY, _movFacingTable, 150); +// _movUnkVar1 = 0; +// if (_lastFindWayRet < findWayReturn) { +// _lastFindWayRet = findWayReturn; +// } +// if (findWayReturn == 0x7D00 || findWayReturn == 0) { +// return 0; +// } +// int *curPos = _movFacingTable; +// bool running = true; +// while (running) { +// // XXX +// } + return 0; +} + +int KyraEngine::cmd_specialEventDisplayBrynnsNote(ScriptState *script) { + warning("STUB: cmd_specialEventDisplayBrynnsNote"); + return 0; +} + +int KyraEngine::cmd_specialEventRemoveBrynnsNote(ScriptState *script) { + warning("STUB: cmd_specialEventRemoveBrynnsNote"); + return 0; +} + +int KyraEngine::cmd_setLogicPage(ScriptState *script) { + warning("STUB: cmd_setLogicPage"); + return 0; +} + +int KyraEngine::cmd_fatPrint(ScriptState *script) { + warning("STUB: cmd_fatPrint"); + return 0; +} + +int KyraEngine::cmd_preserveAllObjectBackgrounds(ScriptState *script) { + warning("STUB: cmd_preserveAllObjectBackgrounds"); + return 0; +} + +int KyraEngine::cmd_updateSceneAnimations(ScriptState *script) { + warning("STUB: cmd_updateSceneAnimations"); + return 0; +} + +int KyraEngine::cmd_sceneAnimationActive(ScriptState *script) { + warning("STUB: cmd_sceneAnimationActive"); + return 0; +} + +int KyraEngine::cmd_setCharactersMovementDelay(ScriptState *script) { + warning("STUB: cmd_setCharactersMovementDelay"); + return 0; +} + +int KyraEngine::cmd_getCharactersFacing(ScriptState *script) { + warning("STUB: cmd_getCharactersFacing"); + return 0; +} + +int KyraEngine::cmd_bkgdScrollSceneAndMasksRight(ScriptState *script) { + warning("STUB: cmd_bkgdScrollSceneAndMasksRight"); + return 0; +} + +int KyraEngine::cmd_dispelMagicAnimation(ScriptState *script) { + warning("STUB: cmd_dispelMagicAnimation"); + return 0; +} + +int KyraEngine::cmd_findBrightestFireberry(ScriptState *script) { + warning("STUB: cmd_findBrightestFireberry"); + return 0; +} + +int KyraEngine::cmd_setFireberryGlowPalette(ScriptState *script) { + warning("STUB: cmd_setFireberryGlowPalette"); + return 0; +} + +int KyraEngine::cmd_setDeathHandlerFlag(ScriptState *script) { + warning("STUB: cmd_setDeathHandlerFlag"); + return 0; +} + +int KyraEngine::cmd_drinkPotionAnimation(ScriptState *script) { + warning("STUB: cmd_drinkPotionAnimation"); + return 0; +} + +int KyraEngine::cmd_makeAmuletAppear(ScriptState *script) { + warning("STUB: cmd_makeAmuletAppear"); + return 0; +} + +int KyraEngine::cmd_drawItemShapeIntoScene(ScriptState *script) { + warning("STUB: cmd_drawItemShapeIntoScene"); + return 0; +} + +int KyraEngine::cmd_setCharactersCurrentFrame(ScriptState *script) { + warning("STUB: cmd_setCharactersCurrentFrame"); + return 0; +} + +int KyraEngine::cmd_waitForConfirmationMouseClick(ScriptState *script) { + warning("STUB: cmd_waitForConfirmationMouseClick"); + return 0; +} + +int KyraEngine::cmd_pageFlip(ScriptState *script) { + warning("STUB: cmd_pageFlip"); + return 0; +} + +int KyraEngine::cmd_setSceneFile(ScriptState *script) { + warning("STUB: cmd_setSceneFile"); + return 0; +} + +int KyraEngine::cmd_getItemInMarbleVase(ScriptState *script) { + warning("STUB: cmd_getItemInMarbleVase"); + return 0; +} + +int KyraEngine::cmd_setItemInMarbleVase(ScriptState *script) { + debug(9, "cmd_setItemInMarbleVase(0x%X)", script); + _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) { + warning("STUB: cmd_createAmuletJewel"); + return 0; +} + +int KyraEngine::cmd_setSceneAnimCurrXY(ScriptState *script) { + warning("STUB: cmd_setSceneAnimCurrXY"); + return 0; +} + +int KyraEngine::cmd_Poison_Brandon_And_Remaps(ScriptState *script) { + warning("STUB: cmd_Poison_Brandon_And_Remaps"); + return 0; +} + +int KyraEngine::cmd_fillFlaskWithWater(ScriptState *script) { + warning("STUB: cmd_fillFlaskWithWater"); + return 0; +} + +int KyraEngine::cmd_getCharactersMovementDelay(ScriptState *script) { + warning("STUB: cmd_getCharactersMovementDelay"); + return 0; +} + +int KyraEngine::cmd_getBirthstoneGem(ScriptState *script) { + warning("STUB: cmd_getBirthstoneGem"); + return 0; +} + +int KyraEngine::cmd_queryBrandonStatusBit(ScriptState *script) { + warning("STUB: cmd_queryBrandonStatusBit"); + return 0; +} + +int KyraEngine::cmd_playFluteAnimation(ScriptState *script) { + warning("STUB: cmd_playFluteAnimation"); + return 0; +} + +int KyraEngine::cmd_playWinterScrollSequence(ScriptState *script) { + warning("STUB: cmd_playWinterScrollSequence"); + return 0; +} + +int KyraEngine::cmd_getIdolGem(ScriptState *script) { + warning("STUB: cmd_getIdolGem"); + return 0; +} + +int KyraEngine::cmd_setIdolGem(ScriptState *script) { + debug(9, "cmd_setIdolGem(0x%X)", script); + _idolGemsTable[stackPos(0)] = stackPos(1); + return 0; +} + +int KyraEngine::cmd_totalItemsInScene(ScriptState *script) { + warning("STUB: cmd_totalItemsInScene"); + return 0; +} + +int KyraEngine::cmd_restoreBrandonsMovementDelay(ScriptState *script) { + warning("STUB: cmd_restoreBrandonsMovementDelay"); + return 0; +} + +int KyraEngine::cmd_setMousePos(ScriptState *script) { + warning("STUB: cmd_setMousePos"); + return 0; +} + +int KyraEngine::cmd_getMouseState(ScriptState *script) { + warning("STUB: cmd_getMouseState"); + return 0; +} + +int KyraEngine::cmd_setEntranceMouseCursorTrack(ScriptState *script) { + debug(9, "cmd_setEntranceMouseCursorTrack(0x%X)", script); + _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(3); + return 0; +} + +int KyraEngine::cmd_itemAppearsOnGround(ScriptState *script) { + warning("STUB: cmd_itemAppearsOnGround"); + return 0; +} + +int KyraEngine::cmd_setNoDrawShapesFlag(ScriptState *script) { + warning("STUB: cmd_setNoDrawShapesFlag"); + return 0; +} + +int KyraEngine::cmd_fadeEntirePalette(ScriptState *script) { + warning("STUB: cmd_fadeEntirePalette"); + return 0; +} + +int KyraEngine::cmd_itemOnGroundHere(ScriptState *script) { + warning("STUB: cmd_itemOnGroundHere"); + return 0; +} + +int KyraEngine::cmd_queryCauldronState(ScriptState *script) { + warning("STUB: cmd_queryCauldronState"); + return 0; +} + +int KyraEngine::cmd_setCauldronState(ScriptState *script) { + warning("STUB: cmd_setCauldronState"); + return 0; +} + +int KyraEngine::cmd_queryCrystalState(ScriptState *script) { + warning("STUB: cmd_queryCrystalState"); + return 0; +} + +int KyraEngine::cmd_setCrystalState(ScriptState *script) { + warning("STUB: cmd_setCrystalState"); + return 0; +} + +int KyraEngine::cmd_setPaletteRange(ScriptState *script) { + warning("STUB: cmd_setPaletteRange"); + return 0; +} + +int KyraEngine::cmd_shrinkBrandonDown(ScriptState *script) { + warning("STUB: cmd_shrinkBrandonDown"); + return 0; +} + +int KyraEngine::cmd_growBrandonUp(ScriptState *script) { + warning("STUB: cmd_growBrandonUp"); + return 0; +} + +int KyraEngine::cmd_setBrandonScaleXAndY(ScriptState *script) { + warning("STUB: cmd_setBrandonScaleXAndY"); + return 0; +} + +int KyraEngine::cmd_resetScaleMode(ScriptState *script) { + warning("STUB: cmd_resetScaleMode"); + return 0; +} + +int KyraEngine::cmd_getScaleDepthTableValue(ScriptState *script) { + warning("STUB: cmd_getScaleDepthTableValue"); + return 0; +} + +int KyraEngine::cmd_setScaleDepthTableValue(ScriptState *script) { + warning("STUB: cmd_setScaleDepthTableValue"); + return 0; +} + +int KyraEngine::cmd_message(ScriptState *script) { + warning("STUB: cmd_message"); + return 0; +} + +int KyraEngine::cmd_checkClickOnNPC(ScriptState *script) { + warning("STUB: cmd_checkClickOnNPC"); + return 0; +} + +int KyraEngine::cmd_getFoyerItem(ScriptState *script) { + warning("STUB: cmd_getFoyerItem"); + return 0; +} + +int KyraEngine::cmd_setFoyerItem(ScriptState *script) { + warning("STUB: cmd_setFoyerItem"); + return 0; +} + +int KyraEngine::cmd_setNoItemDropRegion(ScriptState *script) { + debug(9, "cmd_setNoItemDropRegion(0x%X)", script); + addToNoDropRects(stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + return 0; +} + +int KyraEngine::cmd_walkMalcolmOn(ScriptState *script) { + warning("STUB: cmd_walkMalcolmOn"); + return 0; +} + +int KyraEngine::cmd_passiveProtection(ScriptState *script) { + warning("STUB: cmd_passiveProtection"); + return 0; +} + +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) { + warning("STUB: cmd_brandonHealingSequence"); + return 0; +} + +int KyraEngine::cmd_protectCommandLine(ScriptState *script) { + warning("STUB: cmd_protectCommandLine"); + return 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) { + warning("STUB: cmd_setPaletteChangeFlag"); + return 0; +} + +int KyraEngine::cmd_fillRect(ScriptState *script) { + warning("STUB: cmd_fillRect"); + return 0; +} + +int KyraEngine::cmd_dummy(ScriptState *script) { + debug(9, "cmd_dummy(0x%X)", script); + return 0; +} + } // end of namespace Kyra diff --git a/kyra/sprites.cpp b/kyra/sprites.cpp index a9ca8eda44..62fb5ff26f 100644 --- a/kyra/sprites.cpp +++ b/kyra/sprites.cpp @@ -37,16 +37,20 @@ Sprites::Sprites(KyraEngine *engine, OSystem *system) { _system = system; _dat = 0; memset(_anims, 0, sizeof(_anims)); + memset( _sceneShapes, 0, sizeof(_sceneShapes)); _animDelay = 16; + _spriteDefStart = 0; + } Sprites::~Sprites() { delete[] _dat; + freeSceneShapes(); } -Sprite Sprites::getSprite(uint8 spriteID) { - assert( spriteID < MAX_NUM_SPRITES); - return _sprites[spriteID]; +uint8 *Sprites::getSceneShape(uint8 sceneShapeID) { + assert( sceneShapeID < ARRAYSIZE(_sceneShapes)); + return _sceneShapes[sceneShapeID]; } void Sprites::drawSprites(uint8 srcPage, uint8 dstPage) { @@ -56,21 +60,23 @@ void Sprites::drawSprites(uint8 srcPage, uint8 dstPage) { if (_anims[i].script == 0 || !_anims[i].play) break; if (_anims[i].sprite >= 0) { - assert( _anims[i].sprite < MAX_NUM_SPRITES); - Sprite sprite = _sprites[_anims[i].sprite]; + assert( _anims[i].sprite < ARRAYSIZE(_sceneShapes)); + uint8 *sprite = _sceneShapes[_anims[i].sprite]; //debug(1, "Drawing from X %i, Y %i, to X %i, Y %i, width %i, height %i, srcPage %i, dstPage %i", // sprite.x, sprite.y, _anims[i].x, _anims[i].y, sprite.width, sprite.height, srcPage, dstPage); flags = Screen::CR_CLIPPED; if (_anims[i].flipX) flags |= Screen::CR_X_FLIPPED; - - _screen->copyRegion(sprite.x, sprite.y, _anims[i].x, _anims[i].y, sprite.width, sprite.height, srcPage, dstPage, flags); + + //_screen->copyRegion(sprite.x, sprite.y, _anims[i].x, _anims[i].y, sprite.width, sprite.height, srcPage, dstPage, flags); + _screen->drawShape(0, sprite, _anims[i].x, _anims[i].y, 0, 0, 0); } } } void Sprites::doAnims() { + debug(9, "Sprites::doAnims()"); uint32 currTime = _system->getMillis(); 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) @@ -363,6 +369,10 @@ void Sprites::loadDAT(const char *filename) { assert(fileSize > 0x6D); + _engine->_northExitHeight = READ_BE_UINT16(_dat + 0x15); + if (_engine->_northExitHeight & 1) + _engine->_northExitHeight += 1; + // XXX memcpy(_screen->_currentPalette + 745 - 0x3D, _dat + 0x17, 0x3D); _screen->setScreenPalette(_screen->_currentPalette); uint8 *data = _dat + 0x6B; @@ -398,27 +408,10 @@ void Sprites::loadDAT(const char *filename) { break; case 0xFF84: data += 2; + _spriteDefStart = data; while (READ_LE_UINT16(data) != 0xFF85) { - uint16 spriteNum = READ_LE_UINT16(data); - //debug(1, "Spritenum: %i", spriteNum); - assert(spriteNum < MAX_NUM_SPRITES); - data += 2; - _sprites[spriteNum].x = READ_LE_UINT16(data) * 8; - data += 2; - _sprites[spriteNum].y = READ_LE_UINT16(data); - data += 2; - _sprites[spriteNum].width = READ_LE_UINT16(data) * 8; data += 2; - _sprites[spriteNum].height = READ_LE_UINT16(data); - data += 2; - spritesLoaded++; - //debug(1, "Got sprite index: %i", spriteNum); - //debug(1, "X: %i", _sprites[spriteNum].x); - //debug(1, "Y: %i", _sprites[spriteNum].y); - //debug(1, "Width: %i", _sprites[spriteNum].width); - //debug(1, "Height: %i", _sprites[spriteNum].height); } - //debug(1, "End of sprite images."); data += 2; break; case 0xFF86: @@ -453,6 +446,48 @@ void Sprites::loadDAT(const char *filename) { assert(fileSize - (data - _dat) == 0xC); //TODO: Read in character entry coords here + SceneExits &exits = _engine->sceneExits(); + + 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() { + for (int i = 0; i < ARRAYSIZE(_sceneShapes); i++ ) + free(_sceneShapes[i]); + +} + +void Sprites::loadSceneShapes() { + debug(9, "Sprites::loadSceneShapes()"); + uint8 *data = _spriteDefStart; + int spriteNum, x, y, width, height; + + assert(_spriteDefStart); + + freeSceneShapes(); + memset( _sceneShapes, 0, sizeof(_sceneShapes)); + + 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, 0); + } } } // end of namespace Kyra diff --git a/kyra/sprites.h b/kyra/sprites.h index 348f7da247..c64bf045cc 100644 --- a/kyra/sprites.h +++ b/kyra/sprites.h @@ -24,7 +24,6 @@ namespace Kyra { -#define MAX_NUM_SPRITES 50 #define MAX_NUM_ANIMS 11 struct Sprite { @@ -55,23 +54,27 @@ public: ~Sprites(); void doAnims(); - void loadDAT(const char* filename); - Sprite getSprite(uint8 spriteID); + void loadDAT(const char *filename); + uint8 *getSceneShape(uint8 sceneShapeID); void drawSprites(uint8 srcPage, uint8 dstPage); + void loadSceneShapes(); void enableAnim(uint8 anim) { _anims[anim].play = true; } void disableAnim(uint8 anim) { _anims[anim].play = false; } protected: + void freeSceneShapes(); + KyraEngine *_engine; Resource *_res; OSystem *_system; Screen *_screen; - Sprite _sprites[MAX_NUM_SPRITES]; + uint8 *_sceneShapes[50]; uint8 *_dat; Anim _anims[MAX_NUM_ANIMS]; Common::RandomSource _rnd; uint8 _animDelay; + uint8 *_spriteDefStart; }; } // End of namespace Kyra diff --git a/kyra/staticres.cpp b/kyra/staticres.cpp index 2918dd477f..cff46c2276 100644 --- a/kyra/staticres.cpp +++ b/kyra/staticres.cpp @@ -26,7 +26,7 @@ namespace Kyra { -#define RESFILE_VERSION 1 +#define RESFILE_VERSION 2 #define GAME_FLAGS (GF_FLOPPY | GF_TALKIE | GF_DEMO | GF_AUDIOCD) #define LANGUAGE_FLAGS (GF_ENGLISH | GF_FRENCH | GF_GERMAN | GF_SPANISH | GF_LNGUNK) @@ -129,31 +129,41 @@ void KyraEngine::res_loadResources(int type) { loadNativeLanguage = false; } -#define loadRawFile(x, y, z) \ +#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) \ - if (_features & GF_TALKIE) { \ - temp = getFile(x, y ".CD"); \ - } else if (_features & GF_DEMO) { \ - temp = getFile(x, y ".DEM"); \ - } else { \ - temp = getFile(x, y); \ - } \ + 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_ALL) { loadRawFile(resFile, "FOREST.SEQ", _seq_Forest); @@ -174,8 +184,19 @@ void KyraEngine::res_loadResources(int type) { res_loadLangTable("INTRO-STRINGS.", &resFile, (byte***)&_seq_textsTable, &_seq_textsTable_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); + } + +#undef loadRooms #undef loadTable #undef loadRawFile +#undef getFileEx } void KyraEngine::res_unloadResources(int type) { @@ -220,6 +241,27 @@ void KyraEngine::res_unloadResources(int type) { delete [] _seq_Demo3; _seq_Demo3 = 0; delete [] _seq_Demo4; _seq_Demo4 = 0; } + + if ((type & RES_INGAME) || type == RES_ALL) { + for (int i = 0; i < _roomFilenameTableSize; ++i) { + delete [] _roomFilenameTable[i]; + } + delete [] _roomFilenameTable; + _roomFilenameTableSize = 0; + _roomFilenameTable = 0; + + delete [] _roomTable; _roomTable = 0; + _roomTableSize = 0; + + for (int i = 0; i < _characterImageTableSize; ++i) { + delete [] _characterImageTable[i]; + } + delete [] _characterImageTable; + _characterImageTableSize = 0; + + delete [] _defaultShapeTable; + _defaultShapeTableSize = 0; + } } void KyraEngine::res_loadLangTable(const char *filename, PAKFile *res, byte ***loadTo, int *size, bool nativ) { @@ -263,6 +305,41 @@ void KyraEngine::res_loadTable(const byte *src, byte ***loadTo, int *size) { } } +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_LE_UINT16(src); src += 2; + (*loadTo)[i].eastExit = READ_LE_UINT16(src); src += 2; + (*loadTo)[i].southExit = READ_LE_UINT16(src); src += 2; + (*loadTo)[i].westExit = READ_LE_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].unkField3, 0, sizeof((*loadTo)[i].unkField3)); + } +} + +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++; + } +} + const ScreenDim Screen::_screenDimTable[] = { { 0x00, 0x00, 0x28, 0xC8, 0x0F, 0x0C, 0x00, 0x00 }, { 0x08, 0x48, 0x18, 0x38, 0x0F, 0x0C, 0x00, 0x00 }, @@ -279,12 +356,207 @@ const ScreenDim Screen::_screenDimTable[] = { const int Screen::_screenDimTableCount = ARRAYSIZE(_screenDimTable); -const Screen::DrawShapePlotPixelCallback Screen::_drawShapePlotPixelTable[] = { - &Screen::drawShapePlotPixelCallback1 - // XXX +// 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_Poison_Brandon_And_Remaps), + 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_dummy) }; +#undef Opcode -const int Screen::_drawShapePlotPixelCount = ARRAYSIZE(_drawShapePlotPixelTable); +const int KyraEngine::_opcodeTableSize = ARRAYSIZE(_opcodeTable); const char *KyraEngine::_xmidiFiles[] = { "INTRO.XMI", @@ -301,4 +573,12 @@ const char *KyraEngine::_xmidiFiles[] = { const int KyraEngine::_xmidiFilesCount = ARRAYSIZE(_xmidiFiles); +const int8 KyraEngine::_charXPosTable[] = { + 0x00, 0x04, 0x04, 0x04, 0x00, 0xFC, 0xFC, 0xFC +}; + +const int8 KyraEngine::_charYPosTable[] = { + 0xFE, 0xFE, 0x00, 0x03, 0x02, 0x02, 0x00, 0xFE +}; + } // End of namespace Kyra |