ADL: Partially implement hires5 opcodes

This commit is contained in:
Walter van Niftrik 2016-12-16 15:29:02 +01:00
parent f29a2f31f1
commit 1f801bee43
6 changed files with 413 additions and 9 deletions

View File

@ -428,6 +428,20 @@ void AdlEngine::bell(uint count) const {
_speaker->bell(count);
}
const Region &AdlEngine::getRegion(uint i) const {
if (i < 1 || i > _state.regions.size())
error("Region %i out of range [1, %i]", i, _state.regions.size());
return _state.regions[i - 1];
}
Region &AdlEngine::getRegion(uint i) {
if (i < 1 || i > _state.regions.size())
error("Region %i out of range [1, %i]", i, _state.regions.size());
return _state.regions[i - 1];
}
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());
@ -442,6 +456,14 @@ Room &AdlEngine::getRoom(uint i) {
return _state.rooms[i - 1];
}
const Region &AdlEngine::getCurRegion() const {
return getRegion(_state.region);
}
Region &AdlEngine::getCurRegion() {
return getRegion(_state.region);
}
const Room &AdlEngine::getCurRoom() const {
return getRoom(_state.room);
}

View File

@ -143,6 +143,7 @@ enum {
struct Item {
byte id;
byte noun;
byte region;
byte room;
byte picture;
bool isLineArt;
@ -175,14 +176,14 @@ struct State {
Common::List<Item> items;
Common::Array<byte> vars;
byte region;
byte region, prevRegion;
byte room;
byte curPicture;
uint16 moves;
bool isDark;
Time time;
State() : region(0), room(1), curPicture(0), moves(1), isDark(false) { }
State() : region(0), prevRegion(0), room(1), curPicture(0), moves(1), isDark(false) { }
};
typedef Common::List<Command> Commands;
@ -300,8 +301,12 @@ protected:
void bell(uint count = 1) const;
// Game state functions
const Region &getRegion(uint i) const;
Region &getRegion(uint i);
const Room &getRoom(uint i) const;
Room &getRoom(uint i);
const Region &getCurRegion() const;
Region &getCurRegion();
const Room &getCurRoom() const;
Room &getCurRoom();
const Item &getItem(uint i) const;
@ -309,7 +314,7 @@ protected:
byte getVar(uint i) const;
void setVar(uint i, byte value);
virtual void takeItem(byte noun);
void dropItem(byte noun);
virtual void dropItem(byte noun);
bool matchCommand(ScriptEnv &env) const;
void doActions(ScriptEnv &env);
bool doOneCommand(const Commands &commands, byte verb, byte noun);

View File

@ -372,7 +372,7 @@ void AdlEngine_v2::loadItems(Common::ReadStream &stream) {
item.noun = stream.readByte();
item.room = stream.readByte();
item.picture = stream.readByte();
item.isLineArt = stream.readByte(); // Disk number in later games
item.region = stream.readByte();
item.position.x = stream.readByte();
item.position.y = stream.readByte();
item.state = stream.readByte();

View File

@ -195,13 +195,14 @@ void AdlEngine_v4::loadRegion(byte region) {
break;
}
case 0x7b00:
// Global commands
readCommands(*stream, _globalCommands);
break;
case 0x9500:
// TODO: hires6 has global and room lists swapped
// Room commands
readCommands(*stream, _roomCommands);
break;
case 0x9500:
// Global commands
readCommands(*stream, _globalCommands);
break;
default:
error("Unknown data block found (addr %04x; size %04x)", addr, size);
}
@ -216,6 +217,8 @@ void AdlEngine_v4::loadRegion(byte region) {
}
}
}
restoreVars();
}
void AdlEngine_v4::loadItemPicIndex(Common::ReadStream &stream, uint items) {
@ -225,4 +228,205 @@ void AdlEngine_v4::loadItemPicIndex(Common::ReadStream &stream, uint items) {
error("Error reading item index");
}
void AdlEngine_v4::backupRoomState(byte room) {
RoomState &backup = getCurRegion().rooms[room - 1];
backup.isFirstTime = getRoom(room).isFirstTime;
backup.picture = getRoom(room).picture;
}
void AdlEngine_v4::restoreRoomState(byte room) {
const RoomState &backup = getCurRegion().rooms[room - 1];
getRoom(room).isFirstTime = backup.isFirstTime;
getRoom(room).picture = backup.picture;
}
void AdlEngine_v4::backupVars() {
Region &region = getCurRegion();
for (uint i = 0; i < region.vars.size(); ++i)
region.vars[i] = getVar(i);
}
void AdlEngine_v4::restoreVars() {
const Region &region = getCurRegion();
for (uint i = 0; i < region.vars.size(); ++i)
setVar(i, region.vars[i]);
}
void AdlEngine_v4::switchRegion(byte region) {
backupVars();
backupRoomState(_state.room);
_state.prevRegion = _state.region;
_state.region = region;
loadRegion(region);
_state.room = 1;
_picOnScreen = _roomOnScreen = 0;
}
// TODO: Merge this into v2?
void AdlEngine_v4::takeItem(byte noun) {
Common::List<Item>::iterator item;
for (item = _state.items.begin(); item != _state.items.end(); ++item) {
if (item->noun != noun || item->room != _state.room || item->region != _state.region)
continue;
if (item->state == IDI_ITEM_DOESNT_MOVE) {
printMessage(_messageIds.itemDoesntMove);
return;
}
if (item->state == IDI_ITEM_DROPPED) {
item->room = IDI_ANY;
_itemRemoved = true;
return;
}
Common::Array<byte>::const_iterator pic;
for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) {
if (*pic == getCurRoom().curPicture || *pic == IDI_ANY) {
if (!isInventoryFull()) {
item->room = IDI_ANY;
_itemRemoved = true;
item->state = IDI_ITEM_DROPPED;
}
return;
}
}
}
printMessage(_messageIds.itemNotHere);
}
// TODO: Merge this into v2?
void AdlEngine_v4::dropItem(byte noun) {
Common::List<Item>::iterator item;
for (item = _state.items.begin(); item != _state.items.end(); ++item) {
if (item->noun != noun || item->room != IDI_ANY)
continue;
item->room = _state.room;
item->region = _state.region;
item->state = IDI_ITEM_DROPPED;
return;
}
printMessage(_messageIds.dontUnderstand);
}
int AdlEngine_v4::o4_isItemInRoom(ScriptEnv &e) {
OP_DEBUG_2("\t&& GET_ITEM_ROOM(%s) == %s", itemStr(e.arg(1)).c_str(), itemRoomStr(e.arg(2)).c_str());
const Item &item = getItem(e.arg(1));
if (e.arg(2) != IDI_ANY && item.region != _state.region)
return -1;
if (item.room == roomArg(e.arg(2)))
return 2;
return -1;
}
int AdlEngine_v4::o4_isVarGT(ScriptEnv &e) {
OP_DEBUG_2("\t&& VARS[%d] > %d", e.arg(1), e.arg(2));
if (getVar(e.arg(1)) > e.arg(2))
return 2;
return -1;
}
int AdlEngine_v4::o4_moveItem(ScriptEnv &e) {
o2_moveItem(e);
getItem(e.arg(1)).region = _state.region;
return 2;
}
int AdlEngine_v4::o4_setRoom(ScriptEnv &e) {
OP_DEBUG_1("\tROOM = %d", e.arg(1));
getCurRoom().curPicture = getCurRoom().picture;
getCurRoom().isFirstTime = false;
backupRoomState(_state.room);
_state.room = e.arg(1);
restoreRoomState(_state.room);
return 1;
}
int AdlEngine_v4::o4_setRegionToPrev(ScriptEnv &e) {
OP_DEBUG_0("\tREGION = PREV_REGION");
switchRegion(_state.prevRegion);
// Long jump
_isRestarting = true;
return -1;
}
int AdlEngine_v4::o4_moveAllItems(ScriptEnv &e) {
OP_DEBUG_2("\tMOVE_ALL_ITEMS(%s, %s)", itemRoomStr(e.arg(1)).c_str(), itemRoomStr(e.arg(2)).c_str());
byte room1 = roomArg(e.arg(1));
if (room1 == _state.room)
_picOnScreen = 0;
byte room2 = roomArg(e.arg(2));
Common::List<Item>::iterator item;
for (item = _state.items.begin(); item != _state.items.end(); ++item) {
if (room1 != item->room)
continue;
if (room1 != IDI_ANY) {
if (_state.region != item->region)
continue;
if (room2 == IDI_ANY) {
if (isInventoryFull())
break;
if (item->state == IDI_ITEM_DOESNT_MOVE)
continue;
}
}
item->room = room2;
item->region = _state.region;
if (room1 == IDI_ANY)
item->state = IDI_ITEM_DROPPED;
}
return 2;
}
int AdlEngine_v4::o4_setRegion(ScriptEnv &e) {
OP_DEBUG_1("\tREGION = %d", e.arg(1));
switchRegion(e.arg(1));
// Long jump
_isRestarting = true;
return -1;
}
int AdlEngine_v4::o4_setRegionRoom(ScriptEnv &e) {
OP_DEBUG_2("\tSET_REGION_ROOM(%d, %d)", e.arg(1), e.arg(2));
switchRegion(e.arg(1));
_state.room = e.arg(2);
// Long jump
_isRestarting = true;
return -1;
}
int AdlEngine_v4::o4_setRoomPic(ScriptEnv &e) {
o1_setRoomPic(e);
backupRoomState(e.arg(1));
return 2;
}
} // End of namespace Adl

View File

@ -63,6 +63,24 @@ protected:
void fixupDiskOffset(byte &track, byte &sector) const;
void loadRegion(byte region);
void loadItemPicIndex(Common::ReadStream &stream, uint items);
void backupRoomState(byte room);
void restoreRoomState(byte room);
void backupVars();
void restoreVars();
void switchRegion(byte region);
virtual bool isInventoryFull() { return false; }
virtual void takeItem(byte noun);
virtual void dropItem(byte noun);
int o4_isItemInRoom(ScriptEnv &e);
int o4_isVarGT(ScriptEnv &e);
int o4_moveItem(ScriptEnv &e);
int o4_setRoom(ScriptEnv &e);
int o4_setRegionToPrev(ScriptEnv &e);
int o4_moveAllItems(ScriptEnv &e);
int o4_setRegion(ScriptEnv &e);
int o4_setRegionRoom(ScriptEnv &e);
int o4_setRoomPic(ScriptEnv &e);
byte _currentVolume;
Common::Array<RegionLocation> _regionLocations;

View File

@ -41,14 +41,159 @@ public:
private:
// AdlEngine
void setupOpcodeTables();
void runIntro();
void init();
void initGameState();
// AdlEngine_v4
bool isInventoryFull();
int o_checkItemTimeLimits(ScriptEnv &e);
int o_startAnimation(ScriptEnv &e);
int o_winGame(ScriptEnv &e);
static const uint kRegions = 41;
static const uint kItems = 69;
Common::Array<byte> _itemTimeLimits;
Common::String _itemTimeLimitMsg;
struct {
Common::String itemTimeLimit;
Common::String carryingTooMuch;
} _gameStrings;
};
typedef Common::Functor1Mem<ScriptEnv &, int, HiRes5Engine> OpcodeH5;
#define SetOpcodeTable(x) table = &x;
#define Opcode(x) table->push_back(new OpcodeH5(this, &HiRes5Engine::x))
#define OpcodeUnImpl() table->push_back(new OpcodeH5(this, 0))
void HiRes5Engine::setupOpcodeTables() {
Common::Array<const Opcode *> *table = 0;
SetOpcodeTable(_condOpcodes);
// 0x00
OpcodeUnImpl();
Opcode(o2_isFirstTime);
Opcode(o2_isRandomGT);
Opcode(o4_isItemInRoom);
// 0x04
Opcode(o3_isNounNotInRoom);
Opcode(o1_isMovesGT);
Opcode(o1_isVarEQ);
Opcode(o2_isCarryingSomething);
// 0x08
Opcode(o4_isVarGT);
Opcode(o1_isCurPicEQ);
OpcodeUnImpl();
SetOpcodeTable(_actOpcodes);
// 0x00
OpcodeUnImpl();
Opcode(o1_varAdd);
Opcode(o1_varSub);
Opcode(o1_varSet);
// 0x04
Opcode(o1_listInv);
Opcode(o4_moveItem);
Opcode(o4_setRoom);
Opcode(o2_setCurPic);
// 0x08
Opcode(o2_setPic);
Opcode(o1_printMsg);
Opcode(o4_setRegionToPrev);
Opcode(o_checkItemTimeLimits);
// 0x0c
Opcode(o4_moveAllItems);
Opcode(o1_quit);
Opcode(o4_setRegion);
Opcode(o2_save); // TODO
// 0x10
Opcode(o2_restore); // TODO
Opcode(o1_restart); // TODO
Opcode(o4_setRegionRoom);
Opcode(o_startAnimation);
// 0x14
Opcode(o1_resetPic);
Opcode(o1_goDirection<IDI_DIR_NORTH>);
Opcode(o1_goDirection<IDI_DIR_SOUTH>);
Opcode(o1_goDirection<IDI_DIR_EAST>);
// 0x18
Opcode(o1_goDirection<IDI_DIR_WEST>);
Opcode(o1_goDirection<IDI_DIR_UP>);
Opcode(o1_goDirection<IDI_DIR_DOWN>);
Opcode(o1_takeItem);
// 0x1c
Opcode(o1_dropItem);
Opcode(o4_setRoomPic);
Opcode(o_winGame);
OpcodeUnImpl();
// 0x20
Opcode(o2_initDisk);
}
bool HiRes5Engine::isInventoryFull() {
Common::List<Item>::const_iterator item;
byte weight = 0;
for (item = _state.items.begin(); item != _state.items.end(); ++item) {
if (item->room == IDI_ANY)
weight += item->description;
}
if (weight >= 100) {
printString(_gameStrings.carryingTooMuch);
inputString();
return true;
}
return false;
}
int HiRes5Engine::o_checkItemTimeLimits(ScriptEnv &e) {
OP_DEBUG_1("\tCHECK_ITEM_TIME_LIMITS(VARS[%d])", e.arg(1));
bool lostAnItem = false;
Common::List<Item>::iterator item;
for (item = _state.items.begin(); item != _state.items.end(); ++item) {
const byte room = item->room;
const byte region = item->region;
if (room == IDI_ANY || room == IDI_CUR_ROOM || (room == _state.room && region == _state.region)) {
if (getVar(e.arg(1)) < _itemTimeLimits[item->id - 1]) {
item->room = IDI_VOID_ROOM;
lostAnItem = true;
}
}
}
if (lostAnItem) {
printString(_gameStrings.itemTimeLimit);
inputString();
}
return 1;
}
int HiRes5Engine::o_startAnimation(ScriptEnv &e) {
OP_DEBUG_0("\tSTART_ANIMATION()");
// TODO: sets a flag that triggers an animation
return 0;
}
int HiRes5Engine::o_winGame(ScriptEnv &e) {
OP_DEBUG_0("\tWIN_GAME()");
// TODO: draws room and plays music
return o1_quit(e);
}
void HiRes5Engine::runIntro() {
insertDisk(2);
@ -114,8 +259,18 @@ void HiRes5Engine::init() {
stream.reset(_disk->createReadStream(0xb, 0xa, 0x05, 1));
loadItemPicIndex(*stream, kItems);
stream.reset(_disk->createReadStream(0x7, 0x8, 0x01));
for (uint i = 0; i < kItems; ++i)
_itemTimeLimits.push_back(stream->readByte());
if (stream->eos() || stream->err())
error("Error reading item index");
error("Failed to read item time limits");
stream.reset(_disk->createReadStream(0x8, 0x2, 0x2d));
_gameStrings.itemTimeLimit = readString(*stream);
stream.reset(_disk->createReadStream(0x8, 0x7, 0x02));
_gameStrings.carryingTooMuch = readString(*stream);
}
void HiRes5Engine::initGameState() {