diff options
Diffstat (limited to 'engines/toltecs/toltecs.cpp')
-rw-r--r-- | engines/toltecs/toltecs.cpp | 641 |
1 files changed, 641 insertions, 0 deletions
diff --git a/engines/toltecs/toltecs.cpp b/engines/toltecs/toltecs.cpp new file mode 100644 index 0000000000..d403e0499b --- /dev/null +++ b/engines/toltecs/toltecs.cpp @@ -0,0 +1,641 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "common/config-manager.h" +#include "common/events.h" +#include "common/random.h" +#include "common/str.h" +#include "common/error.h" +#include "common/textconsole.h" + +#include "base/plugins.h" +#include "base/version.h" + +#include "graphics/cursorman.h" + +#include "engines/util.h" + +#include "audio/mixer.h" + +#include "toltecs/toltecs.h" +#include "toltecs/animation.h" +#include "toltecs/menu.h" +#include "toltecs/movie.h" +#include "toltecs/music.h" +#include "toltecs/palette.h" +#include "toltecs/render.h" +#include "toltecs/resource.h" +#include "toltecs/script.h" +#include "toltecs/screen.h" +#include "toltecs/segmap.h" +#include "toltecs/sound.h" +#include "toltecs/microtiles.h" + +namespace Toltecs { + +struct GameSettings { + const char *gameid; + const char *description; + byte id; + uint32 features; + const char *detectname; +}; + +ToltecsEngine::ToltecsEngine(OSystem *syst, const ToltecsGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) { + + // Setup mixer + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); + + _rnd = new Common::RandomSource("toltecs"); +} + +ToltecsEngine::~ToltecsEngine() { + delete _rnd; +} + +Common::Error ToltecsEngine::run() { + initGraphics(640, 400, true); + + _isSaveAllowed = true; + + _counter01 = 0; + _counter02 = 0; + _movieSceneFlag = false; + _flag01 = 0; + + _saveLoadRequested = 0; + + _cameraX = 0; + _cameraY = 0; + _newCameraX = 0; + _newCameraY = 0; + _cameraHeight = 0; + + _guiHeight = 26; + + _sceneWidth = 0; + _sceneHeight = 0; + + _doSpeech = true; + _doText = true; + + _walkSpeedY = 5; + _walkSpeedX = 1; + + _mouseX = 0; + _mouseY = 0; + _mouseDblClickTicks = 60; + _mouseWaitForRelease = false; + _mouseButton = 0; + _mouseDisabled = 0; + _leftButtonDown = false; + _rightButtonDown = false; + + _arc = new ArchiveReader(); + _arc->openArchive("WESTERN"); + + _res = new ResourceCache(this); + + _screen = new Screen(this); + + _script = new ScriptInterpreter(this); + _anim = new AnimationPlayer(this); + _palette = new Palette(this); + _segmap = new SegmentMap(this); + _moviePlayer = new MoviePlayer(this); + _music = new Music(_arc); + _menuSystem = new MenuSystem(this); + + _sound = new Sound(this); + + syncSoundSettings(); + + CursorMan.showMouse(true); + + setupSysStrings(); + +//#define TEST_MENU +#ifdef TEST_MENU + _screen->registerFont(0, 0x0D); + _screen->registerFont(1, 0x0E); + _screen->loadMouseCursor(12); + _palette->loadAddPalette(9, 224); + _palette->setDeltaPalette(_palette->getMainPalette(), 7, 0, 31, 224); + _screen->finishTalkTextItems(); + _screen->clearSprites(); + _menuSystem->run(); + /* + while (1) { + //updateInput(); + _menuSystem->update(); + updateScreen(); + } + */ + return Common::kNoError; +#endif + + // Start main game loop + setTotalPlayTime(0); + _script->loadScript(0, 0); + _script->setMainScript(0); + if (ConfMan.hasKey("save_slot")) { + int saveSlot = ConfMan.getInt("save_slot"); + if (saveSlot >= 0 && saveSlot <= 99) { + _screen->loadMouseCursor(12); + loadGameState(saveSlot); + } + } + _script->runScript(); + + _music->stopSequence(); + _sound->stopAll(); + + delete _arc; + delete _res; + delete _screen; + delete _script; + delete _anim; + delete _palette; + delete _segmap; + delete _music; + delete _moviePlayer; + delete _menuSystem; + + delete _sound; + + return Common::kNoError; +} + +void ToltecsEngine::setupSysStrings() { + Resource *sysStringsResource = _res->load(15); + const char *sysStrings = (const char*)sysStringsResource->data; + for (int i = 0; i < kSysStrCount; i++) { + debug(1, "sysStrings[%d] = [%s]", i, sysStrings); + _sysStrings[i] = sysStrings; + sysStrings += strlen(sysStrings) + 1; + } + // TODO: Set yes/no chars +} + +void ToltecsEngine::requestSavegame(int slotNum, Common::String &description) { + _saveLoadRequested = 2; + _saveLoadSlot = slotNum; + _saveLoadDescription = description; +} + +void ToltecsEngine::requestLoadgame(int slotNum) { + _saveLoadRequested = 1; + _saveLoadSlot = slotNum; +} + +void ToltecsEngine::loadScene(uint resIndex) { + + Resource *sceneResource = _res->load(resIndex); + byte *scene = sceneResource->data; + + uint32 imageSize = READ_LE_UINT32(scene); + _sceneResIndex = resIndex; + _sceneHeight = READ_LE_UINT16(scene + 4); + _sceneWidth = READ_LE_UINT16(scene + 6); + + // Load scene palette + _palette->loadAddPaletteFrom(scene + 8, 0, 128); + + // Load scene background + byte *source = scene + 392; + byte *destp = _screen->_backScreen; + byte *destEnd = destp + _sceneWidth * _sceneHeight; + while (destp < destEnd) { + int count = 1; + byte pixel = *source++; + if (pixel & 0x80) { + pixel &= 0x7F; + count = *source++; + count += 2; + } + memset(destp, pixel, count); + destp += count; + } + + debug(0, "_sceneWidth = %d; _sceneHeight = %d", _sceneWidth, _sceneHeight); + + // Load scene segmap + _segmap->load(scene + imageSize + 4); + + _screen->_fullRefresh = true; + _screen->_renderQueue->clear(); + +} + +void ToltecsEngine::updateScreen() { + + _sound->updateSpeech(); + + _screen->updateShakeScreen(); + + // TODO: Set quit flag + if (shouldQuit()) + return; + + if (!_movieSceneFlag) + updateInput(); + else + _mouseButton = 0; + + // TODO? Check keyb + + _counter01--; + if (_counter01 <= 0) { + _counter01 = MIN(_counter02, 30); + _counter02 = 0; + drawScreen(); + _flag01 = 1; + _counter02 = 1; + } else { + _screen->clearSprites(); + _flag01 = 0; + } + + static uint32 prevUpdateTime = 0; + uint32 currUpdateTime; + do { + currUpdateTime = _system->getMillis(); + _counter02 = (currUpdateTime - prevUpdateTime) / 13; + } while (_counter02 == 0); + prevUpdateTime = currUpdateTime; + +} + +void ToltecsEngine::drawScreen() { + // FIXME: Quick hack, sometimes cameraY was negative (the code in updateCamera was at fault) + if (_cameraY < 0) _cameraY = 0; + + _segmap->addMasksToRenderQueue(); + _screen->addTalkTextItemsToRenderQueue(); + + _screen->_renderQueue->update(); + + //debug("_guiHeight = %d\n", _guiHeight); + + if (_screen->_guiRefresh && _guiHeight > 0 && _cameraHeight > 0) { + // Update the GUI when needed and it's visible + _system->copyRectToScreen((const byte *)_screen->_frontScreen + _cameraHeight * 640, + 640, 0, _cameraHeight, 640, _guiHeight); + _screen->_guiRefresh = false; + } + + _system->updateScreen(); + + updateCamera(); +} + +void ToltecsEngine::updateInput() { + + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + while (eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_KEYDOWN: + _keyState = event.kbd; + + //debug("key: flags = %02X; keycode = %d", _keyState.flags, _keyState.keycode); + + // FIXME: This is just for debugging + switch (event.kbd.keycode) { + case Common::KEYCODE_F7: + savegame("toltecs.001", "Quicksave"); + break; + case Common::KEYCODE_F9: + loadgame("toltecs.001"); + break; + case Common::KEYCODE_ESCAPE: + // Skip current dialog line, if a dialog is active + if (_screen->getTalkTextDuration() > 0) { + _sound->stopSpeech(); + _screen->finishTalkTextItems(); + _keyState.reset(); // event consumed + } + break; + default: + break; + } + + break; + case Common::EVENT_KEYUP: + _keyState.reset(); + break; + case Common::EVENT_QUIT: + quitGame(); + break; + case Common::EVENT_MOUSEMOVE: + _mouseX = event.mouse.x; + _mouseY = event.mouse.y; + break; + case Common::EVENT_LBUTTONDOWN: + _mouseX = event.mouse.x; + _mouseY = event.mouse.y; + _leftButtonDown = true; + break; + case Common::EVENT_LBUTTONUP: + _mouseX = event.mouse.x; + _mouseY = event.mouse.y; + _leftButtonDown = false; + break; + case Common::EVENT_RBUTTONDOWN: + _mouseX = event.mouse.x; + _mouseY = event.mouse.y; + _rightButtonDown = true; + break; + case Common::EVENT_RBUTTONUP: + _mouseX = event.mouse.x; + _mouseY = event.mouse.y; + _rightButtonDown = false; + break; + default: + break; + } + } + + if (!_mouseDisabled) { + + if (_mouseDblClickTicks > 0) + _mouseDblClickTicks--; + + byte mouseButtons = 0; + if (_leftButtonDown) + mouseButtons |= 1; + if (_rightButtonDown) + mouseButtons |= 2; + + if (mouseButtons != 0) { + if (!_mouseWaitForRelease) { + _mouseButton = mouseButtons; + if (_mouseDblClickTicks > 0) + _mouseButton = 0x80; + //if (_mouseButton == 0x80) debug("DBL!"); + _mouseDblClickTicks = 30; // maybe TODO + _mouseWaitForRelease = true; + } else { + _mouseButton = 0; + } + } else { + _mouseWaitForRelease = false; + _mouseButton = 0; + } + + } + +} + +void ToltecsEngine::setGuiHeight(int16 guiHeight) { + if (guiHeight != _guiHeight) { + _guiHeight = guiHeight; + _cameraHeight = 400 - _guiHeight; + _screen->_guiRefresh = true; + debug(0, "ToltecsEngine::setGuiHeight() _guiHeight = %d; _cameraHeight = %d", _guiHeight, _cameraHeight); + // TODO: clearScreen(); + } +} + +void ToltecsEngine::setCamera(int16 x, int16 y) { + _screen->finishTalkTextItems(); + + _screen->clearSprites(); + + _cameraX = x; + _newCameraX = x; + + _cameraY = y; + _newCameraY = y; +} + +bool ToltecsEngine::getCameraChanged() { + return _cameraX != _newCameraX || _cameraY != _newCameraY; +} + +void ToltecsEngine::scrollCameraUp(int16 delta) { + if (_newCameraY > 0) { + if (_newCameraY < delta) + _newCameraY = 0; + else + _newCameraY -= delta; + } +} + +void ToltecsEngine::scrollCameraDown(int16 delta) { + debug(0, "ToltecsEngine::scrollCameraDown(%d)", delta); + if (_newCameraY != _sceneHeight - _cameraHeight) { + if (_sceneHeight - _cameraHeight < _newCameraY + delta) + delta += (_sceneHeight - _cameraHeight) - (delta + _newCameraY); + _newCameraY += delta; + debug(0, "ToltecsEngine::scrollCameraDown() _newCameraY = %d; delta = %d", _newCameraY, delta); + } +} + +void ToltecsEngine::scrollCameraLeft(int16 delta) { + if (_newCameraX > 0) { + if (_newCameraX < delta) + _newCameraX = 0; + else + _newCameraX -= delta; + } +} + +void ToltecsEngine::scrollCameraRight(int16 delta) { + debug(0, "ToltecsEngine::scrollCameraRight(%d)", delta); + if (_newCameraX != _sceneWidth - 640) { + if (_sceneWidth - 640 < delta + _newCameraX) + delta += (_sceneWidth - 640) - (delta + _newCameraX); + _newCameraX += delta; + debug(0, "ToltecsEngine::scrollCameraRight() _newCameraX = %d; delta = %d", _newCameraY, delta); + } +} + +void ToltecsEngine::updateCamera() { + + if (_cameraX != _newCameraX) { + _cameraX = _newCameraX; + _screen->_fullRefresh = true; + _screen->finishTalkTextItems(); + } + + if (_cameraY != _newCameraY) { + _cameraY = _newCameraY; + _screen->_fullRefresh = true; + _screen->finishTalkTextItems(); + } + + //debug(0, "ToltecsEngine::updateCamera() _cameraX = %d; _cameraY = %d", _cameraX, _cameraY); + +} + +void ToltecsEngine::talk(int16 slotIndex, int16 slotOffset) { + + byte *scanData = _script->getSlotData(slotIndex) + slotOffset; + + while (*scanData < 0xF0) { + if (*scanData == 0x19) { + scanData++; + } else if (*scanData == 0x14) { + scanData++; + } else if (*scanData == 0x0A) { + scanData += 4; + } else if (*scanData < 0x0A) { + scanData++; + } + scanData++; + } + + if (*scanData == 0xFE) { + if (_doSpeech) { + int16 resIndex = READ_LE_UINT16(scanData + 1); + debug(0, "ToltecsEngine::talk() playSound(resIndex: %d)", resIndex); + _sound->playSpeech(resIndex); + } + if (_doText) { + _screen->updateTalkText(slotIndex, slotOffset); + } else { + _screen->keepTalkTextItemsAlive(); + } + } else { + _screen->updateTalkText(slotIndex, slotOffset); + } + +} + +void ToltecsEngine::walk(byte *walkData) { + + int16 xdelta, ydelta, v8, v10, v11; + int16 xstep, ystep; + ScriptWalk walkInfo; + + walkInfo.y = READ_LE_UINT16(walkData + 0); + walkInfo.x = READ_LE_UINT16(walkData + 2); + walkInfo.y1 = READ_LE_UINT16(walkData + 4); + walkInfo.x1 = READ_LE_UINT16(walkData + 6); + walkInfo.y2 = READ_LE_UINT16(walkData + 8); + walkInfo.x2 = READ_LE_UINT16(walkData + 10); + walkInfo.yerror = READ_LE_UINT16(walkData + 12); + walkInfo.xerror = READ_LE_UINT16(walkData + 14); + walkInfo.mulValue = READ_LE_UINT16(walkData + 16); + walkInfo.scaling = READ_LE_UINT16(walkData + 18); + + walkInfo.scaling = -_segmap->getScalingAtPoint(walkInfo.x, walkInfo.y); + + if (walkInfo.y1 < walkInfo.y2) + ystep = -1; + else + ystep = 1; + ydelta = ABS(walkInfo.y1 - walkInfo.y2) * _walkSpeedY; + + if (walkInfo.x1 < walkInfo.x2) + xstep = -1; + else + xstep = 1; + xdelta = ABS(walkInfo.x1 - walkInfo.x2) * _walkSpeedX; + + debug(0, "ToltecsEngine::walk() xdelta = %d; ydelta = %d", xdelta, ydelta); + + if (xdelta > ydelta) + SWAP(xdelta, ydelta); + + v8 = 100 * xdelta; + if (v8 != 0) { + if (walkInfo.scaling > 0) + v8 -= v8 * ABS(walkInfo.scaling) / 100; + else + v8 += v8 * ABS(walkInfo.scaling) / 100; + if (ydelta != 0) + v8 /= ydelta; + } + + if (ydelta > ABS(walkInfo.x1 - walkInfo.x2) * _walkSpeedX) { + v10 = 100 - walkInfo.scaling; + v11 = v8; + } else { + v10 = v8; + v11 = 100 - walkInfo.scaling; + } + + walkInfo.yerror += walkInfo.mulValue * v10; + while (walkInfo.yerror >= 100 * _walkSpeedY) { + walkInfo.yerror -= 100 * _walkSpeedY; + if (walkInfo.y == walkInfo.y1) { + walkInfo.x = walkInfo.x1; + break; + } + walkInfo.y += ystep; + } + + walkInfo.xerror += walkInfo.mulValue * v11; + while (walkInfo.xerror >= 100 * _walkSpeedX) { + walkInfo.xerror -= 100 * _walkSpeedX; + if (walkInfo.x == walkInfo.x1) { + walkInfo.y = walkInfo.y1; + break; + } + walkInfo.x += xstep; + } + + WRITE_LE_UINT16(walkData + 0, walkInfo.y); + WRITE_LE_UINT16(walkData + 2, walkInfo.x); + WRITE_LE_UINT16(walkData + 4, walkInfo.y1); + WRITE_LE_UINT16(walkData + 6, walkInfo.x1); + WRITE_LE_UINT16(walkData + 8, walkInfo.y2); + WRITE_LE_UINT16(walkData + 10, walkInfo.x2); + WRITE_LE_UINT16(walkData + 12, walkInfo.yerror); + WRITE_LE_UINT16(walkData + 14, walkInfo.xerror); + WRITE_LE_UINT16(walkData + 16, walkInfo.mulValue); + WRITE_LE_UINT16(walkData + 18, walkInfo.scaling); + +} + +int16 ToltecsEngine::findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize, + byte *rectDataEnd) { + + rectData += index * itemSize; + + while (rectData < rectDataEnd) { + int16 rectY = READ_LE_UINT16(rectData); + if (rectY == -10) + break; + int16 rectX = READ_LE_UINT16(rectData + 2); + int16 rectH = READ_LE_UINT16(rectData + 4); + int16 rectW = READ_LE_UINT16(rectData + 6); + + debug(0, "x = %d; y = %d; x1 = %d; y2 = %d; w = %d; h = %d", + x, y, rectX, rectY, rectW, rectH); + + if (x >= rectX && x <= rectX + rectW && y >= rectY && y <= rectY + rectH) { + return index; + } + index++; + rectData += itemSize; + } + + return -1; + +} + +} // End of namespace Toltecs |