diff options
Diffstat (limited to 'engines/adl/adl_v2.cpp')
-rw-r--r-- | engines/adl/adl_v2.cpp | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/engines/adl/adl_v2.cpp b/engines/adl/adl_v2.cpp new file mode 100644 index 0000000000..4fdf796701 --- /dev/null +++ b/engines/adl/adl_v2.cpp @@ -0,0 +1,539 @@ +/* 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/random.h" +#include "common/error.h" + +#include "adl/adl_v2.h" +#include "adl/display.h" +#include "adl/graphics.h" + +namespace Adl { + +AdlEngine_v2::~AdlEngine_v2() { + delete _random; + delete _disk; +} + +AdlEngine_v2::AdlEngine_v2(OSystem *syst, const AdlGameDescription *gd) : + AdlEngine(syst, gd), + _linesPrinted(0), + _disk(nullptr), + _itemRemoved(false), + _roomOnScreen(0), + _picOnScreen(0), + _itemsOnScreen(0) { + _random = new Common::RandomSource("adl"); +} + +typedef Common::Functor1Mem<ScriptEnv &, int, AdlEngine_v2> OpcodeV2; +#define SetOpcodeTable(x) table = &x; +#define Opcode(x) table->push_back(new OpcodeV2(this, &AdlEngine_v2::x)) +#define OpcodeUnImpl() table->push_back(new OpcodeV2(this, 0)) + +void AdlEngine_v2::setupOpcodeTables() { + Common::Array<const Opcode *> *table = 0; + + SetOpcodeTable(_condOpcodes); + // 0x00 + OpcodeUnImpl(); + Opcode(o2_isFirstTime); + Opcode(o2_isRandomGT); + Opcode(o1_isItemInRoom); + // 0x04 + Opcode(o2_isNounNotInRoom); + Opcode(o1_isMovesGT); + Opcode(o1_isVarEQ); + Opcode(o2_isCarryingSomething); + // 0x08 + OpcodeUnImpl(); + Opcode(o1_isCurPicEQ); + Opcode(o1_isItemPicEQ); + + SetOpcodeTable(_actOpcodes); + // 0x00 + OpcodeUnImpl(); + Opcode(o1_varAdd); + Opcode(o1_varSub); + Opcode(o1_varSet); + // 0x04 + Opcode(o1_listInv); + Opcode(o2_moveItem); + Opcode(o1_setRoom); + Opcode(o1_setCurPic); + // 0x08 + Opcode(o1_setPic); + Opcode(o1_printMsg); + Opcode(o1_setLight); + Opcode(o1_setDark); + // 0x0c + Opcode(o2_moveAllItems); + Opcode(o1_quit); + OpcodeUnImpl(); + Opcode(o2_save); + // 0x10 + Opcode(o2_restore); + Opcode(o1_restart); + Opcode(o2_placeItem); + Opcode(o1_setItemPic); + // 0x14 + Opcode(o1_resetPic); + Opcode(o1_goDirection<IDI_DIR_NORTH>); + Opcode(o1_goDirection<IDI_DIR_SOUTH>); + Opcode(o1_goDirection<IDI_DIR_EAST>); + // 0x18 + Opcode(o1_goDirection<IDI_DIR_WEST>); + Opcode(o1_goDirection<IDI_DIR_UP>); + Opcode(o1_goDirection<IDI_DIR_DOWN>); + Opcode(o1_takeItem); + // 0x1c + Opcode(o1_dropItem); + Opcode(o1_setRoomPic); + Opcode(o2_tellTime); + Opcode(o2_setRoomFromVar); + // 0x20 + Opcode(o2_initDisk); +} + +void AdlEngine_v2::initState() { + AdlEngine::initState(); + + _linesPrinted = 0; + _picOnScreen = 0; + _roomOnScreen = 0; + _itemRemoved = false; + _itemsOnScreen = 0; +} + +byte AdlEngine_v2::roomArg(byte room) const { + if (room == IDI_CUR_ROOM) + return _state.room; + return room; +} + +void AdlEngine_v2::advanceClock() { + Time &time = _state.time; + + time.minutes += 5; + + if (time.minutes == 60) { + time.minutes = 0; + + ++time.hours; + + if (time.hours == 13) + time.hours = 1; + } +} + +void AdlEngine_v2::checkTextOverflow(char c) { + if (c != APPLECHAR('\r')) + return; + + ++_linesPrinted; + + if (_linesPrinted < 4) + return; + + _linesPrinted = 0; + _display->updateTextScreen(); + bell(); + + while (true) { + char key = inputKey(false); + + if (shouldQuit()) + return; + + if (key == APPLECHAR('\r')) + break; + + bell(3); + } +} + +Common::String AdlEngine_v2::loadMessage(uint idx) const { + if (_messages[idx]) { + StreamPtr strStream(_messages[idx]->createReadStream()); + return readString(*strStream, 0xff); + } + + return Common::String(); +} + +void AdlEngine_v2::printString(const Common::String &str) { + Common::String s(str); + byte endPos = TEXT_WIDTH - 1; + byte pos = 0; + + while (true) { + while (pos <= endPos && pos != s.size()) { + s.setChar(APPLECHAR(s[pos]), pos); + ++pos; + } + + if (pos == s.size()) + break; + + while (s[pos] != APPLECHAR(' ') && s[pos] != APPLECHAR('\r')) + --pos; + + s.setChar(APPLECHAR('\r'), pos); + endPos = pos + TEXT_WIDTH; + ++pos; + } + + pos = 0; + while (pos != s.size()) { + checkTextOverflow(s[pos]); + _display->printChar(s[pos]); + ++pos; + } + + checkTextOverflow(APPLECHAR('\r')); + _display->printChar(APPLECHAR('\r')); + _display->updateTextScreen(); +} + +void AdlEngine_v2::drawItem(Item &item, const Common::Point &pos) { + item.isOnScreen = true; + StreamPtr stream(_itemPics[item.picture - 1]->createReadStream()); + stream->readByte(); // Skip clear opcode + _graphics->drawPic(*stream, pos); +} + +void AdlEngine_v2::loadRoom(byte roomNr) { + Room &room = getRoom(roomNr); + StreamPtr stream(room.data->createReadStream()); + + uint16 descOffset = stream->readUint16LE(); + uint16 commandOffset = stream->readUint16LE(); + + _roomData.pictures.clear(); + // There's no picture count. The original engine always checks at most + // five pictures. We use the description offset to bound our search. + uint16 picCount = (descOffset - 4) / 5; + + for (uint i = 0; i < picCount; ++i) { + byte nr = stream->readByte(); + _roomData.pictures[nr] = readDataBlockPtr(*stream); + } + + _roomData.description = readStringAt(*stream, descOffset, 0xff); + + _roomData.commands.clear(); + if (commandOffset != 0) { + stream->seek(commandOffset); + readCommands(*stream, _roomData.commands); + } +} + +void AdlEngine_v2::showRoom() { + bool redrawPic = false; + + if (_state.room != _roomOnScreen) { + loadRoom(_state.room); + clearScreen(); + + if (!_state.isDark) + redrawPic = true; + } else { + if (getCurRoom().curPicture != _picOnScreen || _itemRemoved) + redrawPic = true; + } + + if (redrawPic) { + _roomOnScreen = _state.room; + _picOnScreen = getCurRoom().curPicture; + + drawPic(getCurRoom().curPicture); + _itemRemoved = false; + _itemsOnScreen = 0; + + Common::List<Item>::iterator item; + for (item = _state.items.begin(); item != _state.items.end(); ++item) + item->isOnScreen = false; + } + + if (!_state.isDark) + drawItems(); + + _display->updateHiResScreen(); + printString(_roomData.description); + + // FIXME: move to main loop? + _linesPrinted = 0; +} + +void AdlEngine_v2::takeItem(byte noun) { + Common::List<Item>::iterator item; + + for (item = _state.items.begin(); item != _state.items.end(); ++item) { + if (item->noun != noun || item->room != _state.room) + continue; + + if (item->state == IDI_ITEM_DOESNT_MOVE) { + printMessage(_messageIds.itemDoesntMove); + return; + } + + if (item->state == IDI_ITEM_DROPPED) { + item->room = IDI_ANY; + _itemRemoved = true; + return; + } + + Common::Array<byte>::const_iterator pic; + for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) { + if (*pic == getCurRoom().curPicture || *pic == IDI_ANY) { + item->room = IDI_ANY; + _itemRemoved = true; + item->state = IDI_ITEM_DROPPED; + return; + } + } + } + + printMessage(_messageIds.itemNotHere); +} + +void AdlEngine_v2::drawItems() { + Common::List<Item>::iterator item; + + for (item = _state.items.begin(); item != _state.items.end(); ++item) { + // Skip items not in this room + if (item->room != _state.room) + continue; + + if (item->isOnScreen) + continue; + + if (item->state == IDI_ITEM_DROPPED) { + // Draw dropped item if in normal view + if (getCurRoom().picture == getCurRoom().curPicture) + drawItem(*item, _itemOffsets[_itemsOnScreen++]); + } else { + // Draw fixed item if current view is in the pic list + Common::Array<byte>::const_iterator pic; + + for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) { + if (*pic == getCurRoom().curPicture || *pic == IDI_ANY) { + drawItem(*item, item->position); + break; + } + } + } + } +} + +DataBlockPtr AdlEngine_v2::readDataBlockPtr(Common::ReadStream &f) const { + byte track = f.readByte(); + byte sector = f.readByte(); + byte offset = f.readByte(); + byte size = f.readByte(); + + if (f.eos() || f.err()) + error("Error reading DataBlockPtr"); + + if (track == 0 && sector == 0 && offset == 0 && size == 0) + return DataBlockPtr(); + + return _disk->getDataBlock(track, sector, offset, size); +} + +int AdlEngine_v2::o2_isFirstTime(ScriptEnv &e) { + OP_DEBUG_0("\t&& IS_FIRST_TIME()"); + + bool oldFlag = getCurRoom().isFirstTime; + + getCurRoom().isFirstTime = false; + + if (!oldFlag) + return -1; + + return 0; +} + +int AdlEngine_v2::o2_isRandomGT(ScriptEnv &e) { + OP_DEBUG_1("\t&& RAND() > %d", e.arg(1)); + + byte rnd = _random->getRandomNumber(255); + + if (rnd > e.arg(1)) + return 1; + + return -1; +} + +int AdlEngine_v2::o2_isNounNotInRoom(ScriptEnv &e) { + OP_DEBUG_1("\t&& NO_SUCH_ITEMS_IN_ROOM(%s)", itemRoomStr(e.arg(1)).c_str()); + + Common::List<Item>::const_iterator item; + + for (item = _state.items.begin(); item != _state.items.end(); ++item) + if (item->noun == e.getNoun() && (item->room == roomArg(e.arg(1)))) + return -1; + + return 1; +} + +int AdlEngine_v2::o2_isCarryingSomething(ScriptEnv &e) { + OP_DEBUG_0("\t&& IS_CARRYING_SOMETHING()"); + + Common::List<Item>::const_iterator item; + + for (item = _state.items.begin(); item != _state.items.end(); ++item) + if (item->room == IDI_ANY) + return 0; + return -1; +} + +int AdlEngine_v2::o2_moveItem(ScriptEnv &e) { + OP_DEBUG_2("\tSET_ITEM_ROOM(%s, %s)", itemStr(e.arg(1)).c_str(), itemRoomStr(e.arg(2)).c_str()); + + byte room = roomArg(e.arg(2)); + + Item &item = getItem(e.arg(1)); + + if (item.room == _roomOnScreen) + _picOnScreen = 0; + + // Set items that move from inventory to a room to state "dropped" + if (item.room == IDI_ANY && room != IDI_VOID_ROOM) + item.state = IDI_ITEM_DROPPED; + + item.room = room; + return 2; +} + +int AdlEngine_v2::o2_moveAllItems(ScriptEnv &e) { + OP_DEBUG_2("\tMOVE_ALL_ITEMS(%s, %s)", itemRoomStr(e.arg(1)).c_str(), itemRoomStr(e.arg(2)).c_str()); + + byte room1 = roomArg(e.arg(1)); + + if (room1 == _state.room) + _picOnScreen = 0; + + byte room2 = roomArg(e.arg(2)); + + Common::List<Item>::iterator item; + + for (item = _state.items.begin(); item != _state.items.end(); ++item) + if (item->room == room1) { + item->room = room2; + if (room1 == IDI_ANY) + item->state = IDI_ITEM_DROPPED; + } + + return 2; +} + +int AdlEngine_v2::o2_save(ScriptEnv &e) { + OP_DEBUG_0("\tSAVE_GAME()"); + + int slot = askForSlot(_strings_v2.saveInsert); + + if (slot < 0) + return -1; + + saveGameState(slot, ""); + + _display->printString(_strings_v2.saveReplace); + inputString(); + return 0; +} + +int AdlEngine_v2::o2_restore(ScriptEnv &e) { + OP_DEBUG_0("\tRESTORE_GAME()"); + + int slot = askForSlot(_strings_v2.restoreInsert); + + if (slot < 0) + return -1; + + loadGameState(slot); + _isRestoring = false; + + _display->printString(_strings_v2.restoreReplace); + inputString(); + _picOnScreen = 0; + _roomOnScreen = 0; + return 0; +} + +int AdlEngine_v2::o2_placeItem(ScriptEnv &e) { + OP_DEBUG_4("\tPLACE_ITEM(%s, %s, (%d, %d))", itemStr(e.arg(1)).c_str(), itemRoomStr(e.arg(2)).c_str(), e.arg(3), e.arg(4)); + + Item &item = getItem(e.arg(1)); + + item.room = roomArg(e.arg(2)); + item.position.x = e.arg(3); + item.position.y = e.arg(4); + item.state = IDI_ITEM_NOT_MOVED; + + return 4; +} + +int AdlEngine_v2::o2_tellTime(ScriptEnv &e) { + OP_DEBUG_0("\tTELL_TIME()"); + + Common::String time = _strings_v2.time; + + time.setChar(APPLECHAR('0') + _state.time.hours / 10, 12); + time.setChar(APPLECHAR('0') + _state.time.hours % 10, 13); + time.setChar(APPLECHAR('0') + _state.time.minutes / 10, 15); + time.setChar(APPLECHAR('0') + _state.time.minutes % 10, 16); + + printString(time); + + return 0; +} + +int AdlEngine_v2::o2_setRoomFromVar(ScriptEnv &e) { + OP_DEBUG_1("\tROOM = VAR[%d]", e.arg(1)); + getCurRoom().curPicture = getCurRoom().picture; + _state.room = getVar(e.arg(1)); + return 1; +} + +int AdlEngine_v2::o2_initDisk(ScriptEnv &e) { + OP_DEBUG_0("\tINIT_DISK()"); + + _display->printAsciiString("NOT REQUIRED\r"); + return 0; +} + +int AdlEngine_v2::askForSlot(const Common::String &question) { + while (1) { + _display->printString(question); + + Common::String input = inputString(); + + if (shouldQuit()) + return -1; + + if (input.size() > 0 && input[0] >= APPLECHAR('A') && input[0] <= APPLECHAR('O')) + return input[0] - APPLECHAR('A'); + } +} + +} // End of namespace Adl |