/* 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/scummsys.h" #include "common/translation.h" #include "common/savefile.h" #include "graphics/thumbnail.h" #include "common/config-manager.h" #include "common/error.h" #include "graphics/cursorman.h" #include "graphics/surface.h" #include "graphics/screen.h" #include "graphics/palette.h" #include "common/system.h" #include "image/iff.h" #include "engines/util.h" #include "common/debug.h" #include "common/debug-channels.h" #include "common/stream.h" #include "common/memstream.h" #include "common/events.h" #include "audio/audiostream.h" #include "audio/mixer.h" #include "audio/decoders/raw.h" #include "gui/saveload.h" #include "video/mve_decoder.h" #include "kingdom/kingdom.h" namespace Kingdom { KingdomGame::KingdomGame(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) { _console = nullptr; _rnd = new Common::RandomSource("kingdom"); DebugMan.addDebugChannel(kDebugGeneral, "general", "General debug level"); _logic = nullptr; _asPtr = nullptr; _quit = false; _kingartEntries = nullptr; _tickCount = 0; _oldTime = g_system->getMillis(); _showHotspots = false; const Common::FSNode gameDataDir(ConfMan.get("path")); SearchMan.addSubDirectoryMatching(gameDataDir, "MAPS"); SearchMan.addSubDirectoryMatching(gameDataDir, "PICS"); SearchMan.addSubDirectoryMatching(gameDataDir, "SOUNDS"); SearchMan.addSubDirectoryMatching(gameDataDir, "SOUNDM"); SearchMan.addSubDirectoryMatching(gameDataDir, "MOVIELF"); SearchMan.addSubDirectoryMatching(gameDataDir, "MOVIES"); initVariables(); } void KingdomGame::initVariables() { _tsIconOnly = false; _treeRightSta = 0; _treeRightPic = 0; _treeLeftSta = 0; _treeLeftPic = 0; _treeHGUPic = 0; _treeHGTimer = 0; _treeHGSta = 0; _treeHGPic = 0; _treeEyeTimer = 0; _treeEyeSta = 0; _treeEyePic = 0; _noIFScreen = false; _iconSel = 0; _iconSelect = 0; _iconsClosed = false; for (int i = 0; i < 7; i++) _iconPic[i] = 0; _iconRedraw = false; _healthTmr = 0; _palStepFlag = false; _skylarTimerFlag = false; _skylarTimer = 0; _cTimerFlag = false; _cTimer = 0; _bTimerFlag = false; _bTimer = 0; _aTimerFlag = false; _aTimer = 0; _zoom = 0; _wizard = false; _userInput = 0; // _track = 0; // _statMap = 0; // _quitFlag = 0; _soundNumber = 0; _pMovie = 0; _oldTRS = 0; _oldTLS = 0; _oldIconsClosed = false; _fstFwd = false; _noMusic = false; _mouseValue = 0; _mapEx = false; _loopFlag = false; _lastSound = 0; _keyActive = false; _itemInhibit = false; _gameMode = 0; _fullScreen = false; _frameStop = 0; _daelonCntr = 0; _sound = false; _asMode = false; for (int i = 0; i < 510; i++) { _rezPointers[i] = nullptr; _rezSize[i] = 0; } _mouseDebound = false; _mouseButton = 0; _cursorDrawn = false; _oldCursorPos = Common::Point(0, 0); _oldCursorDef = 0; _cursorDef = 0; _cursorPos = Common::Point(0, 0); _asMap = 0; } KingdomGame::~KingdomGame() { delete _console; delete _logic; delete _rnd; } bool KingdomGame::isDemo() const { return (bool)(_gameDescription->flags & ADGF_DEMO); } Common::Error KingdomGame::run() { initGraphics(320, 200); _console = new Console(this); _logic = new Logic(this); _logic->initVariables(); setupPics(); initTools(); titlePage(); initPlay(); initHelp(); while (!_quit) { _loopFlag = false; _logic->gameHelp(); if (_gameMode == 0) { // Same behavior than in the original. Switching from a // group to a higher one skips refreshScreen and (eventually) // getUserInput if (_logic->_statPlay < 250) _logic->executeOpcode(); if (_logic->_statPlay > 249 && _logic->_statPlay < 500) _logic->executeOpcode(); if (_logic->_statPlay > 499 && _logic->_statPlay < 900) _logic->executeOpcode(); if (_logic->_statPlay > 899) _logic->executeOpcode(); } if (!_loopFlag) getUserInput(); refreshScreen(); } fadeToBlack2(); return Common::kNoError; } void KingdomGame::refreshScreen() { displayDebugHotSpots(); g_system->updateScreen(); checkTimers(); // Signal the ScummVM debugger _console->onFrame(); } void KingdomGame::checkTimers() { uint32 newTime = g_system->getMillis(); int32 delay = 11 - (newTime - _oldTime); if (delay > 0) g_system->delayMillis(delay); _oldTime = newTime; _tickCount++; if (_tickCount == 5) { _tickCount = 0; } else return; if (_aTimer != 0) { _aTimer--; if (_aTimer == 0) _aTimerFlag = true; } if (_bTimer != 0) { _bTimer--; if (_bTimer == 0) _bTimerFlag = true; } if (_cTimer != 0) { _cTimer--; if (_cTimer == 0) { _cTimerFlag = true; _cTimer = 4; } } else _cTimer = 4; if (_skylarTimer != 0) { _skylarTimer--; if (_skylarTimer == 0) _skylarTimerFlag = true; } _palStepFlag = false; } void KingdomGame::drawScreen() { //TODO _console->onFrame(); } void KingdomGame::setupPics() { loadKingArt(); } void KingdomGame::initTools() { initMouse(); //CHECKME: InitTimers? showPic(124); initCursor(); setMouse(); fadeToBlack2(); initMPlayer(); } void KingdomGame::titlePage() { // TODO: Check on QuitFlag == 2 if (shouldQuit()) return; _fstFwd = true; _noIFScreen = true; _sound = false; fadeToBlack2(); playMovie(200); if (shouldQuit()) { return; } fadeToBlack2(); playMovie(206); if (shouldQuit()) { return; } fadeToBlack2(); playMovie(198); if (shouldQuit()) { return; } fadeToBlack2(); } void KingdomGame::initPlay() { for (int i = 0; i < 7; i++) _iconPic[i] = 89 + i; _frameStop = 0; _gameMode = 0; _daelonCntr = 0; _itemInhibit = false; _asMode = false; _aTimerFlag = false; _bTimerFlag = false; _cTimerFlag = false; _skylarTimerFlag = false; _aTimer = 0; _bTimer = 0; _cTimer = 0; _skylarTimer = 0; _mapEx = false; _healthTmr = 0; _treeEyeTimer = 0; _treeHGTimer = 0; _treeHGUPic = 147; _treeLeftPic = 0; _treeRightPic = 0; _treeRightSta = 1; _tsIconOnly = false; _noIFScreen = true; _noMusic = false; _fstFwd = true; delete[] _asPtr; _asPtr = nullptr; _logic->initPlay(); } void KingdomGame::initHelp() { _gameMode = 0; } void KingdomGame::fadeToBlack1() { debug("STUB: FadeToBlack1"); } void KingdomGame::fadeToBlack2() { debug("STUB: FadeToBlack2"); } void KingdomGame::loadKingArt() { loadAResource(0x97); Common::SeekableReadStream *kingartStream = _rezPointers[0x97]; int val = kingartStream->readUint32LE(); int size = val / 4; uint32 *kingartIdx = new uint32[size + 1]; _kingartEntries = new KingArtEntry[size]; kingartIdx[0] = val; for (int i = 1; i < size; i++) kingartIdx[i] = kingartStream->readUint32LE(); kingartIdx[size] = kingartStream->size(); for (int i = 0; i < size; i++) { int chunkSize = kingartIdx[i + 1] - kingartIdx[i]; _kingartEntries[i]._width = kingartStream->readByte(); _kingartEntries[i]._height = kingartStream->readByte(); assert(_kingartEntries[i]._width * _kingartEntries[i]._height == chunkSize - 2); _kingartEntries[i]._data = new byte[chunkSize - 2]; kingartStream->read(_kingartEntries[i]._data, chunkSize - 2); } delete[] kingartIdx; } void KingdomGame::loadAResource(int reznum) { Common::String path = Common::String(_rezNames[reznum]); path.toUppercase(); debug("Loading resource: %i (%s)\n", reznum, path.c_str()); if(!_rezSize[reznum]) { Common::File *file = new Common::File(); if(!file->open(path)) warning("Failed to open %s", path.c_str()); else { _rezSize[reznum] = file->size(); file->seek(0, SEEK_SET); _rezPointers[reznum] = file->readStream(_rezSize[reznum]); file->close(); delete file; } } } void KingdomGame::releaseAResource(int reznum) { if (_rezSize[reznum]) { delete _rezPointers[reznum]; _rezSize[reznum] = 0; } } void KingdomGame::showPic(int reznum) { eraseCursor(); loadAResource(reznum); Image::IFFDecoder decoder; if (!_rezPointers[reznum] || !decoder.loadStream(*_rezPointers[reznum])) return; const byte *palette = decoder.getPalette(); int paletteColorCount = decoder.getPaletteColorCount(); g_system->getPaletteManager()->setPalette(palette, 0, paletteColorCount); const Graphics::Surface *surface = decoder.getSurface(); const byte *data = (const byte *)surface->getPixels(); ::Graphics::Surface *screen = g_system->lockScreen(); for (uint curX = 0; curX < 320; curX++) { for (uint curY = 0; curY < 200; curY++) { const byte *src = data + (curY * 320) + curX; byte *dst = (byte *)screen->getBasePtr(curX, curY); if (*src != 0xFF) *dst = *src; } } g_system->unlockScreen(); g_system->updateScreen(); releaseAResource(reznum); } void KingdomGame::fShowPic(int reznum) { eraseCursor(); fadeToBlack1(); drawRect(4, 17, 228, 161, 0); showPic(reznum); } void KingdomGame::initCursor() { initMouse(); setCursor(0x19C / 4); _cursorDrawn = false; drawCursor(); } void KingdomGame::initMouse() { // No implementation required } void KingdomGame::setMouse() { g_system->warpMouse(272, 157); _cursorPos = Common::Point(272, 157); } void KingdomGame::initMPlayer() { // No implementation for ScummVM, everything is in the player class' constructor } void KingdomGame::playMovie(int movieNum) { if (movieNum == 1 || movieNum == 3 || movieNum == 54 || movieNum == 198 || movieNum == 200 || movieNum == 206) _fullScreen = true; else _fullScreen = false; _mixer->stopAll(); _aTimer = 0; _asMode = false; eraseCursor(); if (!_fullScreen) { _treeLeftSta = (_fstFwd == 0) ? 0 : 1; _treeRightSta = 0; _iconSel = _iconSelect; _iconsClosed = true; checkMainScreen(); setMouse(); _oldCursorPos = _cursorPos; } _pMovie = movieNum; readMouse(); _mouseButton = 0; _keyActive = false; const Common::String path = Common::String::format("King%.3d.mve", movieNum); // Check if the file is available. If not the original does the following: _ATimer = 55, display of error with a check of timer, exit // That can be replaced by an error() int x = _fullScreen ? 0 : 4; int y = _fullScreen ? 0 : 17; Video::MveDecoder *decoder = new Video::MveDecoder(); if (decoder->loadFile(path)) { decoder->setAudioTrack(_sound); decoder->start(); bool skipMovie = false; while (!decoder->endOfVideo() && !skipMovie && !shouldQuit()) { unsigned int delay = MIN(decoder->getTimeToNextFrame(), 10u); g_system->delayMillis(delay); const Graphics::Surface *frame = nullptr; if (decoder->needsUpdate()) { frame = decoder->decodeNextFrame(); } if (frame) { ::Graphics::Surface *screen = g_system->lockScreen(); screen->copyRectToSurface(*frame, x, y, Common::Rect(frame->w, frame->h)); g_system->unlockScreen(); if (decoder->hasDirtyPalette()) { PaletteManager *paletteManager = g_system->getPaletteManager(); decoder->applyPalette(paletteManager); } g_system->updateScreen(); } Common::Event event; while (g_system->getEventManager()->pollEvent(event)) { switch (event.type) { case Common::EVENT_QUIT: case Common::EVENT_RETURN_TO_LAUNCHER: _quit = true; break; case Common::EVENT_KEYDOWN: if (event.kbd.keycode == Common::KEYCODE_ESCAPE) { skipMovie = true; } default: break; } } } } delete decoder; // This is hidden somewhere in RunMovieCtl callback... showPic(300 + _pMovie); if (!_fullScreen) { _treeRightSta = 1; _iconsClosed = false; _iconSel = 9; _treeLeftSta = _logic->_replay ? 2 : 0; checkMainScreen(); drawCursor(); _fstFwd = true; _frameStop = 0; _lastSound = _sound; _sound = false; _userInput = 0; } } void KingdomGame::saveAS() { byte palette[256 * 3]; delete[] _asPtr; _asPtr = new byte[224 * 146 + 768]; g_system->getPaletteManager()->grabPalette(palette, 0, 256); ::Graphics::Surface *screen = g_system->lockScreen(); for (uint curX = 0; curX < 224; curX++) { for (uint curY = 0; curY < 146; curY++) { byte *ptr = (byte *)screen->getBasePtr(curX + 4, curY + 15); _asPtr[curY * 224 + curX] = *ptr; } } for (uint i = 0; i < 768; i++) _asPtr[224 * 146 + i] = palette[i]; g_system->unlockScreen(); g_system->updateScreen(); } void KingdomGame::restoreAS() { byte palette[256 * 3]; for (uint i = 0; i < 768; i++) palette[i] = _asPtr[224 * 146 + i]; g_system->getPaletteManager()->setPalette(palette, 0, 256); ::Graphics::Surface *screen = g_system->lockScreen(); for (uint curX = 0; curX < 224; curX++) { for (uint curY = 0; curY < 146; curY++) { byte *ptr = (byte *)screen->getBasePtr(curX + 4, curY + 15); *ptr = _asPtr[curY * 224 + curX]; } } g_system->unlockScreen(); g_system->updateScreen(); delete[] _asPtr; _asPtr = nullptr; } void KingdomGame::drawHelpScreen() { int picNum; switch(_logic->_health) { case 2: picNum = 166; break; case 4: picNum = 165; break; case 6: picNum = 164; break; case 8: picNum = 163; break; case 10: picNum = 162; break; case 12: default: picNum = 161; break; } if (_noMusic) picNum += 6; showPic(picNum); } void KingdomGame::drawRect(uint minX, uint minY, uint maxX, uint maxY, int color) { ::Graphics::Surface *screen = g_system->lockScreen(); for (uint curX = minX; curX < maxX; curX++) { for (uint curY = minY; curY < maxY; curY++) { byte *dst = (byte *)screen->getBasePtr(curX, curY); *dst = color; } } g_system->unlockScreen(); g_system->updateScreen(); } void KingdomGame::drawEmptyRect(Common::Rect rect, int color) { ::Graphics::Surface *screen = g_system->lockScreen(); for (int curX = rect.left; curX < rect.right; curX++) { byte *dst = (byte *)screen->getBasePtr(curX, rect.top); *dst = color; dst = (byte *)screen->getBasePtr(curX, rect.bottom); *dst = color; } for (int curY = rect.top; curY < rect.bottom; curY++) { byte *dst = (byte *)screen->getBasePtr(rect.left, curY); *dst = color; dst = (byte *)screen->getBasePtr(rect.right, curY); *dst = color; } g_system->unlockScreen(); g_system->updateScreen(); } void KingdomGame::drawInventory() { fShowPic(108); if (_logic->_nodes[28] == 1 || _logic->_nodes[67] == 1 || _itemInhibit) return; if (_logic->_inventory[0] > 0) drawIcon(136, 102, 180); if (_logic->_inventory[1] > 0) drawIcon(73, 65, 175); if (_logic->_inventory[2] > 0) drawIcon(171, 96, 179); if (_logic->_inventory[3] > 0) drawIcon(120, 34, 174); if (_logic->_inventory[4] > 0) drawIcon(160, 41, 177); if (_logic->_inventory[5] > 0) drawIcon(21, 124, 184); if (_logic->_inventory[6] > 0) drawIcon(201, 42, 178); if (_logic->_inventory[7] > 0) drawIcon(76, 119, 186); if (_logic->_inventory[8] > 0) drawIcon(18, 31, 170); if (_logic->_inventory[9] > 0) drawIcon(57, 88, 185); if (_logic->_inventory[10] > 0) drawIcon(182, 124, 181); if (_logic->_inventory[11] > 0) drawIcon(176, 26, 183); if (_logic->_inventory[12] > 0) drawIcon(54, 23, 171); if (_logic->_inventory[13] > 0) drawIcon(120, 133, 182); if (_logic->_inventory[14] > 0) drawIcon(94, 92, 187); if (_logic->_inventory[15] > 0) drawIcon(135, 67, 176); if (_logic->_inventory[16] > 0) drawIcon(84, 30, 173); if (_logic->_inventory[17] > 0) drawIcon(20, 78, 172); if (_logic->_inventory[0] > 0) drawIcon(158, 117, 134 + _logic->_inventory[0]); if (_logic->_inventory[1] > 0) drawIcon(94, 67, 134 + _logic->_inventory[1]); if (_logic->_inventory[2] > 0) drawIcon(193, 105, 134 + _logic->_inventory[2]); if (_logic->_inventory[3] > 0) drawIcon(131, 39, 134 + _logic->_inventory[3]); } Common::String KingdomGame::getSavegameFilename(int slot) { return Common::String::format("%s.%03d", _targetName.c_str(), slot); } void KingdomGame::saveGame() { GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true); int16 savegameId = dialog->runModalWithCurrentTarget(); Common::String savegameDescription = dialog->getResultString(); delete dialog; if (savegameId < 0) return; // dialog aborted saveGameState(savegameId, savegameDescription); } void KingdomGame::restoreGame() { GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false); int16 savegameId = dialog->runModalWithCurrentTarget(); delete dialog; if (savegameId < 0) return; // dialog aborted loadGameState(savegameId); } Common::Error KingdomGame::saveGameState(int slot, const Common::String &desc, bool isAutosave) { Common::String savegameFile = getSavegameFilename(slot); Common::SaveFileManager *saveMan = g_system->getSavefileManager(); Common::OutSaveFile *out = saveMan->openForSaving(savegameFile); if (!out) return Common::kCreatingFileFailed; KingdomSavegameHeader header; header._saveName = desc; writeSavegameHeader(out, header); Common::Serializer s(nullptr, out); synchronize(s); out->finalize(); delete out; return Common::kNoError; } Common::Error KingdomGame::loadGameState(int slot) { Common::String savegameFile = getSavegameFilename(slot); Common::SaveFileManager *saveMan = g_system->getSavefileManager(); Common::InSaveFile *inFile = saveMan->openForLoading(savegameFile); if (!inFile) return Common::kReadingFailed; Common::Serializer s(inFile, nullptr); KingdomSavegameHeader header; if (!readSavegameHeader(inFile, header)) error("Invalid savegame"); if (header._thumbnail) { header._thumbnail->free(); delete header._thumbnail; } // Load most of the savegame data synchronize(s); delete inFile; delete[] _asPtr; _asPtr = nullptr; playSound(_soundNumber); for (int i = 0; i < 7; i++) _iconPic[i] = 89 + i; _frameStop = 0; _gameMode = 0; _asMode = false; _healthTmr = 0; _noIFScreen = false; _iconRedraw = true; _treeRightSta = 1; _aTimerFlag = false; _aTimer = 0; _bTimerFlag = false; _bTimer = 0; _treeEyeTimer = 0; _treeEyePic = 0; _treeHGUPic = 0; _cursorDrawn = false; showPic(106); _gameMode = 0; _iconsClosed = false; drawRect(4, 17, 228, 161, 0); _userInput = 0x43E; _loopFlag = true; return Common::kNoError; } void KingdomGame::synchronize(Common::Serializer &s) { s.syncAsSint16LE(_asMap); s.syncAsSint16LE(_daelonCntr); s.syncAsSint16LE(_pMovie); s.syncAsSint16LE(_soundNumber); s.syncAsSint16LE(_treeEyePic); s.syncAsSint16LE(_treeEyeSta); s.syncAsSint16LE(_treeHGPic); s.syncAsSint16LE(_treeHGSta); s.syncAsSint16LE(_oldTLS); s.syncAsSint16LE(_cTimer); s.syncAsSint16LE(_skylarTimer); s.syncAsByte(_fstFwd); s.syncAsByte(_itemInhibit); s.syncAsByte(_lastSound); s.syncAsByte(_mapEx); s.syncAsByte(_noMusic); s.syncAsByte(_wizard); s.syncAsByte(_tsIconOnly); s.syncAsByte(_cTimerFlag); s.syncAsByte(_skylarTimerFlag); _logic->synchronize(s); // Present in the original. Looks unused. // s.syncAsSint16LE(_StatMap); } const char *const SAVEGAME_STR = "KTFR"; #define SAVEGAME_STR_SIZE 4 #define KTFR_SAVEGAME_VERSION 1 void KingdomGame::writeSavegameHeader(Common::OutSaveFile *out, KingdomSavegameHeader &header) { // Write out a savegame header out->write(SAVEGAME_STR, SAVEGAME_STR_SIZE + 1); out->writeByte(KTFR_SAVEGAME_VERSION); // Write savegame name out->writeString(header._saveName); out->writeByte('\0'); Common::MemoryWriteStreamDynamic *tempThumbnail = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES); Graphics::saveThumbnail(*tempThumbnail); out->write(tempThumbnail->getData(), tempThumbnail->size()); delete tempThumbnail; // Write out the save date/time TimeDate td; g_system->getTimeAndDate(td); out->writeSint16LE(td.tm_year + 1900); out->writeSint16LE(td.tm_mon + 1); out->writeSint16LE(td.tm_mday); out->writeSint16LE(td.tm_hour); out->writeSint16LE(td.tm_min); } bool KingdomGame::readSavegameHeader(Common::InSaveFile *in, KingdomSavegameHeader &header) { char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; header._thumbnail = nullptr; // Validate the header Id in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1); if (strncmp(saveIdentBuffer, SAVEGAME_STR, SAVEGAME_STR_SIZE)) return false; header._version = in->readByte(); if (header._version > KTFR_SAVEGAME_VERSION) return false; // Read in the string header._saveName.clear(); char ch; while ((ch = (char)in->readByte()) != '\0') header._saveName += ch; // Get the thumbnail Graphics::loadThumbnail(*in, header._thumbnail); if (!header._thumbnail) return false; // Read in save date/time header._year = in->readSint16LE(); header._month = in->readSint16LE(); header._day = in->readSint16LE(); header._hour = in->readSint16LE(); header._minute = in->readSint16LE(); return true; } void KingdomGame::playSound(int idx) { if (idx > 43 || _soundNumber == idx) return; // Stop Sound if (_mixer->isSoundHandleActive(_soundHandle)) { _mixer->stopHandle(_soundHandle); releaseAResource(idx); } _soundNumber = idx; if (_soundNumber == 0 || _noMusic) return; int realIdx = _soundNumber + 200; // Or +250, depending in the original on the sound card debug("PlaySound %d : %s", idx, _rezNames[realIdx]); loadAResource(realIdx); Common::SeekableReadStream *soundStream = _rezPointers[realIdx]; Audio::RewindableAudioStream *rewindableStream = Audio::makeRawStream(soundStream, 22050, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN, DisposeAfterUse::NO); _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, Audio::Mixer::kMaxMixerVolume); _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, rewindableStream); // In the original, there's an array describing whether a sound should loop or not. // The array is full of 'false'. If a variant uses looping sound/music, the following code // and the loop array should be added. // Audio::AudioStream *stream = Audio::makeLoopingAudioStream(rewindableStream, _loopArray[idx]); // _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, stream); } void KingdomGame::eraseCursor() { CursorMan.showMouse(false); } void KingdomGame::readMouse() { Common::EventManager *eventManager = g_system->getEventManager(); _mouseButton = eventManager->getButtonState(); _cursorPos = eventManager->getMousePos(); } void KingdomGame::getUserInput() { // CHECKME: _QuitFlag != 0 if (_quit) return; _userInput = waitKey(); if (_quit) return; if (_userInput == 2 && _logic->_eye) _userInput = _asMode ? 0x43B : 0x43A; if (_userInput == 1) _userInput = _mouseValue; if (_userInput == 0x2F5) { _logic->_statPlay = 600; _loopFlag = true; } if (_userInput == 0x42B && _logic->_statPlay == 53 && _gameMode == 0) { _logic->_oldStatPlay = _logic->_statPlay; _logic->_statPlay = 900; _loopFlag = true; } if (_userInput == 0x12D && _logic->_currMap == 1) // CHECKME: _quitFlag = 2; _quit = true; } void KingdomGame::eraseCursorAsm() { CursorMan.showMouse(false); } void KingdomGame::drawLocation() { if (_daelonCntr > 0) _daelonCntr--; playSound(0); _iconsClosed = true; _tsIconOnly = false; _aTimer = 0; _aTimerFlag = false; int emlValue = _emlTable[_logic->_nodeNum]; if (emlValue > 0) _logic->enAll(); if (!_mapEx || !emlValue || _logic->_resurrect) { if (_logic->_statPlay != 50) _logic->_resurrect = false; _iconsClosed = false; } else { _mapEx = false; saveAS(); fShowPic(emlValue); _bTimer = 16; while(_bTimer) { checkTimers(); refreshSound(); checkMainScreen(); } fadeToBlack1(); drawRect(4, 17, 228, 161, 0); _iconsClosed = false; _tsIconOnly = false; } } void KingdomGame::processMap(int mapNum, int zoom) { int var6 = _zoomTable[mapNum][zoom][0]; if (!_asMode) switchAtoM(); fShowPic(var6); _logic->_currMap = _zoomTable[mapNum][zoom][1]; if (zoom > 0) _treeLeftSta = _zoomTable[mapNum][zoom - 1][0] == 0 ? 0 : 3; else _treeLeftSta = 0; if (zoom < 8) _treeRightSta = _zoomTable[mapNum][zoom + 1][0] == 0 ? 0 : 2; else _treeRightSta = 0; } void KingdomGame::processMapInput(int mapNum) { switch(_userInput) { case 0x43B: case 0x443: switchMtoA(); _logic->_mapStat = 0; _logic->_statPlay--; break; case 0x43F: if (_treeLeftSta == 3) { _zoom--; processMap(mapNum, _zoom); } else _userInput = 0; break; case 0x440: if (_treeRightSta == 2) { _zoom++; processMap(mapNum, _zoom); } else _userInput = 0; break; default: if (_userInput > 0x3FF && _userInput < 0x428) { _logic->_statPlay = _mapExit[_userInput - 0x400]; _mapEx = true; _loopFlag = true; _logic->switchAS(); } if (_userInput > 0x440) { switchMtoA(); _logic->_mapStat = false; _logic->_statPlay--; _loopFlag = true; } break; } } void KingdomGame::drawPic(int reznum) { eraseCursor(); loadAResource(reznum); Image::IFFDecoder decoder; if (!decoder.loadStream(*_rezPointers[reznum])) return; const Graphics::Surface *surface = decoder.getSurface(); const byte *data = (const byte *)surface->getPixels(); ::Graphics::Surface *screen = g_system->lockScreen(); for (uint curX = 0; curX < 320; curX++) { for (uint curY = 0; curY < 200; curY++) { const byte *src = data + (curY * 320) + curX; byte *dst = (byte *)screen->getBasePtr(curX, curY); if (*src != 0xFF) *dst = *src; } } g_system->unlockScreen(); g_system->updateScreen(); releaseAResource(reznum); } void KingdomGame::displayIcon(int reznum) { // The demo isn't calling playsound(0). // We keep the call because it stops properly the previous sound, if any. playSound(0); playSound(30); saveAS(); fShowPic(reznum); _bTimer = 76; readMouse(); while(_bTimer != 0 && _mouseButton == 0) { checkTimers(); refreshSound(); readMouse(); } fadeToBlack1(); drawRect(4, 17, 228, 161, 0); restoreAS(); } void KingdomGame::setATimer() { _aTimerFlag = true; _aTimer = 0; int wrkNodeNum = _logic->_nodeNum; if (_logic->_nodes[28] == 1 || _logic->_nodes[67] == 1) return; if (_tsIconOnly) wrkNodeNum = 79; if (_logic->_nodeNum == 56 && _logic->_inventory[8] < 1 && _wizard) wrkNodeNum = 80; for (int i = 0; i < 7; i++) { int idx = _iconActTable[wrkNodeNum][i]; if (_logic->_inventory[idx] > 0) { _aTimerFlag = false; _aTimer = _wizard ? 114 : 133; playSound(0); if (!isDemo()) playSound(34); break; } } } void KingdomGame::refreshSound() { // No implementation needed in ScummVM // The only useful check is against _sndReplayTbl, which determines // which sound should be replayed. But this array contains only false } void KingdomGame::checkMainScreen() { if (_cTimerFlag || _logic->_statPlay == 900 || _logic->_statPlay == 901) return; _cTimerFlag = false; if (_noIFScreen) return; if (_logic->_healthOld != _logic->_health) { if (_healthTmr > 0) _healthTmr--; else { if (_logic->_health <= _logic->_healthOld) _logic->_healthOld--; else _logic->_healthOld++; int iconIndex; if (_logic->_healthOld == 0) iconIndex = 12 - 1; else iconIndex = 12 - _logic->_healthOld; drawIcon(4, 0, iconIndex); _healthTmr = 1; } } if (_iconRedraw) { _iconRedraw = false; drawIcon(4, 0, 12 - _logic->_healthOld); drawIcon(11, 178, _iconPic[0]); drawIcon(38, 178, _iconPic[1]); drawIcon(65, 178, _iconPic[2]); drawIcon(92, 178, _iconPic[3]); drawIcon(119, 178, _iconPic[4]); drawIcon(146, 178, _iconPic[5]); drawIcon(173, 178, _iconPic[6]); _treeLeftPic = 0; _treeRightPic = 0; _treeEyeTimer = 0; if (_skylarTimer != 0 || _aTimer != 0) { _treeHGTimer = 0; _treeHGUPic = 0; } if (_logic->_tideCntl) drawPic(178); } for (int i = 0; i < 7; i++) { int wrkNodeNum = _logic->_nodeNum; if (_tsIconOnly) wrkNodeNum = 79; if (_logic->_nodeNum == 56 && _logic->_inventory[16] < 1 && _wizard) wrkNodeNum = 80; if (_logic->_nodeNum == 21 && _logic->_nodes[21] == 9 && !isDemo()) wrkNodeNum = 81; int idx = _iconActTable[wrkNodeNum][i]; if (_logic->_inventory[idx] >= 1 && _logic->_nodes[28] != 1 && _logic->_nodes[67] != 1 && !_itemInhibit && !_iconsClosed) { if (_iconPic[i] != 12 + idx) { if (_iconPic[i] == 89 + i) _iconPic[i] = 96 + i; else if (_iconPic[i] == 96 + i) _iconPic[i] = 31; else if (_iconPic[i] == 31) _iconPic[i] = 32; else if (_iconPic[i] == 32) _iconPic[i] = 12 + idx; else _iconPic[i] = 89 + i; } } else if (_iconSel != i && _iconPic[i] != 89 + i) { if (_iconPic[i] != 12 + idx) _iconPic[i] = 32; else if (_iconPic[i] == 32) _iconPic[i] = 31; else if (_iconPic[i] == 31) _iconPic[i] = 96 + i; else if (_iconPic[i] == 96 + i) _iconPic[i] = 32; else _iconPic[i] = 89 + i; } else continue; int posX = (27 * i) + 11; drawIcon(posX, 178, _iconPic[i]); } switch (_treeLeftSta) { case 0: if (_treeLeftPic != 33) { drawIcon(243, 141, 33); _treeLeftPic = 33; } break; case 1: if (_treeLeftPic != 34) { drawIcon(243, 141, 34); _treeLeftPic = 34; } break; case 2: if (!_logic->_replay) { if (_treeLeftPic != 33) { drawIcon(243, 141, 33); _treeLeftPic = 33; } } else if (_treeLeftPic != 35) { drawIcon(243, 141, 35); _treeLeftPic = 35; } break; case 3: if (_treeLeftPic != 36) { drawIcon(243, 141, 36); _treeLeftPic = 36; } break; default: _treeLeftPic = 33; _treeLeftSta = 0; drawIcon(243, 141, 33); break; } switch (_treeRightSta) { case 0: if (_treeRightPic == 37) { drawIcon(290, 143, 37); _treeRightPic = 37; } break; case 1: if (_logic->_help) { if (_treeRightPic != 38) { drawIcon(290, 143, 38); _treeRightPic = 38; } } else if (_treeRightPic != 37) { drawIcon(290, 143, 37); _treeRightPic = 37; } break; case 2: if (_treeRightPic != 39) { drawIcon(290, 143, 39); _treeRightPic = 39; } break; default: _treeRightPic = 37; _treeRightSta = 0; drawIcon(290, 143, 37); break; } if (_logic->_eye) { if (_treeEyeTimer == 0) { _treeEyePic = _teaSeq[_treeEyeSta][0]; drawIcon(261, 51, _treeEyePic); _treeEyeTimer = _teaSeq[_treeEyeSta][1]; _treeEyeSta++; if (_treeEyeSta == 5) _treeEyeSta = 0; } else _treeEyeTimer--; } else if (_treeEyePic != 37) { drawIcon(261, 51, 146); _treeEyePic = 37; _treeEyeSta = 0; _treeEyeTimer = 0; } int timer = 0; int delta = 7; // CHECKME: the variable is the same than the one used for the first for(), and the value should therefore be 7 if (_skylarTimer != 0) { delta = 772; timer = _skylarTimer; } if (_aTimer != 0) { delta = 19; timer = _aTimer; } if (timer == 0) { if (_treeHGUPic != 147) { eraseCursor(); drawIcon(249, 171, 147); _treeHGUPic = 147; } } else if (_treeHGTimer == 0) { _treeHGPic = _hgaSeq[_treeHGSta][0]; drawIcon(249, 185, _treeHGPic); _treeHGTimer = _hgaSeq[_treeHGSta][1]; _treeHGSta++; if (_treeHGSta > 3) _treeHGSta = 0; int var2 = 6; while (true) { if (timer <= 1) break; timer -= delta; if (timer > 1) var2--; else { drawIcon(249, 171, 40 + var2); _treeHGUPic = 40 + var2; } } } else _treeHGTimer--; } void KingdomGame::switchAtoM() { _asMode = true; _asMap = _logic->_currMap; saveAS(); _iconSel = 9; _oldTLS = _treeLeftSta; _oldTRS = _treeRightSta; _logic->_oldPouch = _logic->_pouch; _logic->_oldHelp = _logic->_help; _oldIconsClosed = _iconsClosed; _treeLeftSta = 0; _treeRightSta = 0; _logic->_pouch = false; _logic->_help = false; _iconsClosed = true; } void KingdomGame::switchMtoA() { _logic->switchAS(); fadeToBlack1(); drawRect(4, 17, 228, 161, 0); restoreAS(); } void KingdomGame::drawIcon(int x, int y, int index) { const byte *data = _kingartEntries[index]._data; int width = _kingartEntries[index]._width; int height = _kingartEntries[index]._height; ::Graphics::Surface *screen = g_system->lockScreen(); for (int curX = 0; curX < width; curX++) { for (int curY = 0; curY < height; curY++) { const byte *src = data + (curY * width) + curX; byte *dst = (byte *)screen->getBasePtr(curX + x, curY + y); if (*src != 0xFF) *dst = *src; } } g_system->unlockScreen(); g_system->updateScreen(); } int KingdomGame::getAKey() { drawCursor(); if (_mouseButton == 0) { _mouseDebound = false; } else if (_mouseDebound == false) { _mouseDebound = true; return (_mouseButton & 2) ? 2 : 1; } int retval = 0; Common::Event event; while (g_system->getEventManager()->pollEvent(event)) { switch (event.type) { case Common::EVENT_QUIT: case Common::EVENT_RETURN_TO_LAUNCHER: _quit = true; return 0; break; case Common::EVENT_KEYDOWN: if (!event.kbd.hasFlags(Common::KBD_ALT)) { retval = event.kbd.keycode; if (event.kbd.hasFlags(Common::KBD_CTRL)) { retval += 0x100; } } // TODO: Reenable ScummVM custom key commands // else if (event.kbd.keycode == Common::KEYCODE_d && event.kbd.hasFlags(Common::KBD_CTRL)) // _console->attach(); // else if (event.kbd.keycode == Common::KEYCODE_c && event.kbd.hasFlags(Common::KBD_CTRL)) { // _userInput = 0x12D; // _QuitFlag = 2; // _quit = true; // } break; default: break; } } // If no keyboard, check timers if (retval == 0) { refreshSound(); checkMainScreen(); if (_aTimerFlag) { _aTimerFlag = false; retval = 0x2F1; } else if (_bTimerFlag) { _bTimerFlag = false; retval = 0x2F2; } else if (_skylarTimerFlag) { _skylarTimerFlag = false; retval = 0x2F5; } } return retval; } int KingdomGame::waitKey() { return getAKey(); } void KingdomGame::drawCursor() { readMouse(); cursorType(); setCursor(_cursorDef); _oldCursorPos = _cursorPos; _oldCursorDef = _cursorDef; CursorMan.showMouse(true); _cursorDrawn = true; } void KingdomGame::cursorType() { _mouseValue = 0; if (_logic->_currMap != 1 && _logic->_statPlay >= 30) { int startId = _logic->_statPlay == 901 ? 16 : 0; int hotspotCount = _logic->_statPlay == 901 ? 35 : 16; HotSpot *mouseMapMS = isDemo() ? _mouseMapMSDemo : _mouseMapMSFull; for (int i = 0; i < hotspotCount + 1; i++) { if (i == hotspotCount) { int tmpVal = checkMouseMapAS(); if (tmpVal == -1) { cursorTypeExit(); return; } else _mouseValue = tmpVal; } else if (mouseMapMS[startId + i]._area.contains(_cursorPos)) { _mouseValue = mouseMapMS[startId + i]._mouseValue; break; } } } else { int tmpVal = checkMouseMapAS(); if (tmpVal == -1) { cursorTypeExit(); return; } else { _mouseValue = tmpVal; } } switch(_mouseValue) { case 0x18A: if (_logic->_eye) _mouseValue = !_asMode ? 0x43A : 0x43B; else _mouseValue = 0; break; case 0x18C: if (_treeLeftSta == 1) _mouseValue = 0x43D; else if (_treeLeftSta == 3) _mouseValue = 0x43F; else if (_treeLeftSta == 0) _mouseValue = 0; else if (_treeLeftSta == 2 && _logic->_replay) _mouseValue = 0x43E; else _mouseValue = 0; break; case 0x18D: if (_treeRightSta == 1) _mouseValue = _logic->_help ? 0x43C : 0; if (_treeRightSta == 2) _mouseValue = 0x440; break; case 0x24A: // Restore game. // No more check in ScummVM, we display the load screen break; case 0x407: if (_logic->_statPlay == 182 && _logic->_nodes[18] < 9) _mouseValue = 0; break; case 0x40D: if (_logic->_nodes[28] == 1) _mouseValue = 0; break; case 0x41F: if (_logic->_nodes[31] == 0) _mouseValue = 0; break; case 0x422: case 0x425: if (!_wizard) _mouseValue = 0; break; case 0x428: if (_logic->_nodeNum == 5 && _gameMode != 2 && _logic->_spell1) _mouseValue = 0; break; case 0x42A: if (_logic->_nodeNum == 5 && _gameMode != 2 && _logic->_spell2) _mouseValue = 0; break; case 0x42B: if (_logic->_nodeNum == 5 && _gameMode != 2 && _logic->_spell3) _mouseValue = 0; break; case 0x445: if (_logic->_statPlay == 161 && _logic->_nodes[16] == 0 && _wizard) _mouseValue = 0x450; break; case 0x44F: if (!_logic->_pouch) _mouseValue = 0; break; case 0x457: if (!_logic->_tideCntl) _mouseValue = 0; break; } _iconSelect = 9; for (int var6 = 0; var6 < 8; var6++) { if (_mouseValue == 181 + var6) { int var2 = _logic->_nodeNum; if (_tsIconOnly) var2 = 79; if (_logic->_nodeNum == 56 && _logic->_inventory[8] < 1 && _wizard) var2 = 80; int indx = _iconActTable[var2][var6]; if (_logic->_inventory[indx] != 0 && _logic->_nodes[28] != 1 && _logic->_nodes[67] != 1 && !_iconsClosed && !_itemInhibit) { _mouseValue = indx + 0x428; _iconSelect = var6; break; } _mouseValue = 0; } } if (_logic->_currMap == 11) { if (_mouseValue > 0x427 && _mouseValue < 0x43A) { if (_logic->_inventory[_mouseValue - 0x428] < 1) _mouseValue = 0x241; } } cursorTypeExit(); } void KingdomGame::cursorTypeExit() { if (_mouseValue >= 0x400) _cursorDef = _cursorTable[_mouseValue - 0x400]; else _cursorDef = (_mouseValue != 0) ? 0x68 : 0x67; } int KingdomGame::checkMouseMapAS() { HotSpot *curSceneHotspots; if (isDemo()) curSceneHotspots = _mouseMapASDemo[_logic->_currMap]; else curSceneHotspots = _mouseMapASFull[_logic->_currMap]; for (int i = 0; i < 16; i++) { if (curSceneHotspots[i]._area.contains(_cursorPos)) return curSceneHotspots[i]._mouseValue; } if (_logic->_currMap != 11) return -1; if (isDemo()) curSceneHotspots = _mouseMapASDemo[12]; else curSceneHotspots = _mouseMapASFull[12]; for (int i = 0; i < 16; i++) { if (curSceneHotspots[i]._area.contains(_cursorPos)) return curSceneHotspots[i]._mouseValue; } return -1; } void KingdomGame::displayDebugHotSpots() { if (!_showHotspots) return; if (_logic->_currMap != 1 && _logic->_statPlay >= 30) { int startId = _logic->_statPlay == 901 ? 16 : 0; int hotspotCount = _logic->_statPlay == 901 ? 35 : 16; HotSpot *mouseMapMS = isDemo() ? _mouseMapMSDemo : _mouseMapMSFull; for (int i = 0; i < hotspotCount; i++) { if (mouseMapMS[startId + i]._area != Common::Rect(0, 0, 0, 0)) drawEmptyRect(mouseMapMS[startId + i]._area, 0xDF); } } HotSpot *curSceneHotspots; if (isDemo()) curSceneHotspots = _mouseMapASDemo[_logic->_currMap]; else curSceneHotspots = _mouseMapASFull[_logic->_currMap]; for (int i = 0; i < 16; i++) { if (curSceneHotspots[i]._area != Common::Rect(0,0,0,0)) drawEmptyRect(curSceneHotspots[i]._area, 0xFF); } if (_logic->_currMap != 11) return; if (isDemo()) curSceneHotspots = _mouseMapASDemo[12]; else curSceneHotspots = _mouseMapASFull[12]; for (int i = 0; i < 16; i++) { if (curSceneHotspots[i]._area != Common::Rect(0, 0, 0, 0)) drawEmptyRect(curSceneHotspots[i]._area, 0xFF); } } void KingdomGame::setCursor(int cursor) { KingArtEntry Cursor = _kingartEntries[cursor]; CursorMan.replaceCursor(Cursor._data, Cursor._width, Cursor._height, 0, 0, 255); } } // End of namespace Kingdom