scummvm/engines/adl/adl.cpp

1117 lines
25 KiB
C++
Raw Normal View History

2016-02-26 23:32:06 +01:00
/* 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.
*
*/
2016-02-27 13:35:49 +01:00
#include "common/scummsys.h"
2016-02-26 23:32:06 +01:00
#include "common/config-manager.h"
#include "common/debug.h"
#include "common/error.h"
#include "common/file.h"
#include "common/system.h"
#include "common/events.h"
#include "common/stream.h"
2016-02-29 00:32:47 +01:00
#include "common/savefile.h"
2016-02-26 23:32:06 +01:00
#include "engines/util.h"
2016-02-27 13:35:49 +01:00
#include "graphics/palette.h"
2016-03-02 21:56:06 +01:00
#include "graphics/thumbnail.h"
2016-02-27 13:35:49 +01:00
2016-02-26 23:32:06 +01:00
#include "adl/adl.h"
#include "adl/display.h"
2016-03-06 21:59:55 +01:00
#include "adl/detection.h"
#include "adl/graphics.h"
2016-03-15 20:06:03 +01:00
#include "adl/speaker.h"
2016-02-26 23:32:06 +01:00
namespace Adl {
2016-03-07 20:43:37 +01:00
AdlEngine::~AdlEngine() {
delete _display;
delete _graphics;
2016-03-15 20:06:03 +01:00
delete _speaker;
2016-03-07 20:43:37 +01:00
}
2016-02-26 23:32:06 +01:00
AdlEngine::AdlEngine(OSystem *syst, const AdlGameDescription *gd) :
Engine(syst),
2016-02-27 13:54:47 +01:00
_display(nullptr),
_graphics(nullptr),
2016-03-07 20:43:37 +01:00
_gameDescription(gd),
2016-03-02 11:38:01 +01:00
_isRestarting(false),
_isRestoring(false),
_saveVerb(0),
_saveNoun(0),
_restoreVerb(0),
_restoreNoun(0),
_canSaveNow(false),
_canRestoreNow(false) {
2016-02-26 23:32:06 +01:00
}
2016-03-15 20:06:03 +01:00
bool AdlEngine::pollEvent(Common::Event &event) {
if (g_system->getEventManager()->pollEvent(event)) {
if (event.type != Common::EVENT_KEYDOWN)
return false;
if (event.kbd.flags & Common::KBD_CTRL) {
if (event.kbd.keycode == Common::KEYCODE_q) {
quitGame();
return false;
}
}
return true;
}
return false;
}
2016-03-07 20:43:37 +01:00
Common::String AdlEngine::readString(Common::ReadStream &stream, byte until) const {
Common::String str;
while (1) {
byte b = stream.readByte();
if (stream.eos() || stream.err())
error("Error reading string");
if (b == until)
break;
str += b;
};
return str;
2016-02-26 23:32:06 +01:00
}
2016-03-07 20:43:37 +01:00
Common::String AdlEngine::readStringAt(Common::SeekableReadStream &stream, uint offset, byte until) const {
stream.seek(offset);
return readString(stream, until);
}
2016-03-14 10:40:51 +01:00
void AdlEngine::openFile(Common::File &file, const Common::String &name) const {
if (!file.open(name))
error("Error opening '%s'", name.c_str());
}
2016-03-15 22:34:00 +01:00
void AdlEngine::printMessage(uint idx, bool wait) {
2016-03-07 20:43:37 +01:00
Common::String msg = _messages[idx - 1];
wordWrap(msg);
_display->printString(msg);
if (wait)
delay(14 * 166018 / 1000);
}
void AdlEngine::delay(uint32 ms) const {
uint32 start = g_system->getMillis();
2016-03-15 20:06:03 +01:00
while (!shouldQuit() && g_system->getMillis() - start < ms) {
2016-03-07 20:43:37 +01:00
Common::Event event;
2016-03-15 20:06:03 +01:00
pollEvent(event);
2016-03-07 20:43:37 +01:00
g_system->delayMillis(16);
}
}
Common::String AdlEngine::inputString(byte prompt) const {
Common::String s;
if (prompt > 0)
_display->printString(Common::String(prompt));
while (1) {
byte b = inputKey();
2016-03-15 20:06:03 +01:00
if (shouldQuit() || _isRestoring)
2016-03-07 20:43:37 +01:00
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 {
if (s.size() < 255) {
s += b;
_display->printString(Common::String(b));
}
2016-03-07 20:43:37 +01:00
}
}
}
2016-03-14 15:39:19 +01:00
byte AdlEngine::inputKey(bool showCursor) const {
2016-03-07 20:43:37 +01:00
byte key = 0;
2016-03-14 15:39:19 +01:00
if (showCursor)
_display->showCursor(true);
2016-03-07 20:43:37 +01:00
2016-03-15 20:06:03 +01:00
while (!shouldQuit() && !_isRestoring && key == 0) {
2016-03-07 20:43:37 +01:00
Common::Event event;
2016-03-15 20:06:03 +01:00
if (pollEvent(event)) {
2016-03-07 20:43:37 +01:00
if (event.type != Common::EVENT_KEYDOWN)
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);
};
}
_display->updateTextScreen();
g_system->delayMillis(16);
}
_display->showCursor(false);
return key;
}
void AdlEngine::loadWords(Common::ReadStream &stream, WordMap &map) const {
uint index = 0;
while (1) {
++index;
byte buf[IDI_WORD_SIZE];
if (stream.read(buf, IDI_WORD_SIZE) < IDI_WORD_SIZE)
error("Error reading word list");
Common::String word((char *)buf, IDI_WORD_SIZE);
if (!map.contains(word))
map[word] = index;
byte synonyms = stream.readByte();
if (stream.err() || stream.eos())
error("Error reading word list");
if (synonyms == 0xff)
break;
for (uint i = 0; i < synonyms; ++i) {
if (stream.read((char *)buf, IDI_WORD_SIZE) < IDI_WORD_SIZE)
error("Error reading word list");
word = Common::String((char *)buf, IDI_WORD_SIZE);
if (!map.contains(word))
map[word] = index;
}
}
}
void AdlEngine::readCommands(Common::ReadStream &stream, Commands &commands) {
while (1) {
Command command;
command.room = stream.readByte();
if (command.room == 0xff)
return;
command.verb = stream.readByte();
command.noun = stream.readByte();
byte scriptSize = stream.readByte() - 6;
command.numCond = stream.readByte();
command.numAct = stream.readByte();
for (uint i = 0; i < scriptSize; ++i)
command.script.push_back(stream.readByte());
if (stream.eos() || stream.err())
error("Failed to read commands");
if (command.numCond == 0 && command.script[0] == IDO_ACT_SAVE) {
_saveVerb = command.verb;
_saveNoun = command.noun;
}
if (command.numCond == 0 && command.script[0] == IDO_ACT_LOAD) {
_restoreVerb = command.verb;
_restoreNoun = command.noun;
}
commands.push_back(command);
2016-03-02 11:38:01 +01:00
}
}
2016-03-16 11:04:34 +01:00
void AdlEngine::checkInput(byte verb, byte noun) {
if (!doOneCommand(_roomCommands, verb, noun))
printMessage(_messageIds.dontUnderstand);
}
2016-03-18 00:30:51 +01:00
typedef Common::Functor1Mem<ScriptEnv &, bool, AdlEngine> OpcodeV1;
#define SetOpcodeTable(x) table = &x;
#define Opcode(x) table->push_back(new OpcodeV1(this, &AdlEngine::x))
#define OpcodeUnImpl() table->push_back(new OpcodeV1(this, 0))
void AdlEngine::setupOpcodeTables() {
Common::Array<const Opcode *> *table = 0;
SetOpcodeTable(_condOpcodes);
// 0x00
OpcodeUnImpl();
OpcodeUnImpl();
OpcodeUnImpl();
Opcode(o1_isItemInRoom);
// 0x04
OpcodeUnImpl();
Opcode(o1_isMovesGrEq);
Opcode(o1_isVarEq);
OpcodeUnImpl();
// 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(o1_moveItem);
Opcode(o1_setRoom);
Opcode(o1_setCurPic);
// 0x08
Opcode(o1_setPic);
Opcode(o1_printMsg);
Opcode(o1_setLight);
Opcode(o1_setDark);
// 0x0c
OpcodeUnImpl();
Opcode(o1_quit);
OpcodeUnImpl();
Opcode(o1_save);
// 0x10
Opcode(o1_restore);
Opcode(o1_restart);
Opcode(o1_placeItem);
Opcode(o1_setItemPic);
// 0x14
Opcode(o1_resetPic);
2016-03-18 00:50:30 +01:00
Opcode(o1_goDirection<IDI_DIR_NORTH>);
Opcode(o1_goDirection<IDI_DIR_SOUTH>);
Opcode(o1_goDirection<IDI_DIR_EAST>);
2016-03-18 00:30:51 +01:00
// 0x18
2016-03-18 00:50:30 +01:00
Opcode(o1_goDirection<IDI_DIR_WEST>);
Opcode(o1_goDirection<IDI_DIR_UP>);
Opcode(o1_goDirection<IDI_DIR_DOWN>);
2016-03-18 00:30:51 +01:00
Opcode(o1_takeItem);
// 0x1c
Opcode(o1_dropItem);
Opcode(o1_setRoomPic);
}
2016-03-10 20:44:56 +01:00
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 (getCurRoom().picture == getCurRoom().curPicture) {
const Common::Point &p = _itemOffsets[dropped];
drawItem(*item, p);
2016-03-10 20:44:56 +01:00
++dropped;
}
continue;
}
Common::Array<byte>::const_iterator pic;
for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) {
2016-03-17 10:46:53 +01:00
// IDI_NONE check was added in hires2
if (*pic == getCurRoom().curPicture || *pic == IDI_NONE) {
drawItem(*item, item->position);
2016-03-10 20:44:56 +01:00
continue;
}
}
}
}
2016-03-15 20:06:03 +01:00
void AdlEngine::bell(uint count) const {
_speaker->bell(count);
}
2016-03-10 20:44:56 +01:00
const Room &AdlEngine::getRoom(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::getRoom(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::getCurRoom() const {
return getRoom(_state.room);
}
Room &AdlEngine::getCurRoom() {
return getRoom(_state.room);
}
const Item &AdlEngine::getItem(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::getItem(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];
}
byte AdlEngine::getVar(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];
}
void AdlEngine::setVar(uint i, byte value) {
if (i >= _state.vars.size())
error("Variable %i out of range [0, %i]", i, _state.vars.size() - 1);
_state.vars[i] = value;
}
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 == getCurRoom().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);
}
2016-02-26 23:32:06 +01:00
Common::Error AdlEngine::run() {
2016-03-15 20:06:03 +01:00
_speaker = new Speaker();
2016-02-26 23:32:06 +01:00
_display = new Display();
2016-03-18 00:30:51 +01:00
setupOpcodeTables();
init();
2016-02-29 16:50:24 +01:00
int saveSlot = ConfMan.getInt("save_slot");
if (saveSlot >= 0) {
2016-03-02 21:56:06 +01:00
if (loadGameState(saveSlot).getCode() != Common::kNoError)
2016-02-29 16:50:24 +01:00
error("Failed to load save game from slot %i", saveSlot);
2016-03-04 18:48:31 +01:00
_display->moveCursorTo(Common::Point(0, 23));
2016-03-02 11:38:01 +01:00
_isRestoring = true;
2016-02-29 16:50:24 +01:00
} 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 we just restored from the GMM, we skip this command
// set, as no command has been input by the user
if (!_isRestoring)
2016-03-16 11:04:34 +01:00
checkInput(verb, noun);
}
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++;
}
2016-02-26 23:32:06 +01:00
return Common::kNoError;
}
2016-03-07 20:43:37 +01:00
bool AdlEngine::hasFeature(EngineFeature f) const {
switch (f) {
case kSupportsLoadingDuringRuntime:
case kSupportsSavingDuringRuntime:
case kSupportsRTL:
return true;
default:
return false;
}
}
2016-02-26 23:32:06 +01:00
2016-03-07 20:43:37 +01:00
Common::Error AdlEngine::loadGameState(int slot) {
Common::String fileName = Common::String::format("%s.s%02d", _targetName.c_str(), slot);
Common::InSaveFile *inFile = getSaveFileManager()->openForLoading(fileName);
2016-03-06 11:58:21 +01:00
2016-03-07 20:43:37 +01:00
if (!inFile) {
warning("Failed to open file '%s'", fileName.c_str());
return Common::kUnknownError;
}
2016-02-26 23:32:06 +01:00
2016-03-07 20:43:37 +01:00
if (inFile->readUint32BE() != MKTAG('A', 'D', 'L', ':')) {
warning("No header found in '%s'", fileName.c_str());
delete inFile;
return Common::kUnknownError;
}
2016-02-26 23:32:06 +01:00
2016-03-07 20:43:37 +01:00
byte saveVersion = inFile->readByte();
if (saveVersion != SAVEGAME_VERSION) {
warning("Save game version %i not supported", saveVersion);
delete inFile;
return Common::kUnknownError;
}
2016-02-26 23:32:06 +01:00
2016-03-07 20:43:37 +01:00
// Skip description
inFile->seek(SAVEGAME_NAME_LEN, SEEK_CUR);
// Skip save time
inFile->seek(6, SEEK_CUR);
2016-03-07 20:43:37 +01:00
uint32 playTime = inFile->readUint32BE();
2016-03-07 20:43:37 +01:00
Graphics::skipThumbnail(*inFile);
2016-03-07 20:43:37 +01:00
initState();
2016-03-07 20:43:37 +01:00
_state.room = inFile->readByte();
_state.moves = inFile->readByte();
_state.isDark = inFile->readByte();
uint32 size = inFile->readUint32BE();
if (size != _state.rooms.size())
error("Room count mismatch (expected %i; found %i)", _state.rooms.size(), size);
for (uint i = 0; i < size; ++i) {
_state.rooms[i].picture = inFile->readByte();
_state.rooms[i].curPicture = inFile->readByte();
}
2016-03-07 20:43:37 +01:00
size = inFile->readUint32BE();
if (size != _state.items.size())
error("Item count mismatch (expected %i; found %i)", _state.items.size(), size);
2016-03-07 20:43:37 +01:00
for (uint i = 0; i < size; ++i) {
_state.items[i].room = inFile->readByte();
_state.items[i].picture = inFile->readByte();
_state.items[i].position.x = inFile->readByte();
_state.items[i].position.y = inFile->readByte();
_state.items[i].state = inFile->readByte();
}
size = inFile->readUint32BE();
if (size != _state.vars.size())
error("Variable count mismatch (expected %i; found %i)", _state.vars.size(), size);
for (uint i = 0; i < size; ++i)
_state.vars[i] = inFile->readByte();
if (inFile->err() || inFile->eos())
error("Failed to load game '%s'", fileName.c_str());
delete inFile;
setTotalPlayTime(playTime);
_isRestoring = true;
return Common::kNoError;
}
bool AdlEngine::canLoadGameStateCurrently() {
2016-03-07 20:43:37 +01:00
return _canRestoreNow;
}
2016-03-07 20:43:37 +01:00
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);
2016-03-07 20:43:37 +01:00
if (!outFile) {
warning("Failed to open file '%s'", fileName.c_str());
return Common::kUnknownError;
}
2016-03-07 20:43:37 +01:00
outFile->writeUint32BE(MKTAG('A', 'D', 'L', ':'));
outFile->writeByte(SAVEGAME_VERSION);
2016-03-07 20:43:37 +01:00
char name[SAVEGAME_NAME_LEN] = { };
2016-03-07 20:43:37 +01:00
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);
}
2016-03-07 20:43:37 +01:00
outFile->write(name, sizeof(name));
2016-03-07 20:43:37 +01:00
TimeDate t;
g_system->getTimeAndDate(t);
2016-03-02 11:38:01 +01:00
2016-03-07 20:43:37 +01:00
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);
2016-03-02 11:38:01 +01:00
2016-03-07 20:43:37 +01:00
uint32 playTime = getTotalPlayTime();
outFile->writeUint32BE(playTime);
_display->saveThumbnail(*outFile);
outFile->writeByte(_state.room);
outFile->writeByte(_state.moves);
outFile->writeByte(_state.isDark);
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);
}
2016-03-07 20:43:37 +01:00
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);
}
2016-03-07 20:43:37 +01:00
outFile->writeUint32BE(_state.vars.size());
for (uint i = 0; i < _state.vars.size(); ++i)
outFile->writeByte(_state.vars[i]);
2016-03-07 20:43:37 +01:00
outFile->finalize();
2016-03-07 20:43:37 +01:00
if (outFile->err()) {
delete outFile;
warning("Failed to save game '%s'", fileName.c_str());
return Common::kUnknownError;
}
2016-03-07 20:43:37 +01:00
delete outFile;
return Common::kNoError;
}
bool AdlEngine::canSaveGameStateCurrently() {
2016-03-07 20:43:37 +01:00
if (!_canSaveNow)
return false;
Commands::const_iterator cmd;
// 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) {
2016-03-18 00:30:51 +01:00
ScriptEnv env(*cmd, _saveVerb, _saveNoun);
uint offset;
2016-03-18 00:30:51 +01:00
if (matchCommand(env, &offset))
return cmd->script[offset] == IDO_ACT_SAVE;
}
2016-03-07 20:43:37 +01:00
return false;
}
2016-03-07 20:43:37 +01:00
void AdlEngine::wordWrap(Common::String &str) const {
uint end = 39;
2016-03-07 20:43:37 +01:00
while (1) {
if (str.size() <= end)
return;
2016-03-07 20:43:37 +01:00
while (str[end] != APPLECHAR(' '))
--end;
str.setChar(APPLECHAR('\r'), end);
end += 40;
}
2016-03-07 20:43:37 +01:00
}
2016-03-07 20:43:37 +01:00
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;
}
2016-03-07 20:43:37 +01:00
Common::String AdlEngine::getLine() const {
// Original engine uses a global here, which isn't reset between
// calls and may not match actual mode
bool textMode = false;
2016-03-07 20:43:37 +01:00
while (1) {
Common::String line = inputString(APPLECHAR('?'));
2016-03-07 20:43:37 +01:00
if (shouldQuit() || _isRestoring)
return "";
2016-03-07 20:43:37 +01:00
if ((byte)line[0] == ('\r' | 0x80)) {
textMode = !textMode;
_display->setMode(textMode ? DISPLAY_MODE_TEXT : DISPLAY_MODE_MIXED);
continue;
}
2016-03-07 20:43:37 +01:00
// Remove the return
line.deleteLastChar();
return line;
}
}
2016-03-07 20:43:37 +01:00
Common::String AdlEngine::getWord(const Common::String &line, uint &index) const {
Common::String str;
2016-03-07 20:43:37 +01:00
for (uint i = 0; i < 8; ++i)
str += APPLECHAR(' ');
2016-03-07 20:43:37 +01:00
int copied = 0;
2016-03-07 20:43:37 +01:00
// Skip initial whitespace
while (1) {
if (index == line.size())
return str;
if (line[index] != APPLECHAR(' '))
break;
2016-03-07 20:43:37 +01:00
++index;
}
2016-03-07 20:43:37 +01:00
// Copy up to 8 characters
while (1) {
if (copied < 8)
str.setChar(line[index], copied++);
2016-03-07 20:43:37 +01:00
index++;
if (index == line.size() || line[index] == APPLECHAR(' '))
return str;
}
}
2016-03-07 20:43:37 +01:00
void AdlEngine::getInput(uint &verb, uint &noun) {
while (1) {
_display->printString(_strings.enterCommand);
Common::String line = getLine();
2016-03-07 20:43:37 +01:00
if (shouldQuit() || _isRestoring)
return;
2016-03-07 20:43:37 +01:00
uint index = 0;
Common::String verbStr = getWord(line, index);
if (!_verbs.contains(verbStr)) {
Common::String err = _strings.verbError;
for (uint i = 0; i < verbStr.size(); ++i)
err.setChar(verbStr[i], i + 19);
_display->printString(err);
continue;
2016-03-06 16:43:30 +01:00
}
2016-03-07 20:43:37 +01:00
verb = _verbs[verbStr];
2016-03-07 20:43:37 +01:00
Common::String nounStr = getWord(line, index);
2016-03-07 20:43:37 +01:00
if (!_nouns.contains(nounStr)) {
Common::String err = _strings.nounError;
for (uint i = 0; i < verbStr.size(); ++i)
err.setChar(verbStr[i], i + 19);
for (uint i = 0; i < nounStr.size(); ++i)
err.setChar(nounStr[i], i + 30);
_display->printString(err);
continue;
}
2016-03-02 11:38:01 +01:00
2016-03-07 20:43:37 +01:00
noun = _nouns[nounStr];
return;
2016-02-28 21:44:23 +01:00
}
}
2016-03-18 00:30:51 +01:00
typedef Common::Functor1Mem<ScriptEnv &, bool, AdlEngine> OpcodeV1;
2016-03-01 15:47:34 +01:00
2016-03-18 00:30:51 +01:00
#define ARG(N) (env.cmd.script[env.ip + (N)])
bool AdlEngine::o1_isItemInRoom(ScriptEnv &env) {
if (getItem(ARG(1)).room != ARG(2))
2016-03-07 20:43:37 +01:00
return false;
2016-03-18 00:30:51 +01:00
env.ip += 3;
return true;
}
2016-03-01 15:47:34 +01:00
2016-03-18 00:30:51 +01:00
bool AdlEngine::o1_isMovesGrEq(ScriptEnv &env) {
if (ARG(1) > _state.moves)
2016-03-07 20:43:37 +01:00
return false;
2016-03-18 00:30:51 +01:00
env.ip += 2;
return true;
}
2016-03-01 15:47:34 +01:00
2016-03-18 00:30:51 +01:00
bool AdlEngine::o1_isVarEq(ScriptEnv &env) {
if (getVar(ARG(1)) != ARG(2))
2016-03-07 20:43:37 +01:00
return false;
2016-03-18 00:30:51 +01:00
env.ip += 3;
return true;
}
2016-03-07 20:43:37 +01:00
2016-03-18 00:30:51 +01:00
bool AdlEngine::o1_isCurPicEq(ScriptEnv &env) {
if (getCurRoom().curPicture != ARG(1))
return false;
env.ip += 2;
return true;
}
bool AdlEngine::o1_isItemPicEq(ScriptEnv &env) {
if (getItem(ARG(1)).picture != ARG(2))
return false;
env.ip += 3;
return true;
}
bool AdlEngine::o1_varAdd(ScriptEnv &env) {
setVar(ARG(2), getVar(ARG(2) + ARG(1)));
env.ip += 3;
return true;
}
bool AdlEngine::o1_varSub(ScriptEnv &env) {
setVar(ARG(2), getVar(ARG(2)) - ARG(1));
env.ip += 3;
return true;
}
bool AdlEngine::o1_varSet(ScriptEnv &env) {
setVar(ARG(1), ARG(2));
env.ip += 3;
return true;
}
bool AdlEngine::o1_listInv(ScriptEnv &env) {
Common::Array<Item>::const_iterator item;
for (item = _state.items.begin(); item != _state.items.end(); ++item)
if (item->room == IDI_NONE)
printMessage(item->description);
++env.ip;
return true;
}
bool AdlEngine::o1_moveItem(ScriptEnv &env) {
getItem(ARG(1)).room = ARG(2);
env.ip += 3;
return true;
}
bool AdlEngine::o1_setRoom(ScriptEnv &env) {
getCurRoom().curPicture = getCurRoom().picture;
_state.room = ARG(1);
env.ip += 2;
return true;
}
bool AdlEngine::o1_setCurPic(ScriptEnv &env) {
getCurRoom().curPicture = ARG(1);
env.ip += 2;
return true;
}
bool AdlEngine::o1_setPic(ScriptEnv &env) {
getCurRoom().picture = getCurRoom().curPicture = ARG(1);
env.ip += 2;
return true;
}
bool AdlEngine::o1_printMsg(ScriptEnv &env) {
printMessage(ARG(1));
env.ip += 2;
return true;
}
bool AdlEngine::o1_setLight(ScriptEnv &env) {
_state.isDark = false;
++env.ip;
return true;
}
bool AdlEngine::o1_setDark(ScriptEnv &env) {
_state.isDark = true;
++env.ip;
return true;
}
bool AdlEngine::o1_save(ScriptEnv &env) {
saveGameState(0, "");
++env.ip;
return true;
}
bool AdlEngine::o1_restore(ScriptEnv &env) {
loadGameState(0);
++env.ip;
_isRestoring = false;
return true;
}
bool AdlEngine::o1_restart(ScriptEnv &env) {
_display->printString(_strings.playAgain);
Common::String input = inputString();
if (input.size() == 0 || input[0] != APPLECHAR('N')) {
_isRestarting = true;
_display->clear(0x00);
_display->updateHiResScreen();
restartGame();
return false;
2016-03-01 15:47:34 +01:00
}
2016-03-18 00:30:51 +01:00
return o1_quit(env);
}
bool AdlEngine::o1_quit(ScriptEnv &env) {
printMessage(_messageIds.thanksForPlaying);
quitGame();
return false;
}
2016-03-01 15:47:34 +01:00
2016-03-18 00:30:51 +01:00
bool AdlEngine::o1_placeItem(ScriptEnv &env) {
getItem(ARG(1)).room = ARG(2);
getItem(ARG(1)).position.x = ARG(3);
getItem(ARG(1)).position.y = ARG(4);
env.ip += 5;
2016-03-07 20:43:37 +01:00
return true;
2016-03-01 15:47:34 +01:00
}
2016-03-18 00:30:51 +01:00
bool AdlEngine::o1_setItemPic(ScriptEnv &env) {
getItem(ARG(2)).picture = ARG(1);
env.ip += 3;
return true;
}
2016-03-01 15:47:34 +01:00
2016-03-18 00:30:51 +01:00
bool AdlEngine::o1_resetPic(ScriptEnv &env) {
getCurRoom().curPicture = getCurRoom().picture;
++env.ip;
return true;
}
2016-03-01 15:47:34 +01:00
2016-03-18 00:50:30 +01:00
template <Direction D>
2016-03-18 00:30:51 +01:00
bool AdlEngine::o1_goDirection(ScriptEnv &env) {
2016-03-18 00:50:30 +01:00
byte room = getCurRoom().connections[D];
2016-03-01 15:47:34 +01:00
2016-03-18 00:30:51 +01:00
if (room == 0) {
printMessage(_messageIds.cantGoThere);
return false;
2016-03-01 15:47:34 +01:00
}
2016-03-18 00:30:51 +01:00
getCurRoom().curPicture = getCurRoom().picture;
_state.room = room;
return false;
}
bool AdlEngine::o1_takeItem(ScriptEnv &env) {
takeItem(env.noun);
++env.ip;
return true;
}
bool AdlEngine::o1_dropItem(ScriptEnv &env) {
dropItem(env.noun);
++env.ip;
return true;
}
bool AdlEngine::o1_setRoomPic(ScriptEnv &env) {
getRoom(ARG(1)).picture = getRoom(ARG(1)).curPicture = ARG(2);
env.ip += 3;
return true;
2016-03-01 15:47:34 +01:00
}
2016-03-07 20:43:37 +01:00
#undef ARG
2016-03-01 15:47:34 +01:00
2016-03-18 00:30:51 +01:00
bool AdlEngine::matchCommand(ScriptEnv &env, uint *actions) const {
if (env.cmd.room != IDI_NONE && env.cmd.room != _state.room)
return false;
if (env.cmd.verb != IDI_NONE && env.cmd.verb != env.verb)
return false;
if (env.cmd.noun != IDI_NONE && env.cmd.noun != env.noun)
return false;
for (uint i = 0; i < env.cmd.numCond; ++i) {
byte op = env.cmd.script[env.ip];
if (!_condOpcodes[op] || !_condOpcodes[op]->isValid())
error("Unimplemented condition opcode %02x", op);
if (!(*_condOpcodes[op])(env))
return false;
}
if (actions)
*actions = env.ip;
return true;
}
void AdlEngine::doActions(ScriptEnv &env) {
for (uint i = 0; i < env.cmd.numAct; ++i) {
byte op = env.cmd.script[env.ip];
if (!_actOpcodes[op] || !_actOpcodes[op]->isValid())
error("Unimplemented action opcode %02x", op);
if (!(*_actOpcodes[op])(env))
return;
}
}
2016-03-07 20:43:37 +01:00
bool AdlEngine::doOneCommand(const Commands &commands, byte verb, byte noun) {
Commands::const_iterator cmd;
2016-03-01 15:47:34 +01:00
2016-03-07 20:43:37 +01:00
for (cmd = commands.begin(); cmd != commands.end(); ++cmd) {
2016-03-18 00:30:51 +01:00
ScriptEnv env(*cmd, verb, noun);
if (matchCommand(env)) {
doActions(env);
2016-03-07 20:43:37 +01:00
return true;
2016-03-01 15:47:34 +01:00
}
}
2016-03-07 20:43:37 +01:00
return false;
2016-03-03 19:17:49 +01:00
}
2016-03-07 20:43:37 +01:00
void AdlEngine::doAllCommands(const Commands &commands, byte verb, byte noun) {
Commands::const_iterator cmd;
2016-03-06 19:34:14 +01:00
2016-03-07 20:43:37 +01:00
for (cmd = commands.begin(); cmd != commands.end(); ++cmd) {
2016-03-18 00:30:51 +01:00
ScriptEnv env(*cmd, verb, noun);
if (matchCommand(env))
doActions(env);
2016-03-03 19:17:49 +01:00
}
}
2016-02-26 23:32:06 +01:00
} // End of namespace Adl