aboutsummaryrefslogtreecommitdiff
path: root/engines/adl/adl_v2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/adl/adl_v2.cpp')
-rw-r--r--engines/adl/adl_v2.cpp539
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