diff options
Diffstat (limited to 'engines/cge/cge_main.cpp')
-rw-r--r-- | engines/cge/cge_main.cpp | 1570 |
1 files changed, 1570 insertions, 0 deletions
diff --git a/engines/cge/cge_main.cpp b/engines/cge/cge_main.cpp new file mode 100644 index 0000000000..51cf3bb621 --- /dev/null +++ b/engines/cge/cge_main.cpp @@ -0,0 +1,1570 @@ +/* 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. + * + */ + +/* + * This code is based on original Soltys source code + * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon + */ + +#include "common/scummsys.h" +#include "common/endian.h" +#include "common/memstream.h" +#include "common/savefile.h" +#include "common/serializer.h" +#include "common/str.h" +#include "graphics/palette.h" +#include "graphics/scaler.h" +#include "graphics/thumbnail.h" +#include "cge/vga13h.h" +#include "cge/cge.h" +#include "cge/cge_main.h" +#include "cge/general.h" +#include "cge/sound.h" +#include "cge/snail.h" +#include "cge/text.h" +#include "cge/game.h" +#include "cge/events.h" +#include "cge/talk.h" +#include "cge/vmenu.h" +#include "cge/walk.h" +#include "cge/sound.h" + +namespace CGE { + +const char *savegameStr = "SCUMMVM_CGE"; + +//-------------------------------------------------------------------------- + +const Dac g_stdPal[] = {// R G B + { 0, 60, 0}, // 198 + { 0, 104, 0}, // 199 + { 20, 172, 0}, // 200 + { 82, 82, 0}, // 201 + { 0, 132, 82}, // 202 + { 132, 173, 82}, // 203 + { 82, 0, 0}, // 204 + { 206, 0, 24}, // 205 + { 255, 33, 33}, // 206 + { 123, 41, 0}, // 207 + { 0, 41, 0}, // 208 + { 0, 0, 82}, // 209 + { 132, 0, 0}, // 210 + { 255, 0, 0}, // 211 + { 255, 66, 66}, // 212 + { 148, 66, 16}, // 213 + { 0, 82, 0}, // 214 + { 0, 0, 132}, // 215 + { 173, 0, 0}, // 216 + { 255, 49, 0}, // 217 + { 255, 99, 99}, // 218 + { 181, 107, 49}, // 219 + { 0, 132, 0}, // 220 + { 0, 0, 255}, // 221 + { 173, 41, 0}, // 222 + { 255, 82, 0}, // 223 + { 255, 132, 132}, // 224 + { 214, 148, 74}, // 225 + { 41, 214, 0}, // 226 + { 0, 82, 173}, // 227 + { 255, 214, 0}, // 228 + { 247, 132, 49}, // 229 + { 255, 165, 165}, // 230 + { 239, 198, 123}, // 231 + { 173, 214, 0}, // 232 + { 0, 132, 214}, // 233 + { 57, 57, 57}, // 234 + { 247, 189, 74}, // 235 + { 255, 198, 198}, // 236 + { 255, 239, 173}, // 237 + { 214, 255, 173}, // 238 + { 82, 173, 255}, // 239 + { 107, 107, 107}, // 240 + { 247, 222, 99}, // 241 + { 255, 0, 255}, // 242 + { 255, 132, 255}, // 243 + { 132, 132, 173}, // 244 + { 148, 247, 255}, // 245 + { 148, 148, 148}, // 246 + { 82, 0, 82}, // 247 + { 112, 68, 112}, // 248 + { 176, 88, 144}, // 249 + { 214, 132, 173}, // 250 + { 206, 247, 255}, // 251 + { 198, 198, 198}, // 252 + { 0, 214, 255}, // 253 + { 96, 224, 96 }, // 254 + { 255, 255, 255}, // 255 +}; + +char *CGEEngine::mergeExt(char *buf, const char *name, const char *ext) { + strcpy(buf, name); + char *dot = strrchr(buf, '.'); + if (!dot) + strcat(buf, ext); + + return buf; +} + +int CGEEngine::takeEnum(const char **tab, const char *text) { + const char **e; + if (text) { + for (e = tab; *e; e++) { + if (scumm_stricmp(text, *e) == 0) { + return e - tab; + } + } + } + return -1; +} + +int CGEEngine::newRandom(int range) { + if (!range) + return 0; + + return _randomSource.getRandomNumber(range - 1); +} + +void CGEEngine::sndSetVolume() { + // USeless for ScummVM +} + +void CGEEngine::syncHeader(Common::Serializer &s) { + debugC(1, kCGEDebugEngine, "CGEEngine::syncHeader(s)"); + + int i; + + s.syncAsUint16LE(_now); + s.syncAsUint16LE(_oldLev); + s.syncAsUint16LE(_demoText); + for (i = 0; i < 5; i++) + s.syncAsUint16LE(_game); + s.syncAsSint16LE(i); // unused VGA::Mono variable + s.syncAsUint16LE(_music); + s.syncBytes(_volume, 2); + for (i = 0; i < 4; i++) + s.syncAsUint16LE(_flag[i]); + + if (s.isLoading()) { + // Reset scene values + initSceneValues(); + } + + for (i = 0; i < kSceneMax; i++) { + s.syncAsSint16LE(_heroXY[i].x); + s.syncAsUint16LE(_heroXY[i].y); + } + for (i = 0; i < 1 + kSceneMax; i++) { + s.syncAsByte(_barriers[i]._horz); + s.syncAsByte(_barriers[i]._vert); + } + for (i = 0; i < kPocketNX; i++) + s.syncAsUint16LE(_pocref[i]); + + if (s.isSaving()) { + // Write checksum + int checksum = kSavegameCheckSum; + s.syncAsUint16LE(checksum); + } else { + // Read checksum and validate it + uint16 checksum; + s.syncAsUint16LE(checksum); + if (checksum != kSavegameCheckSum) + error("%s", _text->getText(kBadSVG)); + } +} + +bool CGEEngine::loadGame(int slotNumber, SavegameHeader *header, bool tiny) { + debugC(1, kCGEDebugEngine, "CGEEngine::loadgame(%d, header, %s)", slotNumber, tiny ? "true" : "false"); + + Common::MemoryReadStream *readStream; + SavegameHeader saveHeader; + + if (slotNumber == -1) { + // Loading the data for the initial game state + kSavegame0File file = kSavegame0File(this, kSavegame0Name); + int size = file.size(); + byte *dataBuffer = (byte *)malloc(size); + file.read(dataBuffer, size); + readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES); + + } else { + // Open up the savgame file + Common::String slotName = generateSaveName(slotNumber); + Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName); + + // Read the data into a data buffer + int size = saveFile->size(); + byte *dataBuffer = (byte *)malloc(size); + saveFile->read(dataBuffer, size); + readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES); + } + + // Check to see if it's a ScummVM savegame or not + char buffer[kSavegameStrSize + 1]; + readStream->read(buffer, kSavegameStrSize + 1); + + if (strncmp(buffer, savegameStr, kSavegameStrSize + 1) != 0) { + // It's not, so rewind back to the start + readStream->seek(0); + + if (header) + // Header wanted where none exists, so return false + return false; + } else { + // Found header + if (!readSavegameHeader(readStream, saveHeader)) { + delete readStream; + return false; + } + + if (header) { + *header = saveHeader; + delete readStream; + return true; + } + + // Delete the thumbnail + saveHeader.thumbnail->free(); + delete saveHeader.thumbnail; + } + + // Get in the savegame + syncGame(readStream, NULL, tiny); + + delete readStream; + return true; +} + +/** + * Returns true if a given savegame exists + */ +bool CGEEngine::savegameExists(int slotNumber) { + Common::String slotName = generateSaveName(slotNumber); + + Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName); + bool result = saveFile != NULL; + delete saveFile; + return result; +} + +/** + * Support method that generates a savegame name + * @param slot Slot number + */ +Common::String CGEEngine::generateSaveName(int slot) { + return Common::String::format("%s.%03d", _targetName.c_str(), slot); +} + +Common::Error CGEEngine::loadGameState(int slot) { + // Clear current game activity + sceneDown(); + resetGame(); + + // Load the game + loadGame(slot, NULL); + _commandHandler->addCommand(kCmdLevel, -1, _oldLev, &_sceneLight); + _sceneLight->gotoxy(kSceneX + ((_now - 1) % kSceneNx) * kSceneDx + kSceneSX, + kSceneY + ((_now - 1) / kSceneNx) * kSceneDy + kSceneSY); + sceneUp(); + + return Common::kNoError; +} + +void CGEEngine::resetGame() { + _vga->_spareQ->clear(); +} + +Common::Error CGEEngine::saveGameState(int slot, const Common::String &desc) { + sceneDown(); + _oldLev = _lev; + + // Write out the user's progress + saveGame(slot, desc); + + // Reload the scene + sceneUp(); + + return Common::kNoError; +} + +void CGEEngine::saveGame(int slotNumber, const Common::String &desc) { + // Set up the serializer + Common::String slotName = generateSaveName(slotNumber); + Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(slotName); + + // Write out the ScummVM savegame header + SavegameHeader header; + header.saveName = desc; + header.version = kSavegameVersion; + writeSavegameHeader(saveFile, header); + + // Write out the data of the savegame + syncGame(NULL, saveFile, false); + + // Finish writing out game data + saveFile->finalize(); + delete saveFile; +} + +void CGEEngine::writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header) { + // Write out a savegame header + out->write(savegameStr, kSavegameStrSize + 1); + + out->writeByte(kSavegameVersion); + + // Write savegame name + out->write(header.saveName.c_str(), header.saveName.size() + 1); + + // Get the active palette + uint8 thumbPalette[256 * 3]; + g_system->getPaletteManager()->grabPalette(thumbPalette, 0, 256); + + // Create a thumbnail and save it + Graphics::Surface *thumb = new Graphics::Surface(); + Graphics::Surface *s = _vga->_page[0]; + ::createThumbnail(thumb, (const byte *)s->pixels, kScrWidth, kScrHeight, thumbPalette); + Graphics::saveThumbnail(*out, *thumb); + thumb->free(); + delete thumb; + + // 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); +} + +void CGEEngine::syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream, bool tiny) { + Common::Serializer s(readStream, writeStream); + + if (s.isSaving()) { + for (int i = 0; i < kPocketNX; i++) { + register Sprite *pocSpr = _pocket[i]; + _pocref[i] = (pocSpr) ? pocSpr->_ref : -1; + } + + // Skip Digital and Midi volumes, useless under ScummVM + _volume[0] = 0; + _volume[1] = 0; + } + + // Synchronise header data + syncHeader(s); + + if (s.isSaving()) { + // Loop through saving the sprite data + for (Sprite *spr = _vga->_spareQ->first(); spr; spr = spr->_next) { + if (!s.err()) + spr->sync(s); + } + } else { + // Loading game + if (_soundOk == 1 && _mode == 0) { + // Skip Digital and Midi volumes, useless under ScummVM + sndSetVolume(); + } + + if (!tiny) { // load sprites & pocket + while (readStream->pos() < readStream->size()) { + Sprite S(this, NULL); + S.sync(s); + + S._prev = S._next = NULL; + Sprite *spr = (scumm_stricmp(S._file + 2, "MUCHA") == 0) ? new Fly(this, NULL) + : new Sprite(this, NULL); + assert(spr != NULL); + *spr = S; + _vga->_spareQ->append(spr); + } + + for (int i = 0; i < kPocketNX; i++) { + register int r = _pocref[i]; + _pocket[i] = (r < 0) ? NULL : _vga->_spareQ->locate(r); + } + } + } +} + +bool CGEEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) { + header.thumbnail = NULL; + + // Get the savegame version + header.version = in->readByte(); + if (header.version > kSavegameVersion) + return false; + + // Read in the string + header.saveName.clear(); + char ch; + while ((ch = (char)in->readByte()) != '\0') + header.saveName += ch; + + // Get the thumbnail + header.thumbnail = Graphics::loadThumbnail(*in); + if (!header.thumbnail) { + delete header.thumbnail; + header.thumbnail = NULL; + return false; + } + + // Read in save date/time + header.saveYear = in->readSint16LE(); + header.saveMonth = in->readSint16LE(); + header.saveDay = in->readSint16LE(); + header.saveHour = in->readSint16LE(); + header.saveMinutes = in->readSint16LE(); + + return true; +} + +void CGEEngine::heroCover(int cvr) { + debugC(1, kCGEDebugEngine, "CGEEngine::heroCover(%d)", cvr); + + _commandHandler->addCommand(kCmdCover, 1, cvr, NULL); +} + +void CGEEngine::trouble(int seq, int text) { + debugC(1, kCGEDebugEngine, "CGEEngine::trouble(%d, %d)", seq, text); + + _hero->park(); + _commandHandler->addCommand(kCmdWait, -1, -1, _hero); + _commandHandler->addCommand(kCmdSeq, -1, seq, _hero); + _commandHandler->addCommand(kCmdSound, -1, 2, _hero); + _commandHandler->addCommand(kCmdWait, -1, -1, _hero); + _commandHandler->addCommand(kCmdSay, 1, text, _hero); +} + +void CGEEngine::offUse() { + debugC(1, kCGEDebugEngine, "CGEEngine::offUse()"); + + trouble(kSeqOffUse, kOffUse + newRandom(_offUseCount)); +} + +void CGEEngine::tooFar() { + debugC(1, kCGEDebugEngine, "CGEEngine::tooFar()"); + + trouble(kSeqTooFar, kTooFar); +} + +void CGEEngine::loadHeroXY() { + debugC(1, kCGEDebugEngine, "CGEEngine::loadHeroXY()"); + + EncryptedStream cf(this, "CGE.HXY"); + uint16 x, y; + + memset(_heroXY, 0, sizeof(_heroXY)); + if (!cf.err()) { + for (int i = 0; i < kSceneMax; ++i) { + cf.read((byte *)&x, 2); + cf.read((byte *)&y, 2); + + _heroXY[i].x = (int16)FROM_LE_16(x); + _heroXY[i].y = (int16)FROM_LE_16(y); + } + } +} + +void CGEEngine::loadMapping() { + debugC(1, kCGEDebugEngine, "CGEEngine::loadMapping()"); + + if (_now <= kSceneMax) { + EncryptedStream cf(this, "CGE.TAB"); + if (!cf.err()) { + // Move to the data for the given room + cf.seek((_now - 1) * kMapArrSize); + + // Read in the data + for (int z = 0; z < kMapZCnt; ++z) { + cf.read(&_clusterMap[z][0], kMapXCnt); + } + } + } +} + +Square::Square(CGEEngine *vm) : Sprite(vm, NULL), _vm(vm) { + _flags._kill = true; + _flags._bDel = false; + + BitmapPtr *MB = new BitmapPtr[2]; + MB[0] = new Bitmap(_vm, "BRICK"); + MB[1] = NULL; + setShapeList(MB); +} + +void Square::touch(uint16 mask, int x, int y) { + Sprite::touch(mask, x, y); + if (mask & kMouseLeftUp) { + _vm->XZ(_x + x, _y + y).cell() = 0; + _vm->_commandHandlerTurbo->addCommand(kCmdKill, -1, 0, this); + } +} + +void CGEEngine::setMapBrick(int x, int z) { + debugC(1, kCGEDebugEngine, "CGEEngine::setMapBrick(%d, %d)", x, z); + + Square *s = new Square(this); + if (s) { + char n[6]; + s->gotoxy(x * kMapGridX, kMapTop + z * kMapGridZ); + sprintf(n, "%02d:%02d", x, z); + _clusterMap[z][x] = 1; + s->setName(n); + _vga->_showQ->insert(s, _vga->_showQ->first()); + } +} + +void CGEEngine::keyClick() { + debugC(1, kCGEDebugEngine, "CGEEngine::keyClick()"); + + _commandHandlerTurbo->addCommand(kCmdSound, -1, 5, NULL); +} + +void CGEEngine::resetQSwitch() { + debugC(1, kCGEDebugEngine, "CGEEngine::resetQSwitch()"); + + _commandHandlerTurbo->addCommand(kCmdSeq, 123, 0, NULL); + keyClick(); +} + +void CGEEngine::quit() { + debugC(1, kCGEDebugEngine, "CGEEngine::quit()"); + + static Choice QuitMenu[] = { + { NULL, &CGEEngine::startCountDown }, + { NULL, &CGEEngine::resetQSwitch }, + { NULL, &CGEEngine::dummy } + }; + + if (_commandHandler->idle() && !_hero->_flags._hide) { + if (Vmenu::_addr) { + _commandHandlerTurbo->addCommand(kCmdKill, -1, 0, Vmenu::_addr); + resetQSwitch(); + } else { + QuitMenu[0]._text = _text->getText(kQuit); + QuitMenu[1]._text = _text->getText(kNoQuit); + (new Vmenu(this, QuitMenu, -1, -1))->setName(_text->getText(kQuitTitle)); + _commandHandlerTurbo->addCommand(kCmdSeq, 123, 1, NULL); + keyClick(); + } + } +} + +void CGEEngine::miniStep(int stp) { + debugC(1, kCGEDebugEngine, "CGEEngine::miniStep(%d)", stp); + + if (stp < 0) { + _miniScene->_flags._hide = true; + } else { + *_miniShp[0] = *_miniShpList[stp]; + _miniScene->_flags._hide = false; + } +} + +void CGEEngine::postMiniStep(int step) { + debugC(6, kCGEDebugEngine, "CGEEngine::postMiniStep(%d)", step); + + if (_miniScene && step != _recentStep) + _commandHandlerTurbo->addCallback(kCmdExec, -1, _recentStep = step, kMiniStep); +} + +void CGEEngine::showBak(int ref) { + debugC(1, kCGEDebugEngine, "CGEEngine::showBack(%d)", ref); + + Sprite *spr = _vga->_spareQ->locate(ref); + if (!spr) + return; + + _bitmapPalette = _vga->_sysPal; + spr->expand(); + _bitmapPalette = NULL; + spr->show(2); + _vga->copyPage(1, 2); + _sys->setPal(); + spr->contract(); +} + +void CGEEngine::sceneUp() { + debugC(1, kCGEDebugEngine, "CGEEngine::sceneUp()"); + + const int BakRef = 1000 * _now; + if (_music) + _midiPlayer->loadMidi(_now); + + showBak(BakRef); + loadMapping(); + Sprite *spr = _vga->_spareQ->first(); + while (spr) { + Sprite *n = spr->_next; + if (spr->_scene == _now || spr->_scene == 0) + if (spr->_ref != BakRef) { + if (spr->_flags._back) + spr->backShow(); + else + expandSprite(spr); + } + spr = n; + } + + _sound->stop(); + _fx->clear(); + _fx->preload(0); + _fx->preload(BakRef); + + if (_hero) { + _hero->gotoxy(_heroXY[_now - 1].x, _heroXY[_now - 1].y); + // following 2 lines trims Hero's Z position! + _hero->tick(); + _hero->_time = 1; + _hero->_flags._hide = false; + } + + if (!_dark) + _vga->sunset(); + + _vga->copyPage(0, 1); + selectPocket(-1); + if (_hero) + _vga->_showQ->insert(_vga->_showQ->remove(_hero)); + + if (_shadow) { + _vga->_showQ->remove(_shadow); + _shadow->makeXlat(_vga->glass(_vga->_sysPal, 204, 204, 204)); + _vga->_showQ->insert(_shadow, _hero); + _shadow->_z = _hero->_z; + } + feedSnail(_vga->_showQ->locate(BakRef + 999), kTake); + _vga->show(); + _vga->copyPage(1, 0); + _vga->show(); + _vga->sunrise(_vga->_sysPal); + _dark = false; + if (!_startupMode) + _mouse->on(); +} + +void CGEEngine::sceneDown() { + debugC(1, kCGEDebugEngine, "CGEEngine::sceneDown()"); + + if (_horzLine && !_horzLine->_flags._hide) + switchMapping(); + + for (Sprite *spr = _vga->_showQ->first(); spr;) { + Sprite *n = spr->_next; + if (spr->_ref >= 1000 /*&& spr->_scene*/) { + if (spr->_ref % 1000 == 999) + feedSnail(spr, kTake); + _vga->_spareQ->append(_vga->_showQ->remove(spr)); + } + spr = n; + } +} + +void CGEEngine::xScene() { + debugC(6, kCGEDebugEngine, "CGEEngine::xScene()"); + + sceneDown(); + sceneUp(); +} + +void CGEEngine::qGame() { + debugC(1, kCGEDebugEngine, "CGEEngine::qGame()"); + + sceneDown(); + _oldLev = _lev; + + // Write out the user's progress + saveGame(0, Common::String("Automatic Savegame")); + + _vga->sunset(); + _finis = true; +} + +void CGEEngine::switchScene(int newScene) { + debugC(1, kCGEDebugEngine, "CGEEngine::switchScene(%d)", newScene); + + if (newScene == _now) + return; + + if (newScene < 0) { + _commandHandler->addCommand(kCmdLabel, -1, 0, NULL); // wait for repaint + _commandHandler->addCallback(kCmdExec, -1, 0, kQGame); // quit game + } else { + _now = newScene; + _mouse->off(); + if (_hero) { + _hero->park(); + _hero->step(0); + _vga->_spareQ->_show = 0; + } + _sceneLight->gotoxy(kSceneX + ((_now - 1) % kSceneNx) * kSceneDx + kSceneSX, + kSceneY + ((_now - 1) / kSceneNx) * kSceneDy + kSceneSY); + killText(); + if (!_startupMode) + keyClick(); + _commandHandler->addCommand(kCmdLabel, -1, 0, NULL); // wait for repaint + _commandHandler->addCallback(kCmdExec, 0, 0, kXScene); // switch scene + } +} + +System::System(CGEEngine *vm) : Sprite(vm, NULL), _vm(vm) { + _funDel = kHeroFun0; + setPal(); + tick(); +} + +void System::setPal() { + Dac *p = _vm->_vga->_sysPal + 256 - ARRAYSIZE(g_stdPal); + for (uint i = 0; i < ARRAYSIZE(g_stdPal); i++) { + p[i]._r = g_stdPal[i]._r >> 2; + p[i]._g = g_stdPal[i]._g >> 2; + p[i]._b = g_stdPal[i]._b >> 2; + } +} + +void System::funTouch() { + uint16 n = (_vm->_flag[0]) ? kHeroFun1 : kHeroFun0; // PAIN flag + if (_vm->_talk == NULL || n > _funDel) + _funDel = n; +} + +void System::touch(uint16 mask, int x, int y) { + funTouch(); + + if (mask & kEventKeyb) { + _vm->keyClick(); + _vm->killText(); + if (_vm->_startupMode == 1) { + _vm->_commandHandler->addCommand(kCmdClear, -1, 0, NULL); + return; + } + switch (x) { + case 'X': + if (_vm->_keyboard->_key[kKeyAlt]) + _vm->quit(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + if (_vm->_keyboard->_key[kKeyAlt]) { + _vm->_commandHandler->addCommand(kCmdLevel, -1, x - '0', NULL); + break; + } + break; + } + } else { + if (_vm->_startupMode) + return; + int selectedScene = 0; + _vm->_infoLine->update(NULL); + if (y >= kWorldHeight ) { + if (x < kButtonX) { // select scene? + if (y >= kSceneY && y < kSceneY + kSceneNy * kSceneDy && + x >= kSceneX && x < kSceneX + kSceneNx * kSceneDx && !_vm->_game) { + selectedScene = ((y - kSceneY) / kSceneDy) * kSceneNx + (x - kSceneX) / kSceneDx + 1; + if (selectedScene > _vm->_maxScene) + selectedScene = 0; + } else { + selectedScene = 0; + } + } else if (mask & kMouseLeftUp) { + if (y >= kPocketY && y < kPocketY + kPocketNY * kPocketDY && + x >= kPocketX && x < kPocketX + kPocketNX * kPocketDX) { + int n = ((y - kPocketY) / kPocketDY) * kPocketNX + (x - kPocketX) / kPocketDX; + _vm->selectPocket(n); + } + } + } + + _vm->postMiniStep(selectedScene - 1); + + if (mask & kMouseLeftUp) { + if (selectedScene && _vm->_commandHandler->idle() && _vm->_hero->_tracePtr < 0) + _vm->switchScene(selectedScene); + + if (_vm->_horzLine && !_vm->_horzLine->_flags._hide) { + if (y >= kMapTop && y < kMapTop + kMapHig) { + Cluster tmpCluster = _vm->XZ(x, y); + int16 x1 = tmpCluster._pt.x; + int16 z1 = tmpCluster._pt.y; + _vm->_clusterMap[z1][x1] = 1; + _vm->setMapBrick(x1, z1); + } + } else { + if (!_vm->_talk && _vm->_commandHandler->idle() && _vm->_hero + && y >= kMapTop && y < kMapTop + kMapHig && !_vm->_game) { + _vm->_hero->findWay(_vm->XZ(x, y)); + } + } + } + } +} + +void System::tick() { + if (!_vm->_startupMode) + if (--_funDel == 0) { + _vm->killText(); + if (_vm->_commandHandler->idle()) { + if (_vm->_flag[0]) // Pain flag + _vm->heroCover(9); + else { // CHECKME: Before, was: if (Startup::_core >= CORE_MID) { + int n = _vm->newRandom(100); + if (n > 96) + _vm->heroCover(6 + (_vm->_hero->_x + _vm->_hero->_w / 2 < kScrWidth / 2)); + else if (n > 90) + _vm->heroCover(5); + else if (n > 60) + _vm->heroCover(4); + else + _vm->heroCover(3); + } + } + funTouch(); + } + _time = kSystemRate; +} + +void CGEEngine::switchColorMode() { + debugC(1, kCGEDebugEngine, "CGEEngine::switchColorMode()"); + + _commandHandlerTurbo->addCommand(kCmdSeq, 121, _vga->_mono = !_vga->_mono, NULL); + keyClick(); + _vga->setColors(_vga->_sysPal, 64); +} + +void CGEEngine::switchMusic() { + debugC(1, kCGEDebugEngine, "CGEEngine::switchMusic()"); + + _commandHandlerTurbo->addCommand(kCmdSeq, 122, (_music = !_music), NULL); + keyClick(); + + if (_music) + _midiPlayer->loadMidi(_now); + else + _midiPlayer->killMidi(); +} + +void CGEEngine::startCountDown() { + debugC(1, kCGEDebugEngine, "CGEEngine::startCountDown()"); + + switchScene(-1); +} + +void CGEEngine::switchMapping() { + assert(_horzLine); + debugC(1, kCGEDebugEngine, "CGEEngine::switchMapping()"); + + if (_horzLine && _horzLine->_flags._hide) { + for (int i = 0; i < kMapZCnt; i++) { + for (int j = 0; j < kMapXCnt; j++) { + if (_clusterMap[i][j]) + setMapBrick(j, i); + } + } + } else { + for (Sprite *s = _vga->_showQ->first(); s; s = s->_next) + if (s->_w == kMapGridX && s->_h == kMapGridZ) + _commandHandlerTurbo->addCommand(kCmdKill, -1, 0, s); + } + _horzLine->_flags._hide = !_horzLine->_flags._hide; +} + +void CGEEngine::killSprite() { + debugC(1, kCGEDebugEngine, "CGEEngine::killSprite()"); + + _sprite->_flags._kill = true; + _sprite->_flags._bDel = true; + _commandHandlerTurbo->addCommand(kCmdKill, -1, 0, _sprite); + _sprite = NULL; +} + +void CGEEngine::optionTouch(int opt, uint16 mask) { + switch (opt) { + case 1: + if (mask & kMouseLeftUp) + switchColorMode(); + break; + case 2: + if (mask & kMouseLeftUp) + switchMusic(); + else if (mask & kMouseRightUp) + warning("TODO: Use ScummVM sound dialog"); + break; + case 3: + if (mask & kMouseLeftUp) + quit(); + break; + } +} + +#pragma argsused +void Sprite::touch(uint16 mask, int x, int y) { + _vm->_sys->funTouch(); + + if ((mask & kEventAttn) != 0) + return; + + _vm->_infoLine->update(name()); + + if (mask & (kMouseRightDown | kMouseLeftDown)) + _vm->_sprite = this; + + if (_ref / 10 == 12) { + _vm->optionTouch(_ref % 10, mask); + return; + } + + if (_flags._syst) + return; // cannot access system sprites + + if (_vm->_game) + if (mask & kMouseLeftUp) { + mask &= ~kMouseLeftUp; + mask |= kMouseRightUp; + } + + if ((mask & kMouseRightUp) && _vm->_commandHandler->idle()) { + Sprite *ps = (_vm->_pocLight->_seqPtr) ? _vm->_pocket[_vm->_pocPtr] : NULL; + if (ps) { + if (_flags._kept || _vm->_hero->distance(this) < kDistMax) { + if (works(ps)) { + _vm->feedSnail(ps, kTake); + } else + _vm->offUse(); + _vm->selectPocket(-1); + } else + _vm->tooFar(); + } else { + if (_flags._kept) { + mask |= kMouseLeftUp; + } else { + if (_vm->_hero->distance(this) < kDistMax) { + if (_flags._port) { + if (_vm->findPocket(NULL) < 0) { + _vm->pocFul(); + } else { + _vm->_commandHandler->addCommand(kCmdReach, -1, -1, this); + _vm->_commandHandler->addCommand(kCmdKeep, -1, -1, this); + _flags._port = false; + } + } else { + if (_takePtr != kNoPtr) { + if (snList(kTake)[_takePtr]._commandType == kCmdNext) + _vm->offUse(); + else + _vm->feedSnail(this, kTake); + } else { + _vm->offUse(); + } + } + } else { + _vm->tooFar(); + } + } + } + } + + if ((mask & kMouseLeftUp) && _vm->_commandHandler->idle()) { + if (_flags._kept) { + for (int n = 0; n < kPocketNX; n++) { + if (_vm->_pocket[n] == this) { + _vm->selectPocket(n); + break; + } + } + } else { + _vm->_commandHandler->addCommand(kCmdWalk, -1, -1, this); // Hero->FindWay(this); + } + } +} + +void CGEEngine::loadSprite(const char *fname, int ref, int scene, int col = 0, int row = 0, int pos = 0) { + static const char *Comd[] = { "Name", "Type", "Phase", "East", + "Left", "Right", "Top", "Bottom", + "Seq", "Near", "Take", + "Portable", "Transparent", + NULL + }; + static const char *Type[] = { "DEAD", "AUTO", "WALK", "NEWTON", "LISSAJOUS", + "FLY", NULL + }; + + int shpcnt = 0; + int type = 0; // DEAD + bool east = false; + bool port = false; + bool tran = false; + int i, lcnt = 0; + + char tmpStr[kLineMax + 1]; + Common::String line; + mergeExt(tmpStr, fname, kSprExt); + + if (_resman->exist(tmpStr)) { // sprite description file exist + EncryptedStream sprf(this, tmpStr); + if (sprf.err()) + error("Bad SPR [%s]", tmpStr); + + uint16 len; + for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()) { + len = line.size(); + lcnt++; + strcpy(tmpStr, line.c_str()); + if (len == 0 || *tmpStr == '.') + continue; + + if ((i = takeEnum(Comd, strtok(tmpStr, " =\t"))) < 0) + error("Bad line %d [%s]", lcnt, fname); + + + switch (i) { + case 0 : // Name - will be taken in Expand routine + break; + case 1 : // Type + if ((type = takeEnum(Type, strtok(NULL, " \t,;/"))) < 0) + error("Bad line %d [%s]", lcnt, fname); + break; + case 2 : // Phase + shpcnt++; + break; + case 3 : // East + east = (atoi(strtok(NULL, " \t,;/")) != 0); + break; + case 11 : // Portable + port = (atoi(strtok(NULL, " \t,;/")) != 0); + break; + case 12 : // Transparent + tran = (atoi(strtok(NULL, " \t,;/")) != 0); + break; + } + } + if (! shpcnt) + error("No shapes [%s]", fname); + } else { + // no sprite description: mono-shaped sprite with only .BMP file + ++shpcnt; + } + + // make sprite of choosen type + switch (type) { + case 1: + // AUTO + _sprite = new Sprite(this, NULL); + if (_sprite) { + _sprite->gotoxy(col, row); + } + break; + case 2: + { // WALK + Walk *w = new Walk(this, NULL); + if (w && ref == 1) { + w->gotoxy(col, row); + if (_hero) + error("2nd HERO [%s]", fname); + _hero = w; + } + _sprite = w; + break; + } + case 3: // NEWTON + case 4: // LISSAJOUS + error("Bad type [%s]", fname); + break; + case 5: + { // FLY + Fly *f = new Fly(this, NULL); + _sprite = f; + break; + } + default: + // DEAD + _sprite = new Sprite(this, NULL); + if (_sprite) + _sprite->gotoxy(col, row); + break; + } + + if (_sprite) { + _sprite->_ref = ref; + _sprite->_scene = scene; + _sprite->_z = pos; + _sprite->_flags._east = east; + _sprite->_flags._port = port; + _sprite->_flags._tran = tran; + _sprite->_flags._kill = true; + _sprite->_flags._bDel = true; + + // Extract the filename, without the extension + strcpy(_sprite->_file, fname); + char *p = strchr(_sprite->_file, '.'); + if (p) + *p = '\0'; + + _sprite->_shpCnt = shpcnt; + _vga->_spareQ->append(_sprite); + } +} + +void CGEEngine::loadScript(const char *fname) { + EncryptedStream scrf(this, fname); + + if (scrf.err()) + return; + + bool ok = true; + int lcnt = 0; + + char tmpStr[kLineMax+1]; + Common::String line; + + for (line = scrf.readLine(); !scrf.eos(); line = scrf.readLine()) { + char *p; + + lcnt++; + strcpy(tmpStr, line.c_str()); + if ((line.size() == 0) || (*tmpStr == '.')) + continue; + + ok = false; // not OK if break + + // sprite ident number + if ((p = strtok(tmpStr, " \t\n")) == NULL) + break; + int SpI = atoi(p); + + // sprite file name + char *SpN; + if ((SpN = strtok(NULL, " ,;/\t\n")) == NULL) + break; + + // sprite scene + if ((p = strtok(NULL, " ,;/\t\n")) == NULL) + break; + int SpA = atoi(p); + + // sprite column + if ((p = strtok(NULL, " ,;/\t\n")) == NULL) + break; + int SpX = atoi(p); + + // sprite row + if ((p = strtok(NULL, " ,;/\t\n")) == NULL) + break; + int SpY = atoi(p); + + // sprite Z pos + if ((p = strtok(NULL, " ,;/\t\n")) == NULL) + break; + int SpZ = atoi(p); + + // sprite life + if ((p = strtok(NULL, " ,;/\t\n")) == NULL) + break; + bool BkG = atoi(p) == 0; + + ok = true; // no break: OK + + _sprite = NULL; + loadSprite(SpN, SpI, SpA, SpX, SpY, SpZ); + if (_sprite && BkG) + _sprite->_flags._back = true; + } + + if (!ok) + error("Bad INI line %d [%s]", lcnt, fname); +} + +Sprite *CGEEngine::locate(int ref) { + Sprite *spr = _vga->_showQ->locate(ref); + return (spr) ? spr : _vga->_spareQ->locate(ref); +} + +Sprite *CGEEngine::spriteAt(int x, int y) { + Sprite *spr = NULL, * tail = _vga->_showQ->last(); + if (tail) { + for (spr = tail->_prev; spr; spr = spr->_prev) { + if (! spr->_flags._hide && ! spr->_flags._tran) { + if (spr->shp()->solidAt(x - spr->_x, y - spr->_y)) + break; + } + } + } + return spr; +} + +Cluster CGEEngine::XZ(int16 x, int16 y) { + if (y < kMapTop) + y = kMapTop; + + if (y > kMapTop + kMapHig - kMapGridZ) + y = kMapTop + kMapHig - kMapGridZ; + + return Cluster(this, x / kMapGridX, (y - kMapTop) / kMapGridZ); +} + +void CGEEngine::killText() { + if (!_talk) + return; + + _commandHandlerTurbo->addCommand(kCmdKill, -1, 0, _talk); + _talk = NULL; +} + +void CGEEngine::mainLoop() { + _vga->show(); + _commandHandlerTurbo->runCommand(); + _commandHandler->runCommand(); + + // Handle a delay between game frames + handleFrame(); + + // Handle any pending events + _eventManager->poll(); +} + +void CGEEngine::handleFrame() { + // Game frame delay + uint32 millis = g_system->getMillis(); + while (!_eventManager->_quitFlag && (millis < (_lastFrame + kGameFrameDelay))) { + // Handle any pending events + _eventManager->poll(); + + if (millis >= (_lastTick + kGameTickDelay)) { + // Dispatch the tick to any active objects + tick(); + _lastTick = millis; + } + + // Slight delay + g_system->delayMillis(5); + millis = g_system->getMillis(); + } + _lastFrame = millis; + + if (millis >= (_lastTick + kGameTickDelay)) { + // Dispatch the tick to any active objects + tick(); + _lastTick = millis; + } +} + +void CGEEngine::tick() { + for (Sprite *spr = _vga->_showQ->first(); spr; spr = spr->_next) { + if (spr->_time) { + if (!spr->_flags._hide) { + if (--spr->_time == 0) + spr->tick(); + } + } + } +} + +void CGEEngine::loadUser() { + // set scene + if (_mode == 0) { + // user .SVG file found - load it from slot 0 + loadGame(0, NULL); + } else if (_mode == 1) { + // Load either initial game state savegame or launcher specified savegame + loadGame(_startGameSlot, NULL); + } else { + error("Creating setup savegames not supported"); + } + loadScript("CGE.IN0"); +} + +void CGEEngine::runGame() { + if (_eventManager->_quitFlag) + return; + + loadHeroXY(); + + _sceneLight->_flags._tran = true; + _vga->_showQ->append(_sceneLight); + _sceneLight->_flags._hide = true; + + const Seq pocSeq[] = { + { 0, 0, 0, 0, 20 }, + { 1, 2, 0, 0, 4 }, + { 2, 3, 0, 0, 4 }, + { 3, 4, 0, 0, 16 }, + { 2, 5, 0, 0, 4 }, + { 1, 6, 0, 0, 4 }, + { 0, 1, 0, 0, 16 }, + }; + Seq *seq = (Seq *)malloc(7 * sizeof(Seq)); + Common::copy(pocSeq, pocSeq + 7, seq); + _pocLight->setSeq(seq); + + _pocLight->_flags._tran = true; + _pocLight->_time = 1; + _pocLight->_z = 120; + _vga->_showQ->append(_pocLight); + selectPocket(-1); + + _vga->_showQ->append(_mouse); + +// ___________ + loadUser(); +// ~~~~~~~~~~~ + + if ((_sprite = _vga->_spareQ->locate(121)) != NULL) + _commandHandlerTurbo->addCommand(kCmdSeq, -1, _vga->_mono, _sprite); + if ((_sprite = _vga->_spareQ->locate(122)) != NULL) + _sprite->step(_music); + _commandHandlerTurbo->addCommand(kCmdSeq, -1, _music, _sprite); + if (!_music) + _midiPlayer->killMidi(); + + if (_resman->exist("MINI.SPR")) { + _miniShp = new BitmapPtr[2]; + _miniShp[0] = _miniShp[1] = NULL; + + loadSprite("MINI", -1, 0, kMiniX, kMiniY); + expandSprite(_miniScene = _sprite); // NULL is ok + if (_miniScene) { + _miniScene->_flags._kill = false; + _miniScene->_flags._hide = true; + _miniShp[0] = new Bitmap(this, *_miniScene->shp()); + _miniShpList = _miniScene->setShapeList(_miniShp); + postMiniStep(-1); + } + } + + if (_hero) { + expandSprite(_hero); + _hero->gotoxy(_heroXY[_now - 1].x, _heroXY[_now - 1].y); + if (_resman->exist("00SHADOW.SPR")) { + loadSprite("00SHADOW", -1, 0, _hero->_x + 14, _hero->_y + 51); + delete _shadow; + if ((_shadow = _sprite) != NULL) { + _shadow->_ref = 2; + _shadow->_flags._tran = true; + _shadow->_flags._kill = false; + _hero->_flags._shad = true; + _vga->_showQ->insert(_vga->_spareQ->remove(_shadow), _hero); + } + } + } + + _infoLine->gotoxy(kInfoX, kInfoY); + _infoLine->_flags._tran = true; + _infoLine->update(NULL); + _vga->_showQ->insert(_infoLine); + + _debugLine->_z = 126; + _vga->_showQ->insert(_debugLine); + + if (_horzLine) { + _horzLine->_y = kMapTop - (kMapTop > 0); + _horzLine->_z = 126; + _vga->_showQ->insert(_horzLine); + } + + _mouse->_busy = _vga->_spareQ->locate(kBusyRef); + if (_mouse->_busy) + expandSprite(_mouse->_busy); + + _startupMode = 0; + + _commandHandler->addCommand(kCmdLevel, -1, _oldLev, &_sceneLight); + _sceneLight->gotoxy(kSceneX + ((_now - 1) % kSceneNx) * kSceneDx + kSceneSX, + kSceneY + ((_now - 1) / kSceneNx) * kSceneDy + kSceneSY); + sceneUp(); + + _keyboard->setClient(_sys); + // main loop + while (!_finis && !_eventManager->_quitFlag) { + if (_flag[3]) + _commandHandler->addCallback(kCmdExec, -1, 0, kQGame); + mainLoop(); + } + + // If finishing game due to closing ScummVM window, explicitly save the game + if (!_finis && canSaveGameStateCurrently()) + qGame(); + + _keyboard->setClient(NULL); + _commandHandler->addCommand(kCmdClear, -1, 0, NULL); + _commandHandlerTurbo->addCommand(kCmdClear, -1, 0, NULL); + _mouse->off(); + _vga->_showQ->clear(); + _vga->_spareQ->clear(); + _hero = NULL; + _shadow = NULL; +} + +void CGEEngine::movie(const char *ext) { + assert(ext); + + if (_eventManager->_quitFlag) + return; + + char fn[12]; + sprintf(fn, "CGE.%s", (*ext == '.') ? ext +1 : ext); + + if (_resman->exist(fn)) { + loadScript(fn); + expandSprite(_vga->_spareQ->locate(999)); + feedSnail(_vga->_showQ->locate(999), kTake); + _vga->_showQ->append(_mouse); + _keyboard->setClient(_sys); + while (!_commandHandler->idle() && !_eventManager->_quitFlag) + mainLoop(); + + _keyboard->setClient(NULL); + _commandHandler->addCommand(kCmdClear, -1, 0, NULL); + _commandHandlerTurbo->addCommand(kCmdClear, -1, 0, NULL); + _vga->_showQ->clear(); + _vga->_spareQ->clear(); + } +} + +bool CGEEngine::showTitle(const char *name) { + if (_eventManager->_quitFlag) + return false; + + _bitmapPalette = _vga->_sysPal; + BitmapPtr *LB = new BitmapPtr[2]; + LB[0] = new Bitmap(this, name); + LB[1] = NULL; + _bitmapPalette = NULL; + + Sprite D(this, LB); + D._flags._kill = true; + D._flags._bDel = true; + D.center(); + D.show(2); + + if (_mode == 2) { + inf(kSavegame0Name); + _talk->show(2); + } + + _vga->sunset(); + _vga->copyPage(1, 2); + _vga->copyPage(0, 1); + selectPocket(-1); + _vga->sunrise(_vga->_sysPal); + + if (_mode < 2 && !_soundOk) { + _vga->copyPage(1, 2); + _vga->copyPage(0, 1); + _vga->_showQ->append(_mouse); + _mouse->on(); + for (; !_commandHandler->idle() || Vmenu::_addr;) { + mainLoop(); + if (_eventManager->_quitFlag) + return false; + } + + _mouse->off(); + _vga->_showQ->clear(); + _vga->copyPage(0, 2); + _soundOk = 2; + if (_music) + _midiPlayer->loadMidi(0); + } + + if (_mode < 2) { + // At this point the game originally set the protection variables + // used by the copy protection check + movie(kPaylistExt); // paylist + _vga->copyPage(1, 2); + _vga->copyPage(0, 1); + _vga->_showQ->append(_mouse); + // In the original game, the user had to enter his name + // As it was only used to name savegames, it has been removed + _vga->_showQ->clear(); + _vga->copyPage(0, 2); + + if (_mode == 0) { +// The auto-load of savegame #0 is currently disabled +#if 0 + if (savegameExists(0)) { + // Load the savegame + loadGame(0, NULL, true); // only system vars + _vga->setColors(_vga->_sysPal, 64); + _vga->update(); + if (_flag[3]) { //flag FINIS + _mode++; + _flag[3] = false; + } + } else +#endif + _mode++; + } + } + + if (_mode < 2) + movie(kWinkExt); + + _vga->copyPage(0, 2); + + return true; +} + +void CGEEngine::cge_main() { + memset(_barriers, 0xFF, sizeof(_barriers)); + + if (!_mouse->_exist) + error("%s", _text->getText(kTextNoMouse)); + + if (!_resman->exist(kSavegame0Name)) + _mode = 2; + + _debugLine->_flags._hide = true; + if (_horzLine) + _horzLine->_flags._hide = true; + + if (_music && _soundOk) + _midiPlayer->loadMidi(0); + + if (_startGameSlot != -1) { + // Starting up a savegame from the launcher + _mode++; + runGame(); + + _startupMode = 2; + if (_flag[3]) // Flag FINIS + movie(kEndgExt); + } else { + if (_mode < 2) + movie(kLgoExt); + + if (showTitle("WELCOME")) { + if (_mode == 1) + movie(kIntroExt); + runGame(); + _startupMode = 2; + if (_flag[3]) // Flag FINIS + movie(kEndgExt); + } else + _vga->sunset(); + } +} + +} // End of namespace CGE |