aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWalter van Niftrik2016-03-07 20:43:37 +0100
committerWalter van Niftrik2016-03-09 10:03:13 +0100
commit63adab81edc8f44d4b4387352e0869e3042c2a13 (patch)
tree7fbae76a457730dc61d6d95132c8adb1e0afb1c5
parent86d58534e7138c7b58995e1f730c8531ca2d4273 (diff)
downloadscummvm-rg350-63adab81edc8f44d4b4387352e0869e3042c2a13.tar.gz
scummvm-rg350-63adab81edc8f44d4b4387352e0869e3042c2a13.tar.bz2
scummvm-rg350-63adab81edc8f44d4b4387352e0869e3042c2a13.zip
ADL: Clean up HiRes1Engine class
-rw-r--r--engines/adl/adl.cpp1266
-rw-r--r--engines/adl/adl.h14
-rw-r--r--engines/adl/hires1.cpp214
-rw-r--r--engines/adl/hires1.h2
4 files changed, 746 insertions, 750 deletions
diff --git a/engines/adl/adl.cpp b/engines/adl/adl.cpp
index 5b43a6cd31..dd4f4068d1 100644
--- a/engines/adl/adl.cpp
+++ b/engines/adl/adl.cpp
@@ -41,10 +41,14 @@
namespace Adl {
+AdlEngine::~AdlEngine() {
+ delete _display;
+}
+
AdlEngine::AdlEngine(OSystem *syst, const AdlGameDescription *gd) :
Engine(syst),
- _gameDescription(gd),
_display(nullptr),
+ _gameDescription(gd),
_isRestarting(false),
_isRestoring(false),
_saveVerb(0),
@@ -55,89 +59,6 @@ AdlEngine::AdlEngine(OSystem *syst, const AdlGameDescription *gd) :
_canRestoreNow(false) {
}
-AdlEngine::~AdlEngine() {
- delete _display;
-}
-
-bool AdlEngine::hasFeature(EngineFeature f) const {
- switch (f) {
- case kSupportsLoadingDuringRuntime:
- case kSupportsSavingDuringRuntime:
- case kSupportsRTL:
- return true;
- default:
- return false;
- }
-}
-
-Common::Error AdlEngine::run() {
- _display = new Display();
-
- loadData();
-
- int saveSlot = ConfMan.getInt("save_slot");
- if (saveSlot >= 0) {
- if (loadGameState(saveSlot).getCode() != Common::kNoError)
- error("Failed to load save game from slot %i", saveSlot);
- _display->moveCursorTo(Common::Point(0, 23));
- _isRestoring = true;
- } else {
- runIntro();
- initState();
- }
-
- _display->setMode(DISPLAY_MODE_MIXED);
- _display->printAsciiString("\r\r\r\r\r");
-
- while (1) {
- uint verb = 0, noun = 0;
-
- // When restoring from the launcher, we don't read
- // input on the first iteration. This is needed to
- // ensure that restoring from the launcher and
- // restoring in-game brings us to the same game state.
- // (Also see comment below.)
- if (!_isRestoring) {
- clearScreen();
- showRoom();
-
- _canSaveNow = _canRestoreNow = true;
- getInput(verb, noun);
- _canSaveNow = _canRestoreNow = false;
-
- if (shouldQuit())
- break;
-
- if (!doOneCommand(_roomCommands, verb, noun))
- printMessage(_messageIds.dontUnderstand);
- }
-
- if (_isRestoring) {
- // We restored from the GMM or launcher. As restoring
- // with "RESTORE GAME" does not end command processing,
- // we don't break it off here either. This essentially
- // means that restoring a game will always run through
- // the global commands and increase the move counter
- // before the first user input.
- _display->printAsciiString("\r");
- _isRestoring = false;
- verb = _restoreVerb;
- noun = _restoreNoun;
- }
-
- // Restarting does end command processing
- if (_isRestarting) {
- _isRestarting = false;
- continue;
- }
-
- doAllCommands(_globalCommands, verb, noun);
- _state.moves++;
- }
-
- return Common::kNoError;
-}
-
Common::String AdlEngine::readString(Common::ReadStream &stream, byte until) const {
Common::String str;
@@ -161,21 +82,6 @@ Common::String AdlEngine::readStringAt(Common::SeekableReadStream &stream, uint
return readString(stream, until);
}
-void AdlEngine::wordWrap(Common::String &str) const {
- uint end = 39;
-
- while (1) {
- if (str.size() <= end)
- return;
-
- while (str[end] != APPLECHAR(' '))
- --end;
-
- str.setChar(APPLECHAR('\r'), end);
- end += 40;
- }
-}
-
void AdlEngine::printMessage(uint idx, bool wait) const {
Common::String msg = _messages[idx - 1];
wordWrap(msg);
@@ -185,456 +91,254 @@ void AdlEngine::printMessage(uint idx, bool wait) const {
delay(14 * 166018 / 1000);
}
-void AdlEngine::readCommands(Common::ReadStream &stream, Commands &commands) {
- while (1) {
- Command command;
- command.room = stream.readByte();
+void AdlEngine::delay(uint32 ms) const {
+ Common::EventManager *ev = g_system->getEventManager();
- if (command.room == 0xff)
- return;
+ uint32 start = g_system->getMillis();
- command.verb = stream.readByte();
- command.noun = stream.readByte();
+ while (!g_engine->shouldQuit() && g_system->getMillis() - start < ms) {
+ Common::Event event;
+ if (ev->pollEvent(event)) {
+ if (event.type == Common::EVENT_KEYDOWN && (event.kbd.flags & Common::KBD_CTRL)) {
+ switch(event.kbd.keycode) {
+ case Common::KEYCODE_q:
+ g_engine->quitGame();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ g_system->delayMillis(16);
+ }
+}
- byte scriptSize = stream.readByte() - 6;
+Common::String AdlEngine::inputString(byte prompt) const {
+ Common::String s;
- command.numCond = stream.readByte();
- command.numAct = stream.readByte();
+ if (prompt > 0)
+ _display->printString(Common::String(prompt));
- for (uint i = 0; i < scriptSize; ++i)
- command.script.push_back(stream.readByte());
+ while (1) {
+ byte b = inputKey();
- if (stream.eos() || stream.err())
- error("Failed to read commands");
+ if (g_engine->shouldQuit() || _isRestoring)
+ return 0;
- if (command.numCond == 0 && command.script[0] == IDO_ACT_SAVE) {
- _saveVerb = command.verb;
- _saveNoun = command.noun;
- }
+ if (b == 0)
+ continue;
- if (command.numCond == 0 && command.script[0] == IDO_ACT_LOAD) {
- _restoreVerb = command.verb;
- _restoreNoun = command.noun;
+ if (b == ('\r' | 0x80)) {
+ s += b;
+ _display->printString(Common::String(b));
+ return s;
}
- commands.push_back(command);
+ if (b < 0xa0) {
+ switch (b) {
+ case Common::KEYCODE_BACKSPACE | 0x80:
+ if (!s.empty()) {
+ _display->moveCursorBackward();
+ _display->setCharAtCursor(APPLECHAR(' '));
+ s.deleteLastChar();
+ }
+ break;
+ };
+ } else {
+ s += b;
+ _display->printString(Common::String(b));
+ }
}
}
-void AdlEngine::takeItem(byte noun) {
- Common::Array<Item>::iterator item;
+byte AdlEngine::inputKey() const {
+ Common::EventManager *ev = g_system->getEventManager();
- for (item = _state.items.begin(); item != _state.items.end(); ++item) {
- if (item->noun != noun || item->room != _state.room)
- continue;
+ byte key = 0;
- if (item->state == IDI_ITEM_DOESNT_MOVE) {
- printMessage(_messageIds.itemDoesntMove);
- return;
- }
+ _display->showCursor(true);
- if (item->state == IDI_ITEM_MOVED) {
- item->room = IDI_NONE;
- return;
- }
+ while (!g_engine->shouldQuit() && !_isRestoring && key == 0) {
+ Common::Event event;
+ if (ev->pollEvent(event)) {
+ if (event.type != Common::EVENT_KEYDOWN)
+ continue;
- Common::Array<byte>::const_iterator pic;
- for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) {
- if (*pic == curRoom().curPicture) {
- item->room = IDI_NONE;
- item->state = IDI_ITEM_MOVED;
- return;
+ if (event.kbd.flags & Common::KBD_CTRL) {
+ if (event.kbd.keycode == Common::KEYCODE_q)
+ g_engine->quitGame();
+ continue;
}
- }
- }
-
- printMessage(_messageIds.itemNotHere);
-}
-
-void AdlEngine::dropItem(byte noun) {
- Common::Array<Item>::iterator item;
- for (item = _state.items.begin(); item != _state.items.end(); ++item) {
- if (item->noun != noun || item->room != IDI_NONE)
- continue;
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_BACKSPACE:
+ case Common::KEYCODE_RETURN:
+ key = convertKey(event.kbd.keycode);
+ break;
+ default:
+ if (event.kbd.ascii >= 0x20 && event.kbd.ascii < 0x80)
+ key = convertKey(event.kbd.ascii);
+ };
+ }
- item->room = _state.room;
- item->state = IDI_ITEM_MOVED;
- return;
+ _display->updateTextScreen();
+ g_system->delayMillis(16);
}
- printMessage(_messageIds.dontUnderstand);
+ _display->showCursor(false);
+
+ return key;
}
-#define ARG(N) (command.script[offset + (N)])
+void AdlEngine::loadWords(Common::ReadStream &stream, WordMap &map) const {
+ uint index = 0;
-void AdlEngine::doActions(const Command &command, byte noun, byte offset) {
- for (uint i = 0; i < command.numAct; ++i) {
- switch (ARG(0)) {
- case IDO_ACT_VAR_ADD:
- var(ARG(2)) += ARG(1);
- offset += 3;
- break;
- case IDO_ACT_VAR_SUB:
- var(ARG(2)) -= ARG(1);
- offset += 3;
- break;
- case IDO_ACT_VAR_SET:
- var(ARG(1)) = ARG(2);
- offset += 3;
- break;
- case IDO_ACT_LIST_ITEMS: {
- Common::Array<Item>::const_iterator item;
+ while (1) {
+ ++index;
- for (item = _state.items.begin(); item != _state.items.end(); ++item)
- if (item->room == IDI_NONE)
- printMessage(item->description);
+ byte buf[IDI_WORD_SIZE];
- ++offset;
- break;
- }
- case IDO_ACT_MOVE_ITEM:
- item(ARG(1)).room = ARG(2);
- offset += 3;
- break;
- case IDO_ACT_SET_ROOM:
- curRoom().curPicture = curRoom().picture;
- _state.room = ARG(1);
- offset += 2;
- break;
- case IDO_ACT_SET_CUR_PIC:
- curRoom().curPicture = ARG(1);
- offset += 2;
- break;
- case IDO_ACT_SET_PIC:
- curRoom().picture = curRoom().curPicture = ARG(1);
- offset += 2;
- break;
- case IDO_ACT_PRINT_MSG:
- printMessage(ARG(1));
- offset += 2;
- break;
- case IDO_ACT_SET_LIGHT:
- _state.isDark = false;
- ++offset;
- break;
- case IDO_ACT_SET_DARK:
- _state.isDark = true;
- ++offset;
- break;
- case IDO_ACT_SAVE:
- saveGameState(0, "");
- ++offset;
- break;
- case IDO_ACT_LOAD:
- loadGameState(0);
- ++offset;
- // Original engine does not jump out of the loop,
- // so we don't either.
- // We reset the restore flag, as the restore game
- // process is complete
- _isRestoring = false;
- break;
- case IDO_ACT_RESTART: {
- _display->printString(_strings.playAgain);
+ if (stream.read(buf, IDI_WORD_SIZE) < IDI_WORD_SIZE)
+ error("Error reading word list");
- // We allow restoring via GMM here
- _canRestoreNow = true;
- Common::String input = inputString();
- _canRestoreNow = false;
+ Common::String word((char *)buf, IDI_WORD_SIZE);
- // If the user restored with the GMM, we break off the restart
- if (_isRestoring)
- return;
+ if (!map.contains(word))
+ map[word] = index;
- if (input.size() == 0 || input[0] != APPLECHAR('N')) {
- _isRestarting = true;
- _display->clear(0x00);
- _display->updateHiResScreen();
- restartGame();
- return;
- }
- // Fall-through
- }
- case IDO_ACT_QUIT:
- printMessage(_messageIds.thanksForPlaying);
- quitGame();
- return;
- case IDO_ACT_PLACE_ITEM:
- item(ARG(1)).room = ARG(2);
- item(ARG(1)).position.x = ARG(3);
- item(ARG(1)).position.y = ARG(4);
- offset += 5;
- break;
- case IDO_ACT_SET_ITEM_PIC:
- item(ARG(2)).picture = ARG(1);
- offset += 3;
- break;
- case IDO_ACT_RESET_PIC:
- curRoom().curPicture = curRoom().picture;
- ++offset;
- break;
- case IDO_ACT_GO_NORTH:
- case IDO_ACT_GO_SOUTH:
- case IDO_ACT_GO_EAST:
- case IDO_ACT_GO_WEST:
- case IDO_ACT_GO_UP:
- case IDO_ACT_GO_DOWN: {
- byte room = curRoom().connections[ARG(0) - IDO_ACT_GO_NORTH];
+ byte synonyms = stream.readByte();
- if (room == 0) {
- printMessage(_messageIds.cantGoThere);
- return;
- }
+ if (stream.err() || stream.eos())
+ error("Error reading word list");
- curRoom().curPicture = curRoom().picture;
- _state.room = room;
- return;
- }
- case IDO_ACT_TAKE_ITEM:
- takeItem(noun);
- ++offset;
- break;
- case IDO_ACT_DROP_ITEM:
- dropItem(noun);
- ++offset;
- break;
- case IDO_ACT_SET_ROOM_PIC:
- room(ARG(1)).picture = room(ARG(1)).curPicture = ARG(2);
- offset += 3;
+ if (synonyms == 0xff)
break;
- default:
- error("Invalid action opcode %02x", ARG(0));
- }
- }
-}
-
-bool AdlEngine::matchCommand(const Command &command, byte verb, byte noun, uint *actions) const {
- if (command.room != IDI_NONE && command.room != _state.room)
- return false;
- if (command.verb != IDI_NONE && command.verb != verb)
- return false;
+ for (uint i = 0; i < synonyms; ++i) {
+ if (stream.read((char *)buf, IDI_WORD_SIZE) < IDI_WORD_SIZE)
+ error("Error reading word list");
- if (command.noun != IDI_NONE && command.noun != noun)
- return false;
+ word = Common::String((char *)buf, IDI_WORD_SIZE);
- uint offset = 0;
- for (uint i = 0; i < command.numCond; ++i) {
- switch (ARG(0)) {
- case IDO_CND_ITEM_IN_ROOM:
- if (item(ARG(1)).room != ARG(2))
- return false;
- offset += 3;
- break;
- case IDO_CND_MOVES_GE:
- if (ARG(1) > _state.moves)
- return false;
- offset += 2;
- break;
- case IDO_CND_VAR_EQ:
- if (var(ARG(1)) != ARG(2))
- return false;
- offset += 3;
- break;
- case IDO_CND_CUR_PIC_EQ:
- if (curRoom().curPicture != ARG(1))
- return false;
- offset += 2;
- break;
- case IDO_CND_ITEM_PIC_EQ:
- if (item(ARG(1)).picture != ARG(2))
- return false;
- offset += 3;
- break;
- default:
- error("Invalid condition opcode %02x", command.script[offset]);
+ if (!map.contains(word))
+ map[word] = index;
}
}
-
- *actions = offset;
-
- return true;
}
-#undef ARG
-
-bool AdlEngine::doOneCommand(const Commands &commands, byte verb, byte noun) {
- Commands::const_iterator cmd;
-
- for (cmd = commands.begin(); cmd != commands.end(); ++cmd) {
- uint offset = 0;
- if (matchCommand(*cmd, verb, noun, &offset)) {
- doActions(*cmd, noun, offset);
- return true;
- }
- }
+void AdlEngine::readCommands(Common::ReadStream &stream, Commands &commands) {
+ while (1) {
+ Command command;
+ command.room = stream.readByte();
- return false;
-}
+ if (command.room == 0xff)
+ return;
-void AdlEngine::doAllCommands(const Commands &commands, byte verb, byte noun) {
- Commands::const_iterator cmd;
- bool oldIsRestoring = _isRestoring;
+ command.verb = stream.readByte();
+ command.noun = stream.readByte();
- for (cmd = commands.begin(); cmd != commands.end(); ++cmd) {
- uint offset = 0;
- if (matchCommand(*cmd, verb, noun, &offset))
- doActions(*cmd, noun, offset);
+ byte scriptSize = stream.readByte() - 6;
- // We assume no restarts happen in this command group. This
- // simplifies enabling GMM savegame loading on the restart
- // prompt.
- if (_isRestarting || _isRestoring != oldIsRestoring)
- error("Unexpected restart action encountered");
- }
-}
+ command.numCond = stream.readByte();
+ command.numAct = stream.readByte();
-bool AdlEngine::canSaveGameStateCurrently() const {
- if (!_canSaveNow)
- return false;
+ for (uint i = 0; i < scriptSize; ++i)
+ command.script.push_back(stream.readByte());
- Commands::const_iterator cmd;
+ if (stream.eos() || stream.err())
+ error("Failed to read commands");
- // Here we check whether or not the game currently accepts the command
- // "SAVE GAME". This prevents saving via the GMM in situations where
- // it wouldn't otherwise be possible to do so.
- for (cmd = _roomCommands.begin(); cmd != _roomCommands.end(); ++cmd) {
- if (matchCommand(*cmd, _saveVerb, _saveNoun)) {
- if (cmd->verb != _saveVerb || cmd->noun != _saveNoun)
- return false;
- return cmd->numCond == 0 && cmd->script[0] == IDO_ACT_SAVE;
+ if (command.numCond == 0 && command.script[0] == IDO_ACT_SAVE) {
+ _saveVerb = command.verb;
+ _saveNoun = command.noun;
}
- }
-
- return false;
-}
-
-bool AdlEngine::canLoadGameStateCurrently() const {
- return _canRestoreNow;
-}
-
-void AdlEngine::clearScreen() const {
- _display->setMode(DISPLAY_MODE_MIXED);
- _display->clear(0x00);
-}
-
-void AdlEngine::drawItems() const {
- Common::Array<Item>::const_iterator item;
- uint dropped = 0;
-
- for (item = _state.items.begin(); item != _state.items.end(); ++item) {
- if (item->room != _state.room)
- continue;
-
- if (item->state == IDI_ITEM_MOVED) {
- if (curRoom().picture == curRoom().curPicture) {
- const Common::Point &p = _itemOffsets[dropped];
- if (item->isLineArt)
- drawLineArt(_lineArt[item->picture - 1], p);
- else
- drawPic(item->picture, p);
- ++dropped;
- }
- continue;
+ if (command.numCond == 0 && command.script[0] == IDO_ACT_LOAD) {
+ _restoreVerb = command.verb;
+ _restoreNoun = command.noun;
}
- Common::Array<byte>::const_iterator pic;
-
- for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) {
- if (*pic == curRoom().curPicture) {
- if (item->isLineArt)
- drawLineArt(_lineArt[item->picture - 1], item->position);
- else
- drawPic(item->picture, item->position);
- continue;
- }
- }
+ commands.push_back(command);
}
}
-void AdlEngine::showRoom() const {
- if (!_state.isDark) {
- drawPic(curRoom().curPicture);
- drawItems();
- }
-
- _display->updateHiResScreen();
- printMessage(curRoom().description, false);
-}
+Common::Error AdlEngine::run() {
+ _display = new Display();
-Common::Error AdlEngine::saveGameState(int slot, const Common::String &desc) {
- Common::String fileName = Common::String::format("%s.s%02d", _targetName.c_str(), slot);
- Common::OutSaveFile *outFile = getSaveFileManager()->openForSaving(fileName);
+ loadData();
- if (!outFile) {
- warning("Failed to open file '%s'", fileName.c_str());
- return Common::kUnknownError;
+ int saveSlot = ConfMan.getInt("save_slot");
+ if (saveSlot >= 0) {
+ if (loadGameState(saveSlot).getCode() != Common::kNoError)
+ error("Failed to load save game from slot %i", saveSlot);
+ _display->moveCursorTo(Common::Point(0, 23));
+ _isRestoring = true;
+ } else {
+ runIntro();
+ initState();
}
- outFile->writeUint32BE(MKTAG('A', 'D', 'L', ':'));
- outFile->writeByte(SAVEGAME_VERSION);
-
- char name[SAVEGAME_NAME_LEN] = { };
-
- if (!desc.empty())
- strncpy(name, desc.c_str(), sizeof(name) - 1);
- else {
- Common::String defaultName("Save ");
- defaultName += 'A' + slot;
- strncpy(name, defaultName.c_str(), sizeof(name) - 1);
- }
+ _display->setMode(DISPLAY_MODE_MIXED);
+ _display->printAsciiString("\r\r\r\r\r");
- outFile->write(name, sizeof(name));
+ while (1) {
+ uint verb = 0, noun = 0;
- TimeDate t;
- g_system->getTimeAndDate(t);
+ // When restoring from the launcher, we don't read
+ // input on the first iteration. This is needed to
+ // ensure that restoring from the launcher and
+ // restoring in-game brings us to the same game state.
+ // (Also see comment below.)
+ if (!_isRestoring) {
+ clearScreen();
+ showRoom();
- outFile->writeUint16BE(t.tm_year);
- outFile->writeByte(t.tm_mon);
- outFile->writeByte(t.tm_mday);
- outFile->writeByte(t.tm_hour);
- outFile->writeByte(t.tm_min);
+ _canSaveNow = _canRestoreNow = true;
+ getInput(verb, noun);
+ _canSaveNow = _canRestoreNow = false;
- uint32 playTime = getTotalPlayTime();
- outFile->writeUint32BE(playTime);
+ if (shouldQuit())
+ break;
- _display->saveThumbnail(*outFile);
+ if (!doOneCommand(_roomCommands, verb, noun))
+ printMessage(_messageIds.dontUnderstand);
+ }
- outFile->writeByte(_state.room);
- outFile->writeByte(_state.moves);
- outFile->writeByte(_state.isDark);
+ if (_isRestoring) {
+ // We restored from the GMM or launcher. As restoring
+ // with "RESTORE GAME" does not end command processing,
+ // we don't break it off here either. This essentially
+ // means that restoring a game will always run through
+ // the global commands and increase the move counter
+ // before the first user input.
+ _display->printAsciiString("\r");
+ _isRestoring = false;
+ verb = _restoreVerb;
+ noun = _restoreNoun;
+ }
- outFile->writeUint32BE(_state.rooms.size());
- for (uint i = 0; i < _state.rooms.size(); ++i) {
- outFile->writeByte(_state.rooms[i].picture);
- outFile->writeByte(_state.rooms[i].curPicture);
- }
+ // Restarting does end command processing
+ if (_isRestarting) {
+ _isRestarting = false;
+ continue;
+ }
- outFile->writeUint32BE(_state.items.size());
- for (uint i = 0; i < _state.items.size(); ++i) {
- outFile->writeByte(_state.items[i].room);
- outFile->writeByte(_state.items[i].picture);
- outFile->writeByte(_state.items[i].position.x);
- outFile->writeByte(_state.items[i].position.y);
- outFile->writeByte(_state.items[i].state);
+ doAllCommands(_globalCommands, verb, noun);
+ _state.moves++;
}
- outFile->writeUint32BE(_state.vars.size());
- for (uint i = 0; i < _state.vars.size(); ++i)
- outFile->writeByte(_state.vars[i]);
-
- outFile->finalize();
+ return Common::kNoError;
+}
- if (outFile->err()) {
- delete outFile;
- warning("Failed to save game '%s'", fileName.c_str());
- return Common::kUnknownError;
+bool AdlEngine::hasFeature(EngineFeature f) const {
+ switch (f) {
+ case kSupportsLoadingDuringRuntime:
+ case kSupportsSavingDuringRuntime:
+ case kSupportsRTL:
+ return true;
+ default:
+ return false;
}
-
- delete outFile;
- return Common::kNoError;
}
Common::Error AdlEngine::loadGameState(int slot) {
@@ -713,92 +417,132 @@ Common::Error AdlEngine::loadGameState(int slot) {
return Common::kNoError;
}
-const Room &AdlEngine::room(uint i) const {
- if (i < 1 || i > _state.rooms.size())
- error("Room %i out of range [1, %i]", i, _state.rooms.size());
-
- return _state.rooms[i - 1];
+bool AdlEngine::canLoadGameStateCurrently() const {
+ return _canRestoreNow;
}
-Room &AdlEngine::room(uint i) {
- if (i < 1 || i > _state.rooms.size())
- error("Room %i out of range [1, %i]", i, _state.rooms.size());
+Common::Error AdlEngine::saveGameState(int slot, const Common::String &desc) {
+ Common::String fileName = Common::String::format("%s.s%02d", _targetName.c_str(), slot);
+ Common::OutSaveFile *outFile = getSaveFileManager()->openForSaving(fileName);
- return _state.rooms[i - 1];
-}
+ if (!outFile) {
+ warning("Failed to open file '%s'", fileName.c_str());
+ return Common::kUnknownError;
+ }
-const Room &AdlEngine::curRoom() const {
- return room(_state.room);
-}
+ outFile->writeUint32BE(MKTAG('A', 'D', 'L', ':'));
+ outFile->writeByte(SAVEGAME_VERSION);
-Room &AdlEngine::curRoom() {
- return room(_state.room);
-}
+ char name[SAVEGAME_NAME_LEN] = { };
-const Item &AdlEngine::item(uint i) const {
- if (i < 1 || i > _state.items.size())
- error("Item %i out of range [1, %i]", i, _state.items.size());
+ if (!desc.empty())
+ strncpy(name, desc.c_str(), sizeof(name) - 1);
+ else {
+ Common::String defaultName("Save ");
+ defaultName += 'A' + slot;
+ strncpy(name, defaultName.c_str(), sizeof(name) - 1);
+ }
- return _state.items[i - 1];
-}
+ outFile->write(name, sizeof(name));
-Item &AdlEngine::item(uint i) {
- if (i < 1 || i > _state.items.size())
- error("Item %i out of range [1, %i]", i, _state.items.size());
+ TimeDate t;
+ g_system->getTimeAndDate(t);
- return _state.items[i - 1];
-}
+ outFile->writeUint16BE(t.tm_year);
+ outFile->writeByte(t.tm_mon);
+ outFile->writeByte(t.tm_mday);
+ outFile->writeByte(t.tm_hour);
+ outFile->writeByte(t.tm_min);
-const byte &AdlEngine::var(uint i) const {
- if (i >= _state.vars.size())
- error("Variable %i out of range [0, %i]", i, _state.vars.size() - 1);
+ uint32 playTime = getTotalPlayTime();
+ outFile->writeUint32BE(playTime);
- return _state.vars[i];
-}
+ _display->saveThumbnail(*outFile);
-byte &AdlEngine::var(uint i) {
- if (i >= _state.vars.size())
- error("Variable %i out of range [0, %i]", i, _state.vars.size() - 1);
+ outFile->writeByte(_state.room);
+ outFile->writeByte(_state.moves);
+ outFile->writeByte(_state.isDark);
- return _state.vars[i];
-}
+ outFile->writeUint32BE(_state.rooms.size());
+ for (uint i = 0; i < _state.rooms.size(); ++i) {
+ outFile->writeByte(_state.rooms[i].picture);
+ outFile->writeByte(_state.rooms[i].curPicture);
+ }
-void AdlEngine::loadWords(Common::ReadStream &stream, WordMap &map) const {
- uint index = 0;
+ outFile->writeUint32BE(_state.items.size());
+ for (uint i = 0; i < _state.items.size(); ++i) {
+ outFile->writeByte(_state.items[i].room);
+ outFile->writeByte(_state.items[i].picture);
+ outFile->writeByte(_state.items[i].position.x);
+ outFile->writeByte(_state.items[i].position.y);
+ outFile->writeByte(_state.items[i].state);
+ }
- while (1) {
- ++index;
+ outFile->writeUint32BE(_state.vars.size());
+ for (uint i = 0; i < _state.vars.size(); ++i)
+ outFile->writeByte(_state.vars[i]);
- byte buf[IDI_WORD_SIZE];
+ outFile->finalize();
- if (stream.read(buf, IDI_WORD_SIZE) < IDI_WORD_SIZE)
- error("Error reading word list");
+ if (outFile->err()) {
+ delete outFile;
+ warning("Failed to save game '%s'", fileName.c_str());
+ return Common::kUnknownError;
+ }
- Common::String word((char *)buf, IDI_WORD_SIZE);
+ delete outFile;
+ return Common::kNoError;
+}
- if (!map.contains(word))
- map[word] = index;
+bool AdlEngine::canSaveGameStateCurrently() const {
+ if (!_canSaveNow)
+ return false;
- byte synonyms = stream.readByte();
+ Commands::const_iterator cmd;
- if (stream.err() || stream.eos())
- error("Error reading word list");
+ // Here we check whether or not the game currently accepts the command
+ // "SAVE GAME". This prevents saving via the GMM in situations where
+ // it wouldn't otherwise be possible to do so.
+ for (cmd = _roomCommands.begin(); cmd != _roomCommands.end(); ++cmd) {
+ if (matchCommand(*cmd, _saveVerb, _saveNoun)) {
+ if (cmd->verb != _saveVerb || cmd->noun != _saveNoun)
+ return false;
+ return cmd->numCond == 0 && cmd->script[0] == IDO_ACT_SAVE;
+ }
+ }
- if (synonyms == 0xff)
- break;
+ return false;
+}
- for (uint i = 0; i < synonyms; ++i) {
- if (stream.read((char *)buf, IDI_WORD_SIZE) < IDI_WORD_SIZE)
- error("Error reading word list");
+void AdlEngine::wordWrap(Common::String &str) const {
+ uint end = 39;
- word = Common::String((char *)buf, IDI_WORD_SIZE);
+ while (1) {
+ if (str.size() <= end)
+ return;
- if (!map.contains(word))
- map[word] = index;
- }
+ while (str[end] != APPLECHAR(' '))
+ --end;
+
+ str.setChar(APPLECHAR('\r'), end);
+ end += 40;
}
}
+byte AdlEngine::convertKey(uint16 ascii) const {
+ ascii = toupper(ascii);
+
+ if (ascii >= 0x80)
+ return 0;
+
+ ascii |= 0x80;
+
+ if (ascii >= 0x80 && ascii <= 0xe0)
+ return ascii;
+
+ return 0;
+}
+
Common::String AdlEngine::getLine() const {
// Original engine uses a global here, which isn't reset between
// calls and may not match actual mode
@@ -889,116 +633,53 @@ void AdlEngine::getInput(uint &verb, uint &noun) {
}
}
-Common::String AdlEngine::inputString(byte prompt) const {
- Common::String s;
-
- if (prompt > 0)
- _display->printString(Common::String(prompt));
-
- while (1) {
- byte b = inputKey();
-
- if (g_engine->shouldQuit() || _isRestoring)
- return 0;
-
- if (b == 0)
- continue;
-
- if (b == ('\r' | 0x80)) {
- s += b;
- _display->printString(Common::String(b));
- return s;
- }
-
- if (b < 0xa0) {
- switch (b) {
- case Common::KEYCODE_BACKSPACE | 0x80:
- if (!s.empty()) {
- _display->moveCursorBackward();
- _display->setCharAtCursor(APPLECHAR(' '));
- s.deleteLastChar();
- }
- break;
- };
- } else {
- s += b;
- _display->printString(Common::String(b));
- }
+void AdlEngine::showRoom() const {
+ if (!_state.isDark) {
+ drawPic(curRoom().curPicture);
+ drawItems();
}
-}
-byte AdlEngine::convertKey(uint16 ascii) const {
- ascii = toupper(ascii);
-
- if (ascii >= 0x80)
- return 0;
-
- ascii |= 0x80;
-
- if (ascii >= 0x80 && ascii <= 0xe0)
- return ascii;
-
- return 0;
+ _display->updateHiResScreen();
+ printMessage(curRoom().description, false);
}
-byte AdlEngine::inputKey() const {
- Common::EventManager *ev = g_system->getEventManager();
+void AdlEngine::clearScreen() const {
+ _display->setMode(DISPLAY_MODE_MIXED);
+ _display->clear(0x00);
+}
- byte key = 0;
+void AdlEngine::drawItems() const {
+ Common::Array<Item>::const_iterator item;
- _display->showCursor(true);
+ uint dropped = 0;
- while (!g_engine->shouldQuit() && !_isRestoring && key == 0) {
- Common::Event event;
- if (ev->pollEvent(event)) {
- if (event.type != Common::EVENT_KEYDOWN)
- continue;
+ for (item = _state.items.begin(); item != _state.items.end(); ++item) {
+ if (item->room != _state.room)
+ continue;
- if (event.kbd.flags & Common::KBD_CTRL) {
- if (event.kbd.keycode == Common::KEYCODE_q)
- g_engine->quitGame();
- continue;
+ if (item->state == IDI_ITEM_MOVED) {
+ if (curRoom().picture == curRoom().curPicture) {
+ const Common::Point &p = _itemOffsets[dropped];
+ if (item->isLineArt)
+ drawLineArt(_lineArt[item->picture - 1], p);
+ else
+ drawPic(item->picture, p);
+ ++dropped;
}
-
- switch (event.kbd.keycode) {
- case Common::KEYCODE_BACKSPACE:
- case Common::KEYCODE_RETURN:
- key = convertKey(event.kbd.keycode);
- break;
- default:
- if (event.kbd.ascii >= 0x20 && event.kbd.ascii < 0x80)
- key = convertKey(event.kbd.ascii);
- };
+ continue;
}
- _display->updateTextScreen();
- g_system->delayMillis(16);
- }
-
- _display->showCursor(false);
-
- return key;
-}
-
-void AdlEngine::delay(uint32 ms) const {
- Common::EventManager *ev = g_system->getEventManager();
-
- uint32 start = g_system->getMillis();
+ Common::Array<byte>::const_iterator pic;
- while (!g_engine->shouldQuit() && g_system->getMillis() - start < ms) {
- Common::Event event;
- if (ev->pollEvent(event)) {
- if (event.type == Common::EVENT_KEYDOWN && (event.kbd.flags & Common::KBD_CTRL)) {
- switch(event.kbd.keycode) {
- case Common::KEYCODE_q:
- g_engine->quitGame();
- break;
- default:
- break;
- }
+ for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) {
+ if (*pic == curRoom().curPicture) {
+ if (item->isLineArt)
+ drawLineArt(_lineArt[item->picture - 1], item->position);
+ else
+ drawPic(item->picture, item->position);
+ continue;
}
}
- g_system->delayMillis(16);
}
}
@@ -1047,4 +728,323 @@ void AdlEngine::drawLineArt(const Common::Array<byte> &lineArt, const Common::Po
}
}
+const Room &AdlEngine::room(uint i) const {
+ if (i < 1 || i > _state.rooms.size())
+ error("Room %i out of range [1, %i]", i, _state.rooms.size());
+
+ return _state.rooms[i - 1];
+}
+
+Room &AdlEngine::room(uint i) {
+ if (i < 1 || i > _state.rooms.size())
+ error("Room %i out of range [1, %i]", i, _state.rooms.size());
+
+ return _state.rooms[i - 1];
+}
+
+const Room &AdlEngine::curRoom() const {
+ return room(_state.room);
+}
+
+Room &AdlEngine::curRoom() {
+ return room(_state.room);
+}
+
+const Item &AdlEngine::item(uint i) const {
+ if (i < 1 || i > _state.items.size())
+ error("Item %i out of range [1, %i]", i, _state.items.size());
+
+ return _state.items[i - 1];
+}
+
+Item &AdlEngine::item(uint i) {
+ if (i < 1 || i > _state.items.size())
+ error("Item %i out of range [1, %i]", i, _state.items.size());
+
+ return _state.items[i - 1];
+}
+
+const byte &AdlEngine::var(uint i) const {
+ if (i >= _state.vars.size())
+ error("Variable %i out of range [0, %i]", i, _state.vars.size() - 1);
+
+ return _state.vars[i];
+}
+
+byte &AdlEngine::var(uint i) {
+ if (i >= _state.vars.size())
+ error("Variable %i out of range [0, %i]", i, _state.vars.size() - 1);
+
+ return _state.vars[i];
+}
+
+void AdlEngine::takeItem(byte noun) {
+ Common::Array<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_MOVED) {
+ item->room = IDI_NONE;
+ return;
+ }
+
+ Common::Array<byte>::const_iterator pic;
+ for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) {
+ if (*pic == curRoom().curPicture) {
+ item->room = IDI_NONE;
+ item->state = IDI_ITEM_MOVED;
+ return;
+ }
+ }
+ }
+
+ printMessage(_messageIds.itemNotHere);
+}
+
+void AdlEngine::dropItem(byte noun) {
+ Common::Array<Item>::iterator item;
+
+ for (item = _state.items.begin(); item != _state.items.end(); ++item) {
+ if (item->noun != noun || item->room != IDI_NONE)
+ continue;
+
+ item->room = _state.room;
+ item->state = IDI_ITEM_MOVED;
+ return;
+ }
+
+ printMessage(_messageIds.dontUnderstand);
+}
+
+#define ARG(N) (command.script[offset + (N)])
+
+bool AdlEngine::matchCommand(const Command &command, byte verb, byte noun, uint *actions) const {
+ if (command.room != IDI_NONE && command.room != _state.room)
+ return false;
+
+ if (command.verb != IDI_NONE && command.verb != verb)
+ return false;
+
+ if (command.noun != IDI_NONE && command.noun != noun)
+ return false;
+
+ uint offset = 0;
+ for (uint i = 0; i < command.numCond; ++i) {
+ switch (ARG(0)) {
+ case IDO_CND_ITEM_IN_ROOM:
+ if (item(ARG(1)).room != ARG(2))
+ return false;
+ offset += 3;
+ break;
+ case IDO_CND_MOVES_GE:
+ if (ARG(1) > _state.moves)
+ return false;
+ offset += 2;
+ break;
+ case IDO_CND_VAR_EQ:
+ if (var(ARG(1)) != ARG(2))
+ return false;
+ offset += 3;
+ break;
+ case IDO_CND_CUR_PIC_EQ:
+ if (curRoom().curPicture != ARG(1))
+ return false;
+ offset += 2;
+ break;
+ case IDO_CND_ITEM_PIC_EQ:
+ if (item(ARG(1)).picture != ARG(2))
+ return false;
+ offset += 3;
+ break;
+ default:
+ error("Invalid condition opcode %02x", command.script[offset]);
+ }
+ }
+
+ *actions = offset;
+
+ return true;
+}
+
+void AdlEngine::doActions(const Command &command, byte noun, byte offset) {
+ for (uint i = 0; i < command.numAct; ++i) {
+ switch (ARG(0)) {
+ case IDO_ACT_VAR_ADD:
+ var(ARG(2)) += ARG(1);
+ offset += 3;
+ break;
+ case IDO_ACT_VAR_SUB:
+ var(ARG(2)) -= ARG(1);
+ offset += 3;
+ break;
+ case IDO_ACT_VAR_SET:
+ var(ARG(1)) = ARG(2);
+ offset += 3;
+ break;
+ case IDO_ACT_LIST_ITEMS: {
+ Common::Array<Item>::const_iterator item;
+
+ for (item = _state.items.begin(); item != _state.items.end(); ++item)
+ if (item->room == IDI_NONE)
+ printMessage(item->description);
+
+ ++offset;
+ break;
+ }
+ case IDO_ACT_MOVE_ITEM:
+ item(ARG(1)).room = ARG(2);
+ offset += 3;
+ break;
+ case IDO_ACT_SET_ROOM:
+ curRoom().curPicture = curRoom().picture;
+ _state.room = ARG(1);
+ offset += 2;
+ break;
+ case IDO_ACT_SET_CUR_PIC:
+ curRoom().curPicture = ARG(1);
+ offset += 2;
+ break;
+ case IDO_ACT_SET_PIC:
+ curRoom().picture = curRoom().curPicture = ARG(1);
+ offset += 2;
+ break;
+ case IDO_ACT_PRINT_MSG:
+ printMessage(ARG(1));
+ offset += 2;
+ break;
+ case IDO_ACT_SET_LIGHT:
+ _state.isDark = false;
+ ++offset;
+ break;
+ case IDO_ACT_SET_DARK:
+ _state.isDark = true;
+ ++offset;
+ break;
+ case IDO_ACT_SAVE:
+ saveGameState(0, "");
+ ++offset;
+ break;
+ case IDO_ACT_LOAD:
+ loadGameState(0);
+ ++offset;
+ // Original engine does not jump out of the loop,
+ // so we don't either.
+ // We reset the restore flag, as the restore game
+ // process is complete
+ _isRestoring = false;
+ break;
+ case IDO_ACT_RESTART: {
+ _display->printString(_strings.playAgain);
+
+ // We allow restoring via GMM here
+ _canRestoreNow = true;
+ Common::String input = inputString();
+ _canRestoreNow = false;
+
+ // If the user restored with the GMM, we break off the restart
+ if (_isRestoring)
+ return;
+
+ if (input.size() == 0 || input[0] != APPLECHAR('N')) {
+ _isRestarting = true;
+ _display->clear(0x00);
+ _display->updateHiResScreen();
+ restartGame();
+ return;
+ }
+ // Fall-through
+ }
+ case IDO_ACT_QUIT:
+ printMessage(_messageIds.thanksForPlaying);
+ quitGame();
+ return;
+ case IDO_ACT_PLACE_ITEM:
+ item(ARG(1)).room = ARG(2);
+ item(ARG(1)).position.x = ARG(3);
+ item(ARG(1)).position.y = ARG(4);
+ offset += 5;
+ break;
+ case IDO_ACT_SET_ITEM_PIC:
+ item(ARG(2)).picture = ARG(1);
+ offset += 3;
+ break;
+ case IDO_ACT_RESET_PIC:
+ curRoom().curPicture = curRoom().picture;
+ ++offset;
+ break;
+ case IDO_ACT_GO_NORTH:
+ case IDO_ACT_GO_SOUTH:
+ case IDO_ACT_GO_EAST:
+ case IDO_ACT_GO_WEST:
+ case IDO_ACT_GO_UP:
+ case IDO_ACT_GO_DOWN: {
+ byte room = curRoom().connections[ARG(0) - IDO_ACT_GO_NORTH];
+
+ if (room == 0) {
+ printMessage(_messageIds.cantGoThere);
+ return;
+ }
+
+ curRoom().curPicture = curRoom().picture;
+ _state.room = room;
+ return;
+ }
+ case IDO_ACT_TAKE_ITEM:
+ takeItem(noun);
+ ++offset;
+ break;
+ case IDO_ACT_DROP_ITEM:
+ dropItem(noun);
+ ++offset;
+ break;
+ case IDO_ACT_SET_ROOM_PIC:
+ room(ARG(1)).picture = room(ARG(1)).curPicture = ARG(2);
+ offset += 3;
+ break;
+ default:
+ error("Invalid action opcode %02x", ARG(0));
+ }
+ }
+}
+
+#undef ARG
+
+bool AdlEngine::doOneCommand(const Commands &commands, byte verb, byte noun) {
+ Commands::const_iterator cmd;
+
+ for (cmd = commands.begin(); cmd != commands.end(); ++cmd) {
+ uint offset = 0;
+ if (matchCommand(*cmd, verb, noun, &offset)) {
+ doActions(*cmd, noun, offset);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void AdlEngine::doAllCommands(const Commands &commands, byte verb, byte noun) {
+ Commands::const_iterator cmd;
+ bool oldIsRestoring = _isRestoring;
+
+ for (cmd = commands.begin(); cmd != commands.end(); ++cmd) {
+ uint offset = 0;
+ if (matchCommand(*cmd, verb, noun, &offset))
+ doActions(*cmd, noun, offset);
+
+ // We assume no restarts happen in this command group. This
+ // simplifies enabling GMM savegame loading on the restart
+ // prompt.
+ if (_isRestarting || _isRestoring != oldIsRestoring)
+ error("Unexpected restart action encountered");
+ }
+}
+
} // End of namespace Adl
diff --git a/engines/adl/adl.h b/engines/adl/adl.h
index b1d5b7c0b3..a230f0f03b 100644
--- a/engines/adl/adl.h
+++ b/engines/adl/adl.h
@@ -23,8 +23,9 @@
#ifndef ADL_ADL_H
#define ADL_ADL_H
-#include "common/random.h"
+#include "common/array.h"
#include "common/rect.h"
+#include "common/str.h"
#include "engines/engine.h"
@@ -34,16 +35,10 @@ class SeekableReadStream;
}
namespace Adl {
+
class Display;
-class Parser;
-class Console;
struct AdlGameDescription;
-struct StringOffset {
- int stringIdx;
- uint offset;
-};
-
// Conditional opcodes
#define IDO_CND_ITEM_IN_ROOM 0x03
#define IDO_CND_MOVES_GE 0x05
@@ -155,7 +150,6 @@ protected:
void readCommands(Common::ReadStream &stream, Commands &commands);
Display *_display;
- Parser *_parser;
// Message strings in data file
Common::Array<Common::String> _messages;
@@ -236,9 +230,9 @@ private:
void takeItem(byte noun);
void dropItem(byte noun);
bool matchCommand(const Command &command, byte verb, byte noun, uint *actions = nullptr) const;
+ void doActions(const Command &command, byte noun, byte offset);
bool doOneCommand(const Commands &commands, byte verb, byte noun);
void doAllCommands(const Commands &commands, byte verb, byte noun);
- void doActions(const Command &command, byte noun, byte offset);
const AdlGameDescription *_gameDescription;
bool _isRestarting, _isRestoring;
diff --git a/engines/adl/hires1.cpp b/engines/adl/hires1.cpp
index 95f02899a2..6e1e31df9f 100644
--- a/engines/adl/hires1.cpp
+++ b/engines/adl/hires1.cpp
@@ -136,113 +136,6 @@ void HiRes1Engine::runIntro() const {
delay(2000);
}
-void HiRes1Engine::drawPic(Common::ReadStream &stream, const Common::Point &pos) const {
- byte x, y;
- bool bNewLine = false;
- byte oldX = 0, oldY = 0;
- while (1) {
- x = stream.readByte();
- y = stream.readByte();
-
- if (stream.err() || stream.eos())
- error("Failed to read picture");
-
- if (x == 0xff && y == 0xff)
- return;
-
- if (x == 0 && y == 0) {
- bNewLine = true;
- continue;
- }
-
- x += pos.x;
- y += pos.y;
-
- if (y > 160)
- y = 160;
-
- if (bNewLine) {
- _display->putPixel(Common::Point(x, y), 0x7f);
- bNewLine = false;
- } else {
- drawLine(Common::Point(oldX, oldY), Common::Point(x, y), 0x7f);
- }
-
- oldX = x;
- oldY = y;
- }
-}
-
-void HiRes1Engine::drawPic(byte pic, Common::Point pos) const {
- Common::File f;
- Common::String name = Common::String::format("BLOCK%i", _pictures[pic].block);
-
- if (!f.open(name))
- error("Failed to open file '%s'", name.c_str());
-
- f.seek(_pictures[pic].offset);
- drawPic(f, pos);
-}
-
-void HiRes1Engine::initState() {
- Common::File f;
-
- _state.room = 1;
- _state.moves = 0;
- _state.isDark = false;
-
- _state.vars.clear();
- _state.vars.resize(IDI_HR1_NUM_VARS);
-
- if (!f.open(IDS_HR1_EXE_1))
- error("Failed to open file '" IDS_HR1_EXE_1 "'");
-
- // Load room data from executable
- _state.rooms.clear();
- f.seek(IDI_HR1_OFS_ROOMS);
- for (uint i = 0; i < IDI_HR1_NUM_ROOMS; ++i) {
- Room room;
- f.readByte();
- room.description = f.readByte();
- for (uint j = 0; j < 6; ++j)
- room.connections[j] = f.readByte();
- room.picture = f.readByte();
- room.curPicture = f.readByte();
- _state.rooms.push_back(room);
- }
-
- // Load item data from executable
- _state.items.clear();
- f.seek(IDI_HR1_OFS_ITEMS);
- while (f.readByte() != 0xff) {
- Item item;
- item.noun = f.readByte();
- item.room = f.readByte();
- item.picture = f.readByte();
- item.isLineArt = f.readByte();
- item.position.x = f.readByte();
- item.position.y = f.readByte();
- item.state = f.readByte();
- item.description = f.readByte();
-
- f.readByte();
-
- byte size = f.readByte();
-
- for (uint i = 0; i < size; ++i)
- item.roomPictures.push_back(f.readByte());
-
- _state.items.push_back(item);
- }
-}
-
-void HiRes1Engine::restartGame() {
- initState();
- _display->printString(_gameStrings.pressReturn);
- inputString(); // Missing in the original
- _display->printAsciiString("\r\r\r\r\r");
-}
-
void HiRes1Engine::loadData() {
Common::File f;
@@ -331,6 +224,76 @@ void HiRes1Engine::loadData() {
loadWords(f, _nouns);
}
+void HiRes1Engine::initState() {
+ Common::File f;
+
+ _state.room = 1;
+ _state.moves = 0;
+ _state.isDark = false;
+
+ _state.vars.clear();
+ _state.vars.resize(IDI_HR1_NUM_VARS);
+
+ if (!f.open(IDS_HR1_EXE_1))
+ error("Failed to open file '" IDS_HR1_EXE_1 "'");
+
+ // Load room data from executable
+ _state.rooms.clear();
+ f.seek(IDI_HR1_OFS_ROOMS);
+ for (uint i = 0; i < IDI_HR1_NUM_ROOMS; ++i) {
+ Room room;
+ f.readByte();
+ room.description = f.readByte();
+ for (uint j = 0; j < 6; ++j)
+ room.connections[j] = f.readByte();
+ room.picture = f.readByte();
+ room.curPicture = f.readByte();
+ _state.rooms.push_back(room);
+ }
+
+ // Load item data from executable
+ _state.items.clear();
+ f.seek(IDI_HR1_OFS_ITEMS);
+ while (f.readByte() != 0xff) {
+ Item item;
+ item.noun = f.readByte();
+ item.room = f.readByte();
+ item.picture = f.readByte();
+ item.isLineArt = f.readByte();
+ item.position.x = f.readByte();
+ item.position.y = f.readByte();
+ item.state = f.readByte();
+ item.description = f.readByte();
+
+ f.readByte();
+
+ byte size = f.readByte();
+
+ for (uint i = 0; i < size; ++i)
+ item.roomPictures.push_back(f.readByte());
+
+ _state.items.push_back(item);
+ }
+}
+
+void HiRes1Engine::restartGame() {
+ initState();
+ _display->printString(_gameStrings.pressReturn);
+ inputString(); // Missing in the original
+ _display->printAsciiString("\r\r\r\r\r");
+}
+
+void HiRes1Engine::drawPic(byte pic, Common::Point pos) const {
+ Common::File f;
+ Common::String name = Common::String::format("BLOCK%i", _pictures[pic].block);
+
+ if (!f.open(name))
+ error("Failed to open file '%s'", name.c_str());
+
+ f.seek(_pictures[pic].offset);
+ drawPic(f, pos);
+}
+
void HiRes1Engine::printMessage(uint idx, bool wait) const {
// Messages with hardcoded overrides don't delay after printing.
// It's unclear if this is a bug or not. In some cases the result
@@ -389,6 +352,43 @@ void HiRes1Engine::drawLine(const Common::Point &p1, const Common::Point &p2, by
}
}
+void HiRes1Engine::drawPic(Common::ReadStream &stream, const Common::Point &pos) const {
+ byte x, y;
+ bool bNewLine = false;
+ byte oldX = 0, oldY = 0;
+ while (1) {
+ x = stream.readByte();
+ y = stream.readByte();
+
+ if (stream.err() || stream.eos())
+ error("Failed to read picture");
+
+ if (x == 0xff && y == 0xff)
+ return;
+
+ if (x == 0 && y == 0) {
+ bNewLine = true;
+ continue;
+ }
+
+ x += pos.x;
+ y += pos.y;
+
+ if (y > 160)
+ y = 160;
+
+ if (bNewLine) {
+ _display->putPixel(Common::Point(x, y), 0x7f);
+ bNewLine = false;
+ } else {
+ drawLine(Common::Point(oldX, oldY), Common::Point(x, y), 0x7f);
+ }
+
+ oldX = x;
+ oldY = y;
+ }
+}
+
Engine *HiRes1Engine_create(OSystem *syst, const AdlGameDescription *gd) {
return new HiRes1Engine(syst, gd);
}
diff --git a/engines/adl/hires1.h b/engines/adl/hires1.h
index b7c7f41e12..d9d67c46e4 100644
--- a/engines/adl/hires1.h
+++ b/engines/adl/hires1.h
@@ -23,6 +23,8 @@
#ifndef ADL_HIRES1_H
#define ADL_HIRES1_H
+#include "common/str.h"
+
#include "adl/adl.h"
namespace Common {