/* 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$ * $Id$ * */ #include "common/config-manager.h" #include "common/events.h" #include "common/system.h" #include "graphics/cursorman.h" #include "sound/voc.h" #include "igor/igor.h" #include "igor/midi.h" namespace Igor { IgorEngine::IgorEngine(OSystem *system, const DetectedGameVersion *dgv) : Engine(system), _game(*dgv) { _screenVGA = (uint8 *)malloc(320 * 200); for (int i = 0; i < 4; ++i) { _facingIgorFrames[i] = (uint8 *)malloc(13500); } _screenLayer1 = (uint8 *)malloc(320 * 144); _screenLayer2 = (uint8 *)malloc(320 * 144); _screenTextLayer = (uint8 *)malloc(320 * 144); _screenTempLayer = (uint8 *)malloc(9996); _igorHeadFrames = (uint8 *)malloc(3696); _animFramesBuffer = (uint8 *)malloc(65535); _inventoryPanelBuffer = (uint8 *)malloc(9600 * 2); _inventoryImagesBuffer = (uint8 *)malloc(48000); _verbsPanelBuffer = (uint8 *)malloc(3840); Common::addDebugChannel(kDebugEngine, "Engine", "Engine debug level"); Common::addDebugChannel(kDebugResource, "Resource", "Resource debug level"); Common::addDebugChannel(kDebugScreen, "Screen", "Screen debug level"); Common::addDebugChannel(kDebugWalk, "Walk", "Walk debug level"); Common::addDebugChannel(kDebugGame, "Game", "Game debug level"); if (_game.flags & kFlagFloppy) { _midiPlayer = new MidiPlayer(this); } else { _midiPlayer = 0; } } IgorEngine::~IgorEngine() { free(_resourceEntries); free(_soundOffsets); free(_screenVGA); for (int i = 0; i < 4; ++i) { free(_facingIgorFrames[i]); } free(_screenLayer1); free(_screenLayer2); free(_screenTextLayer); free(_screenTempLayer); free(_igorHeadFrames); free(_animFramesBuffer); free(_inventoryPanelBuffer); free(_inventoryImagesBuffer); free(_verbsPanelBuffer); Common::clearAllDebugChannels(); delete _midiPlayer; } void IgorEngine::restart() { _screenVGAVOffset = 0; memset(&_gameState, 0, sizeof(_gameState)); _nextTimer = 0; _fastMode = false; _language = 0; memset(_walkData, 0, sizeof(_walkData)); _walkCurrentPos = 0; _walkDataLastIndex = _walkDataCurrentIndex = 0; _walkCurrentFrame = 0; _walkDataCurrentPosX = _walkDataCurrentPosY = 0; _walkToObjectPosX = _walkToObjectPosY = 0; memset(&_currentAction, 0, sizeof(_currentAction)); _currentAction.verb = kVerbWalk; _actionCode = 0; _actionWalkPoint = 0; memset(_inputVars, 0, sizeof(_inputVars)); _musicData = 0; _talkDelay = _talkSpeechCounter = _talkDelayCounter = 0; memset(_dialogueTextsTable, 0, sizeof(_dialogueTextsTable)); _dialogueTextsStart = 0; _dialogueTextsCount = 0; _dialogueDirtyRectY = 0; _dialogueDirtyRectSize = 0; memset(_dialogueQuestions, 0, sizeof(_dialogueQuestions)); memset(_dialogueReplies, 0, sizeof(_dialogueReplies)); _dialogueEnded = false; _dialogueChoiceSelected = 0; memset(_dialogueInfo, 0, sizeof(_dialogueInfo)); memset(_objectsState, 0, sizeof(_objectsState)); memcpy(_inventoryImages, INVENTORY_IMG_INIT, 36); memset(_inventoryInfo, 0, sizeof(_inventoryInfo)); memset(_verbPrepositions, 0, sizeof(_verbPrepositions)); memset(_globalObjectNames, 0, sizeof(_globalObjectNames)); memset(_globalDialogueTexts, 0, sizeof(_globalDialogueTexts)); memset(_verbsName, 0, sizeof(_verbsName)); memset(_roomObjectNames, 0, sizeof(_roomObjectNames)); _igorTempFrames = _facingIgorFrames[0] + 10500; memset(_roomObjectAreasTable, 0, sizeof(_roomObjectAreasTable)); memset(_roomActionsTable, 0, sizeof(_roomActionsTable)); _executeMainAction = 0; _executeRoomAction = 0; _previousMusic = 0; _musicData = 0; _actionCode = 0; _actionWalkPoint = 0; memset(_inputVars, 0, sizeof(_inputVars)); _scrollInventory = false; _roomCursorOn = true; _currentCursor = 0; _dialogueCursorOn = true; _updateDialogue = 0; _updateRoomBackground = 0; _resourceEntriesCount = 0; _resourceEntries = 0; _soundOffsetsCount = 0; _soundOffsets = 0; _demoActionsCounter = 0; _gameTicks = 0; } Common::Error IgorEngine::run() { initGraphics(320, 200, false); _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); restart(); setupDefaultPalette(); _currentPart = ConfMan.getInt("boot_param"); if (_currentPart == 0) { _currentPart = kStartupPart; } if (!_ovlFile.open(_game.ovlFileName)) { error("Unable to open '%s'", _game.ovlFileName); } if (!_sndFile.open(_game.sfxFileName)) { error("Unable to open '%s'", _game.sfxFileName); } readTableFile(); loadMainTexts(); loadIgorFrames(); _gameState.talkMode = kTalkModeTextOnly; _gameState.talkSpeed = 3; _talkSpeechCounter = 5; _eventQuitGame = false; PART_MAIN(); _ovlFile.close(); _sndFile.close(); return Common::kNoError; } void IgorEngine::readTableFile() { Common::File tblFile; uint32 stringsEntriesOffset = 0, resourcesEntriesOffset = 0, soundEntriesOffset = 0; if (tblFile.open("IGOR.TBL") && tblFile.readUint32BE() == MKID_BE('ITBL') && tblFile.readUint32BE() == 4) { stringsEntriesOffset = tblFile.readUint32BE(); uint32 borlandOverlaySize = _ovlFile.size(); int gameVersionsCount = tblFile.readByte(); for (int i = 0; i < gameVersionsCount; ++i) { uint32 size = tblFile.readUint32BE(); if (size == borlandOverlaySize) { resourcesEntriesOffset = tblFile.readUint32BE(); soundEntriesOffset = tblFile.readUint32BE(); break; } tblFile.skip(8); } } if (resourcesEntriesOffset != 0 && soundEntriesOffset != 0) { tblFile.seek(resourcesEntriesOffset); _resourceEntriesCount = tblFile.readUint16BE(); _resourceEntries = (ResourceEntry *)malloc(sizeof(ResourceEntry) * _resourceEntriesCount); for (int i = 0; i < _resourceEntriesCount; ++i) { _resourceEntries[i].id = tblFile.readUint16BE(); _resourceEntries[i].offs = tblFile.readUint32BE(); _resourceEntries[i].size = tblFile.readUint32BE(); } tblFile.seek(soundEntriesOffset); _soundOffsetsCount = tblFile.readUint16BE(); _soundOffsets = (uint32 *)malloc(sizeof(uint32) * _soundOffsetsCount); for (int i = 0; i < _soundOffsetsCount; ++i) { _soundOffsets[i] = tblFile.readUint32BE(); } tblFile.seek(stringsEntriesOffset); int stringsCount = tblFile.readUint16BE(); for (int i = 0; i < stringsCount; ++i) { int id = tblFile.readUint16BE(); int lang = tblFile.readByte(); int len = tblFile.readByte(); bool skipString = (lang == 1 && _game.language != Common::EN_ANY) || (lang == 2 && _game.language != Common::ES_ESP); if (skipString) { tblFile.skip(len); } else { char buf[256]; tblFile.read(buf, len); buf[len] = 0; _stringEntries.push_back(StringEntry(id, buf)); } } return; } error("Unable to read 'IGOR.TBL'"); } const char *IgorEngine::getString(int id) const { const char *str = 0; for (Common::Array::const_iterator it = _stringEntries.begin(); it != _stringEntries.end(); ++it) { if ((*it).id == id) { str = (*it).str.c_str(); break; } } return str; } void IgorEngine::waitForTimer(int ticks) { _system->copyRectToScreen(_screenVGA, 320, 0, _screenVGAVOffset, 320, 200 - _screenVGAVOffset); _system->updateScreen(); uint32 endTicks = (ticks == -1) ? _nextTimer : _system->getMillis() + ticks * 1000 / kTickDelay; do { Common::Event ev; while (_eventMan->pollEvent(ev)) { switch (ev.type) { case Common::EVENT_QUIT: _inputVars[kInputEscape] = 1; _currentPart = kInvalidPart; _eventQuitGame = true; break; case Common::EVENT_KEYDOWN: if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) { _inputVars[kInputEscape] = 1; } else if (ev.kbd.keycode == Common::KEYCODE_SPACE) { _inputVars[kInputOptions] = 1; } else if (ev.kbd.keycode == Common::KEYCODE_p) { _inputVars[kInputPause] = 1; } else if (ev.kbd.keycode == Common::KEYCODE_F11) { sprintf(_saveStateDescriptions[kQuickSaveSlot], "Quicksave part %d", _currentPart); saveGameState(kQuickSaveSlot); } else if (ev.kbd.keycode == Common::KEYCODE_F12) { loadGameState(kQuickSaveSlot); } break; case Common::EVENT_MOUSEMOVE: _inputVars[kInputCursorXPos] = ev.mouse.x; _inputVars[kInputCursorYPos] = ev.mouse.y; break; case Common::EVENT_RBUTTONDOWN: _inputVars[kInputSkipDialogue] = 1; break; case Common::EVENT_LBUTTONDOWN: _inputVars[kInputClick] = 1; _inputVars[kInputCursorXPos] = ev.mouse.x; _inputVars[kInputCursorYPos] = ev.mouse.y; break; default: break; } } _system->delayMillis(10); if (_system->getMillis() >= endTicks) { break; } } while (!_fastMode); _nextTimer = _system->getMillis() + kTimerTicksCount * 1000 / kTickDelay; if (ticks != -1) { return; } _gameTicks += kTimerTicksCount; if ((_gameTicks & 31) == 0) { setCursor(_currentCursor); _currentCursor = (_currentCursor + 1) & 3; } if (_game.flags & kFlagFloppy) { updateMusic(); } if (_gameTicks == 64) { _gameTicks = 0; } } void IgorEngine::copyArea(uint8 *dst, int dstOffset, int dstPitch, const uint8 *src, int srcPitch, int w, int h, bool transparent) { uint8 *p = dst + dstOffset; for (int y = 0; y < h; ++y) { if (transparent) { for (int x = 0; x < w; ++x) { if (src[x] != 0) { p[x] = src[x]; } } } else { memcpy(p, src, w); } p += dstPitch; src += srcPitch; } } int IgorEngine::getRandomNumber(int m) { assert(m > 0); return _rnd.getRandomNumber(m - 1); } void IgorEngine::startMusic(int cmf) { _midiPlayer->stopMusic(); free(_musicData); int musicDataSize; _musicData = loadData(cmf, 0, &musicDataSize); _midiPlayer->playMusic(_musicData, musicDataSize); } void IgorEngine::playMusic(int num) { debugC(9, kDebugEngine, "playMusic() %d", num); if (_game.flags & kFlagFloppy) { static const int cmf[] = { 0, 0, CMF_2_1, CMF_3, CMF_4, 0, 0, CMF_7_1, CMF_8, CMF_9, CMF_10, CMF_11, CMF_12 }; assert(num < ARRAYSIZE(cmf) && cmf[num] != 0); _gameState.musicNum = num; _gameState.musicSequenceIndex = 1; startMusic(cmf[num]); } else { // TODO: play CD track } } void IgorEngine::updateMusic() { static const int cmf2Seq[] = { CMF_2_1, CMF_2_2, CMF_2_3, CMF_2_4 }; static const int cmf7Seq[] = { CMF_7_1, CMF_7_2, CMF_7_3, CMF_7_4 }; if (_gameState.jumpToNextMusic && 0) { // TODO: enable switch (_gameState.musicNum) { case 2: _gameState.musicSequenceIndex = getRandomNumber(4) + 1; startMusic(cmf2Seq[_gameState.musicSequenceIndex - 1]); // _timerHandler0x1CCounter = 5; break; case 7: if (_gameState.musicSequenceIndex == 4) { _gameState.musicSequenceIndex = 1; } else { ++_gameState.musicSequenceIndex; } startMusic(cmf7Seq[_gameState.musicSequenceIndex - 1]); // _timerHandler0x1CCounter = 5; break; case 3: case 4: case 8: case 9: case 10: // _timerHandler0x1CCounter = 50; break; case 11: // _timerHandler0x1CCounter = 5; break; } } } void IgorEngine::playSound(int num, int type) { debugC(9, kDebugEngine, "playSound() %d", num); --num; int soundOffset = -1; Audio::Mixer::SoundType soundType; Audio::SoundHandle *soundHandle = 0; if (type == 1) { if (_mixer->isSoundHandleActive(_sfxHandle)) { return; } assert(num >= 0 && num < _soundOffsetsCount); soundOffset = _soundOffsets[num]; soundType = Audio::Mixer::kSFXSoundType; soundHandle = &_sfxHandle; } else if (type == 0 && (_game.flags & kFlagTalkie) != 0 && num != kNoSpeechSound) { if (_mixer->isSoundHandleActive(_speechHandle)) { _mixer->stopHandle(_speechHandle); } num += 100; assert(num >= 0 && num < _soundOffsetsCount); soundOffset = _soundOffsets[num]; soundType = Audio::Mixer::kSpeechSoundType; soundHandle = &_speechHandle; } else { return; } _sndFile.seek(soundOffset); Audio::AudioStream *stream = Audio::makeVOCStream(_sndFile, Audio::Mixer::FLAG_UNSIGNED); if (stream) { _mixer->playInputStream(soundType, soundHandle, stream); } } void IgorEngine::stopSound() { _mixer->stopHandle(_sfxHandle); } void IgorEngine::loadIgorFrames() { loadData(FRM_IgorDirBack, _facingIgorFrames[0]); loadData(FRM_IgorDirRight, _facingIgorFrames[1]); loadData(FRM_IgorDirFront, _facingIgorFrames[2]); loadData(FRM_IgorDirLeft, _facingIgorFrames[3]); loadData(FRM_IgorHead, _igorHeadFrames); memcpy(_igorPalette, PAL_IGOR_1, 48); } void IgorEngine::loadIgorFrames2() { loadData(FRM_IgorDirBack2, _facingIgorFrames[0]); loadData(FRM_IgorDirRight2, _facingIgorFrames[1]); loadData(FRM_IgorDirFront2, _facingIgorFrames[2]); loadData(FRM_IgorDirLeft2, _facingIgorFrames[3]); loadData(FRM_IgorHead2, _igorHeadFrames); memcpy(_igorPalette, PAL_IGOR_2, 48); } void IgorEngine::fixDialogueTextPosition(int num, int count, int *x, int *y) { int textLineWidth = 0; for (int i = 0; i < count; ++i) { int w = getStringWidth(_globalDialogueTexts[num + i]); if (w > textLineWidth) { textLineWidth = w; } } int textX = *x; textX += textLineWidth / 2 - 1; if (textX > 318) { textX = 317 - textLineWidth; } else { textX -= textLineWidth; } if (textX < 1) { textX = textLineWidth / 2 + 1; } else { textX += textLineWidth / 2; } *x = textX; int textY = *y; textY -= count * 10; if (textY < 1) { textY = 1; } *y = textY; } void IgorEngine::startCutsceneDialogue(int x, int y, int r, int g, int b) { debugC(9, kDebugEngine, "startCutsceneDialogue() pos %d,%d color %d,%d,%d", x, y, r, g, b); --_dialogueTextsCount; int talkX = x; int talkY = y; const DialogueText *dt = &_dialogueTextsTable[_dialogueTextsStart]; fixDialogueTextPosition(dt->num, dt->count, &talkX, &talkY); _dialogueDirtyRectY = talkY * 320; _dialogueDirtyRectSize = dt->count * 11 * 320; assert(_dialogueDirtyRectSize < 320 * 72); memcpy(_screenTextLayer, _screenVGA + _dialogueDirtyRectY, _dialogueDirtyRectSize); memcpy(_screenTextLayer + 320 * 72, _screenVGA + _dialogueDirtyRectY, _dialogueDirtyRectSize); for (int i = 0; i < dt->count; ++i) { const char *textLine = _globalDialogueTexts[dt->num + i]; int textLineWidth = getStringWidth(textLine); int textX = talkX - textLineWidth / 2; int textY = i * 10; drawString(_screenTextLayer, textLine, textX, textY, kTalkColor, kTalkShadowColor, kTalkShadowColor); } setPaletteColor(kTalkColor, r, g, b); setPaletteColor(kTalkShadowColor, 0, 0, 0); if (_gameState.talkMode != kTalkModeSpeechOnly) { memcpy(_screenVGA + _dialogueDirtyRectY, _screenTextLayer, _dialogueDirtyRectSize); } if (_gameState.talkMode == kTalkModeTextOnly) { _talkDelay = (2 * dt->count) * _talkDelays[_gameState.talkSpeed]; _talkDelayCounter = 0; } else { _talkDelay = -1; _talkDelayCounter = 0; } if (_gameState.talkMode == kTalkModeTextOnly) { playSound(24, 0); } _gameState.dialogueTextRunning = true; _inputVars[kInputSkipDialogue] = 0; } void IgorEngine::waitForEndOfCutsceneDialogue(int x, int y, int r, int g, int b) { do { if (_gameState.dialogueTextRunning && _inputVars[kInputSkipDialogue]) { _talkDelayCounter = _talkDelay; _inputVars[kInputSkipDialogue] = 0; } if (compareGameTick(19, 32) && _gameState.dialogueTextRunning) { if (_talkSpeechCounter > 2) { if (_gameState.talkMode != kTalkModeTextOnly) { _talkDelayCounter = _talkDelay; } if (_talkDelay == _talkDelayCounter) { if (_updateDialogue) { (this->*_updateDialogue)(kUpdateDialogueAnimEndOfSentence); } memcpy(_screenVGA + _dialogueDirtyRectY, _screenTextLayer + 23040, _dialogueDirtyRectSize); if (_dialogueTextsCount == 0) { _gameState.dialogueTextRunning = 0; } else { ++_dialogueTextsStart; if (_gameState.talkMode != kTalkModeTextOnly) { if (_talkSpeechCounter != -1) { _talkSpeechCounter = 0; } else { _talkSpeechCounter = 5; startCutsceneDialogue(x, y, r, g, b); } } else { startCutsceneDialogue(x, y, r, g, b); } } } else { ++_talkDelayCounter; if (_updateDialogue) { (this->*_updateDialogue)(kUpdateDialogueAnimMiddleOfSentence); } } } else { if (_talkSpeechCounter == 2) { startCutsceneDialogue(x, y, r, g, b); } ++_talkSpeechCounter; } } if (_updateRoomBackground) { (this->*_updateRoomBackground)(); } if (_inputVars[kInputEscape]) return; waitForTimer(); } while (_gameState.dialogueTextRunning); } void IgorEngine::fixIgorDialogueTextPosition(int num, int count, int *x, int *y) { WalkData *wd = &_walkData[_walkDataLastIndex - 1]; *x = wd->x; *y = wd->y - wd->scaleWidth - 3; fixDialogueTextPosition(num, count, x, y); } void IgorEngine::startIgorDialogue() { debugC(9, kDebugEngine, "startIgorDialogue()"); --_dialogueTextsCount; int talkX, talkY; const DialogueText *dt = &_dialogueTextsTable[_dialogueTextsStart]; fixIgorDialogueTextPosition(dt->num, dt->count, &talkX, &talkY); _dialogueDirtyRectY = talkY * 320; _dialogueDirtyRectSize = dt->count * 11 * 320; assert(_dialogueDirtyRectSize < 320 * 72); memcpy(_screenTextLayer, _screenVGA + _dialogueDirtyRectY, _dialogueDirtyRectSize); if (_currentPart == 690) { memcpy(_screenTextLayer + 320 * 72, _screenLayer1 + _dialogueDirtyRectY, _dialogueDirtyRectSize); if (_currentAction.verb == kVerbLook && _currentAction.object1Num == 15) { memcpy(_screenTextLayer + 320 * 72, _screenVGA + _dialogueDirtyRectY, _dialogueDirtyRectSize); } if (_currentAction.verb == kVerbLook && _currentAction.object1Num == 19) { memcpy(_screenTextLayer + 320 * 72, _screenVGA + _dialogueDirtyRectY, _dialogueDirtyRectSize); } } else { memcpy(_screenTextLayer + 320 * 72, _screenVGA + _dialogueDirtyRectY, _dialogueDirtyRectSize); } for (int i = 0; i < dt->count; ++i) { const char *textLine = _globalDialogueTexts[dt->num + i]; int textLineWidth = getStringWidth(textLine); int textX = talkX - textLineWidth / 2; int textY = i * 10; drawString(_screenTextLayer, textLine, textX, textY, kTalkColor, kTalkShadowColor, kTalkShadowColor); } setPaletteColor(kTalkColor, _dialogueColor[0], _dialogueColor[1], _dialogueColor[2]); setPaletteColor(kTalkShadowColor, 0, 0, 0); if (_gameState.talkMode != kTalkModeSpeechOnly) { memcpy(_screenVGA + _dialogueDirtyRectY, _screenTextLayer, _dialogueDirtyRectSize); } if (_gameState.talkMode == kTalkModeTextOnly) { _talkDelay = (2 * dt->count) * _talkDelays[_gameState.talkSpeed]; _talkDelayCounter = 0; } else { _talkDelay = -1; _talkDelayCounter = 0; } if (_gameState.talkMode == kTalkModeTextOnly) { playSound(24, 0); } _gameState.dialogueTextRunning = true; _inputVars[kInputSkipDialogue] = 0; } void IgorEngine::waitForEndOfIgorDialogue() { do { if (_gameState.dialogueTextRunning && _inputVars[kInputSkipDialogue]) { _talkDelayCounter = _talkDelay; _inputVars[kInputSkipDialogue] = 0; } if (compareGameTick(19, 32) && _gameState.dialogueTextRunning) { if (_talkSpeechCounter > 2) { if (_gameState.talkMode != kTalkModeTextOnly) { _talkDelayCounter = _talkDelay; } if (_talkDelay == _talkDelayCounter) { animateIgorTalking(0); memcpy(_screenVGA + _dialogueDirtyRectY, _screenTextLayer + 23040, _dialogueDirtyRectSize); if (_dialogueTextsCount == 0) { _gameState.dialogueTextRunning = 0; } else { ++_dialogueTextsStart; if (_gameState.talkMode != kTalkModeTextOnly) { if (_talkSpeechCounter != -1) { _talkSpeechCounter = 0; } else { _talkSpeechCounter = 5; startIgorDialogue(); } } else { startIgorDialogue(); } } } else { animateIgorTalking(getRandomNumber(6)); ++_talkDelayCounter; } } else { if (_talkSpeechCounter == 2) { startIgorDialogue(); } ++_talkSpeechCounter; } } if (_updateRoomBackground) { (this->*_updateRoomBackground)(); } if (_inputVars[kInputEscape]) return; waitForTimer(); } while (_gameState.dialogueTextRunning); } int IgorEngine::getObjectFromInventory(int x) const { if (x >= 20 && x <= 299) { int i = (x - 20) / 40 + _inventoryInfo[72]; if (i <= _inventoryInfo[73]) { return _inventoryInfo[i - 1]; } } return 0; } static int compareResourceEntry(const void *a, const void *b) { int id = *(const int *)a; const ResourceEntry *entry = (const ResourceEntry *)b; return id - entry->id; } ResourceEntry *IgorEngine::findData(int id) { ResourceEntry *re = (ResourceEntry *)bsearch(&id, _resourceEntries, _resourceEntriesCount, sizeof(ResourceEntry), compareResourceEntry); assert(re); return re; } uint8 *IgorEngine::loadData(int id, uint8 *dst, int *size) { debugC(9, kDebugResource, "loadData() id %d", id); ResourceEntry *re = findData(id); if (!dst) { dst = (uint8 *)malloc(re->size); if (!dst) { error("Unable to allocate %d bytes", re->size); } } _ovlFile.seek(re->offs); _ovlFile.read(dst, re->size); if (size) { *size = re->size; } return dst; } static void decodeRoomString(const uint8 *src, char *dst, int sz) { for (int i = 0; i < sz; ++i) { uint8 code = *src++; if ((code >= 0xAE && code <= 0xC7) || (code >= 0xCE && code <= 0xE7)) { code -= 0x6D; } else if (code > 0xE7) { switch (code) { case 0xE8: code = 0xA0; break; case 0xE9: code = 0x82; break; case 0xEA: code = 0xA1; break; case 0xEB: code = 0xA2; break; case 0xEC: code = 0xA3; break; case 0xED: code = 0xA4; break; case 0xEE: code = 0xA5; break; } } *dst++ = (char)code; } } void IgorEngine::decodeRoomStrings(const uint8 *p, bool skipObjectNames) { if (!skipObjectNames) { for (int i = 0; i < 20; ++i) { _roomObjectNames[i][0] = '\0'; } uint8 code = *p++; int index = -1; while (code != 0xF6) { if (code == 0xF4) { ++index; } int len = *p++; if (len != 0) { assert(index >= 0); decodeRoomString(p, _roomObjectNames[index], len); p += len; _roomObjectNames[index][len] = '\0'; debugC(9, kDebugResource, "decodeRoomStrings() _roomObjectNames[%d] '%s'", index, _roomObjectNames[index]); } code = *p++; } } for (int i = 200; i < 250; ++i) { _globalDialogueTexts[i][0] = '\0'; } uint8 code = *p++; int index = 200; while (code != 0xF6) { if (code == 0xF4) { ++index; } int len = *p++; if (len != 0) { decodeRoomString(p, _globalDialogueTexts[index], len); p += len; _globalDialogueTexts[index][len] = '\0'; debugC(9, kDebugResource, "decodeRoomStrings() _globalDialogueTexts[%d] '%s'", index, _globalDialogueTexts[index]); } code = *p++; } } void IgorEngine::decodeRoomText(const uint8 *p) { debugC(9, kDebugResource, "decodeRoomText()"); memcpy(_walkXScaleRoom, p, 320); p += 320; memcpy(_walkYScaleRoom, p, 432); p += 432; decodeRoomStrings(p); } void IgorEngine::decodeRoomAreas(const uint8 *p, int count) { for (int i = 0; i < count; ++i) { RoomObjectArea *roa = &_roomObjectAreasTable[i]; roa->area = *p++; assert((roa->area & 0x80) == 0); roa->object = *p++; roa->y1Lum = *p++; roa->y2Lum = *p++; roa->deltaLum = *p++; } } void IgorEngine::decodeRoomMask(const uint8 *p) { uint8 *dst = _screenLayer2; int sz = 320 * 144; while (sz != 0) { uint8 b = *p++; int len = READ_LE_UINT16(p); p += 2; if (len > sz) { len = sz; } memset(dst, b, len); dst += len; sz -= len; } } void IgorEngine::loadRoomData(int pal, int img, int box, int msk, int txt) { if (pal != 0) { loadData(pal, _paletteBuffer); } if (img != 0) { loadData(img, _screenLayer1); } if (box != 0) { int sz; uint8 *p = loadData(box, 0, &sz); memset(_roomObjectAreasTable, 0, sizeof(_roomObjectAreasTable)); assert((sz % 5) == 0); decodeRoomAreas(p, sz / 5); free(p); } if (msk != 0) { uint8 *p = loadData(msk); decodeRoomMask(p); free(p); } if (txt != 0) { uint8 *p = loadData(txt); decodeRoomText(p); free(p); } } void IgorEngine::loadAnimData(const int *anm, int loadOffset) { if (loadOffset == 0) { memset(_animFramesBuffer, 0, 65535); } while (*anm) { int dataSize; loadData(*anm++, &_animFramesBuffer[loadOffset], &dataSize); loadOffset += dataSize; } } void IgorEngine::loadActionData(int act) { if (act != 0) { assert(findData(act)->size <= 0x2000); loadData(act, _roomActionsTable); } } void IgorEngine::loadDialogueData(int dlg) { uint8 *p = loadData(dlg); int dialogueDataSize = _roomDataOffsets.dlg.questionsOffset + 164 + 41; assert(dialogueDataSize <= 500); memcpy(_gameState.dialogueData, p, dialogueDataSize); assert(_roomDataOffsets.dlg.questionsSize <= MAX_DIALOGUE_QUESTIONS); for (int i = 0; i < _roomDataOffsets.dlg.questionsSize; ++i) { for (int n = 0; n < 2; ++n) { const uint8 *src = p + _roomDataOffsets.dlg.questionsOffset + (i + 1) * 164 + _language * 82 + (n + 1) * 41; int len = *src++; if (len != 0) { decodeRoomString(src, _dialogueQuestions[i][n], len); _dialogueQuestions[i][n][len] = '\0'; debugC(9, kDebugResource, "loadDialogueData() _dialogueQuestions[%d][%d] '%s'", i, n, _dialogueQuestions[i][n]); } else { _dialogueQuestions[i][n][0] = '\0'; } } } assert(_roomDataOffsets.dlg.repliesSize <= MAX_DIALOGUE_REPLIES); for (int i = 0; i < _roomDataOffsets.dlg.repliesSize; ++i) { const uint8 *src = p + _roomDataOffsets.dlg.repliesOffset + (i + 1) * 102 + _language * 51; int len = *src++; if (len != 0) { decodeRoomString(src, _dialogueReplies[i], len); _dialogueReplies[i][len] = '\0'; debugC(9, kDebugResource, "loadDialogueData() _dialogueReplies[%d] '%s'", i, _dialogueReplies[i]); } else { _dialogueReplies[i][0] = '\0'; } } free(p); } static void decodeMainString(const uint8 *src, char *dst) { int sz = *src - 0x6D; if (sz != 0) { ++src; for (int i = 0; i < sz; ++i) { uint8 code = src[i] - 0x6D; dst[i] = (char)code; } } dst[sz] = '\0'; } void IgorEngine::loadMainTexts() { loadData(IMG_VerbsPanel, _verbsPanelBuffer); if (_game.version == kIdSpaCD) { const struct { int strId; int x; } verbTexts[] = { { STR_Talk, 21 }, { STR_Take, 67 }, { STR_Look, 113 }, { STR_Use, 159 }, { STR_Open, 205 }, { STR_Close, 251 }, { STR_Give, 297 } }; for (int i = 0; i < 7; ++i) { const char *s = getString(verbTexts[i].strId); int x = verbTexts[i].x - getStringWidth(s) / 2; drawString(_verbsPanelBuffer, s, x, 0, 0xF2, -1, 0); } } int dataSize; uint8 *p = loadData(TXT_MainTable, 0, &dataSize); const uint8 *src = &p[0] + _language * 7; for (int i = 0; i < 3; ++i, src += 7 * 2) { decodeMainString(src, _verbPrepositions[i]); debugC(9, kDebugResource, "loadMainTexts() _verbPrepositions[%d] '%s'", i, _verbPrepositions[i]); } src = &p[0x2A] + _language * 31; for (int i = 0; i < 35; ++i, src += 31 * 2) { decodeMainString(src, _globalObjectNames[i]); debugC(9, kDebugResource, "loadMainTexts() _globalObjectNames[%d] '%s'", i, _globalObjectNames[i]); } src = &p[0x8BA] + _language * 51; for (int i = 0; i < 250; ++i, src += 51 * 2) { decodeMainString(src, _globalDialogueTexts[i]); debugC(9, kDebugResource, "loadMainTexts() _globalDialogueTexts[%d] '%s'", i, _globalDialogueTexts[i]); } src = &p[0x6CA4] + _language * 12; for (int i = 0; i < 9; ++i, src += 12 * 2) { decodeMainString(src, _verbsName[i]); debugC(9, kDebugResource, "loadMainTexts() _verbsName[%d] '%s'", i, _verbsName[i]); } free(p); } void IgorEngine::setupDefaultPalette() { memset(_currentPalette, 0, 255 * 3); memset(&_currentPalette[255 * 3], 63, 3); updatePalette(768); } void IgorEngine::updatePalette(int count) { assert(count <= 768); uint8 pal[1024]; for (int j = 0, i = 0; i < count; ++i) { pal[j++] = (_currentPalette[i] << 2) | (_currentPalette[i] >> 4); if (((i + 1) % 3) == 0) { pal[j++] = 0; } } _system->setPalette(pal, 0, count / 3); } void IgorEngine::clearPalette() { memset(_currentPalette, 0, 768); updatePalette(768); } void IgorEngine::setPaletteColor(uint8 index, uint8 r, uint8 g, uint8 b) { uint8 pal[4]; pal[0] = (r << 2) | (r >> 4); pal[1] = (g << 2) | (g >> 4); pal[2] = (b << 2) | (b >> 4); pal[3] = 0; _system->setPalette(pal, index, 1); } void IgorEngine::setPaletteRange(int startColor, int endColor) { debugC(9, kDebugScreen, "setPaletteRange(%d, %d)", startColor, endColor); assert(endColor - startColor + 1 <= 256); for (int i = startColor; i <= endColor; ++i) { setPaletteColor(i, _currentPalette[3 * i], _currentPalette[3 * i + 1], _currentPalette[3 * i + 2]); } } void IgorEngine::fadeInPalette(int count) { debugC(9, kDebugScreen, "fadeInPalette(%d)", count); _system->copyRectToScreen(_screenVGA, 320, 0, _screenVGAVOffset, 320, 200 - _screenVGAVOffset); int m = 66; do { m -= 3; for (int i = 0; i < count; ++i) { if (_paletteBuffer[i] >= m) { uint8 color = _currentPalette[i] + 3; if (color > _paletteBuffer[i]) { color = _paletteBuffer[i]; } _currentPalette[i] = color; } } updatePalette(count); _system->updateScreen(); _system->delayMillis(1000 / 60); } while (m > 0); } void IgorEngine::fadeOutPalette(int count) { debugC(9, kDebugScreen, "fadeOutPalette(%d)", count); _system->copyRectToScreen(_screenVGA, 320, 0, _screenVGAVOffset, 320, 200 - _screenVGAVOffset); memcpy(_paletteBuffer, _currentPalette, 768); int m = 0; do { for (int i = 0; i < count; ++i) { if (_paletteBuffer[i] >= m) { uint8 color = _currentPalette[i]; if (color >= 3) { color -= 3; } else { color = 0; } _currentPalette[i] = color; } } updatePalette(count); _system->updateScreen(); _system->delayMillis(1000 / 60); m += 3; } while (m < 66); } void IgorEngine::scrollPalette(int startColor, int endColor) { debugC(9, kDebugScreen, "scrollPalette(%d, %d)", startColor, endColor); uint8 c[3]; memcpy(c, &_currentPalette[startColor * 3], 3); memmove(&_currentPalette[startColor * 3], &_currentPalette[(startColor + 1) * 3], (endColor - startColor) * 3); memcpy(&_currentPalette[endColor * 3], c, 3); } void IgorEngine::drawChar(uint8 *dst, int chr, int x, int y, int color1, int color2, int color3) { dst += y * 320 + x; for (int j = 0; j < 11; ++j, dst += 320) { uint32 chrLineMask = _fontData[chr * 11 + j]; for (int i = 0; i < 9; ++i, chrLineMask >>= 2) { switch (chrLineMask & 3) { case 1: dst[i] = color1; break; case 2: if (color2 != -1) { dst[i] = color2; } break; case 3: if (color3 != -1) { dst[i] = color3; } break; } } } } void IgorEngine::drawString(uint8 *dst, const char *s, int x, int y, int color1, int color2, int color3) { for (; *s; ++s) { if (*s == ' ') { x += 5; } else { int chr = _fontCharIndex[(uint8)*s]; if (chr == 99) { continue; } if (x + _fontCharWidth[chr] > 320) { break; } drawChar(dst, chr, x, y, color1, color2, color3); x += _fontCharWidth[chr]; } } } int IgorEngine::getStringWidth(const char *s) const { int w = 0; for (; *s; ++s) { if (*s == ' ') { w += 5; } else { int chr = _fontCharIndex[(uint8)*s]; if (chr == 99) { continue; } w += _fontCharWidth[chr]; } } return w; } void IgorEngine::drawActionSentence(const char *sentence, uint8 color) { memset(_screenVGA + 144 * 320, 0, 11 * 320); int w = getStringWidth(sentence); int x = (320 - w) / 2; drawString(_screenVGA, sentence, x, 144, color, 0, 0); } void IgorEngine::formatActionSentence(uint8 color) { char actionSentence[512]; if (_currentAction.verb == kVerbWalk && _inputVars[kInputCursorYPos] > 143) { _currentAction.object1Num = 0; } strcpy(actionSentence, _verbsName[_currentAction.verb]); if (_currentAction.object1Num != 0) { if (_currentAction.object1Type == kObjectTypeInventory) { strcat(actionSentence, _globalObjectNames[_currentAction.object1Num]); } else { strcat(actionSentence, _roomObjectNames[_currentAction.object1Num]); } if (_currentAction.verbType != 0) { strcat(actionSentence, _verbPrepositions[_currentAction.verbType]); if (_currentAction.object2Num != 0) { if (_currentAction.object2Type == kObjectTypeInventory) { strcat(actionSentence, _globalObjectNames[_currentAction.object2Num]); } else { strcat(actionSentence, _roomObjectNames[_currentAction.object2Num]); } } } } drawActionSentence(actionSentence, _sentenceColorIndex[color]); } const uint8 *IgorEngine::getAnimFrame(int baseOffset, int tableOffset, int frame) { const uint8 *src = _animFramesBuffer + baseOffset; assert(frame >= 1); int frameOffset = READ_LE_UINT16(src + tableOffset + (frame - 1) * 2); return src + frameOffset - 1; } void IgorEngine::decodeAnimFrame(const uint8 *src, uint8 *dst, bool preserveText) { int y = READ_LE_UINT16(src) * 320; src += 2; int h = READ_LE_UINT16(src); src += 2; while (h--) { int w = *src++; int pos = y; while (w--) { pos += *src++; int len = *src++; if (len & 0x80) { uint8 color = *src++; len = 256 - len; if (preserveText) { for (int i = pos; i < pos + len; ++i) { if (dst[i] != kTalkColor && dst[i] != kTalkShadowColor) { dst[i] = color; } } } else { memset(dst + pos, color, len); } pos += len; } else { if (preserveText) { for (int i = pos; i < pos + len; ++i) { if (dst[i] != kTalkColor && dst[i] != kTalkShadowColor) { dst[i] = src[i - pos]; } } } else { memcpy(dst + pos, src, len); } src += len; pos += len; } } y += 320; } } void IgorEngine::copyAnimFrame(int srcOffset, int frame, int frameSize, int w, int h, int dstOffset) { for (int y = 0; y <= h; ++y) { memcpy(_screenLayer1 + y * 320 + dstOffset, _animFramesBuffer + frame * frameSize + y * w + srcOffset, w); } } void IgorEngine::setCursor(int num) { uint8 cursor[11 * 11]; memset(cursor, 0, 11 * 11); const uint8 *mask = &_mouseCursorMask[num * 24]; for (int i = 0; i < 24; ++i) { if (mask[i]) { const int offset = ((int8)_mouseCursorData[i + 24] + 5) * 11 + ((int8)_mouseCursorData[i] + 5); cursor[offset] = 255; } } CursorMan.replaceCursor(cursor, 11, 11, 5, 5, 0); } void IgorEngine::showCursor() { debugC(9, kDebugEngine, "showCursor()"); _roomCursorOn = true; CursorMan.showMouse(_roomCursorOn); } void IgorEngine::hideCursor() { debugC(9, kDebugEngine, "hideCursor()"); _roomCursorOn = false; CursorMan.showMouse(_roomCursorOn); } void IgorEngine::updateRoomLight(int fl) { WalkData *wd = &_walkData[_walkDataLastIndex - 1]; if (wd->scaleHeight != 50 || _gameState.dialogueTextRunning) { return; } int offset = 320 * (wd->y + 1 - wd->scaleWidth); int x = wd->x - _walkWidthScaleTable[wd->scaleHeight - 1] / 2; if (x <= 0) { return; } offset += x; RoomObjectArea *roa; int color = (fl == 0) ? 196 : 195; switch (wd->posNum) { case 2: roa = &_roomObjectAreasTable[_screenLayer2[offset + 1298]]; if (wd->y > roa->y1Lum) { if (wd->y <= roa->y2Lum && _gameState.enableLight == 1) { color -= roa->deltaLum; } _screenVGA[offset + 1298] = color; } break; case 3: roa = &_roomObjectAreasTable[_screenLayer2[offset + 1293]]; if (wd->y > roa->y1Lum) { if (wd->y <= roa->y2Lum && _gameState.enableLight == 1) { color -= roa->deltaLum; } _screenVGA[offset + 1293] = color; } color = (fl == 0) ? 196 : 195; roa = &_roomObjectAreasTable[_screenLayer2[offset + 1296]]; if (wd->y > roa->y1Lum) { if (wd->y <= roa->y2Lum && _gameState.enableLight == 1) { color -= roa->deltaLum; } _screenVGA[offset + 1296] = color; } break; case 4: roa = &_roomObjectAreasTable[_screenLayer2[offset + 1291]]; if (wd->y > roa->y1Lum) { if (wd->y <= roa->y2Lum && _gameState.enableLight == 1) { color -= roa->deltaLum; } _screenVGA[offset + 1291] = color; } break; } } void IgorEngine::drawVerbsPanel() { memcpy(_screenVGA + 320 * 156, _verbsPanelBuffer, 320 * 12); } void IgorEngine::redrawVerb(uint8 verb, bool highlight) { uint8 verbBitmap[44 * 12]; if (verb >= 2 && verb <= 8) { verb -= 2; for (int i = 0; i <= 11; ++i) { for (int j = 0; j <= 43; ++j) { uint8 color = _verbsPanelBuffer[i * 320 + verb * 46 + j]; if (highlight && color != 0) { color += 8; } verbBitmap[i * 44 + j] = color; } } for (int i = 0; i <= 11; ++i) { memcpy(_screenVGA + 320 * 156 + verb * 46 + i * 320, verbBitmap + i * 44, 44); } } } void IgorEngine::drawInventory(int start, int mode) { loadData(IMG_InventoryPanel, _inventoryPanelBuffer); loadData(IMG_Objects, _inventoryImagesBuffer); int y, i; int end = start + 6; int x = 1; for (y = start; y != end; ++y) { if (_inventoryInfo[y - 1] == 0) { for (i = 1; i <= 30; ++i) { memset(_inventoryPanelBuffer + x * 40 - 20 + (i - 1) * 320, 0, 40); } } else { for (i = 1; i <= 30; ++i) { int img = _inventoryInfo[y - 1]; assert(img >= 1); memcpy(_inventoryPanelBuffer + x * 40 - 20 + i * 320 - 321, _inventoryImagesBuffer + (i - 1) * 40 + (_inventoryImages[img - 1] - 1) * 1200, 40); } } ++x; } if (_inventoryInfo[72] == 1) { // 'hide' scroll up for (y = 5; y <= 11; ++y) { for (x = 4; x <= 12; ++x) { uint8 *p = _inventoryPanelBuffer + y * 320 + x - 321; if (*p == 0xF2) { *p = 0xF3; p = _inventoryPanelBuffer + y * 320 + x + 305 - 321; *p = 0xF3; } } } } if (_inventoryInfo[73] <= _inventoryInfo[72] + 6 || _inventoryInfo[72] >= _inventoryInfo[73] - 6) { // 'hide' scroll down for (y = 19; y <= 25; ++y) { for (x = 4; x <= 12; ++x) { uint8 *p = _inventoryPanelBuffer + y * 320 + x - 321; if (*p == 0xF2) { *p = 0xF3; p = _inventoryPanelBuffer + y * 320 + x + 305 - 321; *p = 0xF3; } } } } switch (mode) { case 0: memcpy(_screenVGA + 54400, _inventoryPanelBuffer, 9600); _scrollInventory = false; break; case 1: for (y = 0; y <= 11; ++y) { for (x = 0; x <= 14; ++x) { uint8 *p = _screenVGA + x + y * 320 + 59520; if ((*p & 0x80) != 0) { *p += 8; p = _screenVGA + x + y * 320 + 59825; *p += 8; } } } memmove(_inventoryPanelBuffer + 9600, _inventoryPanelBuffer, 9600); memcpy(_inventoryPanelBuffer, _screenVGA + 54400, 9600); _scrollInventoryStartY = 7; _scrollInventoryEndY = 31; _scrollInventoryDy = 6; _scrollInventory = true; break; case 2: for (y = 0; y <= 11; ++y) { for (x = 0; x <= 14; ++x) { uint8 *p = _screenVGA + x + y * 320 + 55040; if ((*p & 0x80) != 0) { *p += 8; p = _screenVGA + x + y * 320 + 55345; *p += 8; } } } memmove(_inventoryPanelBuffer + 9600, _inventoryPanelBuffer, 9600); memcpy(_inventoryPanelBuffer + 9600, _screenVGA + 54400, 9600); _scrollInventoryStartY = 25; _scrollInventoryEndY = 1; _scrollInventoryDy = -6; _scrollInventory = true; break; } } void IgorEngine::packInventory() { for (int i = 1; i <= _inventoryInfo[73]; ++i) { if (_inventoryImages[i - 1] != 0) { continue; } int count = _inventoryInfo[73] - 1; for (int index = i; index <= count; ++index) { _inventoryImages[index - 1] = _inventoryImages[index]; _inventoryImages[_inventoryImages[index - 1] - 1] = index; } _inventoryImages[_inventoryInfo[73] - 1] = 0; --_inventoryInfo[73]; } } void IgorEngine::scrollInventory() { if (_scrollInventoryStartY == _scrollInventoryEndY) { memcpy(_screenVGA + 54400, _inventoryPanelBuffer + (_scrollInventoryStartY - 1) * 320, 9600); _scrollInventory = false; } else { int offset = 54420; for (int y = _scrollInventoryStartY; y < _scrollInventoryStartY + 29; ++y) { memcpy(_screenVGA + offset, _inventoryPanelBuffer + 320 * y - 300, 280); offset += 320; } _scrollInventoryStartY += _scrollInventoryDy; } } void IgorEngine::addObjectToInventory(int object, int index) { ++_inventoryInfo[73]; _inventoryInfo[_inventoryInfo[73] - 1] = object; _inventoryInfo[index] = _inventoryInfo[73]; _inventoryInfo[72] = _inventoryOffsetTable[(_inventoryInfo[73] - 1) / 7]; drawInventory(_inventoryInfo[72], 0); playSound(51, 1); } void IgorEngine::removeObjectFromInventory(int index) { _inventoryInfo[_inventoryInfo[index] - 1] = 0; _inventoryInfo[index] = 0; packInventory(); if (_inventoryInfo[72] > _inventoryInfo[73]) { _inventoryInfo[72] = _inventoryOffsetTable[(_inventoryInfo[73] - 1) / 7]; } drawInventory(_inventoryInfo[72], 0); playSound(63, 1); } void IgorEngine::executeAction(int action) { debugC(9, kDebugEngine, "executeAction %d", action); assert(action < 200); if (action <= 100) { (this->*_executeMainAction)(action); } else { (this->*_executeRoomAction)(action); } } void IgorEngine::clearAction() { redrawVerb(_currentAction.verb, false); memset(&_currentAction, 0, sizeof(_currentAction)); _currentAction.verb = kVerbWalk; _actionCode = 0; _actionWalkPoint = 0; } void IgorEngine::handleRoomInput() { if (_inputVars[kInputPause]) { _inputVars[kInputPause] = 0; handlePause(); } if (_inputVars[kInputOptions]) { _inputVars[kInputOptions] = 0; handleOptionsMenu(); } if (_inputVars[kInputSkipDialogue] && _gameState.dialogueTextRunning) { _talkDelayCounter = _talkDelay; if (_gameState.talkMode != kTalkModeTextOnly && _talkSpeechCounter > 2) { stopSound(); _talkSpeechCounter = -1; } _inputVars[kInputSkipDialogue] = 0; } if (!_roomCursorOn || _gameState.dialogueTextRunning || _scrollInventory) { return; } if (_inputVars[kInputCursorYPos] >= 156 && _inputVars[kInputCursorYPos] <= 167) { if (_inputVars[kInputClick]) { int verb = getVerbUnderCursor(_inputVars[kInputCursorXPos]); if (verb != _currentAction.verb) { redrawVerb(_currentAction.verb, false); _currentAction.verb = verb; redrawVerb(_currentAction.verb, true); } // reset action command memset(&_currentAction, 0, sizeof(_currentAction)); _currentAction.verb = verb; _currentAction.verbType = 0; formatActionSentence(0); _inputVars[kInputClick] = 0; } return; } if (_inputVars[kInputCursorYPos] >= 172 && _inputVars[kInputCursorYPos] <= 183 && (_inputVars[kInputCursorXPos] < 15 || _inputVars[kInputCursorXPos] > 304)) { if (_inputVars[kInputClick]) { if (_inventoryInfo[72] > 1) { _inventoryInfo[72] -= 7; drawInventory(_inventoryInfo[72], 2); } _inputVars[kInputClick] = 0; } return; } if (_inputVars[kInputCursorYPos] >= 186 && _inputVars[kInputCursorYPos] <= 197 && (_inputVars[kInputCursorXPos] < 15 || _inputVars[kInputCursorXPos] > 304)) { if (_inputVars[kInputClick]) { if (_inventoryInfo[73] > _inventoryInfo[72] + 6) { _inventoryInfo[72] += 7; drawInventory(_inventoryInfo[72], 1); } _inputVars[kInputClick] = 0; } return; } /* if (_inputVars[kInputClick]) { if (_gameState.igorMoving) { _walkDataCurrentPosX = _walkData[_walkDataCurrentIndex - 1].x; _walkDataCurrentPosY = _walkData[_walkDataCurrentIndex - 1].y; if (_roomObjectAreasTable[_screenLayer2[_walkDataCurrentPosY * 320 + _walkDataCurrentPosX].area == 0) { return; } _walkDataCurrentPosX = _walkData[_walkDataCurrentIndex + 1].x; _walkDataCurrentPosY = _walkData[_walkDataCurrentIndex + 1].y; if (_roomObjectAreasTable[_screenLayer2[_walkDataCurrentPosY * 320 + _walkDataCurrentPosX].area == 0) { return; } } _inputVars[kInputClick] = 0; }*/ bool actionHovering = !_inputVars[kInputClick]; _inputVars[kInputClick] = 0; if (actionHovering && _actionCode != 0) { return; } Action previousAction = _currentAction; if (_inputVars[kInputCursorYPos] >= 170 && _inputVars[kInputCursorYPos] <= 199) { int object = getObjectFromInventory(_inputVars[kInputCursorXPos]); if (_currentAction.verbType == 0) { _currentAction.object1Num = object; _currentAction.object1Type = kObjectTypeInventory; if (_currentAction.verb == kVerbUse && _roomActionsTable[_roomDataOffsets.action.useVerb + 10 + _currentAction.object1Num] != 0) { formatActionSentence(0); if (!actionHovering) { _currentAction.verbType = 1; } return; } if (_currentAction.verb == kVerbGive && _roomActionsTable[_roomDataOffsets.action.giveVerb + 10 + _currentAction.object1Num] != 0) { formatActionSentence(0); if (!actionHovering) { _currentAction.verbType = 2; } return; } } else { _currentAction.object2Num = object; _currentAction.object2Type = kObjectTypeInventory; } } else if (_inputVars[kInputCursorYPos] < 144) { int area = _screenLayer2[_inputVars[kInputCursorYPos] * 320 + _inputVars[kInputCursorXPos]]; int object = _roomObjectAreasTable[area].object; if (_currentAction.verbType == 0) { _currentAction.object1Num = object; _currentAction.object1Type = kObjectTypeRoom; if (_currentAction.verb == kVerbUse && _roomActionsTable[_roomDataOffsets.action.useVerb + 48 + _currentAction.object1Num] != 0) { formatActionSentence(0); if (!actionHovering) { _currentAction.verbType = 1; } return; } if (_currentAction.verb == kVerbGive && _roomActionsTable[_roomDataOffsets.action.giveVerb + 48 + _currentAction.object1Num] != 0) { formatActionSentence(0); if (!actionHovering) { _currentAction.verbType = 2; } return; } } else { _currentAction.object2Num = object; _currentAction.object2Type = kObjectTypeRoom; } } else { return; } if (_currentAction.verbType == 0) { if (_currentAction.object1Type == kObjectTypeInventory) { _actionCode = _inventoryActionsTable[(_currentAction.verb - 1) * 2 + _currentAction.object1Num * 20]; } else { _actionCode = _roomActionsTable[_roomDataOffsets.action.defaultVerb + _currentAction.verb * 2 + _currentAction.object1Num * 20]; } } if (_currentAction.verbType == 1) { int offset = _roomActionsTable[_roomDataOffsets.action.object2 + _currentAction.object2Num + _currentAction.object2Type * 38] * 2; offset += _roomActionsTable[_roomDataOffsets.action.object1 + _currentAction.object1Num + _currentAction.object1Type * 38] * _roomDataOffsets.action.objectSize; _actionCode = _roomActionsTable[_roomDataOffsets.action.useVerb + offset]; } if (_currentAction.verbType == 2) { int offset = _roomActionsTable[_roomDataOffsets.action.object2 + _currentAction.object2Num + _currentAction.object2Type * 38] * 2; offset += _roomActionsTable[_roomDataOffsets.action.object1 + _currentAction.object1Num + _currentAction.object1Type * 38] * _roomDataOffsets.action.objectSize; _actionCode = _roomActionsTable[_roomDataOffsets.action.giveVerb + offset]; } if (actionHovering) { formatActionSentence(0); _currentAction.object2Num = 0; _actionCode = 0; return; } debugC(9, kDebugEngine, "handleRoomInput() actionCode %d", _actionCode); if (_actionCode == 0) { clearAction(); return; } formatActionSentence(1); if (_currentAction.verbType == 0) { if (_currentAction.object1Type == kObjectTypeRoom) { _actionWalkPoint = _roomActionsTable[_roomDataOffsets.action.defaultVerb + _currentAction.verb * 2 + _currentAction.object1Num * 20 + 1]; if (_actionWalkPoint > 0) { if (_currentAction.object1Num == 0) { // no object selected, just walk _walkToObjectPosX = _inputVars[kInputCursorXPos]; _walkToObjectPosY = _inputVars[kInputCursorYPos]; if (_roomObjectAreasTable[_screenLayer2[_walkToObjectPosY * 320 + _walkToObjectPosX]].area == 0) { fixWalkPosition(&_walkToObjectPosX, &_walkToObjectPosY); } } else { // walk to object int offset = READ_LE_UINT16(_roomActionsTable + _roomDataOffsets.obj.walkPoints + _currentAction.object1Num * 2); _walkToObjectPosX = offset % 320; _walkToObjectPosY = offset / 320; debugC(9, kDebugEngine, "handleRoomInput() walkToObject offset %d (0x%X)", offset, _roomDataOffsets.obj.walkPoints); } if (_gameState.igorMoving) { // stop igor at the current position _walkDataLastIndex = _walkDataCurrentIndex - 1; _walkDataCurrentPosX = _walkData[_walkDataLastIndex].x; _walkDataCurrentPosY = _walkData[_walkDataLastIndex].y; _walkCurrentFrame = _walkData[_walkDataLastIndex].frameNum; _walkCurrentPos = _walkData[_walkDataLastIndex].posNum; WalkData::setNextFrame(_walkCurrentPos, _walkCurrentFrame); } else { --_walkDataLastIndex; _walkDataCurrentPosX = _walkData[_walkDataLastIndex].x; _walkDataCurrentPosY = _walkData[_walkDataLastIndex].y; _walkCurrentPos = _walkData[_walkDataLastIndex].posNum; _walkCurrentFrame = 1; } if (_walkDataCurrentPosX != _walkToObjectPosX || _walkDataCurrentPosY != _walkToObjectPosY) { if (_roomDataOffsets.area.boxSize == 0) { buildWalkPathSimple(_walkDataCurrentPosX, _walkDataCurrentPosY, _walkToObjectPosX, _walkToObjectPosY); } else { buildWalkPath(_walkDataCurrentPosX, _walkDataCurrentPosY, _walkToObjectPosX, _walkToObjectPosY); } if (_actionWalkPoint != 3) { _walkCurrentFrame = 0; _walkData[_walkDataLastIndex].frameNum = 0; } if (_actionWalkPoint == 1) { _walkCurrentPos = _roomActionsTable[_roomDataOffsets.obj.walkFacingPosition + _currentAction.object1Num]; _walkData[_walkDataLastIndex].posNum = _walkCurrentPos; } _walkDataCurrentIndex = 1; _gameState.igorMoving = true; } return; } } hideCursor(); executeAction(_actionCode); if (!_gameState.dialogueTextRunning) { showCursor(); } clearAction(); return; } } void IgorEngine::animateIgorTalking(int frame) { if (getPart() == 4) { return; } if (getPart() == 85) { PART_85_HELPER_6(frame); return; } WalkData *wd = &_walkData[_walkDataLastIndex - 1]; int y = (wd->y - wd->scaleWidth + 1) * 320; int delta = wd->x - _walkWidthScaleTable[wd->scaleHeight - 1] / 2; if (delta > 0) { y += delta; } for (int yOffset = 0; yOffset < wd->scaleWidth; y += 320, ++yOffset) { int index = READ_LE_UINT16(_walkScaleTable + 0x6CE + wd->scaleHeight * 2) + yOffset; uint8 yScale = _walkScaleTable[index]; if (yScale >= 11) { continue; } for (int x = 0, xOffset = wd->clipSkipX - 1; x < wd->clipWidth; ++x, ++xOffset) { index = READ_LE_UINT16(_walkScaleTable + 0x734 + _walkWidthScaleTable[wd->scaleHeight - 1] * 2) + xOffset; uint8 xScale = _walkScaleTable[0x4FC + index]; if (xScale < 8 || xScale > 21) { continue; } uint8 screenColor = _screenVGA[y + x]; if (screenColor < kTalkColor || screenColor > kTalkShadowColor) { int offset = yScale * 14 + frame * 154 + (wd->posNum - 1) * 924 + (xScale - 8); uint8 srcColor = _igorHeadFrames[offset]; if (srcColor == 0) { _screenVGA[y + x] = _screenLayer1[y + x]; continue; } RoomObjectArea *roa = &_roomObjectAreasTable[_screenLayer2[y + x]]; if (wd->y <= roa->y1Lum) { _screenVGA[y + x] = _screenLayer1[y + x]; continue; } if (wd->y <= roa->y2Lum && _gameState.enableLight == 1) { srcColor -= roa->deltaLum; } _screenVGA[y + x] = srcColor; } } } } void IgorEngine::handleRoomDialogue() { if (_gameState.dialogueTextRunning) { if (_talkDelayCounter == _talkDelay) { animateIgorTalking(0); memcpy(_screenVGA + _dialogueDirtyRectY, _screenTextLayer + 23040, _dialogueDirtyRectSize); if (_dialogueTextsCount == 0) { _gameState.dialogueTextRunning = false; showCursor(); } else { ++_dialogueTextsStart; startIgorDialogue(); } } else { animateIgorTalking(getRandomNumber(6)); ++_talkDelayCounter; } } } void IgorEngine::handleRoomIgorWalk() { if (_walkDataCurrentIndex > _walkDataLastIndex) { _gameState.igorMoving = false; _walkDataLastIndex = _walkDataCurrentIndex; if (_actionCode > 0) { hideCursor(); executeAction(_actionCode); if (!_gameState.dialogueTextRunning) { showCursor(); } clearAction(); } } if (_gameState.igorMoving) { moveIgor(_walkData[_walkDataCurrentIndex].posNum, _walkData[_walkDataCurrentIndex].frameNum); ++_walkDataCurrentIndex; } } void IgorEngine::handleRoomInventoryScroll() { if (_scrollInventory) { scrollInventory(); } } void IgorEngine::handleRoomLight() { if (_gameState.dialogueTextRunning || _gameState.igorMoving) { _gameState.updateLight = false; } else if (_gameState.updateLight) { updateRoomLight(0); _gameState.updateLight = 0; } else if (getRandomNumber(10) == 0) { updateRoomLight(1); _gameState.updateLight = true; } } void IgorEngine::enterPartLoop() { if (!_gameState.dialogueTextRunning) { showCursor(); } _gameState.igorMoving = false; if (_game.version == kIdEngDemo110) { CHECK_FOR_END_OF_DEMO(); } } void IgorEngine::leavePartLoop() { hideCursor(); SET_EXEC_ACTION_FUNC(1, 0); _updateRoomBackground = 0; } void IgorEngine::runPartLoop() { handleRoomInput(); if (compareGameTick(1, 16)) { handleRoomIgorWalk(); } if (compareGameTick(19, 32)) { handleRoomDialogue(); } if (compareGameTick(4, 8)) { handleRoomInventoryScroll(); } if (compareGameTick(1)) { handleRoomLight(); } if (_updateRoomBackground) { (this->*_updateRoomBackground)(); } waitForTimer(); } int IgorEngine::lookupScale(int xOffset, int yOffset, int h) const { int index = READ_LE_UINT16(_walkScaleTable + 0x734 + _walkWidthScaleTable[h - 1] * 2); int offset = _walkScaleTable[0x4FC + index + xOffset]; index = READ_LE_UINT16(_walkScaleTable + 0x6CE + h * 2); offset += _walkScaleTable[index + yOffset] * 30; return offset; } void IgorEngine::moveIgor(int pos, int frame) { assert(_gameState.enableLight == 1 || _gameState.enableLight == 2); debugC(9, kDebugWalk, "moveIgorHelper _walkDataCurrentIndex %d pos %d frame %d", _walkDataCurrentIndex, pos, frame); WalkData *wd = &_walkData[_walkDataCurrentIndex]; uint8 _walkClipSkipX = wd->clipSkipX; uint8 _walkHeightScale = wd->scaleHeight; int16 _walkClipWidth = wd->clipWidth; uint16 _walkScaleWidth = wd->scaleWidth; uint8 _walkXPosChanged = wd->xPosChanged; int16 _walkDxPos = wd->dxPos + 1; uint8 _walkYPosChanged = wd->yPosChanged; int16 _walkDyPos = wd->dyPos; int16 _walkDataCurrentPosX2 = wd->x; int16 _walkDataCurrentPosY2 = wd->y; uint16 _walkDataDrawOffset = (wd->y - wd->scaleWidth + 1) * 320; int xPos = _walkWidthScaleTable[wd->scaleHeight - 1] / 2; if (wd->x > xPos) { _walkDataDrawOffset += wd->x - xPos; } if (_walkXPosChanged != 0) { _walkDataDrawOffset -= _walkDxPos; } if (_walkYPosChanged != 0) { _walkDataDrawOffset -= _walkDyPos * 320; } if (_gameState.enableLight == 2) { int8 colorLum = _roomObjectAreasTable[_screenLayer2[_walkDataCurrentPosY2 * 320 + _walkDataCurrentPosX2]].y2Lum; if (_gameState.colorLum != colorLum) { for (int color = 192 * 3; color <= 207 * 3; ++color) { int c = _currentPalette[color] + colorLum; if (c < 1) { c = 0; } else if (c > 62) { c = 63; } _currentPalette[color] = c; } setPaletteRange(192, 207); _gameState.colorLum = colorLum; } } uint16 screenIgorDrawOffset = _walkDataDrawOffset; uint16 igorScaledWidth = _walkDxPos + _walkClipWidth; uint16 igorScaledHeight = _walkHeightScale + _walkDyPos; uint16 igorBodyScanLine = 0; if (_walkYPosChanged != 0) { for (int i = 1; i <= _walkDyPos; ++i) { memcpy(_igorTempFrames + igorBodyScanLine * 50, _screenLayer1 + _walkDataDrawOffset, igorScaledWidth); _walkDataDrawOffset += 320; ++igorBodyScanLine; } } if (_walkXPosChanged != 0) { for (int yOffset = 0; yOffset < _walkScaleWidth; ++yOffset) { assert(_walkDxPos > 0); assert(igorBodyScanLine * 50 + _walkDxPos <= 3000); memcpy(_igorTempFrames + igorBodyScanLine * 50, _screenLayer1 + _walkDataDrawOffset, _walkDxPos); int xOffset = _walkClipSkipX - 1; for (int i = 0; i < _walkClipWidth; ++i) { int offset = lookupScale(xOffset, yOffset, _walkHeightScale); offset += frame * 1500; uint8 color = _facingIgorFrames[pos - 1][offset]; if (color != 0) { assert(_walkDataDrawOffset + _walkDxPos + i >= 0); int index = _screenLayer2[_walkDataDrawOffset + _walkDxPos + i]; int yPos = _roomObjectAreasTable[index].y1Lum; if (wd->y <= yPos) { _igorTempFrames[igorBodyScanLine * 50 + i + _walkDxPos] = _screenLayer1[_walkDataDrawOffset + _walkDxPos + i]; } else { if (_gameState.enableLight == 1 && wd->y <= _roomObjectAreasTable[index].y2Lum) { color -= _roomObjectAreasTable[index].deltaLum; } _igorTempFrames[igorBodyScanLine * 50 + i + _walkDxPos] = color; } } else { _igorTempFrames[igorBodyScanLine * 50 + i + _walkDxPos] = _screenLayer1[_walkDataDrawOffset + _walkDxPos + i]; } ++xOffset; } _walkDataDrawOffset += 320; ++igorBodyScanLine; } } else { for (int yOffset = 0; yOffset < _walkScaleWidth; ++yOffset) { int xOffset = _walkClipSkipX - 1; for (int i = 0; i < _walkClipWidth; ++i) { int offset = lookupScale(xOffset, yOffset, _walkHeightScale); offset += frame * 1500; uint8 color = _facingIgorFrames[pos - 1][offset]; if (color != 0) { assert(_walkDataDrawOffset + i >= 0); int index = _screenLayer2[_walkDataDrawOffset + i]; int yPos = _roomObjectAreasTable[index].y1Lum; if (wd->y <= yPos) { _igorTempFrames[igorBodyScanLine * 50 + i] = _screenLayer1[_walkDataDrawOffset + i]; } else { if (_gameState.enableLight == 1 && wd->y <= _roomObjectAreasTable[index].y2Lum) { color -= _roomObjectAreasTable[index].deltaLum; } _igorTempFrames[igorBodyScanLine * 50 + i] = color; } } else { _igorTempFrames[igorBodyScanLine * 50 + i] = _screenLayer1[_walkDataDrawOffset + i]; } ++xOffset; } const uint8 *src = _screenLayer1 + _walkDataDrawOffset + _walkClipWidth; memcpy(_igorTempFrames + igorBodyScanLine * 50 + _walkClipWidth, src, _walkDxPos); _walkDataDrawOffset += 320; ++igorBodyScanLine; } } if (_walkYPosChanged == 0) { for (int i = 1; i <= _walkDyPos; ++i) { memcpy(_igorTempFrames + igorBodyScanLine * 50, _screenLayer1 + _walkDataDrawOffset, igorScaledWidth); _walkDataDrawOffset += 320; ++igorBodyScanLine; } } for (igorBodyScanLine = 0; igorBodyScanLine < igorScaledHeight; ++igorBodyScanLine) { assert(screenIgorDrawOffset + igorScaledWidth <= 320 * 200); memcpy(_screenVGA + screenIgorDrawOffset, _igorTempFrames + igorBodyScanLine * 50, igorScaledWidth); screenIgorDrawOffset += 320; } } void IgorEngine::buildWalkPathSimple(int srcX, int srcY, int dstX, int dstY) { debugC(9, kDebugWalk, "IgorEngine::buildWalkPathSimple(%d, %d, %d, %d)", srcX, srcY, dstX, dstY); if (srcX != dstX || srcY != dstY) { _walkData[0] = _walkData[_walkDataLastIndex]; _walkDataLastIndex = 1; buildWalkPathArea(srcX, srcY, dstX, dstY); --_walkDataLastIndex; } } void IgorEngine::getClosestAreaTrianglePoint(int dstArea, int srcArea, int *dstY, int *dstX, int srcY, int srcX) { int minSqrDist = -1; assert(dstArea >= 1 && srcArea >= 1); const uint8 *p = _roomActionsTable + ((dstArea - 1) + (srcArea - 1) * _roomDataOffsets.area.boxSize) * 6; for (int i = 0; i < 3; ++i) { const uint16 offset = READ_LE_UINT16(p + i * 2); const int yPos = offset / 320; const int xPos = offset % 320; int y = srcY - yPos; int x = srcX - xPos; int sqrDist = y * y + x * x; if (minSqrDist == -1 || sqrDist < minSqrDist) { *dstY = yPos; *dstX = xPos; minSqrDist = sqrDist; } } debugC(9, kDebugWalk, "getClosestAreaTrianglePoint() sqrDist %d pos %d,%d", minSqrDist, *dstX, *dstY); } void IgorEngine::getClosestAreaTrianglePoint2(int dstArea, int srcArea, int *dstY, int *dstX, int srcY1, int srcX1, int srcY2, int srcX2) { int minSqrDist = -1; assert(dstArea >= 1 && srcArea >= 1); const uint8 *p = _roomActionsTable + ((dstArea - 1) + (srcArea - 1) * _roomDataOffsets.area.boxSize) * 6; for (int i = 0; i < 3; ++i) { const uint16 offset = READ_LE_UINT16(p + i * 2); const int yPos = offset / 320; const int xPos = offset % 320; int y1 = srcY1 - yPos; int x1 = srcX1 - xPos; int y2 = srcY2 - yPos; int x2 = srcX2 - xPos; int sqrDist = y1 * y1 + x1 * x1 + y2 * y2 + x2 * x2; if (minSqrDist == -1 || sqrDist < minSqrDist) { *dstY = yPos; *dstX = xPos; minSqrDist = sqrDist; } } debugC(9, kDebugWalk, "getClosestAreaTrianglePoint2() sqrDist %d pos %d,%d", minSqrDist, *dstX, *dstY); } void IgorEngine::buildWalkPath(int srcX, int srcY, int dstX, int dstY) { if (srcX != dstX || srcY != dstY) { _walkData[0] = _walkData[_walkDataLastIndex]; _walkDataLastIndex = 1; int srcArea = _roomObjectAreasTable[_screenLayer2[srcY * 320 + srcX]].area; int dstArea = _roomObjectAreasTable[_screenLayer2[dstY * 320 + dstX]].area; debugC(9, kDebugWalk, "srcArea = %d dstArea = %d", srcArea, dstArea); int currentArea = srcArea; for (int i = 1; dstArea != currentArea; ++i) { const int boxOffset = srcArea * _roomDataOffsets.area.boxSrcSize + dstArea * _roomDataOffsets.area.boxDstSize; int nextArea = _roomActionsTable[boxOffset + i + _roomDataOffsets.area.box]; debugC(9, kDebugWalk, "nextArea %d (%d,%d,%d)", nextArea, _roomDataOffsets.area.box, _roomDataOffsets.area.boxSrcSize, _roomDataOffsets.area.boxDstSize); int nextPosX, nextPosY; if (dstArea != nextArea) { getClosestAreaTrianglePoint(nextArea, currentArea, &nextPosY, &nextPosX, srcY, srcX); } else { getClosestAreaTrianglePoint2(nextArea, currentArea, &nextPosY, &nextPosX, dstY, dstX, srcY, srcX); } debugC(9, kDebugWalk, "buildWalkPath() transitionArea = %d next %d,%d pos %d,%d offset 0x%X", nextArea, nextPosX, nextPosY, dstX, dstY, _roomDataOffsets.area.box); buildWalkPathArea(srcX, srcY, nextPosX, nextPosY); srcX = nextPosX; srcY = nextPosY; currentArea = nextArea; } buildWalkPathArea(srcX, srcY, dstX, dstY); --_walkDataLastIndex; } debugC(9, kDebugWalk, "buildWalkPath() end _walkDataLastIndex %d", _walkDataLastIndex); } void IgorEngine::buildWalkPathArea(int srcX, int srcY, int dstX, int dstY) { if (srcX != dstX || srcY != dstY) { const int dx = dstX - srcX; const int dy = dstY - srcY; debugC(9, kDebugWalk, "buildWalkPathArea() dx = %d dy = %d src %d,%d dst %d,%d", dx, dy, srcX, srcY, dstX, dstY); assert(_walkDataLastIndex > 0); if (ABS(dy) * 2 > ABS(dx)) { if (srcY > dstY) { buildWalkPathAreaUpDirection(srcX, srcY, dstX, dstY); } else { buildWalkPathAreaDownDirection(srcX, srcY, dstX, dstY); } } else { if (srcX < dstX) { buildWalkPathAreaRightDirection(srcX, srcY, dstX, dstY); } else { buildWalkPathAreaLeftDirection(srcX, srcY, dstX, dstY); } } } } static int16 roundReal(float f) { return (int16)(f + .5); } static int16 truncReal(float f) { return (int16)f; } int IgorEngine::getVerticalStepsCount(int minX, int minY, int maxX, int maxY) { debugC(9, kDebugWalk, "getVerticalStepsCount() %d %d %d %d", minX, minY, maxX, maxY); int curX = 2; if ((_walkXScaleRoom[minX] != 1 || _walkXScaleRoom[maxX] != 3) && (_walkXScaleRoom[maxX] != 1 || _walkXScaleRoom[minX] != 3)) { curX = _walkXScaleRoom[minX]; } int curY = minY; int count = 0; while (1) { uint8 scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY]; scale = _walkScaleTable[0x901 + scale]; if (maxY - curY <= scale) { break; } curY += scale; ++count; } return count; } int IgorEngine::getHorizontalStepsCount(int minX, int minY, int maxX, int maxY) { uint8 scale, frame = _walkCurrentFrame; scale = _walkYScaleRoom[(_walkXScaleRoom[maxX] - 1) * 144 + maxY]; float r1 = _walkScaleSpeedTable[scale - 1]; scale = _walkYScaleRoom[(_walkXScaleRoom[minX] - 1) * 144 + minY]; float r2 = _walkScaleSpeedTable[scale - 1]; debugC(9, kDebugWalk, "getHorizontalStepsCount() maxX - minX = %d r1 = %f r2 = %f", maxX - minX, r1, r2); int16 steps = roundReal((maxX - minX) / ((r1 + r2) / 2.0f)); int count = 0; if (steps != 0) { float r3 = (maxY - minY) / (float)steps; int curX = minX; int curY = minY; float r4 = r3; while (1) { scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY]; uint8 c = _walkScaleTable[0x769 + scale * 8 + frame]; if (maxX - curX <= c) { break; } curX += c; curY = minY + truncReal(r4); if (frame == 8) { frame = 1; } else { ++frame; } r4 += r3; ++count; } } return count; } void IgorEngine::lookupScale(int curX, int curY, uint8 &scale, uint8 &xScale, uint8 &yScale) const { scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY]; yScale = _walkScaleTable[0x901 + scale]; xScale = _walkWidthScaleTable[scale - 1]; } void IgorEngine::buildWalkPathAreaUpDirection(int srcX, int srcY, int dstX, int dstY) { int _walkCurrentPosLocalVar = _walkCurrentPos; if (_walkCurrentPos == 3) { _walkCurrentPos = 1; _walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1]; _walkData[_walkDataLastIndex].posNum = (dstX > srcX) ? kFacingPositionRight : kFacingPositionLeft; _walkData[_walkDataLastIndex].frameNum = 5; ++_walkDataLastIndex; } else { _walkCurrentPos = 1; } if (_walkCurrentFrame > 6) { _walkCurrentFrame -= 6; } WalkData *wd; uint8 scale, xScale, yScale; float vStepDist, vStepCur; int curX = srcX; int curY = srcY; if (srcX > dstX) { int vStepsCount = getVerticalStepsCount(dstX, dstY, srcX, srcY); if (vStepsCount > 0) { vStepCur = vStepDist = (srcX - dstX) / (float)vStepsCount; for (int i = 1; i <= vStepsCount; ++i) { wd = &_walkData[_walkDataLastIndex]; curX = srcX - truncReal(vStepCur); lookupScale(curX, curY, scale, xScale, yScale); curY -= yScale; lookupScale(curX, curY, scale, xScale, yScale); wd->setPos(curX, curY, 1, _walkCurrentFrame); WalkData::setNextFrame(kFacingPositionBack, _walkCurrentFrame); wd->setScale(scale, scale); wd->yPosChanged = 0; wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y; int x = xScale - xScale / 2 + curX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 0; wd->dxPos = 0; } else { x = curX - xScale / 2; if (x < 0) { wd->clipWidth = x + xScale; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 0; wd->dxPos = _walkData[_walkDataLastIndex - 1].x - wd->x; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 0; wd->dxPos = _walkData[_walkDataLastIndex - 1].x - wd->x; } } ++_walkDataLastIndex; vStepCur += vStepDist; } } else { if (_walkCurrentPosLocalVar == 1 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) { _walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1]; _walkData[_walkDataLastIndex].posNum = 1; _walkData[_walkDataLastIndex].frameNum = 1; ++_walkDataLastIndex; } } lookupScale(dstX, dstY, scale, xScale, yScale); wd = &_walkData[_walkDataLastIndex]; wd->setPos(dstX, dstY, 1, _walkCurrentFrame); WalkData::setNextFrame(kFacingPositionBack, _walkCurrentFrame); wd->setScale(scale, scale); wd->yPosChanged = 0; wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y; int x = xScale - xScale / 2 + dstX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale / 2); wd->xPosChanged = 0; wd->dxPos = 0; } else { x = dstX - xScale / 2; if (x < 0) { wd->clipWidth = xScale + x; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 0; wd->dxPos = curX - dstX; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 0; wd->dxPos = curX - dstX; } } ++_walkDataLastIndex; } else { int vStepsCount = getVerticalStepsCount(dstX, dstY, srcX, srcY); if (vStepsCount > 0) { vStepCur = vStepDist = (dstX - srcX) / (float)vStepsCount; for (int i = 1; i <= vStepsCount; ++i) { wd = &_walkData[_walkDataLastIndex]; curX = srcX + truncReal(vStepCur); lookupScale(curX, curY, scale, xScale, yScale); curY -= yScale; lookupScale(curX, curY, scale, xScale, yScale); wd->setPos(curX, curY, 1, _walkCurrentFrame); WalkData::setNextFrame(kFacingPositionBack, _walkCurrentFrame); lookupScale(curX, curY, scale, xScale, yScale); wd->setScale(scale, scale); wd->yPosChanged = 0; wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y; int x = xScale - xScale / 2 + curX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 0; wd->dxPos = wd->x - _walkData[_walkDataLastIndex - 1].x; } else { x = curX - xScale / 2; if (x < 0) { wd->clipWidth = x + xScale; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 1; wd->dxPos = 0; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 1; wd->dxPos = wd->x - _walkData[_walkDataLastIndex - 1].x; } } ++_walkDataLastIndex; vStepCur += vStepDist; } } else { if (_walkCurrentPosLocalVar == 1 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) { _walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1]; _walkData[_walkDataLastIndex].posNum = 1; _walkData[_walkDataLastIndex].frameNum = 1; ++_walkDataLastIndex; } } lookupScale(dstX, dstY, scale, xScale, yScale); wd = &_walkData[_walkDataLastIndex]; wd->setPos(dstX, dstY, 1, _walkCurrentFrame); WalkData::setNextFrame(kFacingPositionBack, _walkCurrentFrame); wd->setScale(scale, scale); wd->yPosChanged = 0; wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y; int x = xScale - xScale / 2 + dstX - 1; if (x > 319) { wd->clipSkipX = 1; int16 _dx = x - xScale; int16 _cx = 319 - _dx; wd->clipWidth = _cx; wd->xPosChanged = 0; wd->dxPos = dstX - curX; } else { x = dstX - xScale / 2; if (x < 0) { wd->clipWidth = xScale + x; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 1; wd->dxPos = 0; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 1; wd->dxPos = dstX - curX; } } ++_walkDataLastIndex; } } void IgorEngine::buildWalkPathAreaDownDirection(int srcX, int srcY, int dstX, int dstY) { int _walkCurrentPosLocalVar = _walkCurrentPos; if (_walkCurrentPos == 1) { _walkCurrentPos = 3; _walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1]; _walkData[_walkDataLastIndex].posNum = (srcX > dstX) ? kFacingPositionLeft : kFacingPositionRight; _walkData[_walkDataLastIndex].frameNum = 1; ++_walkDataLastIndex; } else { _walkCurrentPos = 3; } if (_walkCurrentFrame > 6) { _walkCurrentFrame -= 6; } WalkData *wd; uint8 scale, xScale, yScale; float vStepCur, vStepDist; int curX = srcX; int curY = srcY; if (srcX > dstX) { int vStepsCount = getVerticalStepsCount(srcX, srcY, dstX, dstY); if (vStepsCount > 0) { vStepCur = vStepDist = (srcX - dstX) / (float)vStepsCount; for (int i = 1; i <= vStepsCount; ++i) { wd = &_walkData[_walkDataLastIndex]; curX = srcX - truncReal(vStepCur); lookupScale(curX, curY, scale, xScale, yScale); curY += yScale; lookupScale(curX, curY, scale, xScale, yScale); wd->setPos(curX, curY, 3, _walkCurrentFrame); WalkData::setNextFrame(kFacingPositionFront, _walkCurrentFrame); wd->setScale(scale, scale); wd->yPosChanged = 1; wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y; int x = xScale - xScale / 2 + curX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 0; wd->dxPos = 0; } else { x = curX - xScale / 2; if (x < 0) { wd->clipWidth = x + xScale; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 0; wd->dxPos = _walkData[_walkDataLastIndex - 1].x - wd->x; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 0; wd->dxPos = _walkData[_walkDataLastIndex - 1].x - wd->x; } } ++_walkDataLastIndex; vStepCur += vStepDist; } } else { if (_walkCurrentPosLocalVar == 3 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) { _walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1]; _walkData[_walkDataLastIndex].posNum = 3; _walkData[_walkDataLastIndex].frameNum = 1; ++_walkDataLastIndex; } } lookupScale(dstX, dstY, scale, xScale, yScale); wd = &_walkData[_walkDataLastIndex]; wd->setPos(dstX, dstY, 3, _walkCurrentFrame); WalkData::setNextFrame(kFacingPositionFront, _walkCurrentFrame); wd->setScale(scale, scale); wd->yPosChanged = 1; wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y; int x = xScale - xScale / 2 + dstX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 0; wd->dxPos = 0; } else { x = dstX - xScale / 2; if (x < 0) { wd->clipWidth = xScale + x; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 0; wd->dxPos = curX - dstX; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 0; wd->dxPos = curX - dstX; } } ++_walkDataLastIndex; } else { int vStepsCount = getVerticalStepsCount(srcX, srcY, dstX, dstY); if (vStepsCount > 0) { vStepCur = vStepDist = (dstX - srcX) / (float)vStepsCount; for (int i = 1; i <= vStepsCount; ++i) { wd = &_walkData[_walkDataLastIndex]; curX = srcX + truncReal(vStepCur); lookupScale(curX, curY, scale, xScale, yScale); curY += yScale; lookupScale(curX, curY, scale, xScale, yScale); wd->setPos(curX, curY, 3, _walkCurrentFrame); WalkData::setNextFrame(kFacingPositionFront, _walkCurrentFrame); wd->setScale(scale, scale); wd->yPosChanged = 1; wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y; int x = xScale - xScale / 2 + curX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 1; wd->dxPos = wd->x - _walkData[_walkDataLastIndex - 1].x; } else { x = curX - xScale / 2; if (x < 0) { wd->clipWidth = x + xScale; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 1; wd->dxPos = 0; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 1; wd->dxPos = wd->x - _walkData[_walkDataLastIndex - 1].x; } } ++_walkDataLastIndex; vStepCur += vStepDist; } } else { if (_walkCurrentPosLocalVar == 3 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) { _walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1]; _walkData[_walkDataLastIndex].posNum = 3; _walkData[_walkDataLastIndex].frameNum = 1; ++_walkDataLastIndex; } } lookupScale(dstX, dstY, scale, xScale, yScale); wd = &_walkData[_walkDataLastIndex]; wd->setPos(dstX, dstY, 3, _walkCurrentFrame); WalkData::setNextFrame(kFacingPositionFront, _walkCurrentFrame); wd->setScale(scale, scale); wd->yPosChanged = 1; wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y; int x = xScale - xScale / 2 + dstX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 1; wd->dxPos = dstX - curX; } else { x = dstX - xScale / 2; if (x < 0) { wd->clipWidth = xScale + x; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 1; wd->dxPos = 0; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 1; wd->dxPos = dstX - curX; } } ++_walkDataLastIndex; } } void IgorEngine::buildWalkPathAreaRightDirection(int srcX, int srcY, int dstX, int dstY) { int _walkCurrentPosLocalVar = _walkCurrentPos; if (_walkCurrentPos == kFacingPositionLeft) { _walkCurrentPos = kFacingPositionRight; _walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1]; _walkData[_walkDataLastIndex].posNum = (srcY > dstY) ? kFacingPositionBack : kFacingPositionFront; _walkData[_walkDataLastIndex].frameNum = 5; ++_walkDataLastIndex; } else { _walkCurrentPos = kFacingPositionRight; } WalkData *wd; uint8 scale, xScale, xSkip; float hStepCur, hStepDist; int curX = srcX; int curY = srcY; if (srcY > dstY) { int hStepsCount = getHorizontalStepsCount(srcX, dstY, dstX, srcY); if (hStepsCount > 0) { hStepCur = hStepDist = (srcY - dstY) / (float)hStepsCount; for (int i = 1; i <= hStepsCount; ++i) { wd = &_walkData[_walkDataLastIndex]; scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY]; xSkip = _walkScaleTable[0x769 + scale * 8 + _walkCurrentFrame]; if ((curX + xSkip) >= dstX) { break; } curX += xSkip; curY = srcY - truncReal(hStepCur); wd->setPos(curX, curY, 2, _walkCurrentFrame); scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY]; xScale = _walkWidthScaleTable[scale - 1]; wd->setScale(scale, scale); wd->yPosChanged = 0; wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y; int x = xScale - xScale / 2 + curX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 1; wd->dxPos = xSkip; } else { x = x - xScale / 2; if (x < 0) { wd->clipWidth = x + xScale; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 1; wd->dxPos = 0; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 1; wd->dxPos = xSkip; } } ++_walkDataLastIndex; hStepCur += hStepDist; WalkData::setNextFrame(kFacingPositionRight, _walkCurrentFrame); } } else { if (_walkCurrentPosLocalVar == 2 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) { _walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1]; _walkData[_walkDataLastIndex].posNum = 2; _walkData[_walkDataLastIndex].frameNum = 1; ++_walkDataLastIndex; } } wd = &_walkData[_walkDataLastIndex]; wd->setPos(dstX, dstY, 2, _walkCurrentFrame); scale = _walkYScaleRoom[(_walkXScaleRoom[dstX] - 1) * 144 + dstY]; xScale = _walkWidthScaleTable[scale - 1]; wd->setScale(scale, scale); wd->yPosChanged = 0; wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y; int x = xScale - xScale / 2 - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 1; wd->dxPos = dstX - curX; } else { x = dstX - xScale / 2; if (x < 0) { wd->clipWidth = xScale + x; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 1; wd->dxPos = 0; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 1; wd->dxPos = dstX - curX; } } ++_walkDataLastIndex; WalkData::setNextFrame(kFacingPositionLeft, _walkCurrentFrame); } else { int hStepsCount = getHorizontalStepsCount(srcX, srcY, dstX, dstY); if (hStepsCount > 0) { hStepCur = hStepDist = (dstY - srcY) / (float)hStepsCount; for (int i = 1; i <= hStepsCount; ++i) { wd = &_walkData[_walkDataLastIndex]; scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY]; xSkip = _walkScaleTable[0x769 + scale * 8 + _walkCurrentFrame]; if (curX + xSkip >= dstX) { break; } curX += xSkip; curY = srcY + truncReal(hStepCur); _walkData[_walkDataLastIndex].setPos(curX, curY, 2, _walkCurrentFrame); scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY]; xScale = _walkWidthScaleTable[scale - 1]; wd->setScale(scale, scale); wd->yPosChanged = 1; wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y; int x = xScale - xScale / 2 + curX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 1; wd->dxPos = xSkip; } else { x = curX - xScale / 2; if (x < 0) { wd->clipWidth = x + xScale; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 1; wd->dxPos = 0; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 1; wd->dxPos = xSkip; } } ++_walkDataLastIndex; hStepCur += hStepDist; WalkData::setNextFrame(kFacingPositionRight, _walkCurrentFrame); } } else { if (_walkCurrentPosLocalVar == 2 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) { _walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1]; _walkData[_walkDataLastIndex].posNum = 2; _walkData[_walkDataLastIndex].frameNum = 1; ++_walkDataLastIndex; } } wd = &_walkData[_walkDataLastIndex]; wd->setPos(dstX, dstY, 2, _walkCurrentFrame); scale = _walkYScaleRoom[(_walkXScaleRoom[dstX] - 1) * 144 + dstY]; xScale = _walkWidthScaleTable[scale - 1]; wd->setScale(scale, scale); wd->yPosChanged = 1; wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y; int x = xScale - xScale / 2 + dstX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 1; wd->dxPos = dstX - curX; } else { x = dstX - xScale / 2; if (x < 0) { wd->clipWidth = xScale + x; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 1; wd->dxPos = 0; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 1; wd->dxPos = dstX - curX; } } ++_walkDataLastIndex; WalkData::setNextFrame(kFacingPositionRight, _walkCurrentFrame); } } void IgorEngine::buildWalkPathAreaLeftDirection(int srcX, int srcY, int dstX, int dstY) { int _walkCurrentPosLocalVar = _walkCurrentPos; if (_walkCurrentPos == kFacingPositionRight) { _walkCurrentPos = kFacingPositionLeft; _walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1]; _walkData[_walkDataLastIndex].posNum = (srcY > dstY) ? kFacingPositionBack : kFacingPositionFront; _walkData[_walkDataLastIndex].frameNum = 5; ++_walkDataLastIndex; } else { _walkCurrentPos = kFacingPositionLeft; } WalkData *wd; uint8 scale, xScale, xSkip; float hStepCur, hStepDist; int curX = srcX; int curY = srcY; if (srcY > dstY) { int hStepsCount = getHorizontalStepsCount(dstX, dstY, srcX, srcY); if (hStepsCount > 0) { hStepCur = hStepDist = (srcY - dstY) / (float)hStepsCount; for (int i = 1; i <= hStepsCount; ++i) { wd = &_walkData[_walkDataLastIndex]; scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY]; xSkip = _walkScaleTable[0x769 + scale * 8 + _walkCurrentFrame]; if ((curX - xSkip) <= dstX) { break; } curX -= xSkip; curY = srcY - truncReal(hStepCur); wd->setPos(curX, curY, 4, _walkCurrentFrame); scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY]; xScale = _walkWidthScaleTable[scale - 1]; wd->setScale(scale, scale); wd->yPosChanged = 0; wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y; int x = xScale - xScale / 2 + curX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 0; wd->dxPos = 0; } else { x = curX - xScale / 2; if (x < 0) { wd->clipWidth = x + xScale; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 0; wd->dxPos = xSkip; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 0; wd->dxPos = xSkip; } } ++_walkDataLastIndex; hStepCur += hStepDist; WalkData::setNextFrame(kFacingPositionLeft, _walkCurrentFrame); } } else { if (_walkCurrentPosLocalVar == 4 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) { _walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1]; _walkData[_walkDataLastIndex].posNum = 4; _walkData[_walkDataLastIndex].frameNum = 1; ++_walkDataLastIndex; } } wd = &_walkData[_walkDataLastIndex]; wd->setPos(dstX, dstY, 4, _walkCurrentFrame); scale = _walkYScaleRoom[(_walkXScaleRoom[dstX] - 1) * 144 + dstY]; xScale = _walkWidthScaleTable[scale - 1]; wd->setScale(scale, scale); wd->yPosChanged = 0; wd->dyPos = curY - dstY; int x = xScale - xScale / 2 + dstX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 0; wd->dxPos = 0; } else { x = dstX - xScale / 2; if (x < 0) { wd->clipWidth = xScale + x; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 0; wd->dxPos = curX - dstX; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 0; wd->dxPos = curX - dstX; } } ++_walkDataLastIndex; WalkData::setNextFrame(kFacingPositionLeft, _walkCurrentFrame); } else { int hStepsCount = getHorizontalStepsCount(dstX, srcY, srcX, dstY); if (hStepsCount > 0) { hStepCur = hStepDist = (dstY - srcY) / (float)hStepsCount; for (int i = 1; i <= hStepsCount; ++i) { wd = &_walkData[_walkDataLastIndex]; scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY]; xSkip = _walkScaleTable[0x769 + scale * 8 + _walkCurrentFrame]; if (curX - xSkip <= dstX) { break; } curX -= xSkip; curY = srcY + truncReal(hStepCur); wd->setPos(curX, curY, 4, _walkCurrentFrame); scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY]; xScale = _walkWidthScaleTable[scale - 1]; wd->setScale(scale, scale); wd->yPosChanged = 1; wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y; int x = xScale - xScale / 2 + curX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 0; wd->dxPos = 0; } else { x = curX - xScale / 2; if (x < 0) { wd->clipWidth = x + xScale; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 0; wd->dxPos = xSkip; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 0; wd->dxPos = xSkip; } } ++_walkDataLastIndex; hStepCur += hStepDist; WalkData::setNextFrame(kFacingPositionLeft, _walkCurrentFrame); } } else { if (_walkCurrentPosLocalVar == 4 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) { _walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1]; _walkData[_walkDataLastIndex].posNum = 4; _walkData[_walkDataLastIndex].frameNum = 1; ++_walkDataLastIndex; } } wd = &_walkData[_walkDataLastIndex]; wd->setPos(dstX, dstY, 4, _walkCurrentFrame); scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY]; xScale = _walkWidthScaleTable[scale - 1]; wd->setScale(scale, scale); wd->yPosChanged = 1; wd->dyPos = curY - dstY; int x = xScale - xScale / 2 + dstX - 1; if (x > 319) { wd->clipSkipX = 1; wd->clipWidth = 319 - (x - xScale); wd->xPosChanged = 0; wd->dxPos = 0; } else { x = dstX - xScale / 2; if (x < 0) { wd->clipWidth = xScale + x; wd->clipSkipX = xScale - wd->clipWidth + 1; wd->xPosChanged = 0; wd->dxPos = curX - dstX; } else { wd->clipWidth = xScale; wd->clipSkipX = 1; wd->xPosChanged = 0; wd->dxPos = curX - dstX; } } ++_walkDataLastIndex; WalkData::setNextFrame(kFacingPositionLeft, _walkCurrentFrame); } } void IgorEngine::waitForIgorMove() { _gameTicks = 0; do { if (compareGameTick(1, 16)) { if (_walkDataCurrentIndex > _walkDataLastIndex) { _gameState.igorMoving = false; _walkDataLastIndex = _walkDataCurrentIndex; } if (_gameState.igorMoving) { moveIgor(_walkData[_walkDataCurrentIndex].posNum, _walkData[_walkDataCurrentIndex].frameNum); ++_walkDataCurrentIndex; } } if (_updateRoomBackground) { (this->*_updateRoomBackground)(); } waitForTimer(); } while (_gameState.igorMoving); } void IgorEngine::setRoomWalkBounds(int x1, int y1, int x2, int y2) { assert(x1 <= x2 && y1 <= y2); _roomWalkBounds.x1 = x1; _roomWalkBounds.x2 = x2; _roomWalkBounds.y1 = y1; _roomWalkBounds.y2 = y2; } void IgorEngine::fixWalkPosition(int *x, int *y) { int xPos = *x; if (xPos < _roomWalkBounds.x1) { xPos = _roomWalkBounds.x1; } if (xPos > _roomWalkBounds.x2) { xPos = _roomWalkBounds.x2; } if (getPart() == 22) { *x = xPos; *y = _roomWalkBounds.y1; return; } int yPos = *y; if (getPart() == 13) { if (xPos >= 92 && xPos <= 186 && yPos > 127) { *x = xPos; *y = 127; return; } if (xPos >= 191 && xPos <= 289 && yPos > 127) { *x = xPos; *y = 127; return; } } // skip areas from top to bottom while (_roomObjectAreasTable[_screenLayer2[yPos * 320 + xPos]].area == 0 && yPos < _roomWalkBounds.y2) { ++yPos; } if (getPart() == 17) { if (yPos != 143 || _roomObjectAreasTable[_screenLayer2[45760 + xPos]].area != 0) { *x = xPos; *y = yPos; return; } } // skip areas from bottom to top while (_roomObjectAreasTable[_screenLayer2[yPos * 320 + xPos]].area == 0 && yPos > _roomWalkBounds.y1) { --yPos; } *x = xPos; *y = yPos; } void IgorEngine::recolorDialogueChoice(int num, bool highlight) { uint8 *p = _screenVGA + 320 * (11 * num + 135); for (int i = 0; i < 320 * 11; ++i) { if (highlight) { if (p[i] == 240) { p[i] = 241; } } else { if (p[i] == 241) { p[i] = 240; } } } } void IgorEngine::handleDialogue(int x, int y, int r, int g, int b) { _gameState.dialogueStarted = true; _gameState.dialogueChoiceStart = 1; _gameState.dialogueChoiceCount = 1; _dialogueEnded = false; if (getPart() == 12 && _objectsState[44] == 0) { _gameState.dialogueData[6] = 1; dialogueReplyToQuestion(x, y, r, g, b, 40); } do { if (getPart() == 15 && _objectsState[48] == 0) { _gameState.dialogueData[6] = 0; } drawDialogueChoices(); (this->*_updateDialogue)(kUpdateDialogueAnimStanding); _dialogueChoiceSelected = selectDialogue(); if (_dialogueChoiceSelected == 0) { break; } else if (_dialogueChoiceSelected == -1) { return; } dialogueAskQuestion(); dialogueReplyToQuestion(x, y, r, g, b); int offset = (_dialogueInfo[_dialogueChoiceSelected] - 1) * 6 + (_gameState.dialogueChoiceCount - 1) * 30 + (_gameState.dialogueChoiceStart - 1) * _roomDataOffsets.dlg.matSize; int code = _gameState.dialogueData[offset + 5]; if ((code >= 1 && code <= 99) || (getPart() == 15 && code == 1)) { _gameState.dialogueData[offset] = 0; if (getPart() == 21 && (code == 60 || code == 70 || code == 80) && _dialogueInfo[0] == 1) { _gameState.dialogueData[offset + 2] = 4; } if (getPart() == 33 && (code == 21 || code == 22 || code == 23) && _dialogueInfo[0] == 1) { _gameState.dialogueData[offset + 2] = 2; } } debugC(9, kDebugEngine, "handleDialogue() action %d offset %d", _gameState.dialogueData[offset + 2], offset); switch (_gameState.dialogueData[offset + 2]) { case 1: _gameState.dialogueChoiceCount = _gameState.dialogueData[offset + 1]; ++_gameState.dialogueChoiceStart; break; case 2: _gameState.dialogueChoiceCount = _gameState.dialogueData[offset + 1]; --_gameState.dialogueChoiceStart; break; case 4: _gameState.dialogueChoiceCount = _gameState.dialogueData[offset + 1]; _gameState.dialogueChoiceStart -= 2; break; case 0: _dialogueEnded = true; break; } debugC(9, kDebugEngine, "handleDialogue() end %d start %d count %d", _dialogueEnded, _gameState.dialogueChoiceStart, _gameState.dialogueChoiceCount); } while (!_dialogueEnded); memset(_screenVGA + 46080, 0, 17920); drawVerbsPanel(); drawInventory(_inventoryInfo[72], 0); _currentAction.verb = kVerbWalk; _gameState.dialogueStarted = false; } void IgorEngine::drawDialogueChoices() { memset(_screenVGA + 46080, 0, 56 * 320); setPaletteColor(240, 0, 0, 0); _dialogueInfo[0] = 0; for (int i = 1; i <= 5; ++i) { _dialogueInfo[i] = 0; } for (int i = 1; i <= 5; ++i) { int offset = (i - 1) * 6 + (_gameState.dialogueChoiceCount - 1) * 30 + (_gameState.dialogueChoiceStart - 1) * _roomDataOffsets.dlg.matSize; if (_gameState.dialogueData[offset] == 1) { ++_dialogueInfo[0]; _dialogueInfo[_dialogueInfo[0]] = i; int num = _gameState.dialogueData[offset + 3] - 1; char questionText[128]; sprintf(questionText, "@%s %s", _dialogueQuestions[num][0], _dialogueQuestions[num][1]); drawString(_screenVGA, questionText, 0, _dialogueInfo[0] * 11 + 135, 240, 0, 0); } debugC(9, kDebugEngine, "drawDialogueChoices() i %d state %d num %d", i, _gameState.dialogueData[offset], _gameState.dialogueData[offset + 3]); } setPaletteColor(240, _paletteBuffer[0x2F6 + 1], _paletteBuffer[0x2F6 + 2], _paletteBuffer[0x2F6 + 3]); setPaletteColor(241, _paletteBuffer[0x2F0 + 1], _paletteBuffer[0x2F0 + 2], _paletteBuffer[0x2F0 + 3]); } int IgorEngine::selectDialogue() { showCursor(); int hoveredChoice = 0; bool end = false; do { int currentChoice = (_inputVars[kInputCursorYPos] - 134) / 11; if (currentChoice < 0) { currentChoice = 0; } if (currentChoice != hoveredChoice) { if (hoveredChoice != 0) { recolorDialogueChoice(hoveredChoice, false); } hoveredChoice = currentChoice; if (hoveredChoice != 0) { recolorDialogueChoice(hoveredChoice, true); } } if (_inputVars[kInputClick]) { if (hoveredChoice != 0 && hoveredChoice <= _dialogueInfo[0]) { end = true; } _inputVars[kInputClick] = 0; } waitForTimer(); } while (!end && !_eventQuitGame); hideCursor(); return hoveredChoice; } void IgorEngine::dialogueAskQuestion() { memset(_screenVGA + 46080, 0, 17920); int offset = (_dialogueInfo[_dialogueChoiceSelected] - 1) * 6 + (_gameState.dialogueChoiceCount - 1) * 30 + (_gameState.dialogueChoiceStart - 1) * _roomDataOffsets.dlg.matSize; int num = _gameState.dialogueData[offset + 3] - 1; if (getPart() == 17) { num = 5; } debugC(9, kDebugEngine, "dialogueAskQuestion() num %d offset %d", num, offset); strcpy(_globalDialogueTexts[250], _dialogueQuestions[num][0]); strcpy(_globalDialogueTexts[251], _dialogueQuestions[num][1]); if (_globalDialogueTexts[251][0]) { ADD_DIALOGUE_TEXT(250, 2); } else { ADD_DIALOGUE_TEXT(250, 1); } SET_DIALOGUE_TEXT(1, 1); startIgorDialogue(); waitForEndOfIgorDialogue(); } void IgorEngine::dialogueReplyToQuestion(int x, int y, int r, int g, int b, int reply) { if (reply == 0) { int offset = (_dialogueInfo[_dialogueChoiceSelected] - 1) * 6 + (_gameState.dialogueChoiceCount - 1) * 30 + (_gameState.dialogueChoiceStart - 1) * _roomDataOffsets.dlg.matSize; reply = _gameState.dialogueData[offset + 4]; debugC(9, kDebugEngine, "dialogueReplyToQuestion() dialogue choice %d reply %d", _dialogueChoiceSelected, reply); if (reply == 0) { return; } } int offset = 30 + _roomDataOffsets.dlg.matSize + reply; int count = _gameState.dialogueData[offset - 1]; int dialogueIndex = 250; for (int i = 0; i < count; ++i) { int num = _gameState.dialogueData[offset] - 1; int len = _gameState.dialogueData[offset + 1]; debugC(9, kDebugEngine, "dialogueReplyToQuestion() reply %d %d offset %d", num, len, offset); ADD_DIALOGUE_TEXT(dialogueIndex, len); for (int j = 0; j < len; ++j) { strcpy(_globalDialogueTexts[dialogueIndex], _dialogueReplies[num + j]); ++dialogueIndex; } offset += 2; } SET_DIALOGUE_TEXT(1, count); startCutsceneDialogue(x, y, r, g, b); waitForEndOfCutsceneDialogue(x, y, r, g, b); } } // namespace Igor