2016-02-26 22:32:06 +00: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 12:35:49 +00:00
|
|
|
#include "common/scummsys.h"
|
2016-02-26 22:32:06 +00: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-28 23:32:47 +00:00
|
|
|
#include "common/savefile.h"
|
2016-02-26 22:32:06 +00:00
|
|
|
|
|
|
|
#include "engines/util.h"
|
|
|
|
|
2016-02-27 12:35:49 +00:00
|
|
|
#include "graphics/palette.h"
|
2016-03-02 20:56:06 +00:00
|
|
|
#include "graphics/thumbnail.h"
|
2016-02-27 12:35:49 +00:00
|
|
|
|
2016-02-26 22:32:06 +00:00
|
|
|
#include "adl/adl.h"
|
|
|
|
#include "adl/display.h"
|
2016-03-06 20:59:55 +00:00
|
|
|
#include "adl/detection.h"
|
2016-02-26 22:32:06 +00:00
|
|
|
|
|
|
|
namespace Adl {
|
|
|
|
|
|
|
|
AdlEngine::AdlEngine(OSystem *syst, const AdlGameDescription *gd) :
|
|
|
|
Engine(syst),
|
|
|
|
_gameDescription(gd),
|
2016-02-27 12:54:47 +00:00
|
|
|
_display(nullptr),
|
2016-03-02 10:38:01 +00:00
|
|
|
_isRestarting(false),
|
|
|
|
_isRestoring(false),
|
|
|
|
_saveVerb(0),
|
|
|
|
_saveNoun(0),
|
|
|
|
_restoreVerb(0),
|
|
|
|
_restoreNoun(0),
|
|
|
|
_canSaveNow(false),
|
|
|
|
_canRestoreNow(false) {
|
2016-02-26 22:32:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AdlEngine::~AdlEngine() {
|
|
|
|
delete _display;
|
|
|
|
}
|
|
|
|
|
2016-03-02 10:38:01 +00:00
|
|
|
bool AdlEngine::hasFeature(EngineFeature f) const {
|
|
|
|
switch (f) {
|
|
|
|
case kSupportsLoadingDuringRuntime:
|
|
|
|
case kSupportsSavingDuringRuntime:
|
|
|
|
case kSupportsRTL:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-26 22:32:06 +00:00
|
|
|
Common::Error AdlEngine::run() {
|
|
|
|
_display = new Display();
|
|
|
|
|
2016-03-06 12:36:35 +00:00
|
|
|
loadData();
|
|
|
|
|
2016-02-29 15:50:24 +00:00
|
|
|
int saveSlot = ConfMan.getInt("save_slot");
|
|
|
|
if (saveSlot >= 0) {
|
2016-03-02 20:56:06 +00:00
|
|
|
if (loadGameState(saveSlot).getCode() != Common::kNoError)
|
2016-02-29 15:50:24 +00:00
|
|
|
error("Failed to load save game from slot %i", saveSlot);
|
2016-03-04 17:48:31 +00:00
|
|
|
_display->moveCursorTo(Common::Point(0, 23));
|
2016-03-02 10:38:01 +00:00
|
|
|
_isRestoring = true;
|
2016-02-29 15:50:24 +00:00
|
|
|
} else {
|
|
|
|
runIntro();
|
|
|
|
initState();
|
|
|
|
}
|
|
|
|
|
2016-03-06 12:36:35 +00:00
|
|
|
_display->setMode(DISPLAY_MODE_MIXED);
|
|
|
|
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))
|
|
|
|
printEngineMessage(IDI_MSG_DONT_UNDERSTAND);
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
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 22:32:06 +00:00
|
|
|
|
|
|
|
return Common::kNoError;
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
Common::String AdlEngine::readString(Common::ReadStream &stream, byte until) const {
|
2016-02-26 22:32:06 +00:00
|
|
|
Common::String str;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
byte b = stream.readByte();
|
|
|
|
|
2016-03-06 10:58:21 +00:00
|
|
|
if (stream.eos() || stream.err())
|
|
|
|
error("Error reading string");
|
|
|
|
|
|
|
|
if (b == until)
|
2016-02-26 22:32:06 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
str += b;
|
|
|
|
};
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
Common::String AdlEngine::getEngineString(int str) const {
|
2016-02-28 12:24:41 +00:00
|
|
|
return _strings[str];
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
void AdlEngine::wordWrap(Common::String &str) const {
|
2016-02-28 12:24:41 +00:00
|
|
|
uint end = 39;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
if (str.size() <= end)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (str[end] != APPLECHAR(' '))
|
|
|
|
--end;
|
|
|
|
|
|
|
|
str.setChar(APPLECHAR('\r'), end);
|
|
|
|
end += 40;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
void AdlEngine::printMessage(uint idx, bool wait) const {
|
2016-02-28 12:24:41 +00:00
|
|
|
Common::String msg = _messages[idx - 1];
|
|
|
|
wordWrap(msg);
|
|
|
|
_display->printString(msg);
|
|
|
|
|
|
|
|
if (wait)
|
2016-03-01 14:47:34 +00:00
|
|
|
delay(14 * 166018 / 1000);
|
2016-02-28 12:24:41 +00:00
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
void AdlEngine::printEngineMessage(EngineMessage msg) const {
|
2016-02-28 12:24:41 +00:00
|
|
|
printMessage(getEngineMessage(msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
2016-03-02 10:38:01 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-02-28 12:24:41 +00:00
|
|
|
commands.push_back(command);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AdlEngine::takeItem(byte noun) {
|
2016-02-28 15:21:46 +00:00
|
|
|
Common::Array<Item>::iterator item;
|
2016-02-28 12:24:41 +00:00
|
|
|
|
2016-02-28 20:01:01 +00:00
|
|
|
for (item = _state.items.begin(); item != _state.items.end(); ++item) {
|
|
|
|
if (item->noun != noun || item->room != _state.room)
|
2016-02-28 12:24:41 +00:00
|
|
|
continue;
|
|
|
|
|
2016-02-28 15:21:46 +00:00
|
|
|
if (item->state == IDI_ITEM_DOESNT_MOVE) {
|
2016-02-28 12:24:41 +00:00
|
|
|
printEngineMessage(IDI_MSG_ITEM_DOESNT_MOVE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-02-28 15:21:46 +00:00
|
|
|
if (item->state == IDI_ITEM_MOVED) {
|
|
|
|
item->room = IDI_NONE;
|
2016-02-28 12:24:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-02-28 15:21:46 +00:00
|
|
|
Common::Array<byte>::const_iterator pic;
|
2016-02-29 08:32:22 +00:00
|
|
|
for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) {
|
2016-02-29 10:54:57 +00:00
|
|
|
if (*pic == curRoom().curPicture) {
|
2016-02-28 15:21:46 +00:00
|
|
|
item->room = IDI_NONE;
|
|
|
|
item->state = IDI_ITEM_MOVED;
|
2016-02-28 12:24:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
printEngineMessage(IDI_MSG_ITEM_NOT_HERE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AdlEngine::dropItem(byte noun) {
|
2016-02-28 15:21:46 +00:00
|
|
|
Common::Array<Item>::iterator item;
|
2016-02-28 12:24:41 +00:00
|
|
|
|
2016-02-28 20:01:01 +00:00
|
|
|
for (item = _state.items.begin(); item != _state.items.end(); ++item) {
|
2016-02-28 15:21:46 +00:00
|
|
|
if (item->noun != noun || item->room != IDI_NONE)
|
2016-02-28 12:24:41 +00:00
|
|
|
continue;
|
|
|
|
|
2016-02-28 20:01:01 +00:00
|
|
|
item->room = _state.room;
|
2016-02-28 15:21:46 +00:00
|
|
|
item->state = IDI_ITEM_MOVED;
|
2016-02-28 12:24:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
printEngineMessage(IDI_MSG_DONT_UNDERSTAND);
|
|
|
|
}
|
|
|
|
|
2016-02-28 16:12:00 +00:00
|
|
|
#define ARG(N) (command.script[offset + (N)])
|
2016-02-28 15:36:28 +00:00
|
|
|
|
2016-02-28 12:24:41 +00:00
|
|
|
void AdlEngine::doActions(const Command &command, byte noun, byte offset) {
|
|
|
|
for (uint i = 0; i < command.numAct; ++i) {
|
2016-02-28 15:36:28 +00:00
|
|
|
switch (ARG(0)) {
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_VAR_ADD:
|
2016-02-29 10:54:57 +00:00
|
|
|
var(ARG(2)) += ARG(1);
|
2016-02-28 12:24:41 +00:00
|
|
|
offset += 3;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_VAR_SUB:
|
2016-02-29 10:54:57 +00:00
|
|
|
var(ARG(2)) -= ARG(1);
|
2016-02-28 12:24:41 +00:00
|
|
|
offset += 3;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_VAR_SET:
|
2016-02-29 10:54:57 +00:00
|
|
|
var(ARG(1)) = ARG(2);
|
2016-02-28 12:24:41 +00:00
|
|
|
offset += 3;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_LIST_ITEMS: {
|
2016-02-28 15:21:46 +00:00
|
|
|
Common::Array<Item>::const_iterator item;
|
2016-02-28 12:24:41 +00:00
|
|
|
|
2016-02-28 20:01:01 +00:00
|
|
|
for (item = _state.items.begin(); item != _state.items.end(); ++item)
|
2016-02-28 15:21:46 +00:00
|
|
|
if (item->room == IDI_NONE)
|
|
|
|
printMessage(item->description);
|
2016-02-28 12:24:41 +00:00
|
|
|
|
|
|
|
++offset;
|
|
|
|
break;
|
|
|
|
}
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_MOVE_ITEM:
|
2016-02-29 10:54:57 +00:00
|
|
|
item(ARG(1)).room = ARG(2);
|
2016-02-28 12:24:41 +00:00
|
|
|
offset += 3;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_SET_ROOM:
|
2016-02-29 10:54:57 +00:00
|
|
|
curRoom().curPicture = curRoom().picture;
|
2016-02-28 20:01:01 +00:00
|
|
|
_state.room = ARG(1);
|
2016-02-28 12:24:41 +00:00
|
|
|
offset += 2;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_SET_CUR_PIC:
|
2016-02-29 10:54:57 +00:00
|
|
|
curRoom().curPicture = ARG(1);
|
2016-02-28 12:24:41 +00:00
|
|
|
offset += 2;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_SET_PIC:
|
2016-02-29 10:54:57 +00:00
|
|
|
curRoom().picture = curRoom().curPicture = ARG(1);
|
2016-02-28 12:24:41 +00:00
|
|
|
offset += 2;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_PRINT_MSG:
|
2016-02-28 15:36:28 +00:00
|
|
|
printMessage(ARG(1));
|
2016-02-28 12:24:41 +00:00
|
|
|
offset += 2;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_SET_LIGHT:
|
2016-02-28 20:01:01 +00:00
|
|
|
_state.isDark = false;
|
2016-02-28 12:24:41 +00:00
|
|
|
++offset;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_SET_DARK:
|
2016-02-28 20:01:01 +00:00
|
|
|
_state.isDark = true;
|
2016-02-28 12:24:41 +00:00
|
|
|
++offset;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_SAVE:
|
2016-03-02 20:56:06 +00:00
|
|
|
saveGameState(0, "");
|
2016-02-28 12:24:41 +00:00
|
|
|
++offset;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_LOAD:
|
2016-03-02 20:56:06 +00:00
|
|
|
loadGameState(0);
|
2016-02-28 12:24:41 +00:00
|
|
|
++offset;
|
2016-03-04 15:30:40 +00:00
|
|
|
// 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;
|
2016-02-28 12:24:41 +00:00
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_RESTART: {
|
2016-02-28 12:24:41 +00:00
|
|
|
_display->printString(_strings[IDI_STR_PLAY_AGAIN]);
|
2016-03-02 10:38:01 +00:00
|
|
|
|
|
|
|
// We allow restoring via GMM here
|
|
|
|
_canRestoreNow = true;
|
2016-03-01 14:47:34 +00:00
|
|
|
Common::String input = inputString();
|
2016-03-02 10:38:01 +00:00
|
|
|
_canRestoreNow = false;
|
|
|
|
|
2016-03-04 15:30:40 +00:00
|
|
|
// If the user restored with the GMM, we break off the restart
|
2016-03-02 10:38:01 +00:00
|
|
|
if (_isRestoring)
|
|
|
|
return;
|
|
|
|
|
2016-02-28 12:24:41 +00:00
|
|
|
if (input.size() == 0 || input[0] != APPLECHAR('N')) {
|
2016-02-28 20:44:23 +00:00
|
|
|
_isRestarting = true;
|
2016-02-28 23:32:47 +00:00
|
|
|
_display->clear(0x00);
|
2016-03-04 19:06:16 +00:00
|
|
|
_display->updateHiResScreen();
|
2016-02-28 20:44:23 +00:00
|
|
|
restartGame();
|
2016-02-28 12:24:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Fall-through
|
|
|
|
}
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_QUIT:
|
2016-02-28 12:24:41 +00:00
|
|
|
printEngineMessage(IDI_MSG_THANKS_FOR_PLAYING);
|
|
|
|
quitGame();
|
|
|
|
return;
|
2016-02-28 15:36:28 +00:00
|
|
|
case IDO_ACT_PLACE_ITEM:
|
2016-02-29 10:54:57 +00:00
|
|
|
item(ARG(1)).room = ARG(2);
|
|
|
|
item(ARG(1)).position.x = ARG(3);
|
|
|
|
item(ARG(1)).position.y = ARG(4);
|
2016-02-28 12:24:41 +00:00
|
|
|
offset += 5;
|
|
|
|
break;
|
2016-02-28 15:36:28 +00:00
|
|
|
case IDO_ACT_SET_ITEM_PIC:
|
2016-02-29 10:54:57 +00:00
|
|
|
item(ARG(2)).picture = ARG(1);
|
2016-02-28 12:24:41 +00:00
|
|
|
offset += 3;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_RESET_PIC:
|
2016-02-29 10:54:57 +00:00
|
|
|
curRoom().curPicture = curRoom().picture;
|
2016-02-28 12:24:41 +00:00
|
|
|
++offset;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
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: {
|
2016-02-29 10:54:57 +00:00
|
|
|
byte room = curRoom().connections[ARG(0) - IDO_ACT_GO_NORTH];
|
2016-02-28 12:24:41 +00:00
|
|
|
|
|
|
|
if (room == 0) {
|
|
|
|
printEngineMessage(IDI_MSG_CANT_GO_THERE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-02-29 10:54:57 +00:00
|
|
|
curRoom().curPicture = curRoom().picture;
|
2016-02-28 20:01:01 +00:00
|
|
|
_state.room = room;
|
2016-02-28 12:24:41 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_TAKE_ITEM:
|
2016-02-28 12:24:41 +00:00
|
|
|
takeItem(noun);
|
|
|
|
++offset;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_DROP_ITEM:
|
2016-02-28 12:24:41 +00:00
|
|
|
dropItem(noun);
|
|
|
|
++offset;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_ACT_SET_ROOM_PIC:
|
2016-02-29 10:54:57 +00:00
|
|
|
room(ARG(1)).picture = room(ARG(1)).curPicture = ARG(2);
|
2016-02-28 12:24:41 +00:00
|
|
|
offset += 3;
|
|
|
|
break;
|
|
|
|
default:
|
2016-02-28 15:36:28 +00:00
|
|
|
error("Invalid action opcode %02x", ARG(0));
|
2016-02-28 12:24:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
bool AdlEngine::matchCommand(const Command &command, byte verb, byte noun, uint *actions) const {
|
2016-02-28 20:01:01 +00:00
|
|
|
if (command.room != IDI_NONE && command.room != _state.room)
|
2016-02-28 12:24:41 +00:00
|
|
|
return false;
|
|
|
|
|
2016-02-28 13:24:04 +00:00
|
|
|
if (command.verb != IDI_NONE && command.verb != verb)
|
2016-02-28 12:24:41 +00:00
|
|
|
return false;
|
|
|
|
|
2016-02-28 13:24:04 +00:00
|
|
|
if (command.noun != IDI_NONE && command.noun != noun)
|
2016-02-28 12:24:41 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
uint offset = 0;
|
|
|
|
for (uint i = 0; i < command.numCond; ++i) {
|
2016-02-29 10:54:57 +00:00
|
|
|
switch (ARG(0)) {
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_CND_ITEM_IN_ROOM:
|
2016-02-29 10:54:57 +00:00
|
|
|
if (item(ARG(1)).room != ARG(2))
|
2016-02-28 12:24:41 +00:00
|
|
|
return false;
|
|
|
|
offset += 3;
|
|
|
|
break;
|
2016-02-28 20:01:01 +00:00
|
|
|
case IDO_CND_MOVES_GE:
|
|
|
|
if (ARG(1) > _state.moves)
|
2016-02-28 12:24:41 +00:00
|
|
|
return false;
|
|
|
|
offset += 2;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_CND_VAR_EQ:
|
2016-02-29 10:54:57 +00:00
|
|
|
if (var(ARG(1)) != ARG(2))
|
2016-02-28 12:24:41 +00:00
|
|
|
return false;
|
|
|
|
offset += 3;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_CND_CUR_PIC_EQ:
|
2016-02-29 10:54:57 +00:00
|
|
|
if (curRoom().curPicture != ARG(1))
|
2016-02-28 12:24:41 +00:00
|
|
|
return false;
|
|
|
|
offset += 2;
|
|
|
|
break;
|
2016-02-28 14:56:17 +00:00
|
|
|
case IDO_CND_ITEM_PIC_EQ:
|
2016-02-29 10:54:57 +00:00
|
|
|
if (item(ARG(1)).picture != ARG(2))
|
2016-02-28 12:24:41 +00:00
|
|
|
return false;
|
|
|
|
offset += 3;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error("Invalid condition opcode %02x", command.script[offset]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
*actions = offset;
|
2016-02-28 12:24:41 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-02-28 15:36:28 +00:00
|
|
|
#undef ARG
|
|
|
|
|
2016-02-28 12:24:41 +00:00
|
|
|
bool AdlEngine::doOneCommand(const Commands &commands, byte verb, byte noun) {
|
2016-02-28 15:21:46 +00:00
|
|
|
Commands::const_iterator cmd;
|
2016-02-28 12:24:41 +00:00
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
for (cmd = commands.begin(); cmd != commands.end(); ++cmd) {
|
|
|
|
uint offset = 0;
|
|
|
|
if (matchCommand(*cmd, verb, noun, &offset)) {
|
|
|
|
doActions(*cmd, noun, offset);
|
2016-02-28 12:24:41 +00:00
|
|
|
return true;
|
2016-03-06 15:43:30 +00:00
|
|
|
}
|
|
|
|
}
|
2016-02-28 12:24:41 +00:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AdlEngine::doAllCommands(const Commands &commands, byte verb, byte noun) {
|
2016-02-28 15:21:46 +00:00
|
|
|
Commands::const_iterator cmd;
|
2016-03-02 10:38:01 +00:00
|
|
|
bool oldIsRestoring = _isRestoring;
|
2016-02-28 12:24:41 +00:00
|
|
|
|
2016-02-28 20:44:23 +00:00
|
|
|
for (cmd = commands.begin(); cmd != commands.end(); ++cmd) {
|
2016-03-06 15:43:30 +00:00
|
|
|
uint offset = 0;
|
|
|
|
if (matchCommand(*cmd, verb, noun, &offset))
|
|
|
|
doActions(*cmd, noun, offset);
|
2016-03-02 10:38:01 +00:00
|
|
|
|
|
|
|
// 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");
|
2016-02-28 20:44:23 +00:00
|
|
|
}
|
2016-02-28 12:24:41 +00:00
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
bool AdlEngine::canSaveGameStateCurrently() const {
|
2016-03-02 10:38:01 +00: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-06 15:43:30 +00:00
|
|
|
if (matchCommand(*cmd, _saveVerb, _saveNoun)) {
|
2016-03-02 10:38:01 +00:00
|
|
|
if (cmd->verb != _saveVerb || cmd->noun != _saveNoun)
|
|
|
|
return false;
|
|
|
|
return cmd->numCond == 0 && cmd->script[0] == IDO_ACT_SAVE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
bool AdlEngine::canLoadGameStateCurrently() const {
|
2016-03-02 10:38:01 +00:00
|
|
|
return _canRestoreNow;
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
void AdlEngine::clearScreen() const {
|
2016-03-04 17:48:31 +00:00
|
|
|
_display->setMode(DISPLAY_MODE_MIXED);
|
2016-02-28 12:24:41 +00:00
|
|
|
_display->clear(0x00);
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
void AdlEngine::drawItems() const {
|
2016-02-28 16:12:00 +00:00
|
|
|
Common::Array<Item>::const_iterator item;
|
|
|
|
|
|
|
|
uint dropped = 0;
|
|
|
|
|
2016-02-28 20:01:01 +00:00
|
|
|
for (item = _state.items.begin(); item != _state.items.end(); ++item) {
|
|
|
|
if (item->room != _state.room)
|
2016-02-28 16:12:00 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (item->state == IDI_ITEM_MOVED) {
|
2016-02-29 10:54:57 +00:00
|
|
|
if (curRoom().picture == curRoom().curPicture) {
|
2016-02-28 16:12:00 +00:00
|
|
|
const Common::Point &p = _itemOffsets[dropped];
|
2016-02-28 16:21:09 +00:00
|
|
|
if (item->isLineArt)
|
2016-03-03 18:17:49 +00:00
|
|
|
drawLineArt(_lineArt[item->picture - 1], p);
|
2016-02-28 16:12:00 +00:00
|
|
|
else
|
|
|
|
drawPic(item->picture, p);
|
|
|
|
++dropped;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Array<byte>::const_iterator pic;
|
|
|
|
|
|
|
|
for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) {
|
2016-02-29 10:54:57 +00:00
|
|
|
if (*pic == curRoom().curPicture) {
|
2016-02-28 16:21:09 +00:00
|
|
|
if (item->isLineArt)
|
2016-03-03 18:17:49 +00:00
|
|
|
drawLineArt(_lineArt[item->picture - 1], item->position);
|
2016-02-28 16:12:00 +00:00
|
|
|
else
|
|
|
|
drawPic(item->picture, item->position);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
void AdlEngine::showRoom() const {
|
2016-02-28 20:01:01 +00:00
|
|
|
if (!_state.isDark) {
|
2016-02-29 10:54:57 +00:00
|
|
|
drawPic(curRoom().curPicture);
|
2016-02-28 16:12:00 +00:00
|
|
|
drawItems();
|
|
|
|
}
|
|
|
|
|
2016-03-05 16:24:45 +00:00
|
|
|
_display->updateHiResScreen();
|
2016-02-29 10:54:57 +00:00
|
|
|
printMessage(curRoom().description, false);
|
2016-02-28 16:12:00 +00:00
|
|
|
}
|
|
|
|
|
2016-03-02 20:56:06 +00:00
|
|
|
Common::Error AdlEngine::saveGameState(int slot, const Common::String &desc) {
|
2016-02-28 23:32:47 +00:00
|
|
|
Common::String fileName = Common::String::format("%s.s%02d", _targetName.c_str(), slot);
|
|
|
|
Common::OutSaveFile *outFile = getSaveFileManager()->openForSaving(fileName);
|
|
|
|
|
|
|
|
if (!outFile) {
|
|
|
|
warning("Failed to open file '%s'", fileName.c_str());
|
2016-03-02 20:56:06 +00:00
|
|
|
return Common::kUnknownError;
|
2016-02-28 23:32:47 +00:00
|
|
|
}
|
|
|
|
|
2016-02-29 15:50:24 +00:00
|
|
|
outFile->writeUint32BE(MKTAG('A', 'D', 'L', ':'));
|
2016-02-28 23:32:47 +00:00
|
|
|
outFile->writeByte(SAVEGAME_VERSION);
|
|
|
|
|
2016-02-29 15:50:24 +00:00
|
|
|
char name[SAVEGAME_NAME_LEN] = { };
|
|
|
|
|
2016-03-02 20:56:06 +00:00
|
|
|
if (!desc.empty())
|
|
|
|
strncpy(name, desc.c_str(), sizeof(name) - 1);
|
2016-02-29 15:50:24 +00:00
|
|
|
else {
|
|
|
|
Common::String defaultName("Save ");
|
|
|
|
defaultName += 'A' + slot;
|
|
|
|
strncpy(name, defaultName.c_str(), sizeof(name) - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
outFile->write(name, sizeof(name));
|
|
|
|
|
2016-03-02 20:56:06 +00:00
|
|
|
TimeDate t;
|
|
|
|
g_system->getTimeAndDate(t);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
uint32 playTime = getTotalPlayTime();
|
|
|
|
outFile->writeUint32BE(playTime);
|
|
|
|
|
2016-03-03 13:05:19 +00:00
|
|
|
_display->saveThumbnail(*outFile);
|
2016-03-02 20:56:06 +00:00
|
|
|
|
2016-02-28 23:32:47 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
outFile->writeUint32BE(_state.vars.size());
|
|
|
|
for (uint i = 0; i < _state.vars.size(); ++i)
|
|
|
|
outFile->writeByte(_state.vars[i]);
|
|
|
|
|
|
|
|
outFile->finalize();
|
|
|
|
|
|
|
|
if (outFile->err()) {
|
|
|
|
delete outFile;
|
|
|
|
warning("Failed to save game '%s'", fileName.c_str());
|
2016-03-02 20:56:06 +00:00
|
|
|
return Common::kUnknownError;
|
2016-02-28 23:32:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
delete outFile;
|
2016-03-02 20:56:06 +00:00
|
|
|
return Common::kNoError;
|
2016-02-28 23:32:47 +00:00
|
|
|
}
|
|
|
|
|
2016-03-02 20:56:06 +00:00
|
|
|
Common::Error AdlEngine::loadGameState(int slot) {
|
2016-02-28 23:32:47 +00:00
|
|
|
Common::String fileName = Common::String::format("%s.s%02d", _targetName.c_str(), slot);
|
|
|
|
Common::InSaveFile *inFile = getSaveFileManager()->openForLoading(fileName);
|
|
|
|
|
|
|
|
if (!inFile) {
|
|
|
|
warning("Failed to open file '%s'", fileName.c_str());
|
2016-03-02 20:56:06 +00:00
|
|
|
return Common::kUnknownError;
|
2016-02-28 23:32:47 +00:00
|
|
|
}
|
|
|
|
|
2016-02-29 15:50:24 +00:00
|
|
|
if (inFile->readUint32BE() != MKTAG('A', 'D', 'L', ':')) {
|
2016-02-28 23:32:47 +00:00
|
|
|
warning("No header found in '%s'", fileName.c_str());
|
|
|
|
delete inFile;
|
2016-03-02 20:56:06 +00:00
|
|
|
return Common::kUnknownError;
|
2016-02-28 23:32:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
byte saveVersion = inFile->readByte();
|
|
|
|
if (saveVersion != SAVEGAME_VERSION) {
|
|
|
|
warning("Save game version %i not supported", saveVersion);
|
|
|
|
delete inFile;
|
2016-03-02 20:56:06 +00:00
|
|
|
return Common::kUnknownError;
|
2016-02-28 23:32:47 +00:00
|
|
|
}
|
|
|
|
|
2016-03-02 20:56:06 +00:00
|
|
|
// Skip description
|
2016-02-29 15:50:24 +00:00
|
|
|
inFile->seek(SAVEGAME_NAME_LEN, SEEK_CUR);
|
2016-03-02 20:56:06 +00:00
|
|
|
// Skip save time
|
|
|
|
inFile->seek(6, SEEK_CUR);
|
|
|
|
|
|
|
|
uint32 playTime = inFile->readUint32BE();
|
|
|
|
|
|
|
|
Graphics::skipThumbnail(*inFile);
|
|
|
|
|
|
|
|
initState();
|
2016-02-29 15:50:24 +00:00
|
|
|
|
2016-02-28 23:32:47 +00: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();
|
|
|
|
}
|
|
|
|
|
|
|
|
size = inFile->readUint32BE();
|
|
|
|
if (size != _state.items.size())
|
|
|
|
error("Item count mismatch (expected %i; found %i)", _state.items.size(), size);
|
|
|
|
|
|
|
|
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;
|
2016-03-02 20:56:06 +00:00
|
|
|
|
|
|
|
setTotalPlayTime(playTime);
|
|
|
|
|
2016-03-04 15:30:40 +00:00
|
|
|
_isRestoring = true;
|
2016-03-02 20:56:06 +00:00
|
|
|
return Common::kNoError;
|
2016-02-28 23:32:47 +00:00
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2016-02-29 10:54:57 +00:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
const Room &AdlEngine::curRoom() const {
|
|
|
|
return room(_state.room);
|
|
|
|
}
|
|
|
|
|
2016-02-29 10:54:57 +00:00
|
|
|
Room &AdlEngine::curRoom() {
|
|
|
|
return room(_state.room);
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2016-02-29 10:54:57 +00:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2016-02-29 10:54:57 +00:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2016-03-06 18:34:14 +00:00
|
|
|
void AdlEngine::loadWords(Common::ReadStream &stream, WordMap &map) const {
|
2016-03-01 14:47:34 +00:00
|
|
|
uint index = 0;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
++index;
|
|
|
|
|
2016-03-06 18:34:14 +00:00
|
|
|
byte buf[IDI_WORD_SIZE];
|
2016-03-01 14:47:34 +00:00
|
|
|
|
2016-03-06 18:34:14 +00:00
|
|
|
if (stream.read(buf, IDI_WORD_SIZE) < IDI_WORD_SIZE)
|
2016-03-01 14:47:34 +00:00
|
|
|
error("Error reading word list");
|
|
|
|
|
2016-03-06 18:34:14 +00:00
|
|
|
Common::String word((char *)buf, IDI_WORD_SIZE);
|
2016-03-01 14:47:34 +00:00
|
|
|
|
|
|
|
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) {
|
2016-03-06 18:34:14 +00:00
|
|
|
if (stream.read((char *)buf, IDI_WORD_SIZE) < IDI_WORD_SIZE)
|
2016-03-01 14:47:34 +00:00
|
|
|
error("Error reading word list");
|
|
|
|
|
2016-03-06 18:34:14 +00:00
|
|
|
word = Common::String((char *)buf, IDI_WORD_SIZE);
|
2016-03-01 14:47:34 +00:00
|
|
|
|
|
|
|
if (!map.contains(word))
|
|
|
|
map[word] = index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
Common::String AdlEngine::getLine() const {
|
2016-03-01 14:47:34 +00:00
|
|
|
// Original engine uses a global here, which isn't reset between
|
|
|
|
// calls and may not match actual mode
|
|
|
|
bool textMode = false;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
Common::String line = inputString(APPLECHAR('?'));
|
|
|
|
|
2016-03-02 10:38:01 +00:00
|
|
|
if (shouldQuit() || _isRestoring)
|
2016-03-01 14:47:34 +00:00
|
|
|
return "";
|
|
|
|
|
|
|
|
if ((byte)line[0] == ('\r' | 0x80)) {
|
|
|
|
textMode = !textMode;
|
2016-03-04 17:48:31 +00:00
|
|
|
_display->setMode(textMode ? DISPLAY_MODE_TEXT : DISPLAY_MODE_MIXED);
|
2016-03-01 14:47:34 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove the return
|
|
|
|
line.deleteLastChar();
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
Common::String AdlEngine::getWord(const Common::String &line, uint &index) const {
|
2016-03-01 14:47:34 +00:00
|
|
|
Common::String str;
|
|
|
|
|
|
|
|
for (uint i = 0; i < 8; ++i)
|
|
|
|
str += APPLECHAR(' ');
|
|
|
|
|
|
|
|
int copied = 0;
|
|
|
|
|
|
|
|
// Skip initial whitespace
|
|
|
|
while (1) {
|
|
|
|
if (index == line.size())
|
|
|
|
return str;
|
|
|
|
if (line[index] != APPLECHAR(' '))
|
|
|
|
break;
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy up to 8 characters
|
|
|
|
while (1) {
|
|
|
|
if (copied < 8)
|
|
|
|
str.setChar(line[index], copied++);
|
|
|
|
|
|
|
|
index++;
|
|
|
|
|
|
|
|
if (index == line.size() || line[index] == APPLECHAR(' '))
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AdlEngine::getInput(uint &verb, uint &noun) {
|
|
|
|
while (1) {
|
|
|
|
_display->printString(getEngineString(IDI_STR_ENTER_COMMAND));
|
|
|
|
Common::String line = getLine();
|
|
|
|
|
2016-03-02 10:38:01 +00:00
|
|
|
if (shouldQuit() || _isRestoring)
|
2016-03-01 14:47:34 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
uint index = 0;
|
|
|
|
Common::String verbStr = getWord(line, index);
|
|
|
|
|
|
|
|
if (!_verbs.contains(verbStr)) {
|
|
|
|
Common::String err = getEngineString(IDI_STR_VERB_ERROR);
|
|
|
|
for (uint i = 0; i < verbStr.size(); ++i)
|
|
|
|
err.setChar(verbStr[i], i + 19);
|
|
|
|
_display->printString(err);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
verb = _verbs[verbStr];
|
|
|
|
|
|
|
|
Common::String nounStr = getWord(line, index);
|
|
|
|
|
|
|
|
if (!_nouns.contains(nounStr)) {
|
|
|
|
Common::String err = getEngineString(IDI_STR_NOUN_ERROR);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
noun = _nouns[nounStr];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
void AdlEngine::printASCIIString(const Common::String &str) const {
|
2016-03-01 14:47:34 +00:00
|
|
|
Common::String aStr;
|
|
|
|
|
|
|
|
Common::String::const_iterator it;
|
|
|
|
for (it = str.begin(); it != str.end(); ++it)
|
|
|
|
aStr += APPLECHAR(*it);
|
|
|
|
|
|
|
|
_display->printString(aStr);
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
Common::String AdlEngine::inputString(byte prompt) const {
|
2016-03-01 14:47:34 +00:00
|
|
|
Common::String s;
|
|
|
|
|
|
|
|
if (prompt > 0)
|
|
|
|
_display->printString(Common::String(prompt));
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
byte b = inputKey();
|
|
|
|
|
2016-03-02 10:38:01 +00:00
|
|
|
if (g_engine->shouldQuit() || _isRestoring)
|
2016-03-01 14:47:34 +00: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 {
|
|
|
|
s += b;
|
|
|
|
_display->printString(Common::String(b));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
byte AdlEngine::convertKey(uint16 ascii) const {
|
2016-03-01 14:47:34 +00:00
|
|
|
ascii = toupper(ascii);
|
|
|
|
|
|
|
|
if (ascii >= 0x80)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ascii |= 0x80;
|
|
|
|
|
|
|
|
if (ascii >= 0x80 && ascii <= 0xe0)
|
|
|
|
return ascii;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
byte AdlEngine::inputKey() const {
|
2016-03-01 14:47:34 +00:00
|
|
|
Common::EventManager *ev = g_system->getEventManager();
|
|
|
|
|
|
|
|
byte key = 0;
|
|
|
|
|
|
|
|
_display->showCursor(true);
|
|
|
|
|
2016-03-02 10:38:01 +00:00
|
|
|
while (!g_engine->shouldQuit() && !_isRestoring && key == 0) {
|
2016-03-01 14:47:34 +00:00
|
|
|
Common::Event event;
|
|
|
|
if (ev->pollEvent(event)) {
|
|
|
|
if (event.type != Common::EVENT_KEYDOWN)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (event.kbd.flags & Common::KBD_CTRL) {
|
|
|
|
if (event.kbd.keycode == Common::KEYCODE_q)
|
|
|
|
g_engine->quitGame();
|
|
|
|
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);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-03-04 19:06:16 +00:00
|
|
|
_display->updateTextScreen();
|
2016-03-01 14:47:34 +00:00
|
|
|
g_system->delayMillis(16);
|
|
|
|
}
|
|
|
|
|
|
|
|
_display->showCursor(false);
|
|
|
|
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
void AdlEngine::delay(uint32 ms) const {
|
2016-03-01 14:47:34 +00:00
|
|
|
Common::EventManager *ev = g_system->getEventManager();
|
|
|
|
|
|
|
|
uint32 start = g_system->getMillis();
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-06 15:43:30 +00:00
|
|
|
void AdlEngine::drawNextPixel(Common::Point &p, byte color, byte bits, byte quadrant) const {
|
|
|
|
if (bits & 4)
|
2016-03-03 18:17:49 +00:00
|
|
|
_display->putPixel(p, color);
|
|
|
|
|
|
|
|
bits += quadrant;
|
|
|
|
|
|
|
|
if (bits & 1)
|
|
|
|
p.x += (bits & 2 ? -1 : 1);
|
|
|
|
else
|
|
|
|
p.y += (bits & 2 ? 1 : -1);
|
|
|
|
}
|
|
|
|
|
2016-03-06 18:34:14 +00:00
|
|
|
void AdlEngine::drawLineArt(const Common::Array<byte> &lineArt, const Common::Point &pos, byte rotation, byte scaling, byte color) const {
|
2016-03-03 18:17:49 +00:00
|
|
|
const byte stepping[] = {
|
|
|
|
0xff, 0xfe, 0xfa, 0xf4, 0xec, 0xe1, 0xd4, 0xc5,
|
|
|
|
0xb4, 0xa1, 0x8d, 0x78, 0x61, 0x49, 0x31, 0x18,
|
|
|
|
0xff
|
|
|
|
};
|
|
|
|
|
|
|
|
byte quadrant = rotation >> 4;
|
|
|
|
rotation &= 0xf;
|
|
|
|
byte xStep = stepping[rotation];
|
|
|
|
byte yStep = stepping[(rotation ^ 0xf) + 1] + 1;
|
|
|
|
|
2016-03-06 18:34:14 +00:00
|
|
|
Common::Point p(pos);
|
|
|
|
|
2016-03-03 18:17:49 +00:00
|
|
|
for (uint i = 0; i < lineArt.size(); ++i) {
|
|
|
|
byte b = lineArt[i];
|
|
|
|
|
|
|
|
do {
|
|
|
|
byte xFrac = 0x80;
|
|
|
|
byte yFrac = 0x80;
|
|
|
|
for (uint j = 0; j < scaling; ++j) {
|
|
|
|
if (xFrac + xStep + 1 > 255)
|
|
|
|
drawNextPixel(p, color, b, quadrant);
|
|
|
|
xFrac += xStep + 1;
|
|
|
|
if (yFrac + yStep > 255)
|
|
|
|
drawNextPixel(p, color, b, quadrant + 1);
|
|
|
|
yFrac += yStep;
|
|
|
|
}
|
|
|
|
b >>= 3;
|
|
|
|
} while (b != 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-26 22:32:06 +00:00
|
|
|
} // End of namespace Adl
|