Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

kyra_v2.cpp

/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-11-1/engines/kyra/kyra_v2.cpp $
 * $Id: kyra_v2.cpp 30944 2008-02-23 22:50:18Z sev $
 *
 */

#include "kyra/kyra.h"
#include "kyra/kyra_v2.h"
#include "kyra/screen.h"
#include "kyra/resource.h"
#include "kyra/wsamovie.h"
#include "kyra/sound.h"
#include "kyra/script.h"
#include "kyra/text_v2.h"
#include "kyra/timer.h"
#include "kyra/debugger.h"

#include "common/system.h"

namespace Kyra {

KyraEngine_v2::KyraEngine_v2(OSystem *system, const GameFlags &flags) : KyraEngine(system, flags), _updateFunctor(this, &KyraEngine_v2::update) {
      memset(_defaultShapeTable, 0, sizeof(_defaultShapeTable));
      _mouseSHPBuf = 0;
      _debugger = 0;
      _screen = 0;
      _text = 0;

      _seqProcessedString = 0;
      _activeWSA = 0;
      _activeText = 0;
      _seqWsa = 0;

      _gamePlayBuffer = 0;
      _cCodeBuffer = _optionsBuffer = _chapterBuffer = 0;

      _overwriteSceneFacing = false;
      _mainCharX = _mainCharY = -1;
      _drawNoShapeFlag = false;
      _charPalEntry = 0;
      _itemInHand = -1;
      _unkSceneScreenFlag1 = false;
      _noScriptEnter = true;
      _currentChapter = 0;
      _newChapterFile = 1;
      _oldTalkFile = -1;
      _currentTalkFile = 0;
      _handItemSet = -1;
      _lastProcessedSceneScript = 0;
      _specialSceneScriptRunFlag = false;
      memset(_animObjects, 0, sizeof(_animObjects));
      _unkHandleSceneChangeFlag = false;
      _pathfinderFlag = 0;
      _mouseX = _mouseY = 0;
      _newShapeCount = 0;
      _newShapeFiledata = 0;

      _vocHigh = -1;
      _chatVocHigh = -1;
      _chatVocLow = -1;
      _chatText = 0;
      _chatObject = -1;
      _lastIdleScript = -1;

      _currentTalkSections.STATim = NULL;
      _currentTalkSections.TLKTim = NULL;
      _currentTalkSections.ENDTim = NULL;

      _invWsa.wsa = 0;

      memset(&_sceneScriptData, 0, sizeof(_sceneScriptData));
}

KyraEngine_v2::~KyraEngine_v2() {
      seq_uninit();

      delete [] _mouseSHPBuf;
      delete _screen;
      delete _text;
      _text = 0;
      delete _debugger;
      delete _invWsa.wsa;
}

Movie *KyraEngine_v2::createWSAMovie() {
      return new WSAMovieV2(this);
}

int KyraEngine_v2::init() {
      _screen = new Screen_v2(this, _system);
      assert(_screen);
      if (!_screen->init())
            error("_screen->init() failed");

      KyraEngine::init();

      _debugger = new Debugger_v2(this);
      assert(_debugger);
      _text = new TextDisplayer_v2(this, _screen);
      assert(_text);

      setupTimers();

      _screen->loadFont(_screen->FID_6_FNT, "6.FNT");
      _screen->loadFont(_screen->FID_8_FNT, "8FAT.FNT");
      _screen->loadFont(_screen->FID_GOLDFONT_FNT, "GOLDFONT.FNT");
      _screen->loadFont(_screen->FID_BOOKFONT_FNT, "BOOKFONT.FNT");
      _screen->setAnimBlockPtr(3504);
      _screen->setScreenDim(0);

      if (!_sound->init())
            error("Couldn't init sound");

      _abortIntroFlag = false;

      // temporary solution until staticres manager support (kyra.dat) is added for kyra 2
      if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) {
            _sequenceSoundList = (const char * const *) _sequenceSoundList_TOWNS;
            _sequenceSoundListSize = _sequenceSoundListSize_TOWNS;
            _sequenceStrings = (const char * const *) _sequenceStrings_TOWNS_EN;
            _sequenceStringsSize = _sequenceStringsSize_TOWNS_EN;
            _sequences = (const Sequence*) _sequences_TOWNS;
      } else {
            _sequenceSoundList = (const char * const *) _sequenceSoundList_PC;
            _sequenceSoundListSize = _sequenceSoundListSize_PC;
            _sequenceStrings = (const char * const *) _sequenceStrings_PC_EN;
            _sequenceStringsSize = _sequenceStringsSize_PC_EN;
            _sequences = (const Sequence*) _sequences_PC;
      }

      for (int i = 0; i < 33; i++)
            _sequenceStringsDuration[i] = (int) strlen(_sequenceStrings[i]) * 8;

      // No mouse display in demo
      if (_flags.isDemo)
            return 0;

      _mouseSHPBuf = _res->fileData("PWGMOUSE.SHP", 0);
      assert(_mouseSHPBuf);

      for (int i = 0; i < 2; i++) {
            _defaultShapeTable[i] = _screen->getPtrToShape(_mouseSHPBuf, i);
            assert(_defaultShapeTable[i]);
      }

      _screen->setMouseCursor(0, 0, _defaultShapeTable[0]);
      return 0;
}

int KyraEngine_v2::go() {
      if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)
            seq_showStarcraftLogo();

      seq_playSequences(kSequenceVirgin, kSequenceZanfaun);
      //seq_playSequences(kSequenceFunters, kSequenceFrash);

      if (_menuChoice == 1) {
            // load just the pak files needed for ingame
            _res->unloadAllPakFiles();
            if (_flags.platform == Common::kPlatformPC && (_flags.isTalkie || _flags.isDemo)) {
                  _res->loadFileList("FILEDATA.FDT");
            } else if (_flags.platform == Common::kPlatformPC) {
                  //TODO:
                  //What files are needed for floppy version?
            } else if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) {
                  char tmpfilename[13];
                  static const char * pakfiles [] = { "KYRA.DAT", "AUDIO.PAK", "CAULDRON.PAK",
                        "MISC_CPS.PAK", "MISC_EMC.PAK", "OTHER.PAK", "VOC.PAK", "WSCORE.PAK" };
                  for (int i = 0; i < 8; i++)
                        _res->loadPakFile(pakfiles[i]);
                  for (int i = 1; i < 10; i++) {
                        sprintf(tmpfilename, "COST%d_SH.PAK", i);
                        _res->loadPakFile(tmpfilename);
                  }
                  for (int i = 1; i < 6; i++) {
                        sprintf(tmpfilename, "HOFCH_%d.PAK", i);
                        _res->loadPakFile(tmpfilename);
                  }
            }

            startup();
            runLoop();
            cleanup();
      } else if (_menuChoice == 3) {
            // Load Game
      }

      return 0;
}

void KyraEngine_v2::startup() {
      snd_assignMusicData(kMusicIngame);
      // The track map is exactly the same
      // for FM-TOWNS and DOS
      _trackMap = _dosTrackMap;
      _trackMapSize = _dosTrackMapSize;

      _screen->_curPage = 0;
      delete [] _mouseSHPBuf;
      _mouseSHPBuf = 0;

      memset(_defaultShapeTable, 0, sizeof(_defaultShapeTable));
      memset(_sceneShapeTable, 0, sizeof(_sceneShapeTable));
      _gamePlayBuffer = new uint8[46080];
      _unkBuf500Bytes = new uint8[500];

      loadMouseShapes();
      loadItemShapes();

      _screen->setMouseCursor(0, 0, getShapePtr(0));

      _screenBuffer = new uint8[64000];

      loadCCodeBuffer("C_CODE.XXX");
      loadOptionsBuffer("OPTIONS.XXX");
      loadChapterBuffer(_newChapterFile);

      _unkBuf200kByte = new uint8[200000];

      showMessageFromCCode(265, 150, 0);

      openTalkFile(0);
      _currentTalkFile = 1;
      openTalkFile(1);

      showMessage(0, 207);

      _screen->setShapePages(5, 3);

      memset(&_mainCharacter, 0, sizeof(_mainCharacter));
      _mainCharacter.height = 0x30;
      _mainCharacter.facing = 4;
      _mainCharacter.animFrame = 0x12;
      memset(_mainCharacter.inventory, -1, sizeof(_mainCharacter.inventory));

      memset(_sceneAnims, 0, sizeof(_sceneAnims));
      for (int i = 0; i < ARRAYSIZE(_sceneAnimMovie); ++i)
            _sceneAnimMovie[i] = new WSAMovieV2(this);
      memset(_wsaSlots, 0, sizeof(_wsaSlots));
      for (int i = 0; i < ARRAYSIZE(_wsaSlots); ++i)
            _wsaSlots[i] = new WSAMovieV2(this);

      _maskPage = 0;//_screen->getPagePtr(5);
      _screen->_curPage = 0;

      _talkObjectList = new TalkObject[72];
      memset(_talkObjectList, 0, sizeof(TalkObject)*72);
      _shapeDescTable = new ShapeDesc[55];
      memset(_shapeDescTable, 0, sizeof(ShapeDesc)*55);

      for (int i = 9; i <= 32; ++i) {
            _shapeDescTable[i-9].width = 30;
            _shapeDescTable[i-9].height = 55;
            _shapeDescTable[i-9].xAdd = -15;
            _shapeDescTable[i-9].yAdd = -50;
      }

      for (int i = 19; i <= 24; ++i) {
            _shapeDescTable[i-9].width = 53;
            _shapeDescTable[i-9].yAdd = -51;
      }

      _gfxBackUpRect = new uint8[_screen->getRectSize(32, 32)];
      _itemList = new Item[30];
      memset(_itemList, 0, sizeof(Item)*30);
      resetItemList();
      //loadButtonShapes();
      _loadedZTable = 1;
      loadZShapes(_loadedZTable);
      loadInventoryShapes();

      _res->loadFileToBuf("PALETTE.COL", _screen->_currentPalette, 0x300);
      _screen->loadBitmap("_PLAYFLD.CPS", 3, 3, 0);
      _screen->copyPage(3, 0);
      _screen->showMouse();
      _screen->hideMouse();

      clearAnimObjects();

      // XXX
      memset(_hiddenItems, -1, sizeof(_hiddenItems));
      // XXX

      _sceneList = new SceneDesc[86];
      memset(_sceneList, 0, sizeof(SceneDesc)*86);
      _sceneListSize = 86;
      runStartScript(1, 0);
      loadNPCScript();

      // XXX
      snd_playWanderScoreViaMap(52, 1);
      // XXX

      enterNewScene(_mainCharacter.sceneId, _mainCharacter.facing, 0, 0, 1);
      _screen->showMouse();

      //sub_20EE8(1);
      setNextIdleAnimTimer();
      //XXX
      _timer->setDelay(0, 5);
}

void KyraEngine_v2::runLoop() {
      _screen->updateScreen();

      _quitFlag = false;
      while (!_quitFlag) {
            //if (_unk1 >= 0) {
            //    removeHandItem();
            //    waitTicks(5);
            //    sub_270A0();
            //}

            if (_system->getMillis() > _nextIdleAnim)
                  showIdleAnim();

            //if (queryGameFlag(0x159) {
            //    sub_C86A();
            //    resetGameFlag(0x159);
            //}

            if (queryGameFlag(0x124) && !queryGameFlag(0x125)) {
                  _mainCharacter.animFrame = 32;
                  enterNewScene(39, -1, 0, 0, 0);
            }

            //if (queryGameFlag(0xD8)) {
            //    resetGameFlag(0xD8);
            //    if (_mainCharacter.sceneId == 34) {
            //          if (queryGameFlag(0xD1) {
            //                initObject(28);
            //                sub_2741F(getTableString(0xFA, _cCodeBuffer, 1), 28, 0x83, 0xFA);
            //                deinitObject(28);
            //                enterNewScene(35, 4, 0, 0, 0);
            //          } else if (queryGameFlag(0xD0) {
            //                initObject(29);
            //                sub_2741F(getTableString(0xFB, _ccodeBuffer, 1), 29, 0x83, 0xFB);
            //                deinitObject(29);
            //                enterNewScene(33, 6, 0, 0, 0);
            //          }
            //    }
            //}

            int inputFlag = checkInput(0/*dword_324C5*/);
            removeInputTop();

            update();

            if (inputFlag == 198 || inputFlag == 199) {
                  _unk3 = _handItemSet;
                  handleInput(_mouseX, _mouseY);
            }

            //if (queryGameFlag(0x1EE) && inputFlag)
            //    sub_13B19(inputFlag);
      }
}

void KyraEngine_v2::handleInput(int x, int y) {
      setNextIdleAnimTimer();
      if (_unk5) {
            _unk5 = 0;
            return;
      }

      if (!_screen->isMouseVisible())
            return;

      if (_unk3 == -2) {
            snd_playSoundEffect(13);
            return;
      }

      setNextIdleAnimTimer();

      if (x <= 6 || x >= 312 || y <= 6 || y >= 135) {
            bool exitOk = false;
            assert(_unk3 + 6 >= 0);
            switch (_unk3 + 6) {
            case 0:
                  if (_sceneExit1 != 0xFFFF)
                        exitOk = true;
                  break;

            case 1:
                  if (_sceneExit2 != 0xFFFF)
                        exitOk = true;
                  break;

            case 2:
                  if (_sceneExit3 != 0xFFFF)
                        exitOk = true;
                  break;

            case 3:
                  if (_sceneExit4 != 0xFFFF)
                        exitOk = true;
                  break;

            default:
                  break;
            }

            if (exitOk) {
                  inputSceneChange(x, y, 1, 1);
                  return;
            }
      }

      if (checkCharCollision(x, y) >= 0 && _unk3 >= -1) {
            runSceneScript2();
            return;
      } else if (pickUpItem(x, y)) {
            return;
      } else {
            int skipHandling = 0;

            if (checkItemCollision(x, y) == -1) {
                  resetGameFlag(0x1EF);
                  skipHandling = handleInputUnkSub(x, y) ? 1 : 0;

                  if (queryGameFlag(0x1EF)) {
                        resetGameFlag(0x1EF);
                        return;
                  }

                  if (_unk5) {
                        _unk5 = 0;
                        return;
                  }
            }

            //if (_unk1 <= -1)
            //    skipHandling = 1;

            if (skipHandling)
                  return;

            if (checkCharCollision(x, y) >= 0) {
                  runSceneScript2();
                  return;
            }

            if (_itemInHand >= 0) {
                  if (y > 136)
                        return;

                  dropItem(0, _itemInHand, x, y, 1);
            } else {
                  if (_unk3 == -2 || y > 135)
                        return;

                  if (!_unk5) {
                        inputSceneChange(x, y, 1, 1);
                        return;
                  }

                  _unk5 = 0;
            }
      }
}

bool KyraEngine_v2::handleInputUnkSub(int x, int y) {
      if (y > 143/* || _unk1 > -1 */|| queryGameFlag(0x164))
            return false;

      if (_handItemSet <= -3 && findItem(_mainCharacter.sceneId, 13) >= 0) {
            updateCharFacing();
            //objectChat(getTableString(0xFC, _cCodeBuffer, 1), 0, 0x83, 0xFC);
            return true;
      } else {
            _scriptInterpreter->initScript(&_sceneScriptState, &_sceneScriptData);

            _sceneScriptState.regs[1] = x;
            _sceneScriptState.regs[2] = y;
            _sceneScriptState.regs[3] = 0;
            _sceneScriptState.regs[4] = _itemInHand;

            _scriptInterpreter->startScript(&_sceneScriptState, 1);

            while (_scriptInterpreter->validScript(&_sceneScriptState))
                  _scriptInterpreter->runScript(&_sceneScriptState);

            //XXXsys_unkKeyboad (flush? wait? whatever...)

            if (queryGameFlag(0x1ED)) {
                  _sound->beginFadeOut();
                  _screen->fadeToBlack();
                  _quitFlag = true;
            }

            return _sceneScriptState.regs[3] != 0;
      }
}

void KyraEngine_v2::update() {
      updateInput();

      refreshAnimObjectsIfNeed();
      updateMouse();
      updateSpecialSceneScripts();
      _timer->update();
      //sub_274C0();
      updateInvWsa();
      //sub_1574C();
      _screen->updateScreen();
}

void KyraEngine_v2::updateWithText() {
      updateInput();

      updateMouse();
      //sub_157C();
      updateSpecialSceneScripts();
      _timer->update();
      //sub_274C0();
      updateInvWsa();
      restorePage3();
      drawAnimObjects();

      if (1/*textEnabled()*/ && _chatText) {
            int pageBackUp = _screen->_curPage;
            _screen->_curPage = 2;
            objectChatPrintText(_chatText, _chatObject);
            _screen->_curPage = pageBackUp;
      }

      refreshAnimObjects(0);
      _screen->updateScreen();
}

void KyraEngine_v2::updateMouse() {
      int shapeIndex = 0;
      int type = 0;
      int xOffset = 0, yOffset = 0;
      Common::Point mouse = getMousePos();

      if (mouse.y <= 145) {
            if (mouse.x <= 6) {
                  if (_sceneExit4 != 0xFFFF) {
                        type = -3;
                        shapeIndex = 4;
                        xOffset = 1;
                        yOffset = 5;
                  } else {
                        type = -2;
                  }
            } else if (mouse.x >= 312) {
                  if (_sceneExit2 != 0xFFFF) {
                        type = -5;
                        shapeIndex = 2;
                        xOffset = 7;
                        yOffset = 5;
                  } else {
                        type = -2;
                  }
            } else if (mouse.y >= 135) {
                  if (_sceneExit3 != 0xFFFF) {
                        type = -4;
                        shapeIndex = 3;
                        xOffset = 5;
                        yOffset = 10;
                  } else {
                        type = -2;
                  }
            } else if (mouse.y <= 6) {
                  if (_sceneExit1 != 0xFFFF) {
                        type = -6;
                        shapeIndex = 1;
                        xOffset = 5;
                        yOffset = 1;
                  } else {
                        type = -2;
                  }
            }
      }

      for (int i = 0; i < _specialExitCount; ++i) {
            if (checkSpecialSceneExit(i, mouse.x, mouse.y)) {
                  switch (_specialExitTable[20+i]) {
                  case 0:
                        type = -6;
                        shapeIndex = 1;
                        xOffset = 5;
                        yOffset = 1;
                        break;

                  case 2:
                        type = -5;
                        shapeIndex = 2;
                        xOffset = 7;
                        yOffset = 5;
                        break;

                  case 4:
                        type = -4;
                        shapeIndex = 3;
                        xOffset = 5;
                        yOffset = 7;
                        break;

                  case 6:
                        type = -3;
                        shapeIndex = 4;
                        xOffset = 1;
                        yOffset = 5;
                        break;

                  default:
                        break;
                  }
            }
      }

      if (type == -2) {
            shapeIndex = 5;
            xOffset = 5;
            yOffset = 9;
      }

      if (type != 0 && _handItemSet != type) {
            _handItemSet = type;
            _screen->hideMouse();
            _screen->setMouseCursor(xOffset, yOffset, getShapePtr(shapeIndex));
            _screen->showMouse();
      }

      if (type == 0 && _handItemSet != _itemInHand) {
            if ((mouse.y > 145) || (mouse.x > 6 && mouse.x < 312 && mouse.y > 6 && mouse.y < 135)) {
                  _handItemSet = _itemInHand;
                  _screen->hideMouse();
                  if (_itemInHand == -1)
                        _screen->setMouseCursor(0, 0, getShapePtr(0));
                  else
                        _screen->setMouseCursor(8, 15, getShapePtr(_itemInHand+64));
                  _screen->showMouse();
            }
      }
}

void KyraEngine_v2::updateInput() {
      Common::Event event;

      while (_eventMan->pollEvent(event))
            _eventList.push_back(event);
}

int KyraEngine_v2::checkInput(void *p) {
      updateInput();

      int keys = 0;

      while (_eventList.size()) {
            Common::Event event = *_eventList.begin();
            bool breakLoop = false;

            switch (event.type) {
            case Common::EVENT_KEYDOWN:
                  if (event.kbd.keycode == Common::KEYCODE_RETURN) {
                        // this doesn't make sure the mouse position is the same
                        // as when RETURN was pressed, but it *should* work for now
                        Common::Point pos = getMousePos();
                        _mouseX = pos.x;
                        _mouseY = pos.y;
                        keys = 199;
                  }

                  if (event.kbd.flags == Common::KBD_CTRL) {
                        if (event.kbd.keycode == 'd')
                              _debugger->attach();
                  }
                  breakLoop = true;
                  break;

            case Common::EVENT_LBUTTONUP:
                  _mouseX = event.mouse.x;
                  _mouseY = event.mouse.y;
                  keys = 198;
                  breakLoop = true;
                  break;

            case Common::EVENT_QUIT:
                  _quitFlag = true;
                  break;

            default:
                  break;
            }

            if (_debugger->isAttached())
                  _debugger->onFrame();

            if (breakLoop)
                  break;

            _eventList.erase(_eventList.begin());
      }

      _system->delayMillis(10);
      return keys;
}

void KyraEngine_v2::removeInputTop() {
      if (!_eventList.empty())
            _eventList.erase(_eventList.begin());
}

void KyraEngine_v2::delay(uint32 amount, bool updateGame, bool isMainLoop) {

      uint32 start = _system->getMillis();
      do {
            if (updateGame) {
                  if (_chatText)
                        updateWithText();
                  else
                        update();
            }


            if (amount > 0 )
                  _system->delayMillis(amount > 10 ? 10 : amount);

      } while (!_skipFlag && _system->getMillis() < start + amount && !_quitFlag);
}

void KyraEngine_v2::cleanup() {
      delete [] _gamePlayBuffer;
      delete [] _unkBuf500Bytes;
      delete [] _screenBuffer;
      delete [] _unkBuf200kByte;

      resetNewShapes(_newShapeCount, _newShapeFiledata);

      for (int i = 0; i < ARRAYSIZE(_defaultShapeTable); ++i)
            delete [] _defaultShapeTable[i];
      freeSceneShapePtrs();

      delete [] _cCodeBuffer;
      delete [] _optionsBuffer;
      delete [] _chapterBuffer;

      delete [] _talkObjectList;
      delete [] _shapeDescTable;

      delete [] _gfxBackUpRect;

      delete [] _sceneList;

      for (int i = 0; i < ARRAYSIZE(_sceneAnimMovie); ++i)
            delete _sceneAnimMovie[i];
      for (int i = 0; i < ARRAYSIZE(_wsaSlots); ++i)
            delete _wsaSlots[i];
}

#pragma mark - Localization

void KyraEngine_v2::loadCCodeBuffer(const char *file) {
      char tempString[13];
      strcpy(tempString, file);
      changeFileExtension(tempString);

      delete [] _cCodeBuffer;
      _cCodeBuffer = _res->fileData(tempString, 0);
}

void KyraEngine_v2::loadOptionsBuffer(const char *file) {
      char tempString[13];
      strcpy(tempString, file);
      changeFileExtension(tempString);

      delete [] _optionsBuffer;
      _optionsBuffer = _res->fileData(tempString, 0);
}

void KyraEngine_v2::loadChapterBuffer(int chapter) {
      char tempString[14];

      static const char *chapterFilenames[] = {
            "CH1.XXX", "CH2.XXX", "CH3.XXX", "CH4.XXX", "CH5.XXX"
      };

      assert(chapter >= 1 && chapter <= ARRAYSIZE(chapterFilenames));
      strcpy(tempString, chapterFilenames[chapter-1]);
      changeFileExtension(tempString);

      delete [] _chapterBuffer;
      _chapterBuffer = _res->fileData(tempString, 0);
      _currentChapter = chapter;
}

void KyraEngine_v2::changeFileExtension(char *buffer) {
      while (*buffer != '.')
            ++buffer;

      ++buffer;
      strcpy(buffer, _languageExtension[_lang]);
}

const uint8 *KyraEngine_v2::getTableEntry(const uint8 *buffer, int id) {
      return buffer + READ_LE_UINT16(buffer + (id<<1));
}

const char *KyraEngine_v2::getTableString(int id, const uint8 *buffer, int decode) {
      const char *string = (const char*)getTableEntry(buffer, id);

      if (decode) {
            decodeString1(string, _internStringBuf);
            decodeString2(_internStringBuf, _internStringBuf);
            string = _internStringBuf;
      }

      return string;
}

const char *KyraEngine_v2::getChapterString(int id) {
      if (_currentChapter != _newChapterFile)
            loadChapterBuffer(_newChapterFile);

      return getTableString(id, _chapterBuffer, 1);
}

int KyraEngine_v2::decodeString1(const char *src, char *dst) {
      static const uint8 decodeTable1[] = {
            0x20, 0x65, 0x74, 0x61, 0x69, 0x6E, 0x6F, 0x73, 0x72, 0x6C, 0x68,
            0x63, 0x64, 0x75, 0x70, 0x6D
      };

      static const uint8 decodeTable2[] = {
            0x74, 0x61, 0x73, 0x69, 0x6F, 0x20, 0x77, 0x62, 0x20, 0x72, 0x6E,
            0x73, 0x64, 0x61, 0x6C, 0x6D, 0x68, 0x20, 0x69, 0x65, 0x6F, 0x72,
            0x61, 0x73, 0x6E, 0x72, 0x74, 0x6C, 0x63, 0x20, 0x73, 0x79, 0x6E,
            0x73, 0x74, 0x63, 0x6C, 0x6F, 0x65, 0x72, 0x20, 0x64, 0x74, 0x67,
            0x65, 0x73, 0x69, 0x6F, 0x6E, 0x72, 0x20, 0x75, 0x66, 0x6D, 0x73,
            0x77, 0x20, 0x74, 0x65, 0x70, 0x2E, 0x69, 0x63, 0x61, 0x65, 0x20,
            0x6F, 0x69, 0x61, 0x64, 0x75, 0x72, 0x20, 0x6C, 0x61, 0x65, 0x69,
            0x79, 0x6F, 0x64, 0x65, 0x69, 0x61, 0x20, 0x6F, 0x74, 0x72, 0x75,
            0x65, 0x74, 0x6F, 0x61, 0x6B, 0x68, 0x6C, 0x72, 0x20, 0x65, 0x69,
            0x75, 0x2C, 0x2E, 0x6F, 0x61, 0x6E, 0x73, 0x72, 0x63, 0x74, 0x6C,
            0x61, 0x69, 0x6C, 0x65, 0x6F, 0x69, 0x72, 0x61, 0x74, 0x70, 0x65,
            0x61, 0x6F, 0x69, 0x70, 0x20, 0x62, 0x6D
      };

      int size = 0;
      uint cChar = 0;
      while ((cChar = *src++) != 0) {
            if (cChar & 0x80) {
                  cChar &= 0x7F;
                  int index = (cChar & 0x78) >> 3;
                  *dst++ = decodeTable1[index];
                  ++size;
                  assert(cChar < sizeof(decodeTable2));
                  cChar = decodeTable2[cChar];
            }

            *dst++ = cChar;
            ++size;
      }

      *dst++ = 0;
      return size;
}

void KyraEngine_v2::decodeString2(const char *src, char *dst) {
      if (!src || !dst)
            return;

      char out = 0;
      while ((out = *src) != 0) {
            if (*src == 0x1B) {
                  ++src;
                  out = *src + 0x7F;
            }
            *dst++ = out;
            ++src;
      }

      *dst = 0;
}

#pragma mark -

void KyraEngine_v2::showMessageFromCCode(int id, int16 palIndex, int) {
      const char *string = getTableString(id, _cCodeBuffer, 1);
      showMessage(string, palIndex);
}

void KyraEngine_v2::showMessage(const char *string, int16 palIndex) {
      _shownMessage = string;
      _screen->hideMouse();
      _screen->fillRect(0, 190, 319, 199, 0xCF);

      if (string) {
            if (palIndex != -1 || _msgUnk1) {
                  palIndex *= 3;
                  memcpy(_messagePal, _screen->_currentPalette + palIndex, 3);
                  memmove(_screen->_currentPalette + 765, _screen->_currentPalette + palIndex, 3);
                  _screen->setScreenPalette(_screen->_currentPalette);
            }

            int x = _text->getCenterStringX(string, 0, 320);
            _text->printText(string, x, 190, 255, 207, 0);

            setTimer1DelaySecs(7);
      }

      _msgUnk1 = 0;
      _screen->showMouse();
}

void KyraEngine_v2::showChapterMessage(int id, int16 palIndex) {
      showMessage(getChapterString(id), palIndex);
}

void KyraEngine_v2::updateCommandLineEx(int str1, int str2, int16 palIndex) {
      char buffer[0x51];
      char *src = buffer;

      strcpy(src, getTableString(str1, _cCodeBuffer, 1));

      while (*src != 0x20)
            ++src;
      ++src;

      *src = toupper(*src);
      strcpy((char*)_unkBuf500Bytes, src);

      if (str2 > 0) {
            strcat((char*)_unkBuf500Bytes, " ");
            strcat((char*)_unkBuf500Bytes, getTableString(str2, _cCodeBuffer, 1));
      }

      showMessage((char*)_unkBuf500Bytes, palIndex);
}

#pragma mark -

void KyraEngine_v2::loadMouseShapes() {
      _screen->loadBitmap("_MOUSE.CSH", 3, 3, 0);

      for (int i = 0; i <= 8; ++i) {
            _defaultShapeTable[i] = _screen->makeShapeCopy(_screen->getCPagePtr(3), i);
            assert(_defaultShapeTable[i]);
      }
}

void KyraEngine_v2::loadItemShapes() {
      _screen->loadBitmap("_ITEMS.CSH", 3, 3, 0);

      for (int i = 64; i <= 239; ++i) {
            _defaultShapeTable[i] = _screen->makeShapeCopy(_screen->getCPagePtr(3), i-64);
            assert(_defaultShapeTable[i]);
      }

      _res->loadFileToBuf("_ITEMHT.DAT", _itemHtDat, sizeof(_itemHtDat));
      assert(_res->getFileSize("_ITEMHT.DAT") == sizeof(_itemHtDat));

      _screen->_curPage = 0;
}

void KyraEngine_v2::loadZShapes(int shapes) {
      char file[10];
      strcpy(file, "_ZX.SHP");

      _loadedZTable = shapes;
      file[2] = '0' + shapes;

      uint8 *data = _res->fileData(file, 0);
      for (int i = 9; i <= 32; ++i) {
            delete [] _defaultShapeTable[i];
            _defaultShapeTable[i] = _screen->makeShapeCopy(data, i-9);
            assert(_defaultShapeTable[i]);
      }
      delete [] data;

      _loadedZTable = shapes;
}

void KyraEngine_v2::loadInventoryShapes() {
      int curPageBackUp = _screen->_curPage;
      _screen->_curPage = 2;

      _screen->loadBitmap("_PLAYALL.CPS", 3, 3, 0);

      for (int i = 0; i < 10; ++i) {
            _defaultShapeTable[240+i] = _screen->encodeShape(_inventoryX[i], _inventoryY[i], 16, 16, 0);
            assert(_defaultShapeTable[240+i]);
      }

      _screen->_curPage = curPageBackUp;
}

void KyraEngine_v2::runStartScript(int script, int unk1) {
      char filename[14];
      strcpy(filename, "_START0X.EMC");
      filename[7] = script + '0';

      ScriptData scriptData;
      ScriptState scriptState;

      _scriptInterpreter->loadScript(filename, &scriptData, &_opcodes);
      _scriptInterpreter->initScript(&scriptState, &scriptData);
      scriptState.regs[6] = unk1;
      _scriptInterpreter->startScript(&scriptState, 0);
      while (_scriptInterpreter->validScript(&scriptState))
            _scriptInterpreter->runScript(&scriptState);
      _scriptInterpreter->unloadScript(&scriptData);
}

void KyraEngine_v2::loadNPCScript() {
      char filename[12];
      strcpy(filename, "_NPC.EMC");

      switch (_lang) {
      case 0:
            filename[5] = 'E';
            break;

      case 1:
            filename[5] = 'F';
            break;

      case 2:
            filename[5] = 'G';
            break;

      default:
            break;
      };

      _scriptInterpreter->loadScript(filename, &_npcScriptData, &_opcodes);
}

void KyraEngine_v2::runTemporaryScript(const char *filename, int unk1, int unk2, int newShapes, int shapeUnload) {
      memset(&_temporaryScriptData, 0, sizeof(_temporaryScriptData));
      memset(&_temporaryScriptState, 0, sizeof(_temporaryScriptState));

      if (!_scriptInterpreter->loadScript(filename, &_temporaryScriptData, &_opcodesTemporary))
            error("couldn't load temporary script '%s'", filename);

      _scriptInterpreter->initScript(&_temporaryScriptState, &_temporaryScriptData);
      _scriptInterpreter->startScript(&_temporaryScriptState, 0);

      _newShapeFlag = -1;

      while (_scriptInterpreter->validScript(&_temporaryScriptState))
            _scriptInterpreter->runScript(&_temporaryScriptState);

      uint8 *fileData = 0;

      if (newShapes) {
            _newShapeFiledata = _res->fileData(_newShapeFilename, 0);
            assert(_newShapeFiledata);
      }

      fileData = _newShapeFiledata;

      if (!fileData)
            return;

      if (newShapes)
            _newShapeCount = initNewShapes(fileData);

      processNewShapes(unk1, unk2);

      if (shapeUnload) {
            resetNewShapes(_newShapeCount, fileData);
            _newShapeCount = 0;
            _newShapeFiledata = 0;
      }

      _scriptInterpreter->unloadScript(&_temporaryScriptData);
}

#pragma mark -

void KyraEngine_v2::resetScaleTable() {
      Common::set_to(_scaleTable, _scaleTable + ARRAYSIZE(_scaleTable), 0x100);
}

void KyraEngine_v2::setScaleTableItem(int item, int data) {
      if (item >= 1 || item <= 15)
            _scaleTable[item-1] = (data << 8) / 100;
}

int KyraEngine_v2::getScale(int x, int y) {
      return _scaleTable[_screen->getLayer(x, y) - 1];
}

void KyraEngine_v2::setDrawLayerTableEntry(int entry, int data) {
      if (entry >= 1 || entry <= 15)
            _drawLayerTable[entry-1] = data;
}

int KyraEngine_v2::getDrawLayer(int x, int y) {
      int layer = _screen->getLayer(x, y);
      layer = _drawLayerTable[layer-1];
      if (layer < 0)
            layer = 0;
      else if (layer >= 7)
            layer = 6;
      return layer;
}

void KyraEngine_v2::restorePage3() {
      _screen->copyBlockToPage(2, 0, 0, 320, 144, _gamePlayBuffer);
}

void KyraEngine_v2::updateCharPal(int unk1) {
      static bool unkVar1 = false;

      if (!_useCharPal)
            return;

      int layer = _screen->getLayer(_mainCharacter.x1, _mainCharacter.y1);
      int palEntry = _charPalTable[layer];

      if (palEntry != _charPalEntry && unk1) {
            const uint8 *src = &_scenePal[(palEntry << 4) * 3];
            uint8 *ptr = _screen->getPalette(0) + 336;
            for (int i = 0; i < 48; ++i) {
                  *ptr -= (*ptr - *src) >> 1;
                  ++ptr;
                  ++src;
            }
            _screen->setScreenPalette(_screen->getPalette(0));
            unkVar1 = true;
            _charPalEntry = palEntry;
      } else if (unkVar1 && !unk1) {
            memcpy(_screen->getPalette(0) + 336, &_scenePal[(palEntry << 4) * 3], 48);
            _screen->setScreenPalette(_screen->getPalette(0));
            unkVar1 = false;
      }
}

void KyraEngine_v2::setCharPalEntry(int entry, int value) {
      if (entry > 15 || entry < 1)
            entry = 1;
      if (value > 8 || value < 0)
            value = 0;
      _charPalTable[entry] = value;
      _useCharPal = 1;
      _charPalEntry = 0;
}

int KyraEngine_v2::inputSceneChange(int x, int y, int unk1, int unk2) {
      bool refreshNPC = false;
      uint16 curScene = _mainCharacter.sceneId;
      _pathfinderFlag = 15;

      if (!_unkHandleSceneChangeFlag) {
            if (_unk3 == -3) {
                  if (_sceneList[curScene].exit4 != 0xFFFF) {
                        x = 4;
                        y = _sceneEnterY4;
                        _pathfinderFlag = 7;
                  }
            } else if (_unk3 == -5) {
                  if (_sceneList[curScene].exit2 != 0xFFFF) {
                        x = 316;
                        y = _sceneEnterY2;
                        _pathfinderFlag = 7;
                  }
            } else if (_unk3 == -6) {
                  if (_sceneList[curScene].exit1 != 0xFFFF) {
                        x = _sceneEnterX1;
                        y = _sceneEnterY1 - 2;
                        _pathfinderFlag = 14;
                  }
            } else if (_unk3 == -4) {
                  if (_sceneList[curScene].exit3 != 0xFFFF) {
                        x = _sceneEnterX3;
                        y = 147;
                        _pathfinderFlag = 11;
                  }
            }
      }

      if (_pathfinderFlag) {
            if (findItem(curScene, 13) >= 0 && _unk3 <= -3) {
                  //XXX
                  _pathfinderFlag = 0;
                  return 0;
            } else if (_itemInHand == 72) {
                  //XXX
                  _pathfinderFlag = 0;
                  return 0;
            } else if (findItem(curScene, 72) >= 0 && _unk3 <= -3) {
                  //XXX
                  _pathfinderFlag = 0;
                  return 0;
            } else if (0/*XXX*/) {
                  //XXX
                  _pathfinderFlag = 0;
                  return 0;
            }
      }

      if (ABS(_mainCharacter.x1 - x) < 4 || ABS(_mainCharacter.y1 - y) < 2)
            return 0;

      int curX = _mainCharacter.x1 & ~3;
      int curY = _mainCharacter.y1 & ~1;
      int dstX = x & ~3;
      int dstY = y & ~1;

      int wayLength = findWay(curX, curY, dstX, dstY, _movFacingTable, 600);
      _pathfinderFlag = 0;
      _timer->disable(5);

      if (wayLength != 0 && wayLength != 0x7D00)
            refreshNPC = (trySceneChange(_movFacingTable, unk1, unk2) != 0);

      //XXX

      if (refreshNPC)
            enterNewSceneUnk2(0);

      _pathfinderFlag = 0;
      return refreshNPC;
}

bool KyraEngine_v2::checkSpecialSceneExit(int num, int x, int y) {
      if (_specialExitTable[0+num] > x || _specialExitTable[5+num] > y ||
            _specialExitTable[10+num] < x || _specialExitTable[15+num] < y)
            return 0;
      return 1;
}

void KyraEngine_v2::moveCharacter(int facing, int x, int y) {
      _mainCharacter.facing = facing;
      x &= ~3;
      y &= ~1;

      _screen->hideMouse();
      switch (facing) {
      case 0:
            while (y < _mainCharacter.y1)
                  updateCharPosWithUpdate();
            break;

      case 2:
            while (_mainCharacter.x1 < x)
                  updateCharPosWithUpdate();
            break;

      case 4:
            while (y > _mainCharacter.y1)
                  updateCharPosWithUpdate();
            break;

      case 6:
            while (_mainCharacter.x1 > x)
                  updateCharPosWithUpdate();
            break;

      default:
            break;
      }

      _screen->showMouse();
}

int KyraEngine_v2::updateCharPos(int *table) {
      static uint32 nextUpdate = 0;
      static const int updateX[] = { 0, 4, 4, 4, 0, -4, -4, -4 };
      static const int updateY[] = { -2, -2, 0, 2, 2, 2, 0, -2 };

      if (_system->getMillis() < nextUpdate)
            return 0;

      int facing = _mainCharacter.facing;
      _mainCharacter.x1 += updateX[facing];
      _mainCharacter.y1 += updateY[facing];
      updateCharAnimFrame(0, table);
      nextUpdate = _system->getMillis() + _timer->getDelay(0) * _tickLength;
      return 1;
}

void KyraEngine_v2::updateCharPosWithUpdate() {
      updateCharPos(0);
      update();
}

void KyraEngine_v2::updateCharAnimFrame(int charId, int *table) {
      static int unkTable1[] = { 0, 0 };
      static const int unkTable2[] = { 17, 0 };
      static const int unkTable3[] = { 10, 0 };
      static const int unkTable4[] = { 24, 0 };
      static const int unkTable5[] = { 19, 0 };
      static const int unkTable6[] = { 21, 0 };
      static const int unkTable7[] = { 31, 0 };
      static const int unkTable8[] = { 26, 0 };

      Character *character = &_mainCharacter;
      ++character->animFrame;

      int facing = character->facing;

      if (table) {
            if (table[0] != table[-1] && table[-1] == table[1]) {
                  facing = getOppositeFacingDirection(table[-1]);
                  table[0] = table[-1];
            }
      }

      if (!facing) {
            ++unkTable1[charId];
      } else if (facing == 4) {
            ++unkTable1[charId+1];
      } else if (facing == 7 || facing == 1 || facing == 5 || facing == 3) {
            if (facing == 7 || facing == 1) {
                  if (unkTable1[charId] > 2)
                        facing = 0;
            } else {
                  if (unkTable1[charId+1] > 2)
                        facing = 4;
            }

            unkTable1[charId] = 0;
            unkTable1[charId+1] = 0;
      }

      if (facing == 0) {
            if (character->animFrame < unkTable8[charId])
                  character->animFrame = unkTable8[charId];

            if (character->animFrame > unkTable7[charId])
                  character->animFrame = unkTable8[charId];
      } else if (facing == 4) {
            if (character->animFrame < unkTable5[charId])
                  character->animFrame = unkTable5[charId];

            if (character->animFrame > unkTable4[charId])
                  character->animFrame = unkTable5[charId];
      } else {
            if (character->animFrame > unkTable5[charId])
                  character->animFrame = unkTable6[charId];

            if (character->animFrame == unkTable2[charId])
                  character->animFrame = unkTable3[charId];

            if (character->animFrame > unkTable2[charId])
                  character->animFrame = unkTable3[charId] + 2;
      }

      updateCharacterAnim(charId);
}

int KyraEngine_v2::checkCharCollision(int x, int y) {
      int scale1 = 0, scale2 = 0, scale3 = 0;
      int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
      scale1 = getScale(_mainCharacter.x1, _mainCharacter.y1);
      scale2 = (scale1 * 24) >> 8;
      scale3 = (scale1 * 48) >> 8;

      x1 = _mainCharacter.x1 - (scale2 >> 1);
      x2 = _mainCharacter.x1 + (scale2 >> 1);
      y1 = _mainCharacter.y1 - scale3;
      y2 = _mainCharacter.y1;

      if (x >= x1 && x <= x2 && y >= y1 && y <= y2)
            return 0;

      return -1;
}

int KyraEngine_v2::initNewShapes(uint8 *filedata) {
      const int lastEntry = MIN(_newShapeLastEntry, 31);
      for (int i = 0; i < lastEntry; ++i) {
            _defaultShapeTable[33+i] = _screen->getPtrToShape(filedata, i);
            ShapeDesc *desc = &_shapeDescTable[24+i];
            desc->xAdd = _newShapeXAdd;
            desc->yAdd = _newShapeYAdd;
            desc->width = _newShapeWidth;
            desc->height = _newShapeHeight;
      }
      return lastEntry;
}

void KyraEngine_v2::processNewShapes(int unk1, int unk2) {
      setCharacterAnimDim(_newShapeWidth, _newShapeHeight);

      _scriptInterpreter->initScript(&_temporaryScriptState, &_temporaryScriptData);
      _scriptInterpreter->startScript(&_temporaryScriptState, 1);

      _skipFlag = false;

      while (_scriptInterpreter->validScript(&_temporaryScriptState) && !_skipFlag) {
            _temporaryScriptExecBit = false;
            while (_scriptInterpreter->validScript(&_temporaryScriptState) && !_temporaryScriptExecBit)
                  _scriptInterpreter->runScript(&_temporaryScriptState);

            if (_newShapeAnimFrame < 0)
                  continue;

            _mainCharacter.animFrame = _newShapeAnimFrame + 33;
            updateCharacterAnim(0);
            if (_chatText)
                  updateWithText();
            else
                  update();

            uint32 delayEnd = _system->getMillis() + _newShapeDelay * _tickLength;

            while (!_skipFlag && _system->getMillis() < delayEnd) {
                  // XXX skipFlag handling, unk1 seems to make a scene not skipable

                  if (_chatText)
                        updateWithText();
                  else
                        update();

                  delay(10);
            }
      }

      if (unk2) {
            if (_newShapeFlag >= 0) {
                  _mainCharacter.animFrame = _newShapeFlag + 33;
                  updateCharacterAnim(0);
                  if (_chatText)
                        updateWithText();
                  else
                        update();
            }

            _mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing];
            updateCharacterAnim(0);
      }

      _skipFlag = false;

      _newShapeFlag = -1;
      resetCharacterAnimDim();
}

void KyraEngine_v2::resetNewShapes(int count, uint8 *filedata) {
      Common::set_to(_defaultShapeTable+33, _defaultShapeTable+33+count, (uint8*)0);
      delete [] filedata;
      setNextIdleAnimTimer();
}

void KyraEngine_v2::setNextIdleAnimTimer() {
      _nextIdleAnim = _system->getMillis() + _rnd.getRandomNumberRng(10, 15) * 60 * _tickLength;
}

void KyraEngine_v2::showIdleAnim() {
      static const uint8 scriptMinTable[] = {
            0x00, 0x05, 0x07, 0x08, 0x00, 0x09, 0x0A, 0x0B, 0xFF, 0x00
      };

      static const uint8 scriptMaxTable[] = {
            0x04, 0x06, 0x07, 0x08, 0x04, 0x09, 0x0A, 0x0B, 0xFF, 0x00
      };

      if (queryGameFlag(0x159))
            return;

      //if (!word_28432) {
      //    word_28432 = 1;
      //    sub_2715D();
      //} else {
      //    word_28432 = 0;
            if (_loadedZTable > 8)
                  return;

            int scriptMin = scriptMinTable[_loadedZTable-1];
            int scriptMax = scriptMaxTable[_loadedZTable-1];
            int script = 0;

            if (scriptMin < scriptMax) {
                  do {
                        script = _rnd.getRandomNumberRng(scriptMin, scriptMax);
                  } while (script == _lastIdleScript);
            } else {
                  script = scriptMin;
            }

            runIdleScript(script);
            _lastIdleScript = script;
      //}
}

void KyraEngine_v2::runIdleScript(int script) {
      if (script < 0 || script >= 12)
            script = 0;

      if (_mainCharacter.animFrame != 18) {
            setNextIdleAnimTimer();
      } else {
            // FIXME: move this to staticres.cpp?
            static const char *idleScriptFiles[] = {
                  "_IDLHAIR.EMC", "_IDLDUST.EMC", "_IDLLEAN.EMC", "_IDLDIRT.EMC", "_IDLTOSS.EMC", "_IDLNOSE.EMC",
                  "_IDLBRSH.EMC", "_Z3IDLE.EMC", "_Z4IDLE.EMC", "_Z6IDLE.EMC", "_Z7IDLE.EMC", "_Z8IDLE.EMC"
            };

            runTemporaryScript(idleScriptFiles[script], 1, 1, 1, 1);
      }
}

#pragma mark -

void KyraEngine_v2::backUpGfxRect24x24(int x, int y) {
      _screen->copyRegionToBuffer(_screen->_curPage, x, y, 24, 24, _gfxBackUpRect);
}

void KyraEngine_v2::restoreGfxRect24x24(int x, int y) {
      _screen->copyBlockToPage(_screen->_curPage, x, y, 24, 24, _gfxBackUpRect);
}

#pragma mark -

void KyraEngine_v2::openTalkFile(int newFile) {
      char talkFilename[16];

      if (_oldTalkFile > 0) {
            sprintf(talkFilename, "CH%dVOC.TLK", _oldTalkFile);
            _res->unloadPakFile(talkFilename);
            _oldTalkFile = -1;
      }

      if (newFile == 0) {
            strcpy(talkFilename, "ANYTALK.TLK");
            _res->loadPakFile(talkFilename);
      } else {
            sprintf(talkFilename, "CH%dVOC.TLK", newFile);
            _res->loadPakFile(talkFilename);
      }

      _oldTalkFile = newFile;
}

void KyraEngine_v2::snd_playVoiceFile(int id) {
      debugC(9, kDebugLevelMain | kDebugLevelSound, "KyraEngine_v2::snd_playVoiceFile(%d)", id);
      char vocFile[9];
      assert(id >= 0 && id <= 9999999);
      sprintf(vocFile, "%07d", id);
      _sound->voicePlay(vocFile);
}

void KyraEngine_v2::snd_loadSoundFile(int id) {
      if (id < 0 || !_trackMap)
            return;

      assert(id < _trackMapSize);
      int file = _trackMap[id*2];
      _curSfxFile = _curMusicTheme = file;
      _sound->loadSoundFile(file);
}

void KyraEngine_v2::snd_assignMusicData(kMusicDataID id) {
      if (_flags.platform == Common::kPlatformPC) {
            if (id == kMusicIntro)
                  _sound->setSoundFileList(_dosSoundFileListIntro, 1);
            else if (id == kMusicFinale)
                  _sound->setSoundFileList(_dosSoundFileListFinale, 1);
            else
                  _sound->setSoundFileList(_dosSoundFileList, _dosSoundFileListSize);
      } else {
            _sound->assignData(id);
      }
}

void KyraEngine_v2::playVoice(int high, int low) {
      int vocFile = high * 10000 + low * 10;
      snd_playVoiceFile(vocFile);
}

#pragma mark -

void KyraEngine_v2::loadInvWsa(const char *filename, int run, int delayTime, int page, int sfx, int sFrame, int flags) {
      int wsaFlags = 1;
      if (flags)
            wsaFlags |= 2;

      if (!_invWsa.wsa)
            _invWsa.wsa = new WSAMovieV2(this);

      if (!_invWsa.wsa->open(filename, wsaFlags, 0))
            error("Couldn't open inventory WSA file '%s'", filename);

      _invWsa.curFrame = 0;
      _invWsa.lastFrame = _invWsa.wsa->frames();

      _invWsa.x = _invWsa.wsa->xAdd();
      _invWsa.y = _invWsa.wsa->yAdd();
      _invWsa.w = _invWsa.wsa->width();
      _invWsa.h = _invWsa.wsa->height();
      _invWsa.x2 = _invWsa.x + _invWsa.w - 1;
      _invWsa.y2 = _invWsa.y + _invWsa.h - 1;

      _invWsa.delay = delayTime;
      _invWsa.page = page;
      _invWsa.sfx = sfx;

      _invWsa.specialFrame = sFrame;

      if (_invWsa.page)
            _screen->copyRegion(_invWsa.x, _invWsa.y, _invWsa.x, _invWsa.y, _invWsa.w, _invWsa.h, 0, _invWsa.page, Screen::CR_NO_P_CHECK);

      _invWsa.running = true;
      _invWsa.timer = _system->getMillis();

      if (run) {
            while (_invWsa.running && !_skipFlag && !_quitFlag) {
                  update();
                  //XXX delay?
            }
      }
}

void KyraEngine_v2::closeInvWsa() {
      _invWsa.wsa->close();
      delete _invWsa.wsa;
      _invWsa.wsa = 0;
      _invWsa.running = false;
}

void KyraEngine_v2::updateInvWsa() {
      if (!_invWsa.running || !_invWsa.wsa)
            return;

      if (_invWsa.timer > _system->getMillis())
            return;

      _invWsa.wsa->setX(0);
      _invWsa.wsa->setY(0);
      _invWsa.wsa->setDrawPage(_invWsa.page);
      _invWsa.wsa->displayFrame(_invWsa.curFrame, 0, 0, 0);

      if (_invWsa.page)
            _screen->copyRegion(_invWsa.x, _invWsa.y, _invWsa.x, _invWsa.y, _invWsa.w, _invWsa.h, _invWsa.page, 0, Screen::CR_NO_P_CHECK);

      _invWsa.timer = _system->getMillis() + _invWsa.delay * _tickLength;

      ++_invWsa.curFrame;
      if (_invWsa.curFrame >= _invWsa.lastFrame)
            displayInvWsaLastFrame();

      if (_invWsa.curFrame == _invWsa.specialFrame)
            snd_playSoundEffect(_invWsa.sfx);

      if (_invWsa.sfx == -2) {
            switch (_invWsa.curFrame) {
            case 9: case 27: case 40:
                  snd_playSoundEffect(0x39);
                  break;

            case 18: case 34: case 44:
                  snd_playSoundEffect(0x33);
                  break;

            case 48:
                  snd_playSoundEffect(0x38);
                  break;

            default:
                  break;
            }
      }
}

void KyraEngine_v2::displayInvWsaLastFrame() {
      if (!_invWsa.wsa)
            return;

      _invWsa.wsa->setX(0);
      _invWsa.wsa->setY(0);
      _invWsa.wsa->setDrawPage(_invWsa.page);
      _invWsa.wsa->displayFrame(_invWsa.lastFrame-1, 0, 0, 0);

      if (_invWsa.page)
            _screen->copyRegion(_invWsa.x, _invWsa.y, _invWsa.x, _invWsa.y, _invWsa.w, _invWsa.h, _invWsa.page, 0, Screen::CR_NO_P_CHECK);

      closeInvWsa();

      int32 countdown = _rnd.getRandomNumberRng(45, 80);
      _timer->setCountdown(2, countdown * 60);
}

#pragma mark -

typedef Functor1Mem<ScriptState*, int, KyraEngine_v2> OpcodeV2;
#define Opcode(x) OpcodeV2(this, &KyraEngine_v2::x)
#define OpcodeUnImpl() OpcodeV2(this, 0)
void KyraEngine_v2::setupOpcodeTable() {
      static const OpcodeV2 opcodeTable[] = {
            // 0x00
            Opcode(o2_setCharacterFacingRefresh),
            Opcode(o2_setCharacterPos),
            Opcode(o2_defineObject),
            Opcode(o2_refreshCharacter),
            // 0x04
            Opcode(o2_getCharacterX),
            Opcode(o2_getCharacterY),
            Opcode(o2_getCharacterFacing),
            OpcodeUnImpl(),
            // 0x08
            Opcode(o2_setSceneComment),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            Opcode(o2_setCharacterAnimFrame),
            // 0x0c
            OpcodeUnImpl(),
            Opcode(o2_trySceneChange),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x10
            OpcodeUnImpl(),
            Opcode(o2_showChapterMessage),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x14
            Opcode(o2_wsaClose),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            Opcode(o2_displayWsaFrame),
            // 0x18
            Opcode(o2_displayWsaSequentialFrames),
            Opcode(o2_wsaOpen),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x1c
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x20
            Opcode(o2_checkForItem),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            Opcode(o2_defineItem),
            // 0x24
            OpcodeUnImpl(),
            Opcode(o2_countItemInInventory),
            OpcodeUnImpl(),
            Opcode(o2_queryGameFlag),
            // 0x28
            Opcode(o2_resetGameFlag),
            Opcode(o2_setGameFlag),
            Opcode(o2_setHandItem),
            Opcode(o2_removeHandItem),
            // 0x2c
            Opcode(o2_handItemSet),
            Opcode(o2_hideMouse),
            Opcode(o2_addSpecialExit),
            Opcode(o2_setMousePos),
            // 0x30
            Opcode(o2_showMouse),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x34
            OpcodeUnImpl(),
            Opcode(o2_playSoundEffect),
            OpcodeUnImpl(),
            Opcode(o2_delay),
            // 0x38
            Opcode(o2_dummy),
            OpcodeUnImpl(),
            Opcode(o2_setScaleTableItem),
            Opcode(o2_setDrawLayerTableItem),
            // 0x3c
            Opcode(o2_setCharPalEntry),
            OpcodeUnImpl(),
            Opcode(o2_drawSceneShape),
            Opcode(o2_drawSceneShapeOnPage),
            // 0x40
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            Opcode(o2_dummy),
            OpcodeUnImpl(),
            // 0x44
            OpcodeUnImpl(),
            Opcode(o2_restoreBackBuffer),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x48
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            Opcode(o2_update),
            // 0x4c
            OpcodeUnImpl(),
            Opcode(o2_fadeScenePal),
            Opcode(o2_dummy),
            Opcode(o2_dummy),
            // 0x50
            Opcode(o2_enterNewSceneEx),
            Opcode(o2_switchScene),
            Opcode(o2_getShapeFlag1),
            OpcodeUnImpl(),
            // 0x54
            OpcodeUnImpl(),
            Opcode(o2_setLayerFlag),
            Opcode(o2_setZanthiaPos),
            Opcode(o2_loadMusicTrack),
            // 0x58
            Opcode(o2_playWanderScoreViaMap),
            Opcode(o2_playSoundEffect),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x5c
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x60
            Opcode(o2_getRand),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x64
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x68
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x6c
            Opcode(o2_encodeShape),
            Opcode(o2_defineRoomEntrance),
            Opcode(o2_runTemporaryScript),
            Opcode(o2_setSpecialSceneScriptRunTime),
            // 0x70
            Opcode(o2_defineSceneAnim),
            Opcode(o2_updateSceneAnim),
            Opcode(o2_updateSceneAnim),
            OpcodeUnImpl(),
            // 0x74
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x78
            OpcodeUnImpl(),
            Opcode(o2_defineRoom),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x7c
            OpcodeUnImpl(),
            Opcode(o2_dummy),
            Opcode(o2_dummy),
            OpcodeUnImpl(),
            // 0x80
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x84
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x88
            Opcode(o2_countItemInstances),
            OpcodeUnImpl(),
            Opcode(o2_initObject),
            OpcodeUnImpl(),
            // 0x8c
            Opcode(o2_deinitObject),
            OpcodeUnImpl(),
            Opcode(o2_makeBookOrCauldronAppear),
            Opcode(o2_setSpecialSceneScriptState),
            // 0x90
            Opcode(o2_clearSpecialSceneScriptState),
            Opcode(o2_querySpecialSceneScriptState),
            OpcodeUnImpl(),
            Opcode(o2_setHiddenItemsEntry),
            // 0x94
            Opcode(o2_getHiddenItemsEntry),
            OpcodeUnImpl(),
            Opcode(o2_wsaClose),
            OpcodeUnImpl(),
            // 0x98
            Opcode(o2_customChat),
            Opcode(o2_customChatFinish),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0x9c
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0xa0
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            // 0xa4
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            OpcodeUnImpl(),
            Opcode(o2_setVocHigh),
            // 0xa8
            Opcode(o2_getVocHigh),
            Opcode(o2_zanthiaChat),
            Opcode(o2_isVoiceEnabled),
            Opcode(o2_isVoicePlaying),
            // 0xac
            Opcode(o2_stopVoicePlaying),
            Opcode(o2_getGameLanguage),
            Opcode(o2_dummy),
            Opcode(o2_dummy),
      };

      for (int i = 0; i < ARRAYSIZE(opcodeTable); ++i)
            _opcodes.push_back(&opcodeTable[i]);

      static const OpcodeV2 opcodeTemporaryTable[] = {
            Opcode(o2t_defineNewShapes),
            Opcode(o2t_setCurrentFrame),
            Opcode(o2t_playSoundEffect),
            Opcode(o2t_fadeScenePal),
            Opcode(o2t_setShapeFlag),
            Opcode(o2_dummy)
      };

      for (int i = 0; i < ARRAYSIZE(opcodeTemporaryTable); ++i)
            _opcodesTemporary.push_back(&opcodeTemporaryTable[i]);
}

} // end of namespace Kyra


Generated by  Doxygen 1.6.0   Back to index