scummvm/engines/touche/touche.cpp
D G Turner c14f45ee05 TOUCHE: Simplify external digital music support slightly.
Checked with valgrind and no leaks are evident.
Thanks to LordHoto for suggesting these changes.
2012-12-16 16:44:34 +00:00

3392 lines
96 KiB
C++

/* 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.
*
*/
#include "common/config-manager.h"
#include "common/debug-channels.h"
#include "common/events.h"
#include "common/fs.h"
#include "common/system.h"
#include "common/archive.h"
#include "common/debug.h"
#include "common/error.h"
#include "common/keyboard.h"
#include "common/textconsole.h"
#include "audio/mixer.h"
#include "engines/util.h"
#include "graphics/cursorman.h"
#include "graphics/palette.h"
#include "gui/debugger.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), _rnd("touche") {
_saveLoadCurrentPage = 0;
_saveLoadCurrentSlot = 0;
_hideInventoryTexts = false;
_screenRect = Common::Rect(kScreenWidth, kScreenHeight);
_roomAreaRect = Common::Rect(kScreenWidth, kRoomHeight);
memset(_flagsTable, 0, sizeof(_flagsTable));
clearDirtyRects();
_playSoundCounter = 0;
_musicVolume = 0;
_processRandomPaletteCounter = 0;
_fastWalkMode = false;
_fastMode = false;
_currentObjectNum = -1;
_objectDescriptionNum = 0;
_speechPlaying = false;
_roomNeedRedraw = false;
_fullRedrawCounter = 0;
_menuRedrawCounter = 0;
memset(_paletteBuffer, 0, sizeof(_paletteBuffer));
const Common::FSNode gameDataDir(ConfMan.get("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "database");
DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");
DebugMan.addDebugChannel(kDebugGraphics, "Graphics", "Graphics debug level");
DebugMan.addDebugChannel(kDebugResource, "Resource", "Resource debug level");
DebugMan.addDebugChannel(kDebugOpcodes, "Opcodes", "Opcodes debug level");
DebugMan.addDebugChannel(kDebugMenu, "Menu", "Menu debug level");
DebugMan.addDebugChannel(kDebugCharset, "Charset", "Charset debug level");
_console = new ToucheConsole(this);
}
ToucheEngine::~ToucheEngine() {
DebugMan.clearAllDebugChannels();
delete _console;
stopMusic();
delete _midiPlayer;
}
Common::Error ToucheEngine::run() {
initGraphics(kScreenWidth, kScreenHeight, true);
Graphics::setupFont(_language);
setupOpcodes();
initMusic();
// Setup mixer
syncSoundSettings();
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 Common::kNoError;
}
void ToucheEngine::restart() {
stopMusic();
_gameState = kGameStateGameLoop;
_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));
_currentRoomNum = 0;
_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;
}
}
setMusicVolume(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", getMusicVolume());
ConfMan.flushToDisk();
}
Common::Point ToucheEngine::getMousePos() const {
return _eventMan->getMousePos();
}
void ToucheEngine::syncSoundSettings() {
Engine::syncSoundSettings();
readConfigurationSettings();
}
void ToucheEngine::mainLoop() {
restart();
setPalette(0, 255, 0, 0, 0);
readConfigurationSettings();
_inp_leftMouseButtonPressed = false;
_inp_rightMouseButtonPressed = false;
if (ConfMan.hasKey("save_slot")) {
int saveSlot = ConfMan.getInt("save_slot");
if (saveSlot >= 0 && saveSlot <= 99) {
loadGameState(saveSlot);
_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; !shouldQuit(); ++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_KEYDOWN:
if (!handleKeyEvents) {
break;
}
_flagsTable[600] = event.kbd.keycode;
if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
if (_displayQuitDialog) {
if (displayQuitDialog()) {
quitGame();
}
}
} 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.hasFlags(Common::KBD_CTRL)) {
if (event.kbd.keycode == Common::KEYCODE_f) {
_fastMode = !_fastMode;
} else if (event.kbd.keycode == Common::KEYCODE_d) {
this->getDebugger()->attach();
this->getDebugger()->onFrame();
}
} 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 (key2->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);
if (DebugMan.isDebugChannelEnabled(kDebugCharset)) {
debug("stringwidth: %s", str);
debugN("raw:");
const char *p = str;
while (*p) debugN(" %02X", (unsigned char)*p++);
debugN("\n");
}
return Graphics::getStringWidth16(str);
}
void ToucheEngine::drawString(uint16 color, int x, int y, int16 num, StringType strType) {
const int xmax = (_language == Common::ES_ESP && strType == kStringTypeConversation) ? kScreenWidth - 20 : 0;
if (num) {
const char *str = getString(num);
Graphics::drawString16(_offscreenBuffer, kScreenWidth, color, x, y, str, xmax);
}
}
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 && !shouldQuit()) {
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, kStringTypeConversation);
}
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, kStringTypeConversation);
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 quitLoop = false;
int order = 1;
while (!quitLoop) {
quitLoop = 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;
quitLoop = false;
}
if (_programPointsTable[md2].order == order - 1 && _programPointsTable[md1].order > order) {
_programPointsTable[md1].order = order;
quitLoop = 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 quitLoop = false;
while (!quitLoop) {
quitLoop = 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;
quitLoop = false;
}
if (_programPointsTable[md2].order != 0 && _programPointsTable[md1].order == 0) {
_programPointsTable[md1].order = 1;
quitLoop = 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 * 3];
for (int i = firstColor; i < firstColor + colorCount; ++i) {
int r = _paletteBuffer[i * 3 + 0];
r = (r * rScale) >> 8;
pal[i * 3 + 0] = (uint8)r;
int g = _paletteBuffer[i * 3 + 1];
g = (g * gScale) >> 8;
pal[i * 3 + 1] = (uint8)g;
int b = _paletteBuffer[i * 3 + 2];
b = (b * bScale) >> 8;
pal[i * 3 + 2] = (uint8)b;
}
_system->getPaletteManager()->setPalette(&pal[firstColor * 3], 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->getPaletteManager()->setPalette(_paletteBuffer, 0, 256);
}
bool ToucheEngine::canLoadGameStateCurrently() {
return _gameState == kGameStateGameLoop && _flagsTable[618] == 0 && !_hideInventoryTexts;
}
bool ToucheEngine::canSaveGameStateCurrently() {
return _gameState == kGameStateGameLoop && _flagsTable[618] == 0 && !_hideInventoryTexts;
}
void ToucheEngine::initMusic() {
// Detect External Music Files
bool extMusic = true;
for (int num = 0; num < 26 && extMusic; num++) {
Common::String extMusicFilename = Common::String::format("track%02d", num+1);
Audio::SeekableAudioStream *musicStream = Audio::SeekableAudioStream::openStreamFile(extMusicFilename);
if (!musicStream)
extMusic = false;
delete musicStream;
}
if (!extMusic) {
_midiPlayer = new MidiPlayer;
debug(1, "initMusic(): Using midi music!");
} else
debug(1, "initMusic(): Using external digital music!");
}
void ToucheEngine::startMusic(int num) {
debug(1, "startMusic(%d)", num);
uint32 size;
stopMusic();
if (_midiPlayer) {
const uint32 offs = res_getDataOffset(kResourceTypeMusic, num, &size);
_fData.seek(offs);
_midiPlayer->play(_fData, size, true);
} else {
Common::String extMusicFilename = Common::String::format("track%02d", num);
Audio::SeekableAudioStream *extMusicFileStream = Audio::SeekableAudioStream::openStreamFile(extMusicFilename);
if (!extMusicFileStream) {
error("Unable to open %s for reading", extMusicFilename.c_str());
}
Audio::LoopingAudioStream *loopStream = new Audio::LoopingAudioStream(extMusicFileStream, 0);
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, loopStream, -1, _musicVolume);
}
}
void ToucheEngine::stopMusic() {
debug(1, "stopMusic()");
if (_midiPlayer)
_midiPlayer->stop();
else {
_mixer->stopHandle(_musicHandle);
}
}
int ToucheEngine::getMusicVolume() {
if (_midiPlayer)
_musicVolume = _midiPlayer->getVolume();
return _musicVolume;
}
void ToucheEngine::setMusicVolume(int volume) {
debug(1, "setMusicVolume(%d)", volume);
_musicVolume = CLIP(volume, 0, 255);
if (_midiPlayer)
_midiPlayer->setVolume(_musicVolume);
else {
_mixer->setChannelVolume(_musicHandle, _musicVolume);
}
}
void ToucheEngine::adjustMusicVolume(int diff) {
debug(1, "adjustMusicVolume(%d)", diff);
_musicVolume = CLIP(_musicVolume + diff, 0, 255);
if (_midiPlayer)
_midiPlayer->adjustVolume(diff);
else {
_mixer->setChannelVolume(_musicHandle, _musicVolume);
}
}
} // namespace Touche