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

touche.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/touche/touche.cpp $
 * $Id: touche.cpp 30944 2008-02-23 22:50:18Z sev $
 *
 */


#include "common/config-manager.h"
#include "common/events.h"
#include "common/system.h"

#include "graphics/cursorman.h"
#include "sound/mididrv.h"

#include "touche/midi.h"
#include "touche/touche.h"
#include "touche/graphics.h"

namespace Touche {

ToucheEngine::ToucheEngine(OSystem *system, Common::Language language)
      : Engine(system), _midiPlayer(0), _language(language) {

      _saveLoadCurrentPage = 0;
      _saveLoadCurrentSlot = 0;
      _hideInventoryTexts = false;

      _screenRect = Common::Rect(kScreenWidth, kScreenHeight);
      _roomAreaRect = Common::Rect(kScreenWidth, kRoomHeight);

      memset(_flagsTable, 0, sizeof(_flagsTable));

      clearDirtyRects();

      _playSoundCounter = 0;

      _processRandomPaletteCounter = 0;

      _fastWalkMode = false;
      _fastMode = false;

      _currentObjectNum = -1;
      _objectDescriptionNum = 0;
      _speechPlaying = false;

      _roomNeedRedraw = false;
      _fullRedrawCounter = 0;
      _menuRedrawCounter = 0;
      memset(_paletteBuffer, 0, sizeof(_paletteBuffer));

      Common::addSpecialDebugLevel(kDebugEngine,   "Engine",   "Engine debug level");
      Common::addSpecialDebugLevel(kDebugGraphics, "Graphics", "Graphics debug level");
      Common::addSpecialDebugLevel(kDebugResource, "Resource", "Resource debug level");
      Common::addSpecialDebugLevel(kDebugOpcodes,  "Opcodes",  "Opcodes debug level");
      Common::addSpecialDebugLevel(kDebugMenu,     "Menu",     "Menu debug level");

      _eventMan->registerRandomSource(_rnd, "touche");
}

ToucheEngine::~ToucheEngine() {
      Common::clearAllSpecialDebugLevels();
      delete _midiPlayer;
}

int ToucheEngine::init() {
      _system->beginGFXTransaction();
            initCommonGFX(true);
            _system->initSize(kScreenWidth, kScreenHeight);
      _system->endGFXTransaction();

      Graphics::setupFont(_language);

      setupOpcodes();

      int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
      bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
      MidiDriver *driver = MidiDriver::createMidi(midiDriver);
      _midiPlayer = new MidiPlayer(driver, native_mt32);

      _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
      _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume"));
      _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, Audio::Mixer::kMaxMixerVolume);
      return 0;
}

int ToucheEngine::go() {
      res_openDataFile();
      res_allocateTables();
      res_loadSpriteImage(18, _menuKitData);
      res_loadImageHelper(_menuKitData, _currentImageWidth, _currentImageHeight);
      res_loadSpriteImage(19, _convKitData);
      res_loadImageHelper(_convKitData, _currentImageWidth, _currentImageHeight);

      mainLoop();

      res_deallocateTables();
      res_closeDataFile();
      return 0;
}

void ToucheEngine::restart() {
      _midiPlayer->stop();

      _displayQuitDialog = false;

      memset(_flagsTable, 0, sizeof(_flagsTable));

      clearDirtyRects();

      _currentKeyCharNum = 0;
      initKeyChars(-1);

      for (int i = 0; i < NUM_SEQUENCES; ++i) {
            _sequenceEntryTable[i].sprNum = -1;
            _sequenceEntryTable[i].seqNum = -1;
      }

      _disabledInputCounter = 0;
      _currentCursorObject = 0;
      setCursor(0);

      _waitingSetKeyCharNum1 = -1;
      _waitingSetKeyCharNum2 = -1;
      _waitingSetKeyCharNum3 = -1;

      _currentEpisodeNum = 0;
      _newEpisodeNum = kStartupEpisode;

      _newMusicNum = 0;
      _currentMusicNum = 0;

      _newSoundNum = 0;
      _newSoundDelay = 0;
      _newSoundPriority = 0;

      _flagsTable[176] = 0;
      _keyCharsTable[0].money = 25;
      _currentAmountOfMoney = 0;

      _giveItemToKeyCharNum = 0;
      _giveItemToObjectNum = 0;
      _giveItemToCounter = 0;

      clearAnimationTable();

      setupInventoryAreas();
      initInventoryObjectsTable();
      initInventoryLists();
      drawInventory(0, 1);

      _talkListEnd = 0;
      _talkListCurrent = 0;
      _talkTextRectDefined = false;
      _talkTextDisplayed = false;
      _talkTextInitialized = false;
      _skipTalkText = false;
      _talkTextSpeed = 0;
      _keyCharTalkCounter = 0;
      _talkTableLastTalkingKeyChar = -1;
      _talkTableLastOtherKeyChar = -1;
      _talkTableLastStringNum = -1;
      _objectDescriptionNum = 0;
      memset(_talkTable, 0, sizeof(_talkTable));

      _conversationChoicesUpdated = false;
      _conversationReplyNum = -1;
      _conversationEnded = false;
      _conversationNum = 0;
      _scrollConversationChoiceOffset = 0;
      _currentConversation = 0;
      _disableConversationScript = false;
      _conversationAreaCleared = false;
      memset(_conversationChoicesTable, 0, sizeof(_conversationChoicesTable));

      _flagsTable[901] = 1;
//    _flagsTable[902] = 1;
      if (_language == Common::FR_FRA) {
            _flagsTable[621] = 1;
      }
}

void ToucheEngine::readConfigurationSettings() {
      if (ConfMan.getBool("speech_mute")) {
            _talkTextMode = kTalkModeTextOnly;
            if (!ConfMan.getBool("subtitles")) {
                  ConfMan.setBool("subtitles", true);
            }
      } else {
            if (ConfMan.getBool("subtitles")) {
                  _talkTextMode = kTalkModeVoiceAndText;
            } else {
                  _talkTextMode = kTalkModeVoiceOnly;
            }
      }
      _midiPlayer->setVolume(ConfMan.getInt("music_volume"));
}

void ToucheEngine::writeConfigurationSettings() {
      switch (_talkTextMode) {
      case kTalkModeTextOnly:
            ConfMan.setBool("speech_mute", true);
            ConfMan.setBool("subtitles", true);
            break;
      case kTalkModeVoiceOnly:
            ConfMan.setBool("speech_mute", false);
            ConfMan.setBool("subtitles", false);
            break;
      case kTalkModeVoiceAndText:
            ConfMan.setBool("speech_mute", false);
            ConfMan.setBool("subtitles", true);
            break;
      }
      ConfMan.setInt("music_volume", _midiPlayer->getVolume());
      ConfMan.flushToDisk();
}

Common::Point ToucheEngine::getMousePos() const {
      return _eventMan->getMousePos();
}

void ToucheEngine::mainLoop() {
      restart();

      setPalette(0, 255, 0, 0, 0);

      readConfigurationSettings();

      _inp_leftMouseButtonPressed = false;
      _inp_rightMouseButtonPressed = false;

      if (ConfMan.hasKey("save_slot")) {
            loadGameState(ConfMan.getInt("save_slot"));
            _newEpisodeNum = 0;
            resetSortedKeyCharsTable();
            showCursor(true);
      } else {
            _newEpisodeNum = ConfMan.getInt("boot_param");
            if (_newEpisodeNum == 0) {
                  _newEpisodeNum = kStartupEpisode;
            }
            showCursor(_newEpisodeNum != kStartupEpisode);
      }

      uint32 frameTimeStamp = _system->getMillis();
      for (uint32 cycleCounter = 0; _flagsTable[611] == 0; ++cycleCounter) {
            if ((cycleCounter % 3) == 0) {
                  runCycle();
            }
            if ((cycleCounter % 2) == 0) {
                  fadePaletteFromFlags();
            }

            uint32 nextFrame = frameTimeStamp + (_fastMode ? 10 : kCycleDelay);
            uint32 now = _system->getMillis();
            if (nextFrame < now) {
                  nextFrame = now + 1;
            }
            do {
                  processEvents();
                  _system->updateScreen();
                  _system->delayMillis(10);
                  now = _system->getMillis();
            } while (now < nextFrame && !_fastMode);
            frameTimeStamp = nextFrame;
      }

      writeConfigurationSettings();
}

void ToucheEngine::processEvents(bool handleKeyEvents) {
      Common::Event event;
      while (_eventMan->pollEvent(event)) {
            switch (event.type) {
            case Common::EVENT_QUIT:
                  _flagsTable[611] = 1;
                  break;
            case Common::EVENT_KEYDOWN:
                  if (!handleKeyEvents) {
                        break;
                  }
                  _flagsTable[600] = event.kbd.keycode;
                  if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
                        if (_displayQuitDialog) {
                              _flagsTable[611] = displayQuitDialog();
                        }
                  } else if (event.kbd.keycode == Common::KEYCODE_F5) {
                        if (_flagsTable[618] == 0 && !_hideInventoryTexts) {
                              handleOptions(0);
                        }
                  } else if (event.kbd.keycode == Common::KEYCODE_F9) {
                        _fastWalkMode = true;
                  } else if (event.kbd.keycode == Common::KEYCODE_F10) {
                        _fastWalkMode = false;
                  }
                  if (event.kbd.flags == Common::KBD_CTRL) {
                        if (event.kbd.keycode == Common::KEYCODE_d) {
                              // enable debugging stuff ?
                              _flagsTable[777] = 1;
                        } else if (event.kbd.keycode == Common::KEYCODE_f) {
                              _fastMode = !_fastMode;
                        }
                  } else {
                        if (event.kbd.ascii == 't') {
                              ++_talkTextMode;
                              if (_talkTextMode == kTalkModeCount) {
                                    _talkTextMode = 0;
                              }
                              displayTextMode(-(92 + _talkTextMode));
                        } else if (event.kbd.ascii == ' ') {
                              updateKeyCharTalk(2);
                        }
                  }
                  break;
            case Common::EVENT_LBUTTONDOWN:
                  _inp_leftMouseButtonPressed = true;
                  break;
            case Common::EVENT_RBUTTONDOWN:
                  _inp_rightMouseButtonPressed = true;
                  break;
            case Common::EVENT_RBUTTONUP:
                  _inp_rightMouseButtonPressed = false;
                  break;
            default:
                  break;
            }
      }
}

void ToucheEngine::runCycle() {
      debugC(9, kDebugEngine, "ToucheEngine::runCycle()");
      if (_flagsTable[290]) {
            changePaletteRange();
      }
      if (_flagsTable[270]) {
            playSoundInRange();
      }
      if (_conversationEnded) {
            _disabledInputCounter = 0;
            _fullRedrawCounter = 1;
            _roomAreaRect.setHeight(kRoomHeight);
            _hideInventoryTexts = false;
            _conversationEnded = false;
            drawInventory(_currentKeyCharNum, 1);
      }
      if (_giveItemToCounter == 1) {
            _fullRedrawCounter = 1;
            drawInventory(_giveItemToObjectNum, 1);
            ++_giveItemToCounter;
      }
      if (_giveItemToCounter == -1) {
            _giveItemToCounter = 0;
            _roomAreaRect.setHeight(320);
            _keyCharsTable[_giveItemToKeyCharNum].flags &= ~kScriptPaused;
      }
      setupNewEpisode();
      startNewMusic();
      startNewSound();
      updateSpeech();
      handleConversation();
      if (scrollRoom(_currentKeyCharNum)) {
            _fullRedrawCounter |= 1;
      }
      redrawRoom();
      clearDirtyRects();
      updateRoomRegions();
      if (_flagsTable[612] != 0) {
            _flagsTable[613] = getRandomNumber(_flagsTable[612]);
      }
      sortKeyChars();
      for (int i = 0; i < NUM_KEYCHARS; ++i) {
            runKeyCharScript(&_keyCharsTable[i]);
      }
      if (_roomNeedRedraw) {
            scrollRoom(_currentKeyCharNum);
            redrawRoom();
            _roomNeedRedraw = false;
      }
      updateSpeech();
      for (int i = 0; i < NUM_KEYCHARS; ++i) {
            waitForKeyCharPosition(i);
      }
      redrawBackground();
      waitForKeyCharsSet();
      handleMouseInput(0);
      for (int i = 0; i < NUM_KEYCHARS; ++i) {
            drawKeyChar(&_keyCharsTable[i]);
      }
      processAnimationTable();
      updateKeyCharTalk(0);
      updateDirtyScreenAreas();
      ++_flagsTable[295];
      ++_flagsTable[296];
      ++_flagsTable[297];
      if (_flagsTable[298]) {
            --_flagsTable[298];
      }
      if (_flagsTable[299]) {
            --_flagsTable[299];
      }
      processEvents();
}

int16 ToucheEngine::getRandomNumber(int max) {
      assert(max > 0);
      return _rnd.getRandomNumber(max - 1);
}

void ToucheEngine::changePaletteRange() {
      if (_processRandomPaletteCounter) {
            --_processRandomPaletteCounter;
      } else {
            int scale = _flagsTable[291] + getRandomNumber(_flagsTable[292]);
            setPalette(0, 240, scale, scale, scale);
            _processRandomPaletteCounter = _flagsTable[293] + getRandomNumber(_flagsTable[294]);
      }
}

void ToucheEngine::playSoundInRange() {
      if (_playSoundCounter != 0) {
            --_playSoundCounter;
      } else {
            int16 flag = getRandomNumber(_flagsTable[270]);
            int16 num = _flagsTable[273 + flag];
            res_loadSound(0, num);
            _playSoundCounter = _flagsTable[271] + getRandomNumber(_flagsTable[272]);
      }
}

void ToucheEngine::resetSortedKeyCharsTable() {
      for (int i = 0; i < NUM_KEYCHARS; ++i) {
            _sortedKeyCharsTable[i] = &_keyCharsTable[i];
      }
}

void ToucheEngine::setupEpisode(int num) {
      debugC(9, kDebugEngine, "ToucheEngine::setupEpisode() num=%d", num);
      res_stopSpeech();
      resetTalkingVars();
      res_loadSpeech(-1);
      _currentObjectNum = -1;
      if (num != -1) {
            _updatedRoomAreasTable[0] = 1;
            initKeyChars(-1);
            for (int i = 200; i < 300; ++i) {
                  _flagsTable[i] = 0;
            }
            _flagsTable[291] = 240;
            _flagsTable[292] = 16;
            _flagsTable[293] = 0;
            _flagsTable[294] = 1;
            _currentEpisodeNum = num;
            if (_flagsTable[911] != 0) {
                  // load scripts from external data files
            }
            debug(0, "Setting up episode %d", num);
            res_loadProgram(num);
            _disabledInputCounter = 0;
      }
      res_decodeProgramData();
      _roomAreaRect.setHeight(kRoomHeight);
      _disableConversationScript = false;
      _hideInventoryTexts = false;
      _conversationEnded = false;
      clearRoomArea();
      drawInventory(_currentKeyCharNum, 1);
}

void ToucheEngine::setupNewEpisode() {
      debugC(9, kDebugEngine, "ToucheEngine::setupNewEpisode() _newEpisodeNum=%d", _newEpisodeNum);
      if (_newEpisodeNum) {
            if (_newEpisodeNum == 91) {
                  _displayQuitDialog = true;
            }
            res_stopSound();
            res_stopSpeech();
            setupEpisode(_newEpisodeNum);
            runCurrentKeyCharScript(1);
            _newEpisodeNum = 0;
            resetSortedKeyCharsTable();
      }
}

void ToucheEngine::drawKeyChar(KeyChar *key) {
      debugC(9, kDebugEngine, "ToucheEngine::drawKeyChar()");
      if (key->num != 0) {
            Common::Rect r(key->prevBoundingRect);
            r.extend(key->boundingRect);
            addToDirtyRect(r);
      }
}

void ToucheEngine::sortKeyChars() {
      debugC(9, kDebugEngine, "ToucheEngine::sortKeyChars()");
      for (int i = 0; i < NUM_KEYCHARS; ++i) {
            bool hasSwapped = false;
            for (int j = 0; j < NUM_KEYCHARS - 1; ++j) {
                  KeyChar *key1 = _sortedKeyCharsTable[j];
                  KeyChar *key2 = _sortedKeyCharsTable[j + 1];
                  if (key1->num != 0 && key2->num != 0) {
                        if (key1->zPos > key2->zPos) {
                              SWAP(_sortedKeyCharsTable[j], _sortedKeyCharsTable[j + 1]);
                              hasSwapped = true;
                        } else if (key1->zPos == key2->zPos && key1->yPos > key2->yPos) {
                              SWAP(_sortedKeyCharsTable[j], _sortedKeyCharsTable[j + 1]);
                              hasSwapped = true;
                        }
                  } else if (key1->num != 0) {
                        SWAP(_sortedKeyCharsTable[j], _sortedKeyCharsTable[j + 1]);
                        hasSwapped = true;
                  }
            }
            if (!hasSwapped) {
                  break;
            }
      }
}

void ToucheEngine::runKeyCharScript(KeyChar *key) {
      debugC(9, kDebugEngine, "ToucheEngine::runKeyCharScript() keyChar=%d", (int)(key - _keyCharsTable));
      if (key->scriptDataOffset != 0 && (key->flags & (kScriptStopped | kScriptPaused)) == 0) {
            int16 scriptParam = key->num - 1;
            int16 *prevStackDataPtr = _script.stackDataPtr;
            _script.stackDataPtr = key->scriptStackPtr;
            uint16 prevDataOffset = _script.dataOffset;
            _script.dataOffset = key->scriptDataOffset;
            _script.quitFlag = 0;
            while (_script.quitFlag == 0) {
                  executeScriptOpcode(scriptParam);
            }
            switch (_script.quitFlag) {
            case 1: // restart
                  key->scriptDataOffset = key->scriptDataStartOffset;
                  key->scriptStackPtr = &key->scriptStackTable[39];
                  break;
            case 3: // pause
                  key->flags |= kScriptPaused;
                  key->flags &= ~kScriptStopped;
                  key->scriptDataOffset = _script.dataOffset;
                  key->scriptStackPtr = _script.stackDataPtr;
                  break;
            default: // stop
                  key->flags |= kScriptStopped;
                  key->flags &= ~kScriptPaused;
                  key->scriptDataOffset = 0;
                  break;
            }
            _script.dataOffset = prevDataOffset;
            _script.stackDataPtr = prevStackDataPtr;
      }
}

void ToucheEngine::runCurrentKeyCharScript(int mode) {
      debugC(9, kDebugEngine, "ToucheEngine::runCurrentKeyCharScript() _currentKeyCharNum=%d mode=%d", _currentKeyCharNum, mode);
      KeyChar *key = &_keyCharsTable[_currentKeyCharNum];
      if (mode == 1) {
            _script.dataOffset = 0;
            _script.stackDataPtr = key->scriptStackPtr;
      }
      if (mode != 0) {
            while (_script.quitFlag == 0) {
                  executeScriptOpcode(0);
            }
            if (mode == 1) {
                  centerScreenToKeyChar(_currentKeyCharNum);
            }
            if (_script.quitFlag == 3) {
                  key->flags |= kScriptPaused;
                  key->flags &= ~kScriptStopped;
                  key->scriptDataOffset = _script.dataOffset;
                  key->scriptStackPtr = _script.stackDataPtr;
            }
      }
      handleMouseInput(1);
}

void ToucheEngine::executeScriptOpcode(int16 param) {
      debugC(9, kDebugOpcodes, "ToucheEngine::executeScriptOpcode(%d) offset=%04X", param, _script.dataOffset);
      _script.keyCharNum = param;
      _script.opcodeNum = _script.readNextByte();
      if (_script.opcodeNum < _numOpcodes) {
            OpcodeProc op = _opcodesTable[_script.opcodeNum];
            if (op) {
                  (this->*op)();
                  return;
            }
      }
      error("Invalid opcode 0x%X", _script.opcodeNum);
}

void ToucheEngine::initKeyChars(int keyChar) {
      debugC(9, kDebugEngine, "ToucheEngine::initKeyChars() keyChar=%d", keyChar);
      int indexStart, indexEnd;
      if (keyChar == -1) {
            indexStart = 0;
            indexEnd = NUM_KEYCHARS;
      } else {
            indexStart = keyChar;
            indexEnd = keyChar + 1;
      }
      Common::Rect defaultKeyCharRect(10, 10, 11, 11);
      for (int i = indexStart; i < indexEnd; ++i) {
            KeyChar *key = &_keyCharsTable[i];
            if (keyChar != -1 && key->num != 0) {
                  Common::Rect r(key->prevBoundingRect);
                  r.extend(key->boundingRect);
                  addToDirtyRect(r);
            }
            key->num = 0;
            key->strNum = 0;
            key->textColor = 253;
            key->currentAnimCounter = 0;
            key->currentAnimSpeed = 0;
            key->currentAnim = 0;
            key->framesListCount = 0;
            key->currentFrame = 0;
            key->anim1Start = 0;
            key->anim1Count = 1;
            key->anim2Start = 0;
            key->anim2Count = 1;
            key->anim3Start = 0;
            key->anim3Count = 1;
            key->facingDirection = 0;
            key->sequenceDataOffset = 0;
            key->walkDataNum = 0;
            key->walkPointsList[0] = -1;
            key->walkPointsListIndex = 0;
            key->delay = 0;
            key->waitingKeyChar = -1;
            key->flags = 0;
            key->scriptDataOffset = 0;
            key->scriptStackPtr = &key->scriptStackTable[39];
            key->xPos = 10;
            // like the original interpreter, don't reset yPos here. Doing so causes
            // glitches during the introduction for example (talk texts get displayed
            // at the wrong coordinates).
            key->boundingRect = defaultKeyCharRect;
            key->prevBoundingRect = defaultKeyCharRect;
      }
}

void ToucheEngine::setKeyCharTextColor(int keyChar, uint16 color) {
      debugC(9, kDebugEngine, "ToucheEngine::setKeyCharTextColor(%d) color=%d", keyChar, color);
      assert(keyChar >= 0 && keyChar < NUM_KEYCHARS);
      _keyCharsTable[keyChar].textColor = color;
}

void ToucheEngine::waitForKeyCharPosition(int keyChar) {
      debugC(9, kDebugEngine, "ToucheEngine::waitForKeyCharPosition(%d)", keyChar);
      KeyChar *key = _sortedKeyCharsTable[keyChar];
      if (key->num != 0) {
            key->prevBoundingRect = key->boundingRect;
            moveKeyChar(_offscreenBuffer, kScreenWidth, key);
            key->boundingRect = _moveKeyCharRect;
            if (key->delay != 0) {
                  --key->delay;
                  if (key->delay == 0) {
                        key->flags &= ~kScriptPaused;
                  }
                  return;
            }
            if (key->waitingKeyChar == -1) {
                  return;
            }
            KeyChar *nextKey = &_keyCharsTable[key->waitingKeyChar];
            if (nextKey->currentAnim != key->waitingKeyCharPosTable[0] &&
                nextKey->pointsDataNum != key->waitingKeyCharPosTable[1] &&
                nextKey->walkDataNum != key->waitingKeyCharPosTable[2]) {
                  return;
            }
            key->flags &= ~kScriptPaused;
            key->waitingKeyChar = -1;
      }
}

void ToucheEngine::setKeyCharBox(int keyChar, int value) {
      debugC(9, kDebugEngine, "ToucheEngine::setKeyCharBox(%d) value=%d", keyChar, value);
      assert(keyChar >= 0 && keyChar < NUM_KEYCHARS);
      KeyChar *key = &_keyCharsTable[keyChar];
      key->prevPointsDataNum = key->pointsDataNum = value;
      key->xPosPrev = key->xPos = _programPointsTable[value].x;
      key->yPosPrev = key->yPos = _programPointsTable[value].y;
      key->zPosPrev = key->zPos = _programPointsTable[value].z;
      key->prevWalkDataNum = key->walkDataNum = findWalkDataNum(value, 10000);
}

void ToucheEngine::setKeyCharFrame(int keyChar, int16 type, int16 value1, int16 value2) {
      assert(keyChar >= 0 && keyChar < NUM_KEYCHARS);
      KeyChar *key = &_keyCharsTable[keyChar];
      switch (type) {
      case 0:
            key->anim2Start = value1;
            key->anim2Count = value2;
            key->anim3Start = value1;
            key->anim3Count = value2;
            break;
      case 1:
            if (value2 != 0) {
                  value2 = getRandomNumber(value2);
            }
            key->framesList[key->framesListCount] = value1 + value2;
            ++key->framesListCount;
            key->framesListCount &= 15;
            break;
      case 2:
            key->anim1Start = value1;
            key->anim1Count = value2;
            break;
      case 3:
            key->currentAnim = value1;
            key->currentAnimSpeed = 0;
            key->currentAnimCounter = 0;
            break;
      case 4:
            key->anim3Start = value1;
            key->anim3Count = value2;
            break;
      }
}

void ToucheEngine::setKeyCharFacingDirection(int keyChar, int16 dir) {
      assert(keyChar >= 0 && keyChar < NUM_KEYCHARS);
      _keyCharsTable[keyChar].facingDirection = dir;
}

void ToucheEngine::initKeyCharScript(int keyChar, int16 spriteNum, int16 seqDataIndex, int16 seqDataOffs) {
      assert(keyChar >= 0 && keyChar < NUM_KEYCHARS);
      KeyChar *key = &_keyCharsTable[keyChar];
      key->num = keyChar + 1;
      key->spriteNum = spriteNum;
      key->sequenceDataIndex = seqDataIndex;
      key->sequenceDataOffset = seqDataOffs;
      key->scriptDataStartOffset = findProgramKeyCharScriptOffset(keyChar);
      key->scriptDataOffset = key->scriptDataStartOffset;
}

uint16 ToucheEngine::findProgramKeyCharScriptOffset(int keyChar) const {
      for (uint i = 0; i < _programKeyCharScriptOffsetTable.size(); ++i) {
            if (_programKeyCharScriptOffsetTable[i].keyChar == keyChar) {
                  return _programKeyCharScriptOffsetTable[i].offset;
            }
      }
      return 0;
}

bool ToucheEngine::scrollRoom(int keyChar) {
      if (_flagsTable[616] != 0) {
            return 0;
      }
      KeyChar *key = &_keyCharsTable[keyChar];
      bool needRedraw = false;

      // vertical scrolling
      int prevRoomDy = _flagsTable[615];
      _flagsTable[615] = key->yPos + 32 - kScreenHeight / 2;
      int roomHeight;
      if (_hideInventoryTexts) {
            roomHeight = kRoomHeight;
      } else {
            roomHeight = (_flagsTable[606] != 0) ? kScreenHeight : kRoomHeight;
            _roomAreaRect.setHeight(roomHeight);
      }
      _flagsTable[615] = CLIP<int16>(_flagsTable[615], 0, _currentBitmapHeight - roomHeight);
      if (_flagsTable[615] != prevRoomDy) {
            needRedraw = true;
      }

      // horizontal scrolling
      int prevRoomDx = _flagsTable[614];
      if (key->xPos > prevRoomDx + kScreenWidth - 160) {
            int dx = key->xPos - (prevRoomDx + kScreenWidth - 160);
            prevRoomDx += dx;
      } else if (key->xPos < prevRoomDx + 160) {
            int dx = prevRoomDx + 160 - key->xPos;
            prevRoomDx -= dx;
            if (prevRoomDx < 0) {
                  prevRoomDx = 0;
            }
      }
      prevRoomDx = CLIP<int16>(prevRoomDx, 0, _roomWidth - kScreenWidth);
      if (_flagsTable[614] != prevRoomDx) {
            _flagsTable[614] = prevRoomDx;
            return true;
      }
      if (_screenOffset.x == 0) {
            return needRedraw;
      }
      int scrollDx = _screenOffset.x - _flagsTable[614];
      if (scrollDx < -4) {
            scrollDx = -4;
      } else if (scrollDx > 4) {
            scrollDx = 4;
      }
      _flagsTable[614] += scrollDx;

      if (_screenOffset.x == _flagsTable[614]) {
            _screenOffset.x = 0;
      }
      return true;
}

void ToucheEngine::drawIcon(int x, int y, int num) {
      res_loadImage(num, _iconData);
      Graphics::copyRect(_offscreenBuffer, kScreenWidth, x, y,
        _iconData, kIconWidth, 0, 0,
        kIconWidth, kIconHeight,
        Graphics::kTransparent);
}

void ToucheEngine::centerScreenToKeyChar(int keyChar) {
      assert(keyChar >= 0 && keyChar < NUM_KEYCHARS);
      KeyChar *key = &_keyCharsTable[keyChar];
      _flagsTable[614] = key->xPos - kScreenWidth / 2;
      _flagsTable[615] = key->yPos - kScreenHeight / 2;
      _flagsTable[615] = CLIP<int16>(_flagsTable[615], 0, _currentBitmapHeight - kRoomHeight);
      scrollRoom(keyChar);
}

void ToucheEngine::waitForKeyCharsSet() {
      if (_waitingSetKeyCharNum2 != -1) {
            KeyChar *key = &_keyCharsTable[_waitingSetKeyCharNum2];
            if (key->framesListCount == key->currentFrame && key->currentAnim == key->anim2Start) {
                  key = &_keyCharsTable[_waitingSetKeyCharNum1];
                  if (key->framesListCount == key->currentFrame && key->currentAnim == key->anim2Start) {
                        key = &_keyCharsTable[_waitingSetKeyCharNum3];
                        _waitingSetKeyCharNum2 = -1;
                        key->flags &= ~kScriptPaused;
                  }
            }
      }
}

void ToucheEngine::redrawRoom() {
      if (_currentBitmapWidth == 0 || _currentBitmapHeight == 0) {
            return;
      }
      int w = kScreenWidth;
      if (_flagsTable[614] < 0 || _flagsTable[614] > _currentBitmapWidth - w) {
            error("Invalid room_x_offset = %d (w=%d, room_w=%d)", _flagsTable[614], w, _currentBitmapWidth);
      }
      int h = (_flagsTable[606] != 0) ? int(kScreenHeight) : _roomAreaRect.height();
      if (_flagsTable[615] < 0 || _flagsTable[615] > _currentBitmapHeight - h) {
            error("Invalid room_y_offset = %d (h=%d, room_h=%d)", _flagsTable[615], h, _currentBitmapHeight);
      }
      uint8 *dst = _offscreenBuffer;
      const uint8 *src = _backdropBuffer + _flagsTable[615] * _currentBitmapWidth + _flagsTable[614];
      while (h--) {
            memcpy(dst, src, w);
            dst += w;
            src += _currentBitmapWidth;
      }
}

void ToucheEngine::fadePalette(int firstColor, int colorCount, int scale, int scaleInc, int fadingStepsCount) {
      for (int i = 0; i < fadingStepsCount; ++i) {
            scale += scaleInc;
            scale = CLIP(scale, 0, 255);
            setPalette(firstColor, colorCount, scale, scale, scale);
            _system->updateScreen();
            _system->delayMillis(10);
      }
}

void ToucheEngine::fadePaletteFromFlags() {
      if (_flagsTable[603]) {
            setPalette(_flagsTable[607], _flagsTable[608], _flagsTable[605], _flagsTable[605], _flagsTable[605]);
            if (_flagsTable[603] > 0) {
                  if (_flagsTable[605] >= _flagsTable[609]) {
                        _flagsTable[603] = 0;
                  }
            } else {
                  if (_flagsTable[605] <= _flagsTable[610]) {
                        _flagsTable[603] = 0;
                  }
            }
            _flagsTable[605] += _flagsTable[603];
            if (_flagsTable[605] < 0) {
                  _flagsTable[605] = 0;
            } else if (_flagsTable[605] > 255) {
                  _flagsTable[605] = 255;
            }
      }
}

static uint8 *getKeyCharFrameData(uint8 *p, uint16 dir1, uint16 dir2, uint16 dir3, uint8 **dst, int16 sequence_num) {
      uint8 *src;
      uint16 offs, num1;

      // spriteData
      // LE16 offset to "sprite copy" data
      // LE16 offset to 4 * 2 * 10 offsets : "sprite info" offset
      // LE16 data offset
      // LE16 ?
      offs = READ_LE_UINT16(p + sequence_num * 8 + 2);
      offs = READ_LE_UINT16(p + offs + dir1 * 4); // facing
      offs = READ_LE_UINT16(p + offs + dir2 * 2); // top/bottom
      src = p + offs + dir3 * 10; // current frame anim ?
      *dst = src;
      // LE16 : if 0x8000 -> offset "sprite copy" data num
      // LE16 : dx
      // LE16 : dy
      // LE16 : dz

      num1 = READ_LE_UINT16(src) & 0x7FFF;
      offs = READ_LE_UINT16(p + sequence_num * 8 + 0);
      offs = READ_LE_UINT16(p + offs + num1 * 2);
      return p + offs;
      // LE16 : srcX
      // LE16 : srcY
      // LE16 : flags (vflip, hflip)
}

void ToucheEngine::moveKeyChar(uint8 *dst, int dstPitch, KeyChar *key) {
      int16 keyChar = key->num - 1;
      int16 walkDataNum = key->walkDataNum;
      int16 clippingRectNum = 0;
      if (walkDataNum != -1) {
            clippingRectNum = _programWalkTable[walkDataNum].clippingRect;
      }
      Common::Rect clippingRect(_programRectsTable[clippingRectNum]);
      clippingRect.translate(-_flagsTable[614], -_flagsTable[615]);
      if (key->flags & 0x8000) {
            clippingRect.moveTo(clippingRect.left, kRoomHeight);
      }
      clippingRect.clip(_roomAreaRect);
      SpriteData *spr = &_spritesTable[key->spriteNum];
      int x1 =  30000, y1 =  30000;
      int x2 = -30000, y2 = -30000;
      int16 keyCharDirection = _flagsTable[266];
      if (keyCharDirection == 0) {
            keyCharDirection = key->facingDirection;
      }
      int16 facingDirection = keyCharDirection;
      uint8 *sequenceDataBase = _sequenceDataTable[key->sequenceDataIndex];
      uint8 *sequenceData = sequenceDataBase;

      uint16 frameDirFlag = READ_LE_UINT16(sequenceData + key->sequenceDataOffset * 8 + 4);
      if (frameDirFlag) {
            sequenceData += frameDirFlag & ~1;
      }

      uint8 *frameData;
      uint8 *frameDataBase = getKeyCharFrameData(sequenceDataBase, key->currentAnim, facingDirection, key->currentAnimCounter, &frameData, key->sequenceDataOffset);
      uint16 frameFlag = READ_LE_UINT16(frameData); frameData += 2;
      uint16 walkDx = READ_LE_UINT16(frameData); frameData += 2;
      uint16 walkDy = READ_LE_UINT16(frameData); frameData += 2;
      uint16 walkDz = READ_LE_UINT16(frameData); frameData += 2;

      if (key->currentAnimSpeed <= 0) {
            key->currentAnimSpeed = READ_LE_UINT16(frameData);
      }
      --key->currentAnimSpeed;
      if (key->currentAnimSpeed <= 0) {
            ++key->currentAnimCounter;
      }
      if (_fastWalkMode) {
            walkDx *= 2;
            walkDy *= 2;
            walkDz *= 2;
      }
      updateKeyCharWalkPath(key, walkDx, walkDy, walkDz);
      int posX = key->xPos;
      int posY = key->yPos;
      int posZ = key->zPos;
      if (frameFlag & 0x8000) {
            changeKeyCharFrame(key, keyChar);
      }
      posX -= _flagsTable[614];
      posY -= _flagsTable[615];
      if (posZ == 160) { // draw sprite frames without rescaling
            while (1) {
                  int dstX = (int16)READ_LE_UINT16(frameDataBase); frameDataBase += 2;
                  if (dstX == 10000) {
                        _moveKeyCharRect = Common::Rect(x1, y1, x2 + spr->w, y2 + spr->h);
                        break;
                  }
                  int dstY = (int16)READ_LE_UINT16(frameDataBase); frameDataBase += 2;

                  if (facingDirection == 3) {
                        dstX = -dstX - spr->w;
                  }
                  dstX += posX;
                  dstY += posY;
                  x1 = MIN(dstX, x1);
                  x2 = MAX(dstX, x2);
                  y1 = MIN(dstY, y1);
                  y2 = MAX(dstY, y2);

                  int frameDir = READ_LE_UINT16(frameDataBase); frameDataBase += 2;
//                assert((frameDir & 0x4000) == 0); // hflipped
                  bool vflipped = (frameDir & 0x8000) != 0;

                  frameDir &= 0xFFF;
                  if (frameDirFlag) {
                        frameDir = READ_LE_UINT16(sequenceData + frameDir * 2);
                  }
                  if (keyChar == 0) {
                        assert(frameDir < NUM_DIRECTIONS);
                        if (_directionsTable[frameDir] <= _flagsTable[176]) {
                              continue;
                        }
                  }
                  if (frameDir == 0x800) {
                        continue;
                  }

                  assert(spr->w != 0);
                  int framesPerLine = spr->bitmapWidth / spr->w;
                  assert(framesPerLine != 0);
                  const int srcOffsX = spr->w * (frameDir % framesPerLine);
                  const int srcOffsY = spr->h * (frameDir / framesPerLine);

                  Area copyRegion(dstX, dstY, spr->w, spr->h);
                  copyRegion.srcX = 0;
                  copyRegion.srcY = 0;
                  if (!copyRegion.clip(clippingRect)) {
                        continue;
                  }

                  if (facingDirection == 3) {
                        vflipped = !vflipped;
                  }

                  uint8 *dstCur = dst + copyRegion.r.top * dstPitch + copyRegion.r.left;
                  const uint8 *srcSpr = spr->ptr + (srcOffsY + copyRegion.srcY) * spr->bitmapWidth;
                  srcSpr += vflipped ? srcOffsX + spr->w - 1 - copyRegion.srcX : srcOffsX + copyRegion.srcX;
                  for (int h = 0; h < copyRegion.r.height(); ++h) {
                        for (int w = 0; w < copyRegion.r.width(); ++w) {
                              uint8 color = vflipped ? srcSpr[-w] : srcSpr[w];
                              if (color != 0) {
                                    dstCur[w] = color;
                              }
                        }
                        srcSpr += spr->bitmapWidth;
                        dstCur += dstPitch;
                  }
            }
      } else { // draw sprite frames with rescaling
            y2 = posY;
            int clippingRect_x1 = clippingRect.left;
            int clippingRect_y1 = clippingRect.top;
            int clippingRect_x2 = clippingRect.right;
            int clippingRect_y2 = clippingRect.bottom;
            buildSpriteScalingTable(160, posZ);
            while (1) {
                  int dstX = (int16)READ_LE_UINT16(frameDataBase); frameDataBase += 2;
                  if (dstX == 10000) {
                        _moveKeyCharRect = Common::Rect(x1, y1, x2 + 1, y2 + 1);
                        break;
                  }
                  int dstY = (int16)READ_LE_UINT16(frameDataBase); frameDataBase += 2;

                  int frameDir = READ_LE_UINT16(frameDataBase); frameDataBase += 2;
//                assert((frameDir & 0x4000) == 0); // hflipped
                  bool vflipped = (frameDir & 0x8000) != 0;

                  frameDir &= 0xFFF;
                  if (frameDirFlag) {
                        frameDir = READ_LE_UINT16(sequenceData + frameDir * 2);
                  }
                  if (keyChar == 0) {
                        assert(frameDir < NUM_DIRECTIONS);
                        if (_directionsTable[frameDir] <= _flagsTable[176]) {
                              continue;
                        }
                  }
                  if (frameDir == 0x800) {
                        continue;
                  }

                  assert(spr->w != 0);
                  int framesPerLine = spr->bitmapWidth / spr->w;
                  assert(framesPerLine != 0);
                  const int srcOffsX = spr->w * (frameDir % framesPerLine);
                  const int srcOffsY = spr->h * (frameDir / framesPerLine);
                  const uint8 *srcSpr = spr->ptr + srcOffsY * spr->bitmapWidth + srcOffsX;

                  assert(dstY >= -500 && dstY < 500);
                  int scalingIndex = _spriteScalingIndex[500 + dstY];
                  int16 *yScaledTable = &_spriteScalingTable[scalingIndex];
                  int sprScaledY = posY + scalingIndex - 500;
                  y1 = MIN(y1, sprScaledY);

                  if (facingDirection == 3) {
                        dstX = -dstX;
                  }
                  assert(dstX >= -500 && dstX < 500);
                  scalingIndex = _spriteScalingIndex[500 + dstX];
                  int sprScaledX = posX + scalingIndex - 500;
                  int16 *xScaledTable = &_spriteScalingTable[scalingIndex];
                  x1 = MIN(x1, sprScaledX);
                  x2 = MAX(x2, sprScaledX);

                  uint8 *dstCur = dst + sprScaledY * dstPitch + sprScaledX;

                  uint8 *dstStart = dstCur;
                  int sprStartY = 0;
                  while (1) {
                        int sprCurY = *yScaledTable - dstY; ++yScaledTable;
                        if (sprCurY >= spr->h) {
                              break;
                        }
                        sprStartY = sprCurY - sprStartY;
                        while (sprStartY != 0) {
                              srcSpr += spr->bitmapWidth;
                              --sprStartY;
                        }
                        sprStartY = sprCurY;

                        int16 *scalingTable = xScaledTable;
                        int spr_x2 = sprScaledX;
                        dstCur = dstStart;
                        if (sprScaledY < clippingRect_y1 || sprScaledY >= clippingRect_y2) {
                              continue;
                        }
                        if (facingDirection != 3) {
                              while (1) {
                                    int spr_x1 = *scalingTable - dstX; ++scalingTable;
                                    if (spr_x1 >= spr->w || spr_x2 >= clippingRect_x2) {
                                          break;
                                    }
                                    if (spr_x2 >= clippingRect_x1) {
                                          uint8 color = vflipped ? srcSpr[spr->w - 1 - spr_x1] : srcSpr[spr_x1];
                                          if (color != 0) {
                                                *dstCur = color;
                                          }
                                    }
                                    ++spr_x2;
                                    ++dstCur;
                              }
                              x2 = MAX(x2, spr_x2);
                        } else {
                              while (1) {
                                    int spr_x1 = dstX - *scalingTable; --scalingTable;
                                    if (spr_x1 >= spr->w || spr_x2 < clippingRect_x1) {
                                          break;
                                    }
                                    if (spr_x2 < clippingRect_x2) {
                                          uint8 color = vflipped ? srcSpr[spr->w - 1 - spr_x1] : srcSpr[spr_x1];
                                          if (color != 0) {
                                                *dstCur = color;
                                          }
                                    }
                                    --spr_x2;
                                    --dstCur;
                              }
                              x1 = MIN(x1, spr_x2);
                        }
                        ++sprScaledY;
                        dstStart += dstPitch;
                  }
            }
      }
      if (walkDataNum != -1) {
            if (_flagsTable[604] == 0) {
                  int area1 = _programWalkTable[walkDataNum].area1;
                  if (area1 != 0) {
                        findAndRedrawRoomRegion(area1);
                  }
                  int area2 = _programWalkTable[walkDataNum].area2;
                  if (area2 != 0) {
                        findAndRedrawRoomRegion(area2);
                  }
            }
      }
}

void ToucheEngine::changeKeyCharFrame(KeyChar *key, int keyChar) {
      key->currentAnimSpeed = 0;
      key->currentAnimCounter = 0;
      if (key->currentAnim != 1) {
            int16 animStart, animCount;
            if (_currentObjectNum == keyChar && _flagsTable[901] == 1) {
                  animStart = key->anim1Start;
                  animCount = key->anim1Count;
            } else if (key->framesListCount != key->currentFrame) {
                  animStart = key->framesList[key->currentFrame];
                  ++key->currentFrame;
                  key->currentFrame &= 15;
                  animCount = 0;
            } else {
                  animStart = key->anim2Start;
                  animCount = key->anim2Count;
                  if (key->currentAnim >= animStart && key->currentAnim < animStart + animCount) {
                        int rnd = getRandomNumber(100);
                        if (key->flags & 0x10) {
                              if (rnd >= 50 && rnd <= 55) {
                                    KeyChar *followingKey = &_keyCharsTable[key->followingKeyCharNum];
                                    int16 num = followingKey->pointsDataNum;
                                    if (num != 0 && followingKey->currentWalkBox != -1 && num != key->followingKeyCharPos) {
                                          key->followingKeyCharPos = num;
                                          sortPointsData(-1, num);
                                          buildWalkPointsList(key->num - 1);
                                    }
                              }
                        } else {
                              if (rnd >= 50 && rnd <= 51) {
                                    animStart = key->anim3Start;
                                    animCount = key->anim3Count;
                              }
                        }
                  }
            }
            if (animCount != 0) {
                  animCount = getRandomNumber(animCount);
            }
            key->currentAnim = animStart + animCount;
      }
}

void ToucheEngine::setKeyCharRandomFrame(KeyChar *key) {
      key->currentAnimSpeed = 0;
      key->currentAnim = key->anim2Start + getRandomNumber(key->anim2Count);
      key->currentAnimCounter = 0;
}

void ToucheEngine::setKeyCharMoney() {
      _keyCharsTable[_currentKeyCharNum].money += _currentAmountOfMoney;
      _currentAmountOfMoney = 0;
      drawAmountOfMoneyInInventory();
}

const char *ToucheEngine::getString(int num) const {
      if (num < 0) {
            return (const char *)_textData + READ_LE_UINT32(_textData - num * 4);
      } else {
            return (const char *)_programTextDataPtr + READ_LE_UINT32(_programTextDataPtr + num * 4);
      }
}

int ToucheEngine::getStringWidth(int num) const {
      const char *str = getString(num);
      return Graphics::getStringWidth16(str);
}

void ToucheEngine::drawString(uint16 color, int x, int y, int16 num) {
      if (num) {
            const char *str = getString(num);
            Graphics::drawString16(_offscreenBuffer, kScreenWidth, color, x, y, str);
      }
}

void ToucheEngine::drawGameString(uint16 color, int x1, int y, const char *str) {
      int w = Graphics::getStringWidth16(str);
      int x = x1 - w / 2;
      if (x + w >= kScreenWidth) {
            x = kScreenWidth - w - 1;
      }
      while (*str) {
            char chr = *str++;
            if (chr == '\\') {
                  y += kTextHeight;
                  w = Graphics::getStringWidth16(str);
                  x = x1 - w / 2;
            } else {
                  if (x < 0) {
                        x = 0;
                  }
                  x += Graphics::drawChar16(_offscreenBuffer, kScreenWidth, chr, x, y, color);
            }
      }
}

int ToucheEngine::restartKeyCharScriptOnAction(int action, int obj1, int obj2) {
      debugC(9, kDebugEngine, "ToucheEngine::restartKeyCharScriptOnAction(%d, %d, %d)", action, obj1, obj2);
      for (uint i = 0; i < _programActionScriptOffsetTable.size(); ++i) {
            const ProgramActionScriptOffsetData *pasod = &_programActionScriptOffsetTable[i];
            if (pasod->object1 == obj1 && pasod->action == action && pasod->object2 == obj2) {
                  debug(0, "Found matching action i=%d %d,%d,%d offset=0x%X", i, pasod->action, pasod->object1, pasod->object2, pasod->offset);
                  KeyChar *key = &_keyCharsTable[_currentKeyCharNum];
                  key->scriptDataOffset = pasod->offset;
                  key->scriptStackPtr = &key->scriptStackTable[39];
                  key->flags &= ~(kScriptStopped | kScriptPaused);
                  return 1;
            }
      }
      return 0;
}

void ToucheEngine::buildSpriteScalingTable(int z1, int z2) {
      debugC(9, kDebugEngine, "ToucheEngine::buildSpriteScalingTable(%d, %d)", z1, z2);
      if (z2 > 500) {
            z2 = 500;
      }
      if (z2 == 0) {
            z2 = 1;
      }

      memset(_spriteScalingTable, 0, sizeof(_spriteScalingTable));
      const int scaleInc = z1 * 256 / z2;
      int scaleSum = 0;
      for (int i = 0; i < z2; ++i) {
            int value = scaleSum >> 8;
            assert(i < 500);
            _spriteScalingTable[500 + i] =  value;
            _spriteScalingTable[500 - i] = -value;
            scaleSum += scaleInc;
      }

      memset(_spriteScalingIndex, 0, sizeof(_spriteScalingIndex));
      const int16 *p = &_spriteScalingTable[500];
      int16 z1_s = *p++;
      int16 z2_s = *p++;
      for (int i = 0, j = 0; j < z1; ++i) {
            while (z2_s != z1_s) {
                  ++z1_s;
                  assert(j < 500);
                  _spriteScalingIndex[500 + j] = i + 500;
                  _spriteScalingIndex[500 - j] = 500 - i;
                  if (j++ >= z1) {
                        break;
                  }
            }
            z1_s = z2_s;
            z2_s = *p++;
      }
}

void ToucheEngine::drawSpriteOnBackdrop(int num, int x, int y) {
      assert(num >= 0 && num < NUM_SPRITES);
      SpriteData *spr = &_spritesTable[num];
      Graphics::copyRect(_backdropBuffer, _currentBitmapWidth, x, y,
        spr->ptr, spr->bitmapWidth, 0, 0,
        spr->bitmapWidth, spr->bitmapHeight);
}

void ToucheEngine::updateTalkFrames(int keyChar) {
      assert(keyChar >= 0 && keyChar < NUM_KEYCHARS);
      KeyChar *key = &_keyCharsTable[keyChar];
      if (key->currentAnim >= key->anim1Start && key->currentAnim < key->anim1Start + key->anim1Count) {
            key->currentAnim = key->anim2Start;
            key->currentAnimCounter = 0;
            key->currentAnimSpeed = 0;
      }
}

void ToucheEngine::setKeyCharTalkingFrame(int keyChar) {
      assert(keyChar >= 0 && keyChar < NUM_KEYCHARS);
      KeyChar *key = &_keyCharsTable[keyChar];
      if (key->currentAnim != 1) {
            key->currentAnim = key->anim1Start;
            key->currentAnimCounter = 0;
            key->currentAnimSpeed = 0;
      }
}

void ToucheEngine::lockUnlockHitBox(int num, int lock) {
      for (uint i = 0; i < _programHitBoxTable.size(); ++i) {
            if (_programHitBoxTable[i].item == num) {
                  if (lock) {
                        _programHitBoxTable[i].hitBoxes[0].top |= 0x4000;
                  } else {
                        _programHitBoxTable[i].hitBoxes[0].top &= ~0x4000;
                  }
            }
      }
}

void ToucheEngine::drawHitBoxes() {
      for (uint i = 0; i < _programHitBoxTable.size(); ++i) {
            uint16 state = _programHitBoxTable[i].state;
            if (state & 0x8000) {
                  _programHitBoxTable[i].state = state & 0x7FFF;
                  addToDirtyRect(_programHitBoxTable[i].hitBoxes[1]);
            }
      }
}

void ToucheEngine::showCursor(bool show) {
      debugC(9, kDebugEngine, "ToucheEngine::showCursor()");
      CursorMan.showMouse(show);
}

void ToucheEngine::setCursor(int num) {
      debugC(9, kDebugEngine, "ToucheEngine::setCursor(%d)", num);
      _currentCursorObject = num;
      res_loadImage(num, _mouseData);
      CursorMan.replaceCursor(_mouseData, kCursorWidth, kCursorHeight, kCursorWidth / 2, kCursorHeight / 2, 0);
}

void ToucheEngine::setDefaultCursor(int num) {
      debugC(9, kDebugEngine, "ToucheEngine::setDefaultCursor(%d)", num);
      if (_currentCursorObject != 0) {
            if (_currentCursorObject != 1) {
                  addItemToInventory(num, _currentCursorObject);
                  drawInventory(num, 1);
            }
            setCursor(0);
      }
}

void ToucheEngine::handleLeftMouseButtonClickOnInventory() {
      Common::Point mousePos = getMousePos();
      for (int area = 0; area < ARRAYSIZE(_inventoryAreasTable); ++area) {
            if (_inventoryAreasTable[area].contains(mousePos)) {
                  if (area >= kInventoryObject1 && area <= kInventoryObject6) {
                        int item = _inventoryVar1[area - 6 + *_inventoryVar2];
                        _flagsTable[119] = _currentCursorObject;
                        if (_currentCursorObject == 1) {
                              setKeyCharMoney();
                              _flagsTable[118] = _currentAmountOfMoney;
                              _currentAmountOfMoney = 0;
                        }
                        if (item != 0 && _currentCursorObject != 0) {
                              if (restartKeyCharScriptOnAction(-53, item | 0x1000, 0)) {
                                    setDefaultCursor(_objectDescriptionNum);
                                    drawInventory(_objectDescriptionNum, 1);
                              }
                        } else {
                              _inventoryVar1[area - 6 + *_inventoryVar2] = 0;
                              if (_currentCursorObject != 0) {
                                    setDefaultCursor(_objectDescriptionNum);
                              }
                              if (item != 0) {
                                    setCursor(item);
                                    packInventoryItems(0);
                                    packInventoryItems(1);
                              }
                              drawInventory(_objectDescriptionNum, 1);
                        }
                  } else {
                        switch (area) {
                        case kInventoryCharacter:
                              _keyCharsTable[_currentKeyCharNum].money += _currentAmountOfMoney;
                              _currentAmountOfMoney = 0;
                              handleOptions(0);
                              break;
                        case kInventoryMoneyDisplay:
                              setKeyCharMoney();
                              if (_currentCursorObject == 1) {
                                    setCursor(0);
                              }
                              break;
                        case kInventoryGoldCoins:
                              if (_keyCharsTable[_currentKeyCharNum].money >= 10) {
                                    _keyCharsTable[_currentKeyCharNum].money -= 10;
                                    _currentAmountOfMoney += 10;
                                    drawAmountOfMoneyInInventory();
                              }
                              break;
                        case kInventorySilverCoins:
                              if (_keyCharsTable[_currentKeyCharNum].money != 0) {
                                    --_keyCharsTable[_currentKeyCharNum].money;
                                    ++_currentAmountOfMoney;
                                    drawAmountOfMoneyInInventory();
                              }
                              break;
                        case kInventoryMoney:
                              if (_currentAmountOfMoney != 0) {
                                    setDefaultCursor(_objectDescriptionNum);
                                    int money = _currentAmountOfMoney;
                                    _currentAmountOfMoney = 0;
                                    drawAmountOfMoneyInInventory();
                                    setCursor(1);
                                    _currentAmountOfMoney = money;
                              }
                              break;
                        case kInventoryScroller1:
                              if (*_inventoryVar2 != 0) {
                                    *_inventoryVar2 -= 6;
                                    drawInventory(_objectDescriptionNum, 1);
                              }
                              break;
                        case kInventoryScroller2:
                              if (_inventoryVar1[6 + *_inventoryVar2] != 0) {
                                    *_inventoryVar2 += 6;
                                    drawInventory(_objectDescriptionNum, 1);
                              }
                              break;
                        }
                  }
                  break;
            }
      }
}

void ToucheEngine::handleRightMouseButtonClickOnInventory() {
      Common::Point mousePos = getMousePos();
      for (int area = kInventoryObject1; area <= kInventoryObject6; ++area) {
            const Common::Rect &r = _inventoryAreasTable[area];
            if (r.contains(mousePos)) {
                  int item = _inventoryVar1[area - 6 + *_inventoryVar2] | 0x1000;
                  for (uint i = 0; i < _programHitBoxTable.size(); ++i) {
                        const ProgramHitBoxData *hitBox = &_programHitBoxTable[i];
                        if (hitBox->item == item) {
                              const int menuX = r.left + r.width() / 2;
                              const int menuY = kRoomHeight;
                              int act = handleActionMenuUnderCursor(hitBox->actions, menuX, menuY, hitBox->str);
                              if (act != 0) {
                                    restartKeyCharScriptOnAction(act, hitBox->item, 0);
                              }
                        }
                  }
                  break;
            }
      }
}

void ToucheEngine::handleMouseInput(int flag) {
      if (_disabledInputCounter != 0 || _flagsTable[618] != 0) {
            _inp_rightMouseButtonPressed = false;
      }
      if (getMousePos().y < _roomAreaRect.height()) {
            handleMouseClickOnRoom(flag);
      } else {
            handleMouseClickOnInventory(flag);
      }
}

void ToucheEngine::handleMouseClickOnRoom(int flag) {
      if (_hideInventoryTexts && _conversationReplyNum != -1 && !_conversationAreaCleared) {
            drawConversationString(_conversationReplyNum, 0xD6);
      }
      if (_disabledInputCounter == 0 && !_hideInventoryTexts && _flagsTable[618] == 0) {
            bool itemSelected = false;
            bool stringDrawn = false;
            if (_conversationReplyNum != -1 && !_conversationAreaCleared && _giveItemToCounter == 0) {
                  drawConversationString(_conversationReplyNum, 0xD6);
            }
            _conversationReplyNum = -1;
            Common::Point mousePos = getMousePos();
            int keyCharNewPosX = _flagsTable[614] + mousePos.x;
            int keyCharNewPosY = _flagsTable[615] + mousePos.y;
            for (uint i = 0; i < _programHitBoxTable.size(); ++i) {
                  if (_programHitBoxTable[i].item & 0x1000) {
                        break;
                  }
                  bool itemDisabled = false;
                  Common::Rect *hitBox = &_programHitBoxTable[i].hitBoxes[0];
                  int hitPosX = keyCharNewPosX;
                  int hitPosY = keyCharNewPosY;
                  int16 str = _programHitBoxTable[i].str;
                  KeyChar *keyChar;
                  switch (_programHitBoxTable[i].item & 0xF000) {
                  case 0x1000:
                        if (_inventoryItemsInfoTable[_programHitBoxTable[i].item & ~0x1000] != 0x20) {
                              hitPosY = 10000;
                        }
                        break;
                  case 0x2000:
                        itemDisabled = true;
                        break;
                  case 0x4000:
                        keyChar = &_keyCharsTable[_programHitBoxTable[i].item & ~0x4000];
                        hitPosY = 10000;
                        if (keyChar->num != 0) {
                              if ((keyChar->flags & 0x4000) == 0) {
                                    if (keyChar->strNum != 0) {
                                          str = _programHitBoxTable[i].defaultStr;
                                    }
                                    hitBox = &keyChar->prevBoundingRect;
                                    hitPosX = mousePos.x;
                                    hitPosY = mousePos.y;
                              }
                        }
                        break;
                  }
                  if (_giveItemToCounter == 0 && !_hideInventoryTexts) {
                        if (hitBox->contains(hitPosX, hitPosY)) {
                              if (!itemDisabled) {
                                    if (_inp_leftMouseButtonPressed && _currentCursorObject != 0) {
                                          _inp_leftMouseButtonPressed = false;
                                          itemSelected = true;
                                          _flagsTable[119] = _currentCursorObject;
                                          if (_currentCursorObject == 1) {
                                                _flagsTable[118] = _currentAmountOfMoney;
                                                _currentAmountOfMoney = 0;
                                          }
                                          _inventoryItemsInfoTable[_currentCursorObject] = 0x20;
                                          setCursor(0);
                                          if (_giveItemToCounter == 0) {
                                                if (!restartKeyCharScriptOnAction(-53, _programHitBoxTable[i].item, 0)) {
                                                      if (_flagsTable[119] == 1) {
                                                            _currentAmountOfMoney = _flagsTable[118];
                                                      } else {
                                                            addItemToInventory(_currentKeyCharNum, _flagsTable[119]);
                                                            drawInventory(_currentKeyCharNum, 1);
                                                      }
                                                      drawAmountOfMoneyInInventory();
                                                }
                                          } else {
                                                _flagsTable[117] = _programHitBoxTable[i].item - 1;
                                                _giveItemToCounter = -1;
                                          }
                                    }
                                    const char *strData = getString(str);
                                    int strPosY = mousePos.y - 22;
                                    if (_currentCursorObject != 0) {
                                          strPosY -= 8;
                                    }
                                    if (strPosY <= 0) {
                                          strPosY = 1;
                                    }
                                    int strWidth = getStringWidth(str);
                                    int strPosX = mousePos.x - strWidth / 2;
                                    strPosX = CLIP<int>(strPosX, 0, kScreenWidth - strWidth - 1);
                                    if (_talkTextSpeed != 0) {
                                          --_talkTextSpeed;
                                    }
                                    if (!stringDrawn && _talkTextSpeed == 0) {
                                          drawGameString(0xFF, strPosX + strWidth / 2, strPosY, strData);
                                    }
                                    stringDrawn = true;
                                    Common::Rect redrawRect(strPosX, strPosY, strPosX + strWidth, strPosY + kTextHeight);
                                    if (_programHitBoxTable[i].state & 0x8000) {
                                          redrawRect.extend(_programHitBoxTable[i].hitBoxes[1]);
                                    }
                                    addToDirtyRect(redrawRect);
                                    _programHitBoxTable[i].hitBoxes[1] = Common::Rect(strPosX, strPosY, strPosX + strWidth, strPosY + kTextHeight);
                                    _programHitBoxTable[i].state |= 0x8000;
                              }
                              if (_inp_leftMouseButtonPressed) {
                                    _inp_leftMouseButtonPressed = false;
                                    if (_currentCursorObject != 0) {
                                          setDefaultCursor(_currentKeyCharNum);
                                    } else {
                                          drawInventory(_currentKeyCharNum, 0);
                                          if (restartKeyCharScriptOnAction(-49, _programHitBoxTable[i].item, 0) == 0) {
                                                buildWalkPath(keyCharNewPosX, keyCharNewPosY, _currentKeyCharNum);
                                          }
                                    }
                              } else {
                                    if (_inp_rightMouseButtonPressed && !itemDisabled && !itemSelected) {
                                          int act = handleActionMenuUnderCursor(_programHitBoxTable[i].actions, mousePos.x, mousePos.y, str);
                                          _inp_rightMouseButtonPressed = false;
                                          int16 facing = (keyCharNewPosX <= _keyCharsTable[_currentKeyCharNum].xPos) ? 3 : 0;
                                          _keyCharsTable[_currentKeyCharNum].facingDirection = facing;
                                          if (act != 0) {
                                                restartKeyCharScriptOnAction(act, _programHitBoxTable[i].item, 0);
                                          } else {
                                                act = _programHitBoxTable[i].talk;
                                                if (act != 0) {
                                                      addToTalkTable(0, act, _currentKeyCharNum);
                                                }
                                          }
                                    }
                              }
                        } else if (_programHitBoxTable[i].state & 0x8000) {
                              _programHitBoxTable[i].state &= 0x7FFF;
                              addToDirtyRect(_programHitBoxTable[i].hitBoxes[1]);
                        }
                  }
            }
            if (_inp_leftMouseButtonPressed) {
                  _inp_leftMouseButtonPressed = false;
                  if (_currentCursorObject != 0) {
                        if (_currentCursorObject != 1) {
                              addItemToInventory(_currentKeyCharNum, _currentCursorObject);
                              drawInventory(_objectDescriptionNum, 1);
                        }
                        setCursor(0);
                  } else {
                        drawInventory(_currentKeyCharNum, 0);
                        buildWalkPath(keyCharNewPosX, keyCharNewPosY, _currentKeyCharNum);
                  }
            }
      } else {
            if (flag == 0) {
                  drawHitBoxes();
            }
      }
}

void ToucheEngine::handleMouseClickOnInventory(int flag) {
      if (flag == 0) {
            drawHitBoxes();
      }
      if (_hideInventoryTexts && _giveItemToCounter == 0) {
            if (!_conversationAreaCleared) {
                  Common::Point mousePos = getMousePos();
                  if (mousePos.x >= 40) {
                        if (mousePos.y >= 328) {
                              int replyNum = (mousePos.y - 328) / kTextHeight;
                              if (replyNum >= 4) {
                                    replyNum = 3;
                              }
                              if (replyNum != _conversationReplyNum) {
                                    if (_conversationReplyNum != -1) {
                                          drawConversationString(_conversationReplyNum, 0xD6);
                                    }
                                    drawConversationString(replyNum, 0xFF);
                                    _conversationReplyNum = replyNum;
                              }
                              if (_inp_leftMouseButtonPressed) {
                                    _inp_leftMouseButtonPressed = false;
                                    setupConversationScript(replyNum);
                                    _conversationReplyNum = -1;
                              }
                        }
                  } else {
                        if (_conversationReplyNum != -1 && !_conversationAreaCleared) {
                              drawConversationString(_conversationReplyNum, 0xD6);
                        }
                        _conversationReplyNum = -1;
                        if (_inp_leftMouseButtonPressed) {
                              int replyNum = mousePos.y - _roomAreaRect.height();
                              if (replyNum < 40) {
                                    scrollUpConversationChoice();
                              } else {
                                    scrollDownConversationChoice();
                              }
                              _inp_leftMouseButtonPressed = false;
                        }
                  }
            }
      } else if (_disabledInputCounter == 0 && !_hideInventoryTexts) {
            if (_inp_leftMouseButtonPressed) {
                  handleLeftMouseButtonClickOnInventory();
                  _inp_leftMouseButtonPressed = false;
            }
            if (_inp_rightMouseButtonPressed) {
                  handleRightMouseButtonClickOnInventory();
                  _inp_rightMouseButtonPressed = false;
            }
      }
}

void ToucheEngine::scrollScreenToPos(int num) {
      _screenOffset.x = _programPointsTable[num].x - kScreenWidth / 2;
      _screenOffset.y = _programPointsTable[num].y - kScreenHeight / 2;
}

void ToucheEngine::clearRoomArea() {
      int h = (_flagsTable[606] != 0) ? int(kScreenHeight) : _roomAreaRect.height();
      Graphics::fillRect(_offscreenBuffer, kScreenWidth, 0, 0, kScreenWidth, h, 0);
      updateEntireScreen();
}

void ToucheEngine::startNewMusic() {
      if (_newMusicNum != 0 && _newMusicNum != _currentMusicNum) {
            res_loadMusic(_newMusicNum);
            _currentMusicNum = _newMusicNum;
            _newMusicNum = 0;
      }
}

void ToucheEngine::startNewSound() {
      if (_newSoundNum != 0) {
            if (_newSoundDelay == 0) {
                  res_loadSound(_newSoundPriority, _newSoundNum);
                  _newSoundNum = 0;
            } else {
                  --_newSoundDelay;
            }
      }
}

void ToucheEngine::updateSpeech() {
      if (_speechPlaying) {
            if (!_mixer->isSoundHandleActive(_speechHandle)) {
                  _speechPlaying = false;
            }
      }
}

int ToucheEngine::handleActionMenuUnderCursor(const int16 *actions, int offs, int y, int str) {
      if (*actions == 0 || _menuRedrawCounter != 0) {
            return -26;
      }
      int i;
      int16 actionsTable[10];
      int16 *currentAction = actionsTable;
      int drawY = 0;
      for (i = 0; i < 8; ++i) {
            int act = *actions++;
            if (act == 0) {
                  break;
            }
            if (act != -49 && act != -53) {
                  *currentAction++ = act;
                  drawY = 1;
            }
      }
      if (drawY == 0) {
            return -26;
      }
      *currentAction = 0;
      int strW = getStringWidth(str);
      int h = 0;
      for (i = 0; i < 10; ++i) {
            if (actionsTable[i] == 0) {
                  break;
            }
            ++h;
            drawY = getStringWidth(actionsTable[i]);
            if (drawY > strW) {
                  strW = drawY;
            }
      }
      int cursorW = strW + 28;
      int cursorPosX = CLIP<int16>(offs - cursorW / 2, 0, kScreenWidth - cursorW);
      offs = cursorPosX + 14;
      h *= kTextHeight;
      int cursorH = h + 28;
      int cursorPosY = CLIP<int16>(y - 24, 0, kRoomHeight - cursorH);
      y = cursorPosY + 24;
      _cursorObjectRect = Common::Rect(cursorPosX, cursorPosY, cursorPosX + cursorW, cursorPosY + cursorH);
      addToDirtyRect(_cursorObjectRect);

      Graphics::fillRect(_offscreenBuffer, kScreenWidth, cursorPosX + 14, cursorPosY + 24, cursorW - 28, cursorH - 40, 0xF8);
      drawActionsPanel(cursorPosX, cursorPosY, cursorW, cursorH);

      const char *strData = getString(str);
      drawGameString(0xF8FF, offs + strW / 2, cursorPosY + 4, strData);
      for (i = 0; i < 10; ++i) {
            if (actionsTable[i] == 0) {
                  break;
            }
            drawString(0xF8F9, offs, y + i * kTextHeight, actionsTable[i]);
      }
      updateScreenArea(cursorPosX, cursorPosY, cursorW, cursorH);

      _menuRedrawCounter = 2;
      Common::Rect rect(0, y, kScreenWidth, y + h);
      i = -1;
      while (_inp_rightMouseButtonPressed && _flagsTable[611] == 0) {
            Common::Point mousePos = getMousePos();
            if (rect.contains(mousePos)) {
                  int c = (mousePos.y - y) / kTextHeight;
                  if (c != i) {
                        if (i >= 0) {
                              drawY = y + i * kTextHeight;
                              drawString(0xF8F9, offs, drawY, actionsTable[i]);
                              updateScreenArea(offs, drawY, strW, kTextHeight);
                        }
                        i = c;
                        drawY = y + i * kTextHeight;
                        drawString(0xF8FF, offs, drawY, actionsTable[i]);
                        updateScreenArea(offs, drawY, strW, kTextHeight);
                  }
            } else if (i >= 0) {
                  drawY = y + i * kTextHeight;
                  drawString(0xF8F9, offs, drawY, actionsTable[i]);
                  updateScreenArea(offs, drawY, strW, kTextHeight);
                  i = -1;
            }
            processEvents(false);
            _system->updateScreen();
            _system->delayMillis(10);
      }

      const int action = (i >= 0) ? actionsTable[i] : -26;
      return action;
}

void ToucheEngine::redrawBackground() {
      for (uint i = 0; i < _programBackgroundTable.size(); ++i) {
            Area area = _programBackgroundTable[i].area;
            if (area.r.top != 20000) {
                  area.r.translate(-_flagsTable[614], -_flagsTable[615]);
                  if (_programBackgroundTable[i].type == 4) {
                        int16 dx = _programBackgroundTable[i].offset - kScreenWidth / 2 - _flagsTable[614];
                        dx *= _programBackgroundTable[i].scaleMul;
                        dx /= _programBackgroundTable[i].scaleDiv;
                        area.r.translate(dx, 0);
                  }
                  if (area.clip(_roomAreaRect)) {
                        Graphics::copyRect(_offscreenBuffer, kScreenWidth, area.r.left, area.r.top,
                          _backdropBuffer, _currentBitmapWidth, area.srcX, area.srcY,
                          area.r.width(), area.r.height(),
                          Graphics::kTransparent);
                        addToDirtyRect(area.r);
                  }
            }
      }
}

void ToucheEngine::addRoomArea(int num, int flag) {
      debugC(9, kDebugEngine, "ToucheEngine::addRoomArea(%d, %d)", num, flag);
      if (_flagsTable[flag] == 20000) {
            Area area = _programBackgroundTable[num].area;
            area.r.translate(-_flagsTable[614], -_flagsTable[615]);
            addToDirtyRect(area.r);
      }
      _programBackgroundTable[num].area.r.moveTo(_flagsTable[flag], _flagsTable[flag + 1]);
}

void ToucheEngine::updateRoomAreas(int num, int flags) {
      debugC(9, kDebugEngine, "ToucheEngine::updateRoomAreas(%d, %d)", num, flags);
      if (flags != -1) {
            int16 count = _updatedRoomAreasTable[0];
            ++_updatedRoomAreasTable[0];
            if (count == 199) {
                  _updatedRoomAreasTable[0] = 2;
                  count = 1;
            }
            _updatedRoomAreasTable[count] = (uint8)num;
      }
      for (uint i = 0; i < _programAreaTable.size(); ++i) {
            if (_programAreaTable[i].id == num) {
                  Area area = _programAreaTable[i].area;
                  if (i == 14 && _currentRoomNum == 8 && area.r.left == 715) {
                        // Workaround for bug #1751170. area[14].r.left (update rect) should
                        // be equal to area[7].r.left (redraw rect) but it's one off, which
                        // leads to a glitch when that room area needs to be redrawn.
                        area.r.left = 714;
                  }
                  Graphics::copyRect(_backdropBuffer, _currentBitmapWidth, area.r.left, area.r.top,
                    _backdropBuffer, _currentBitmapWidth, area.srcX, area.srcY,
                    area.r.width(), area.r.height(),
                    Graphics::kTransparent);
                  if (flags != 0) {
                        debug(0, "updateRoomAreas(num=%d index=%d)", num, i);
                        redrawRoomRegion(i, true);
//                      area.r.translate(-_flagsTable[614], -_flagsTable[615]);
//                      addToDirtyRect(area.r);
                  }
            }
      }
}

void ToucheEngine::setRoomAreaState(int num, uint16 state) {
      debugC(9, kDebugEngine, "ToucheEngine::setRoomAreaState(%d, %d)", num, state);
      for (uint i = 0; i < _programAreaTable.size(); ++i) {
            if (_programAreaTable[i].id == num) {
                  _programAreaTable[i].state = state;
            }
      }
}

void ToucheEngine::findAndRedrawRoomRegion(int num) {
      debugC(9, kDebugEngine, "ToucheEngine::findAndRedrawRoomRegion(%d)", num);
      for (uint i = 0; i < _programAreaTable.size(); ++i) {
            if (_programAreaTable[i].id == num) {
                  redrawRoomRegion(i, false);
                  break;
            }
      }
}

void ToucheEngine::updateRoomRegions() {
      debugC(9, kDebugEngine, "ToucheEngine::updateRoomRegions()");
      if (_flagsTable[269] == 0) {
            uint i = 0;
            while (i < _programAreaTable.size() && _programAreaTable[i].id != 0) {
                  switch (_programAreaTable[i].state) {
                  case 0:
                        ++i;
                        break;
                  case 1:
                        redrawRoomRegion(i + _programAreaTable[i].animNext, true);
                        ++_programAreaTable[i].animNext;
                        if (_programAreaTable[i].animNext >= _programAreaTable[i].animCount) {
                              _programAreaTable[i].animNext = 0;
                        }
                        i += _programAreaTable[i].animCount;
                        break;
                  case 3:
                        redrawRoomRegion(i + _programAreaTable[i].animNext, true);
                        ++_programAreaTable[i].animNext;
                        if (_programAreaTable[i].animNext >= _programAreaTable[i].animCount) {
                              _programAreaTable[i].animNext = 0;
                        }
                        i += _programAreaTable[i].animCount + 1;
                        break;
                  }
            }
      }
}

void ToucheEngine::redrawRoomRegion(int num, bool markForRedraw) {
      debugC(9, kDebugEngine, "ToucheEngine::redrawRoomRegion(%d)", num);
      Area area = _programAreaTable[num].area;
      area.r.translate(-_flagsTable[614], -_flagsTable[615]);
      if (area.clip(_roomAreaRect)) {
            Graphics::copyRect(_offscreenBuffer, kScreenWidth, area.r.left, area.r.top,
              _backdropBuffer, _currentBitmapWidth, area.srcX, area.srcY,
              area.r.width(), area.r.height(),
              Graphics::kTransparent);
            if (markForRedraw) {
                  addToDirtyRect(area.r);
            }
      }
}

void ToucheEngine::initInventoryObjectsTable() {
      for (int i = 0; i < NUM_INVENTORY_ITEMS; ++i) {
            _inventoryItemsInfoTable[i] = 0x20;
      }
}

void ToucheEngine::initInventoryLists() {
      memset(_inventoryList1, 0, sizeof(_inventoryList1));
      _inventoryList1[100] = -1;
      _inventoryStateTable[0].displayOffset = 0;
      _inventoryStateTable[0].lastItem = 100;
      _inventoryStateTable[0].itemsPerLine = 6;
      _inventoryStateTable[0].itemsList = _inventoryList1;

      memset(_inventoryList2, 0, sizeof(_inventoryList2));
      _inventoryList2[100] = -1;
      _inventoryStateTable[1].displayOffset = 0;
      _inventoryStateTable[1].lastItem = 100;
      _inventoryStateTable[1].itemsPerLine = 6;
      _inventoryStateTable[1].itemsList = _inventoryList2;

      memset(_inventoryList3, 0, sizeof(_inventoryList3));
      _inventoryList3[6] = -1;
      _inventoryStateTable[2].displayOffset = 0;
      _inventoryStateTable[2].lastItem = 6;
      _inventoryStateTable[2].itemsPerLine = 6;
      _inventoryStateTable[2].itemsList = _inventoryList3;
}

void ToucheEngine::setupInventoryAreas() {
      _inventoryAreasTable[kInventoryCharacter]    = Common::Rect(  0, 354,  50, 400);
      _inventoryAreasTable[kInventoryMoneyDisplay] = Common::Rect( 66, 354, 124, 380);
      _inventoryAreasTable[kInventoryGoldCoins]    = Common::Rect( 74, 380, 116, 398);
      _inventoryAreasTable[kInventorySilverCoins]  = Common::Rect(116, 380, 158, 398);
      _inventoryAreasTable[kInventoryMoney]        = Common::Rect(144, 354, 198, 380);
      _inventoryAreasTable[kInventoryScroller1]    = Common::Rect(202, 354, 238, 396);
      _inventoryAreasTable[kInventoryObject1]      = Common::Rect(242, 354, 300, 396);
      _inventoryAreasTable[kInventoryObject2]      = Common::Rect(300, 354, 358, 396);
      _inventoryAreasTable[kInventoryObject3]      = Common::Rect(358, 354, 416, 396);
      _inventoryAreasTable[kInventoryObject4]      = Common::Rect(416, 354, 474, 396);
      _inventoryAreasTable[kInventoryObject5]      = Common::Rect(474, 354, 532, 396);
      _inventoryAreasTable[kInventoryObject6]      = Common::Rect(532, 354, 590, 396);
      _inventoryAreasTable[kInventoryScroller2]    = Common::Rect(594, 354, 640, 395);
}

void ToucheEngine::drawInventory(int index, int flag) {
      if (_flagsTable[606] == 0) {
            if (index > 1) {
                  index = 1;
            }
            if (_objectDescriptionNum == index && flag == 0) {
                  return;
            }
            _inventoryVar1 = _inventoryStateTable[index].itemsList;
            _inventoryVar2 = &_inventoryStateTable[index].displayOffset;
            _objectDescriptionNum = index;
            uint8 *dst = _offscreenBuffer + kScreenWidth * kRoomHeight;
            res_loadSpriteImage(index + 12, dst);
            res_loadImageHelper(dst, _currentImageWidth, _currentImageHeight);
            int firstObjNum = _inventoryVar2[0];
            for (int i = 0, x = 242; i < 6; ++i, x += 58) {
                  int num = _inventoryVar1[firstObjNum + i];
                  if (num == -1) {
                        break;
                  }
                  if (num != 0) {
                        drawIcon(x + 3, 353, num);
                  }
            }
            drawAmountOfMoneyInInventory();
            updateScreenArea(0, kRoomHeight, kScreenWidth, kScreenHeight - kRoomHeight);
      }
}

void ToucheEngine::drawAmountOfMoneyInInventory() {
      if (_flagsTable[606] == 0 && !_hideInventoryTexts) {
            char text[10];
            sprintf(text, "%d", _keyCharsTable[0].money);
            Graphics::fillRect(_offscreenBuffer, kScreenWidth, 74, 354, 40, 16, 0xD2);
            drawGameString(217, 94, 355, text);
            updateScreenArea(74, 354, 40, 16);
            Graphics::fillRect(_offscreenBuffer, kScreenWidth, 150, 353, 40, 41, 0xD2);
            if (_currentAmountOfMoney != 0) {
                  drawIcon(141, 348, 1);
                  sprintf(text, "%d", _currentAmountOfMoney);
                  drawGameString(217, 170, 378, text);
            }
            updateScreenArea(150, 353, 40, 41);
      }
}

void ToucheEngine::packInventoryItems(int index) {
      int16 *p = _inventoryStateTable[index].itemsList;
      for (int i = 0; *p != -1; ++i, ++p) {
            if (p[0] == 0 && p[1] != -1) {
                  p[0] = p[1];
                  p[1] = 0;
            }
      }
}

void ToucheEngine::appendItemToInventoryList(int index) {
      int last = _inventoryStateTable[index].lastItem - 1;
      int16 *p = _inventoryStateTable[index].itemsList;
      if (p[last] != 0) {
            warning("Inventory %d Full", index);
      } else {
            for (int i = last; i > 0; --i) {
                  p[i] = p[i - 1];
            }
            *p = 0;
      }
}

void ToucheEngine::addItemToInventory(int inventory, int16 item) {
      if (item == 0) {
            packInventoryItems(inventory);
      } else if (item == 1)  {
            _currentAmountOfMoney += _flagsTable[118];
            drawAmountOfMoneyInInventory();
      } else {
            appendItemToInventoryList(inventory);
            assert(inventory >= 0 && inventory < 3);
            int16 *p = _inventoryStateTable[inventory].itemsList;
            for (int i = 0; *p != -1; ++i, ++p) {
                  if (*p == 0) {
                        *p = item;
                        _inventoryItemsInfoTable[item] = inventory | 0x10;
                        packInventoryItems(0);
                        packInventoryItems(1);
                        break;
                  }
            }
      }
}

void ToucheEngine::removeItemFromInventory(int inventory, int16 item) {
      if (item == 1) {
            _currentAmountOfMoney = 0;
            drawAmountOfMoneyInInventory();
      } else {
            assert(inventory >= 0 && inventory < 3);
            int16 *p = _inventoryStateTable[inventory].itemsList;
            for (int i = 0; *p != -1; ++i, ++p) {
                  if (*p == item) {
                        *p = 0;
                        packInventoryItems(0);
                        packInventoryItems(1);
                        break;
                  }
            }
      }
}

void ToucheEngine::resetTalkingVars() {
      _talkListCurrent = 0;
      _talkListEnd = 0;
      _keyCharTalkCounter = 0;
      _talkTextRectDefined = false;
      _talkTextDisplayed = false;
      _skipTalkText = false;
      _talkTextInitialized = false;
      if (_speechPlaying) {
            res_stopSpeech();
      }
}

int ToucheEngine::updateKeyCharTalk(int skipFlag) {
      if (skipFlag != 0) {
            if (_speechPlaying) {
                  res_stopSpeech();
            }
            if (_talkListEnd != _talkListCurrent) {
                  _keyCharTalkCounter = 0;
                  _talkTextInitialized = false;
                  if (skipFlag == 2) {
                        _skipTalkText = true;
                  } else {
                        _skipTalkText = false;
                  }
            }
            return 0;
      }
      if (_talkListEnd == _talkListCurrent) {
            return 0;
      }
      int talkingKeyChar = _talkTable[_talkListCurrent].talkingKeyChar;
      int otherKeyChar = _talkTable[_talkListCurrent].otherKeyChar;
      KeyChar *key = &_keyCharsTable[talkingKeyChar];
      int x = key->xPos - _flagsTable[614];
      int y = key->yPos - _flagsTable[615] - (key->zPos / 2 + 16);
      int stringNum = _talkTable[_talkListCurrent].num;
      const char *stringData = getString(stringNum);
      int textWidth = getStringWidth(stringNum);
      if (!_talkTextInitialized && !_skipTalkText) {
            _keyCharTalkCounter = textWidth / 32 + 20;
            setKeyCharTalkingFrame(talkingKeyChar);
            res_loadSpeechSegment(stringNum);
            _talkTextInitialized = true;
      }
      if (_keyCharTalkCounter) {
            --_keyCharTalkCounter;
      }
      _currentObjectNum = talkingKeyChar;
      if (_speechPlaying) {
            _flagsTable[297] = 0;
            _keyCharTalkCounter = 1;
            if (_talkTextMode == kTalkModeVoiceOnly) {
                  return 1;
            }
      }
      if (_keyCharTalkCounter != 0) {
            _talkTextDisplayed = true;
            int textHeight = kTextHeight;
            y -= kTextHeight;
            if (y < 0) {
                  y = 1;
            } else if (y > kRoomHeight) {
                  y = kRoomHeight - 16;
            }
            if (textWidth > 200) {
                  textWidth = 200;
                  stringData = formatTalkText(&y, &textHeight, stringData);
            }
            x -= textWidth / 2;
            if (x < 0) {
                  x = 0;
            }
            if (x + textWidth >= kScreenWidth) {
                  x = kScreenWidth - textWidth - 1;
            }
            drawGameString(key->textColor, x + textWidth / 2, y, stringData);
            _talkTextSpeed = 6;
            _talkTextRect = Common::Rect(x, y, x + textWidth, y + textHeight);
            if (_talkTextRectDefined) {
                  _talkTextRect.extend(_talkTextRect2);
            }
            addToDirtyRect(_talkTextRect);
            _talkTextRect2 = Common::Rect(x, y, x + textWidth, y + textHeight);
            _talkTextRectDefined = true;
            _flagsTable[297] = 0;
      } else {
            updateTalkFrames(_currentObjectNum);
            _currentObjectNum = -1;
            if (_talkTextDisplayed) {
                  addToDirtyRect(_talkTextRect2);
            }
            _talkTextInitialized = false;
            _skipTalkText = false;
            _talkTextRectDefined = false;
            ++_talkListCurrent;
            if (_talkListCurrent == 16) {
                  _talkListCurrent = 0;
            }
            if (otherKeyChar != -1) {
                  _keyCharsTable[otherKeyChar].flags &= ~kScriptPaused;
            }
      }
      return 1;
}

const char *ToucheEngine::formatTalkText(int *y, int *h, const char *text) {
      static char talkTextBuffer[200];
      int newLineWidth = 0;
      int lineWidth = 0;
      char *textBuffer = talkTextBuffer;
      char *textLine = textBuffer;
      while (*text) {
            char chr = *text++;
            int chrWidth = Graphics::getCharWidth16(chr);
            lineWidth += chrWidth;
            if (chr == ' ') {
                  if (lineWidth + newLineWidth >= 200) {
                        *textLine = '\\';
                        newLineWidth = lineWidth - chrWidth;
                        *y -= kTextHeight;
                        *h += kTextHeight;
                        lineWidth = chrWidth;
                  } else {
                        newLineWidth += lineWidth;
                        lineWidth = chrWidth;
                  }
                  *textBuffer = ' ';
                  textLine = textBuffer;
                  textBuffer++;
            } else {
                  *textBuffer++ = chr;
            }
      }
      if (newLineWidth + lineWidth >= 200) {
            *textLine = '\\';
            *y -= kTextHeight;
            *h += kTextHeight;
      }
      *textBuffer = '\0';
      if (*y < 0) {
            *y = 1;
      }
      return talkTextBuffer;
}

void ToucheEngine::addToTalkTable(int talkingKeyChar, int num, int otherKeyChar) {
      if (_talkListEnd != _talkListCurrent) {
            if (_talkTableLastTalkingKeyChar == talkingKeyChar &&
                  _talkTableLastOtherKeyChar == otherKeyChar &&
                  _talkTableLastStringNum == num) {
                  return;
            }
      }
      _talkTableLastTalkingKeyChar = talkingKeyChar;
      _talkTableLastOtherKeyChar = otherKeyChar;
      _talkTableLastStringNum = num;

      removeFromTalkTable(otherKeyChar);

      assert(_talkListEnd < NUM_TALK_ENTRIES);
      TalkEntry *talkEntry = &_talkTable[_talkListEnd];
      talkEntry->talkingKeyChar = talkingKeyChar;
      talkEntry->otherKeyChar = otherKeyChar;
      talkEntry->num = num;

      ++_talkListEnd;
      if (_talkListEnd == NUM_TALK_ENTRIES) {
            _talkListEnd = 0;
      }
}

void ToucheEngine::removeFromTalkTable(int keyChar) {
      debugC(9, kDebugEngine, "ToucheEngine::removeFromTalkTable(%d)", keyChar);
      int i = _talkListCurrent;
      while (i != _talkListEnd) {
            if (_talkTable[i].otherKeyChar == keyChar) {
                  _talkTable[i].otherKeyChar = -1;
            }
            ++i;
            i %= NUM_TALK_ENTRIES;
      }
}

void ToucheEngine::addConversationChoice(int16 num) {
      debugC(9, kDebugEngine, "ToucheEngine::addConversationChoice(%d)", num);
      _conversationChoicesUpdated = true;
      int16 msg = _programConversationTable[_currentConversation + num].msg;
      for (int i = 0; i < NUM_CONVERSATION_CHOICES; ++i) {
            if (_conversationChoicesTable[i].msg == msg) {
                  break;
            }
            if (_conversationChoicesTable[i].msg == 0) {
                  _conversationChoicesTable[i].msg = msg;
                  _conversationChoicesTable[i].num = num;
                  break;
            }
      }
}

void ToucheEngine::removeConversationChoice(int16 num) {
      debugC(9, kDebugEngine, "ToucheEngine::removeConversationChoice(%d)", num);
      for (int i = 0; i < NUM_CONVERSATION_CHOICES; ++i) {
            if (_conversationChoicesTable[i].num == num) {
                  _conversationChoicesUpdated = true;
                  for (; i < NUM_CONVERSATION_CHOICES - 1; ++i) {
                        _conversationChoicesTable[i].num = _conversationChoicesTable[i + 1].num;
                        _conversationChoicesTable[i].msg = _conversationChoicesTable[i + 1].msg;
                  }
                  break;
            }
      }
}

void ToucheEngine::runConversationScript(uint16 offset) {
      debugC(9, kDebugEngine, "ToucheEngine::runConversationScript() offset=0x%X", offset);
      _script.dataOffset = offset;
      _script.quitFlag = 0;
      runCurrentKeyCharScript(2);
}

void ToucheEngine::findConversationByNum(int16 num) {
      debugC(9, kDebugEngine, "ToucheEngine::findConversationByNum(%d)", num);
      for (uint i = 0; i < _programConversationTable.size(); ++i) {
            if (_programConversationTable[i].num == num) {
                  clearConversationChoices();
                  _currentConversation = i;
                  runConversationScript(_programConversationTable[i].offset);
                  break;
            }
      }
}

void ToucheEngine::clearConversationChoices() {
      debugC(9, kDebugEngine, "ToucheEngine::clearConversationChoices()");
      _conversationChoicesUpdated = true;
      for (int i = 0; i < NUM_CONVERSATION_CHOICES; ++i) {
            _conversationChoicesTable[i].num = 0;
            _conversationChoicesTable[i].msg = 0;
      }
      _scrollConversationChoiceOffset = 0;
}

void ToucheEngine::scrollDownConversationChoice() {
      if (_conversationChoicesTable[4 + _scrollConversationChoiceOffset].msg != 0) {
            ++_scrollConversationChoiceOffset;
            drawCharacterConversation();
      }
}

void ToucheEngine::scrollUpConversationChoice() {
      if (_scrollConversationChoiceOffset != 0) {
            --_scrollConversationChoiceOffset;
            drawCharacterConversation();
      }
}

void ToucheEngine::drawCharacterConversation() {
      _conversationChoicesUpdated = false;
      if (!_disableConversationScript) {
            if (_conversationChoicesTable[0].msg == 0) {
                  _conversationEnded = true;
                  return;
            }
            if (_conversationChoicesTable[1].msg == 0) {
                  setupConversationScript(0);
                  return;
            }
      }
      drawConversationPanel();
      for (int i = 0; i < 4; ++i) {
            drawString(214, 42, 328 + i * kTextHeight, _conversationChoicesTable[_scrollConversationChoiceOffset + i].msg);
      }
      updateScreenArea(0, 320, kScreenWidth, kScreenHeight - 320);
      _conversationAreaCleared = false;
}

void ToucheEngine::drawConversationString(int num, uint16 color) {
      const int y = 328 + num * kTextHeight;
      drawString(color, 42, y, _conversationChoicesTable[num + _scrollConversationChoiceOffset].msg);
      updateScreenArea(0, y, kScreenWidth, kTextHeight);
}

void ToucheEngine::clearConversationArea() {
      drawConversationPanel();
      updateScreenArea(0, 320, kScreenWidth, kScreenHeight - 320);
      _conversationAreaCleared = true;
}

void ToucheEngine::setupConversationScript(int num) {
      debugC(9, kDebugEngine, "ToucheEngine::setupConversationScript(%d)", num);
      if (num < 5 && _conversationChoicesTable[num].msg != 0) {
            num = _conversationChoicesTable[_scrollConversationChoiceOffset + num].num;
            KeyChar *key = &_keyCharsTable[_currentKeyCharNum];
            key->scriptDataOffset = _programConversationTable[_currentConversation + num].offset;
            key->scriptStackPtr = &key->scriptStackTable[39];
            _scrollConversationChoiceOffset = 0;
            removeConversationChoice(num);
            clearConversationArea();
      }
}

void ToucheEngine::handleConversation() {
      if (_conversationNum != 0) {
            findConversationByNum(_conversationNum);
            _conversationAreaCleared = false;
            drawCharacterConversation();
            _roomAreaRect.setHeight(320);
            _hideInventoryTexts = true;
            _conversationEnded = false;
            _conversationNum = 0;
      } else if (_hideInventoryTexts && _conversationAreaCleared) {
            if (_keyCharsTable[_currentKeyCharNum].scriptDataOffset == 0) {
                  drawCharacterConversation();
            }
      } else if (!_conversationAreaCleared && _conversationChoicesUpdated) {
            drawCharacterConversation();
      }
}

static int getDirection(int x1, int y1, int z1, int x2, int y2, int z2) {
      int ret = -1;
      x2 -= x1;
      y2 -= y1;
      z2 -= z1;
      if (x2 == 0 && y2 == 0 && z2 == 0) {
            ret = -2;
      } else {
            if (ABS(x2) >= ABS(z2)) {
                  if (ABS(x2) > ABS(y2)) {
                        if (x2 > 0) {
                              ret = 0;
                        } else {
                              ret = 3;
                        }
                  } else {
                        if (y2 > 0) {
                              ret = 1;
                        } else {
                              ret = 2;
                        }
                  }
            } else {
                  if (z2 != 0) {
                        if (z2 > 0) {
                              ret = 1;
                        } else {
                              ret = 2;
                        }
                  } else {
                        if (y2 > 0) {
                              ret = 1;
                        } else {
                              ret = 2;
                        }
                  }
            }
      }
      return ret;
}

void ToucheEngine::buildWalkPointsList(int keyChar) {
      debugC(9, kDebugEngine, "ToucheEngine::buildWalkPointsList(%d)", keyChar);
      assert(keyChar >= 0 && keyChar < NUM_KEYCHARS);
      KeyChar *key = &_keyCharsTable[keyChar];
      uint16 curPos, pos1, pos2;
      if (key->pointsDataNum & 0x8000) {
            const ProgramWalkData *pwd = &_programWalkTable[(key->pointsDataNum & 0x7FFF)];
            if (_programPointsTable[pwd->point1].order < _programPointsTable[pwd->point2].order) {
                  curPos = pwd->point1;
            } else {
                  curPos = pwd->point2;
            }
      } else {
            curPos = key->pointsDataNum;
      }

      int16 posNum = _programPointsTable[curPos].order;
      if (posNum == 32000) {
            return;
      }
      key->walkPointsList[0] = curPos;
      int16 walkPointsCount = 1;
      do {
            for (uint i = 0; i < _programWalkTable.size(); ++i) {
                  if ((_programWalkTable[i].point1 & 0x4000) == 0) {
                        pos1 = _programWalkTable[i].point1;
                        pos2 = _programWalkTable[i].point2;
                        if (pos1 == curPos && posNum > _programPointsTable[pos2].order) {
                              curPos = pos2;
                              assert(walkPointsCount < 40);
                              key->walkPointsList[walkPointsCount] = curPos;
                              ++walkPointsCount;
                              posNum = _programPointsTable[pos2].order;
                              break;
                        }
                        if (pos2 == curPos && posNum > _programPointsTable[pos1].order) {
                              curPos = pos1;
                              assert(walkPointsCount < 40);
                              key->walkPointsList[walkPointsCount] = curPos;
                              ++walkPointsCount;
                              posNum = _programPointsTable[pos1].order;
                              break;
                        }
                  }
            }
      } while (_programPointsTable[curPos].order != 0);
      assert(walkPointsCount < 40);
      key->walkPointsList[walkPointsCount] = -1;

      key->xPosPrev = _programPointsTable[curPos].x;
      key->yPosPrev = _programPointsTable[curPos].y;
      key->zPosPrev = _programPointsTable[curPos].z;
      key->prevWalkDataNum = findWalkDataNum(curPos, -1);
      key->walkPointsListIndex = 0;
      if (key->walkDataNum == -1) {
            return;
      }

      pos1 = _programWalkTable[key->walkDataNum].point1;
      pos2 = _programWalkTable[key->walkDataNum].point2;
      if (key->pointsDataNum == pos1) {
            if (key->walkPointsList[1] == pos2) {
                  ++key->walkPointsListIndex;
            }
            return;
      }
      if (key->pointsDataNum == pos2) {
            if (key->walkPointsList[1] == pos1) {
                  ++key->walkPointsListIndex;
            }
            return;
      }
}

int ToucheEngine::findWalkDataNum(int pointNum1, int pointNum2) {
      debugC(9, kDebugEngine, "ToucheEngine::findWalkDataNum(%d, %d)", pointNum1, pointNum2);
      if (pointNum1 != pointNum2) {
            for (uint i = 0; i < _programWalkTable.size(); ++i) {
                  int p1 = _programWalkTable[i].point1 & 0xFFF;
                  int p2 = _programWalkTable[i].point2 & 0xFFF;
                  if (p1 == pointNum1) {
                        if (p2 == pointNum2 || pointNum2 == 10000) {
                              return i;
                        }
                  } else if (p2 == pointNum1) {
                        if (p1 == pointNum2 || pointNum2 == 10000) {
                              return i;
                        }
                  }
            }
      }
      return -1;
}

void ToucheEngine::changeWalkPath(int num1, int num2, int16 val) {
      debugC(9, kDebugEngine, "ToucheEngine::changeWalkPath(%d, %d)", num1, num2);
      int num = findWalkDataNum(num1, num2);
      if (num != -1) {
            _programWalkTable[num].area1 = val;
      }
}

void ToucheEngine::adjustKeyCharPosToWalkBox(KeyChar *key, int moveType) {
      const ProgramWalkData *pwd = &_programWalkTable[key->walkDataNum];

      const ProgramPointData *pts1 = &_programPointsTable[pwd->point1];
      int16 x1 = pts1->x;
      int16 y1 = pts1->y;
      int16 z1 = pts1->z;

      const ProgramPointData *pts2 = &_programPointsTable[pwd->point2];
      int16 x2 = pts2->x;
      int16 y2 = pts2->y;
      int16 z2 = pts2->z;

      int16 kx = key->xPos;
      int16 ky = key->yPos;
      int16 kz = key->zPos;

      int16 dx = x2 - x1;
      int16 dy = y2 - y1;
      int16 dz = z2 - z1;

      switch (moveType) {
      case 0:
            kx -= x1;
            if (dx != 0) {
                  key->yPos = dy * kx / dx + y1;
                  key->zPos = dz * kx / dx + z1;
            }
            break;
      case 1:
            ky -= y1;
            if (dy != 0) {
                  key->xPos = dx * ky / dy + x1;
                  key->zPos = dz * ky / dy + z1;
            }
            break;
      case 2:
            kz -= z1;
            if (dz != 0) {
                  key->xPos = dx * kz / dz + x1;
                  key->yPos = dy * kz / dz + y1;
            }
            break;
      }
}

void ToucheEngine::lockWalkPath(int num1, int num2) {
      debugC(9, kDebugEngine, "ToucheEngine::lockWalkPath(%d, %d)", num1, num2);
      const int num = findWalkDataNum(num1, num2);
      if (num != -1) {
            _programWalkTable[num].point1 |= 0x4000;
            _programWalkTable[num].point2 |= 0x4000;
      }
}

void ToucheEngine::unlockWalkPath(int num1, int num2) {
      debugC(9, kDebugEngine, "ToucheEngine::unlockWalkPath(%d, %d)", num1, num2);
      const int num = findWalkDataNum(num1, num2);
      if (num != -1) {
            _programWalkTable[num].point1 &= 0xFFF;
            _programWalkTable[num].point2 &= 0xFFF;
      }
}

void ToucheEngine::resetPointsData(int num) {
      debugC(9, kDebugEngine, "ToucheEngine::resetPointsData(%d)", num);
      for (uint i = 1; i < _programPointsTable.size(); ++i) {
            _programPointsTable[i].order = num;
      }
}

bool ToucheEngine::sortPointsData(int num1, int num2) {
      debugC(9, kDebugEngine, "ToucheEngine::sortPointsData(%d, %d)", num1, num2);
      resetPointsData(32000);
      if (num1 == -1) {
            if (num2 == -1) {
                  return false;
            }
            _programPointsTable[num2].order = 0;
      } else {
            const int md1 = _programWalkTable[num1].point1;
            _programPointsTable[md1].order = 0;
            const int md2 = _programWalkTable[num1].point2;
            _programPointsTable[md2].order = 0;
      }
      bool quit = false;
      int order = 1;
      while (!quit) {
            quit = true;
            for (uint i = 0; i < _programWalkTable.size(); ++i) {
                  const int md1 = _programWalkTable[i].point1;
                  const int md2 = _programWalkTable[i].point2;
                  if ((md1 & 0x4000) == 0) {
                        assert((md2 & 0x4000) == 0);
                        if (_programPointsTable[md1].order == order - 1 && _programPointsTable[md2].order > order) {
                              _programPointsTable[md2].order = order;
                              quit = false;
                        }
                        if (_programPointsTable[md2].order == order - 1 && _programPointsTable[md1].order > order) {
                              _programPointsTable[md1].order = order;
                              quit = false;
                        }
                  }
            }
            ++order;
      }
      return true;
}

void ToucheEngine::updateKeyCharWalkPath(KeyChar *key, int16 dx, int16 dy, int16 dz) {
      debugC(9, kDebugEngine, "ToucheEngine::updateKeyCharWalkPath(key=%d, dx=%d, dy=%d, dz=%d)", (int)(key - _keyCharsTable), dx, dy, dz);
      if (key->walkDataNum == -1) {
            return;
      }
      int16 kx = key->xPos;
      int16 ky = key->yPos;
      int16 kz = key->zPos;
      if (kz != 160) {
            if (dx != 0) {
                  dx = dx * kz / 160;
                  if (dx == 0) {
                        dx = 1;
                  }
            }
            if (dy != 0) {
                  dy = dy * kz / 160;
                  if (dy == 0) {
                        dy = 1;
                  }
            }
            if (dz != 0) {
                  dz = dz * kz / 160;
                  if (dz == 0) {
                        dz = 1;
                  }
            }
      }

      int16 curDirection = key->facingDirection;
      if (key->currentAnim > 1) {
            if (dx != 0 || dy != 0 || dz != 0) {
                  if (curDirection == 3) {
                        key->xPos -= dx;
                  } else {
                        key->xPos += dx;
                  }
                  key->xPosPrev = key->xPos;
            }
            return;
      }

      int16 xpos, ypos, zpos, walkPoint1, walkPoint2, newDirection, incDx, incDy, incDz;
      while (1) {
            walkPoint1 = key->walkPointsList[key->walkPointsListIndex];
            walkPoint2 = key->walkPointsList[key->walkPointsListIndex + 1];
            key->currentWalkBox = walkPoint1;
            if (walkPoint1 == -1) {
                  xpos = key->xPosPrev;
                  ypos = key->yPosPrev;
                  zpos = key->zPosPrev;
                  if (key->prevWalkDataNum != -1) {
                        key->walkDataNum = key->prevWalkDataNum;
                        key->prevWalkDataNum = -1;
                  }
            } else {
                  xpos = _programPointsTable[walkPoint1].x;
                  ypos = _programPointsTable[walkPoint1].y;
                  zpos = _programPointsTable[walkPoint1].z;
            }
            newDirection = getDirection(kx, ky, kz, xpos, ypos, zpos);
            if (newDirection < 0) {
                  newDirection = curDirection;
            }
            if (newDirection != curDirection) {
                  key->currentAnimCounter = 0;
                  key->facingDirection = newDirection;
                  return;
            }
            incDx = xpos - kx;
            incDy = ypos - ky;
            incDz = zpos - kz;
            if (incDz != 0 || incDy != 0 || incDx != 0) {
                  break;
            }
            if (walkPoint1 == -1) {
                  if (key->currentAnim == 1) {
                        setKeyCharRandomFrame(key);
                  }
                  return;
            }
            key->prevPointsDataNum = key->pointsDataNum;
            key->pointsDataNum = walkPoint1;
            if (walkPoint2 == -1) {
                  key->walkPointsList[0] = -1;
                  key->walkPointsListIndex = 0;
            } else {
                  ++key->walkPointsListIndex;
                  int16 walkDataNum = findWalkDataNum(walkPoint1, walkPoint2);
                  if (walkDataNum != -1) {
                        key->walkDataNum = walkDataNum;
                  }
            }
      }

      if (key->currentAnim < 1) {
            key->currentAnimCounter = 0;
            key->currentAnim = 1;
            if (dx == 0 && dy == 0 && dz == 0) {
                  return;
            }
      }

      switch (newDirection) {
      case 0:
      case 3:
            if (dx == 0) {
                  return;
            }
            if (newDirection == 3) {
                  dx = -dx;
            }
            if (ABS(dx) >= ABS(incDx)) {
                  if (walkPoint1 != -1) {
                        if (walkPoint2 == -1) {
                              newDirection = getDirection(xpos, ypos, zpos, key->xPosPrev, key->yPosPrev, key->zPosPrev);
                              if (key->prevWalkDataNum != -1) {
                                    key->walkDataNum = key->prevWalkDataNum;
                                    key->prevWalkDataNum = -1;
                              }
                        } else {
                              newDirection = getDirection(xpos, ypos, zpos, _programPointsTable[walkPoint2].x, _programPointsTable[walkPoint2].y, _programPointsTable[walkPoint2].z);
                              int16 walkDataNum = findWalkDataNum(walkPoint1, walkPoint2);
                              if (walkDataNum != -1) {
                                    key->walkDataNum = walkDataNum;
                              }
                        }
                        if (newDirection == -2) {
                              key->xPos = xpos;
                              key->yPos = ypos;
                              key->zPos = zpos;
                              setKeyCharRandomFrame(key);
                              return;
                        }
                        if (newDirection < 0) {
                              newDirection = curDirection;
                        }
                        key->prevPointsDataNum = key->pointsDataNum;
                        key->pointsDataNum = walkPoint1;
                        ++key->walkPointsListIndex;
                        if (newDirection != curDirection) {
                              key->facingDirection = newDirection;
                              key->currentAnimCounter = 0;
                              key->xPos = xpos;
                              key->yPos = ypos;
                              key->zPos = zpos;
                              return;
                        }
                  } else {
                        key->xPos = xpos;
                        key->yPos = ypos;
                        key->zPos = zpos;
                        return;
                  }
            }
            key->xPos += dx;
            adjustKeyCharPosToWalkBox(key, 0);
            break;
      case 1:
      case 2:
            if (ABS(dz) >= ABS(incDz) && incDz != 0) {
                  if (walkPoint1 != -1) {
                        if (walkPoint2 == -1) {
                              newDirection = getDirection(xpos, ypos, zpos, key->xPosPrev, key->yPosPrev, key->zPosPrev);
                        } else {
                              newDirection = getDirection(xpos, ypos, zpos, _programPointsTable[walkPoint2].x, _programPointsTable[walkPoint2].y, _programPointsTable[walkPoint2].z);
                              int16 walkDataNum = findWalkDataNum(walkPoint1, walkPoint2);
                              if (walkDataNum != -1) {
                                    key->walkDataNum = walkDataNum;
                              }
                        }
                        if (newDirection == -2) {
                              key->xPos = xpos;
                              key->yPos = ypos;
                              key->zPos = zpos;
                              setKeyCharRandomFrame(key);
                              return;
                        }
                        if (newDirection < 0) {
                              newDirection = curDirection;
                        }
                        key->prevPointsDataNum = key->pointsDataNum;
                        key->pointsDataNum = walkPoint1;
                        ++key->walkPointsListIndex;
                        if (newDirection != curDirection) {
                              key->facingDirection = newDirection;
                              key->currentAnimCounter = 0;
                              key->xPos = xpos;
                              key->yPos = ypos;
                              key->zPos = zpos;
                              return;
                        }
                  } else {
                        key->xPos = xpos;
                        key->yPos = ypos;
                        key->zPos = zpos;
                        return;
                  }
            }
            if (incDz != 0) {
                  key->zPos += dz;
                  adjustKeyCharPosToWalkBox(key, 2);
            } else {
                  if (ABS(dz) < ABS(incDy)) {
                        key->yPos += dz;
                        adjustKeyCharPosToWalkBox(key, 1);
                  } else {
                        key->xPos = xpos;
                        key->yPos = ypos;
                        key->zPos = zpos;
                  }
            }
            break;
      }
}

void ToucheEngine::markWalkPoints(int keyChar) {
      assert(keyChar >= 0 && keyChar < NUM_KEYCHARS);
      KeyChar *key = &_keyCharsTable[keyChar];
      int16 pointsDataNum = key->pointsDataNum;
      resetPointsData(0);
      if (pointsDataNum != -1) {
            _programPointsTable[pointsDataNum].order = 1;
            bool quit = false;
            while (!quit) {
                  quit = true;
                  for (uint i = 0; i < _programWalkTable.size(); ++i) {
                        int16 md1 = _programWalkTable[i].point1;
                        int16 md2 = _programWalkTable[i].point2;
                        if ((md1 & 0x4000) == 0) {
                              assert((md2 & 0x4000) == 0);
                              if (_programPointsTable[md1].order != 0 && _programPointsTable[md2].order == 0) {
                                    _programPointsTable[md2].order = 1;
                                    quit = false;
                              }
                              if (_programPointsTable[md2].order != 0 && _programPointsTable[md1].order == 0) {
                                    _programPointsTable[md1].order = 1;
                                    quit = false;
                              }
                        }
                  }
            }
      }
}

void ToucheEngine::buildWalkPath(int dstPosX, int dstPosY, int keyChar) {
      debugC(9, kDebugEngine, "ToucheEngine::buildWalkPath(x=%d, y=%d, key=%d)", dstPosX, dstPosY, keyChar);
      if (_currentEpisodeNum == 130) {
            return;
      }
      markWalkPoints(keyChar);

      int minDistance = 0x7D000000;
      int minPointsDataNum = -1;
      for (uint i = 1; i < _programPointsTable.size(); ++i) {
            if (_programPointsTable[i].order != 0) {
                  int dx = _programPointsTable[i].x - dstPosX;
                  int dy = _programPointsTable[i].y - dstPosY;
                  int distance = dx * dx + dy * dy;
                  if (distance < minDistance) {
                        minDistance = distance;
                        minPointsDataNum = i;
                  }
            }
      }

      minDistance = 32000;
      int minWalkDataNum = -1;
      for (uint i = 0; i < _programWalkTable.size(); ++i) {
            const ProgramWalkData *pwd = &_programWalkTable[i];
            if ((pwd->point1 & 0x4000) == 0) {
                  int distance = 32000;
                  ProgramPointData *pts1 = &_programPointsTable[pwd->point1];
                  ProgramPointData *pts2 = &_programPointsTable[pwd->point2];
                  if (pts1->order != 0) {
                        int dx = pts2->x - pts1->x;
                        int dy = pts2->y - pts1->y;
                        if (dx == 0) {
                              if (dstPosY > MIN(pts2->y, pts1->y) && dstPosY < MAX(pts2->y, pts1->y)) {
                                    int d = ABS(dstPosX - pts1->x);
                                    if (d <= 100) {
                                          distance = d * d;
                                    }
                              }
                        } else if (dy == 0) {
                              if (dstPosX > MIN(pts2->x, pts1->x) && dstPosX < MAX(pts2->x, pts1->x)) {
                                    int d = ABS(dstPosY - pts1->y);
                                    if (d <= 100) {
                                          distance = d * d;
                                    }
                              }
                        } else {
                              if (dstPosY > MIN(pts2->y, pts1->y) && dstPosY < MAX(pts2->y, pts1->y) &&
                                    dstPosX > MIN(pts2->x, pts1->x) && dstPosX < MAX(pts2->x, pts1->x) ) {
                                    distance = (dstPosX - pts1->x) * dy - (dstPosY - pts1->y) * dx;
                                    distance /= (dx * dx + dy * dy);
                              }
                        }
                        if (distance < minDistance) {
                              minDistance = distance;
                              minWalkDataNum = i;
                        }
                  }
            }
      }
      if (!sortPointsData(minWalkDataNum, minPointsDataNum)) {
            return;
      }
      int dstPosZ;
      buildWalkPointsList(keyChar);
      KeyChar *key = &_keyCharsTable[keyChar];
      if (minWalkDataNum == -1) {
            dstPosX = _programPointsTable[minPointsDataNum].x;
            dstPosY = _programPointsTable[minPointsDataNum].y;
            dstPosZ = _programPointsTable[minPointsDataNum].z;
      } else {
            ProgramWalkData *pwd = &_programWalkTable[minWalkDataNum];
            ProgramPointData *pts1 = &_programPointsTable[pwd->point1];
            ProgramPointData *pts2 = &_programPointsTable[pwd->point2];
            int16 dx = pts2->x - pts1->x;
            int16 dy = pts2->y - pts1->y;
            int16 dz = pts2->z - pts1->z;
            if (ABS(dy) > ABS(dx)) {
                  dstPosZ = pts2->z - (pts2->y - dstPosY) * dz / dy;
                  dstPosX = pts2->x - (pts2->y - dstPosY) * dx / dy;
            } else {
                  dstPosZ = pts2->z - (pts2->x - dstPosX) * dz / dx;
                  dstPosY = pts2->y - (pts2->x - dstPosX) * dy / dx;
            }
      }
      key->prevWalkDataNum = minWalkDataNum;
      if (key->walkDataNum == key->prevWalkDataNum && key->walkPointsList[1] == -1 && minWalkDataNum != -1) {
            if (key->walkPointsList[0] == _programWalkTable[minWalkDataNum].point1 || key->walkPointsList[0] == _programWalkTable[minWalkDataNum].point2) {
                  ++key->walkPointsListIndex;
            }
      }
      key->xPosPrev = dstPosX;
      key->yPosPrev = dstPosY;
      key->zPosPrev = dstPosZ;
      if (_flagsTable[902] != 0) {
            Graphics::fillRect(_backdropBuffer, _currentBitmapWidth, dstPosX, dstPosY, 4, 4, 0xFC);
      }
}

void ToucheEngine::addToAnimationTable(int num, int posNum, int keyChar, int delayCounter) {
      for (int i = 0; i < NUM_ANIMATION_ENTRIES; ++i) {
            AnimationEntry *anim = &_animationTable[i];
            if (anim->num == 0) {
                  anim->num = num;
                  anim->delayCounter = delayCounter;
                  anim->posNum = posNum;
                  int16 xPos, yPos, x2Pos, y2Pos;
                  if (posNum >= 0) {
                        assert(posNum < NUM_KEYCHARS);
                        xPos = _keyCharsTable[posNum].xPos;
                        yPos = _keyCharsTable[posNum].yPos - 50;
                  } else {
                        posNum = -posNum;
                        assert((uint)posNum < _programPointsTable.size());
                        xPos = _programPointsTable[posNum].x;
                        yPos = _programPointsTable[posNum].y;
                  }
                  xPos -= _flagsTable[614];
                  yPos -= _flagsTable[615];
                  assert(keyChar >= 0 && keyChar < NUM_KEYCHARS);
                  x2Pos = _keyCharsTable[keyChar].xPos - _flagsTable[614];
                  y2Pos = _keyCharsTable[keyChar].yPos - _flagsTable[615] - 50;
                  xPos -= x2Pos;
                  yPos -= y2Pos;
                  xPos /= 8;
                  yPos /= 8;
                  anim->x = x2Pos;
                  anim->y = y2Pos;
                  anim->dx = xPos;
                  anim->dy = yPos;
                  anim->displayCounter = 8;
                  anim->displayRect.left = -1;
                  break;
            }
      }
}

void ToucheEngine::copyAnimationImage(int dstX, int dstY, int w, int h, const uint8 *src, int srcX, int srcY, int fillColor) {
      Area copyRegion(dstX, dstY, w, h);
      copyRegion.srcX = srcX;
      copyRegion.srcY = srcY;
      if (copyRegion.clip(_screenRect)) {
            if (fillColor != -1) {
                  Graphics::copyMask(_offscreenBuffer, kScreenWidth, copyRegion.r.left, copyRegion.r.top,
                    src, kIconWidth, copyRegion.srcX, copyRegion.srcY,
                    copyRegion.r.width(), copyRegion.r.height(),
                    (uint8)fillColor);
            } else {
                  Graphics::copyRect(_offscreenBuffer, kScreenWidth, copyRegion.r.left, copyRegion.r.top,
                    src, kIconWidth, copyRegion.srcX, copyRegion.srcY,
                    copyRegion.r.width(), copyRegion.r.height(),
                    Graphics::kTransparent);
            }
      }
}

void ToucheEngine::drawAnimationImage(AnimationEntry *anim) {
      if (anim->displayRect.left != -1) {
            addToDirtyRect(anim->displayRect);
      }
      int x = anim->x;
      int y = anim->y;
      int dx = -anim->dx;
      int dy = -anim->dy;

      int displayRectX1 = 30000;
      int displayRectY1 = 30000;
      int displayRectX2 = -30000;
      int displayRectY2 = -30000;

      dx /= 3;
      dy /= 3;

      res_loadImage(anim->num, _iconData);
      int color = 0xCF;

      x += dx * 5 - 29;
      y += dy * 5 - 21;
      dx = -dx;
      dy = -dy;
      for (int i = 0; i < 6; ++i) {
            if (i == 5) {
                  color = -1;
            }
            copyAnimationImage(x, y, kIconWidth, kIconHeight, _iconData, 0, 0, color);
            --color;
            displayRectX1 = MIN(x, displayRectX1);
            displayRectX2 = MAX(x, displayRectX2);
            displayRectY1 = MIN(y, displayRectY1);
            displayRectY2 = MAX(y, displayRectY2);
            x += dx;
            y += dy;
      }
      anim->displayRect = Common::Rect(displayRectX1, displayRectY1, displayRectX2 + kIconWidth, displayRectY2 + kIconHeight);
      addToDirtyRect(anim->displayRect);
}

void ToucheEngine::processAnimationTable() {
      for (int i = 0; i < NUM_ANIMATION_ENTRIES; ++i) {
            AnimationEntry *anim = &_animationTable[i];
            if (anim->num != 0) {
                  if (anim->displayCounter == 0) {
                        anim->num = 0;
                        if (anim->displayRect.left != -1) {
                              addToDirtyRect(anim->displayRect);
                        }
                  } else {
                        if (anim->delayCounter != 0) {
                              --anim->delayCounter;
                        } else {
                              anim->x += anim->dx;
                              anim->y += anim->dy;
                              drawAnimationImage(anim);
                              --anim->displayCounter;
                        }
                  }
            }
      }
}

void ToucheEngine::clearAnimationTable() {
      memset(_animationTable, 0, sizeof(_animationTable));
}

void ToucheEngine::addToDirtyRect(const Common::Rect &r) {
      if (_fullRedrawCounter == 0 && r.width() > 0 && r.height() > 0 && r.intersects(_roomAreaRect)) {
            Common::Rect dirtyRect(r);
            dirtyRect.clip(_roomAreaRect);
            if (_dirtyRectsTableCount == 0) {
                  _dirtyRectsTable[_dirtyRectsTableCount] = dirtyRect;
                  ++_dirtyRectsTableCount;
            } else {
                  int index = -1;
                  int minRectSurface = kScreenWidth * kScreenHeight;
                  for (int i = 0; i < _dirtyRectsTableCount; ++i) {
                        if (r.intersects(_dirtyRectsTable[i])) {
                              Common::Rect tmpRect(r);
                              tmpRect.extend(_dirtyRectsTable[i]);
                              int rectSurface = tmpRect.width() * tmpRect.height();
                              if (rectSurface < minRectSurface) {
                                    minRectSurface = rectSurface;
                                    index = i;
                              }
                        }
                  }
                  if (index != -1) {
                        _dirtyRectsTable[index].extend(dirtyRect);
                  } else if (_dirtyRectsTableCount == NUM_DIRTY_RECTS) {
                        debug(0, "Too many dirty rects, performing full screen update");
                        _fullRedrawCounter = 1;
                  } else {
                        _dirtyRectsTable[_dirtyRectsTableCount] = dirtyRect;
                        ++_dirtyRectsTableCount;
                  }
            }
      }
}

void ToucheEngine::clearDirtyRects() {
      _dirtyRectsTableCount = 0;
}

void ToucheEngine::setPalette(int firstColor, int colorCount, int rScale, int gScale, int bScale) {
      uint8 pal[256 * 4];
      for (int i = firstColor; i < firstColor + colorCount; ++i) {
            int r = _paletteBuffer[i * 4 + 0];
            r = (r * rScale) >> 8;
            pal[i * 4 + 0] = (uint8)r;

            int g = _paletteBuffer[i * 4 + 1];
            g = (g * gScale) >> 8;
            pal[i * 4 + 1] = (uint8)g;

            int b = _paletteBuffer[i * 4 + 2];
            b = (b * bScale) >> 8;
            pal[i * 4 + 2] = (uint8)b;

            pal[i * 4 + 3] = 0;
      }
      _system->setPalette(&pal[firstColor * 4], firstColor, colorCount);
}

void ToucheEngine::updateScreenArea(int x, int y, int w, int h) {
      _system->copyRectToScreen(_offscreenBuffer + y * kScreenWidth + x, kScreenWidth, x, y, w, h);
}

void ToucheEngine::updateEntireScreen() {
      int h = (_flagsTable[606] != 0) ? kScreenHeight : kRoomHeight;
      _system->copyRectToScreen(_offscreenBuffer, kScreenWidth, 0, 0, kScreenWidth, h);
}

void ToucheEngine::updateDirtyScreenAreas() {
//    _fullRedrawCounter = 1;
      if (_fullRedrawCounter != 0) {
            updateEntireScreen();
            --_fullRedrawCounter;
      } else {
            debug(1, "dirtyRectsCount=%d", _dirtyRectsTableCount);
            for (int i = 0; i < _dirtyRectsTableCount; ++i) {
                  const Common::Rect &r = _dirtyRectsTable[i];
#if 0
                  Graphics::drawRect(_offscreenBuffer, kScreenWidth, r.left, r.top, r.width(), r.height(), 0xFF, 0xFF);
#endif
                  _system->copyRectToScreen(_offscreenBuffer + r.top * kScreenWidth + r.left, kScreenWidth, r.left, r.top, r.width(), r.height());
            }
            if (_menuRedrawCounter) {
                  const Common::Rect &r = _cursorObjectRect;
                  _system->copyRectToScreen(_offscreenBuffer + r.top * kScreenWidth + r.left, kScreenWidth, r.left, r.top, r.width(), r.height());
                  --_menuRedrawCounter;
            }
      }
}

void ToucheEngine::updatePalette() {
      _system->setPalette(_paletteBuffer, 0, 256);
}

} // namespace Touche

Generated by  Doxygen 1.6.0   Back to index