mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
871 lines
24 KiB
C++
871 lines
24 KiB
C++
/* 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 3 of the License, or
|
|
* of the License; or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful;
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not; write to the Free Software
|
|
* Foundation; Inc.; 51 Franklin Street; Fifth Floor; Boston; MA 02110-1301; USA.
|
|
*
|
|
*/
|
|
|
|
#include "glk/comprehend/game_opcodes.h"
|
|
#include "glk/comprehend/game_data.h"
|
|
#include "glk/comprehend/comprehend.h"
|
|
#include "glk/comprehend/debugger.h"
|
|
#include "common/algorithm.h"
|
|
#include "common/textconsole.h"
|
|
|
|
namespace Glk {
|
|
namespace Comprehend {
|
|
|
|
ComprehendGameOpcodes::ComprehendGameOpcodes() {
|
|
Common::fill(&_opcodeMap[0], &_opcodeMap[0x100], OPCODE_UNKNOWN);
|
|
}
|
|
|
|
void ComprehendGameOpcodes::execute_opcode(const Instruction *instr, const Sentence *sentence,
|
|
FunctionState *func_state) {
|
|
byte verb = sentence ? sentence->_formattedWords[0] : 0;
|
|
byte noun = sentence ? sentence->_formattedWords[2] : 0;
|
|
Room *room = get_room(_currentRoom);
|
|
Item *item;
|
|
uint index;
|
|
|
|
byte opcode = getOpcode(instr);
|
|
switch (_opcodeMap[opcode]) {
|
|
case OPCODE_CALL_FUNC:
|
|
case OPCODE_CALL_FUNC2:
|
|
// Note: CALL_FUNC2 in the original did some extra backing of data which is
|
|
// redundant in the ScummVM version, so it can be handled the same as CALL_FUNC.
|
|
index = instr->_operand[0];
|
|
if (instr->_operand[1] == 0x81)
|
|
index += 256;
|
|
if (index >= _functions.size())
|
|
error("Bad function %.4x >= %.4x\n", index, _functions.size());
|
|
|
|
eval_function(index, sentence);
|
|
break;
|
|
|
|
case OPCODE_CLEAR_CAN_TAKE:
|
|
item = get_item_by_noun(noun);
|
|
item->_flags &= ~ITEMF_CAN_TAKE;
|
|
break;
|
|
|
|
case OPCODE_CLEAR_FLAG:
|
|
_flags[instr->_operand[0]] = false;
|
|
break;
|
|
|
|
case OPCODE_CLEAR_FLAG40:
|
|
item = getItem(instr);
|
|
item->_flags &= ~ITEMF_UNKNOWN;
|
|
break;
|
|
|
|
case OPCODE_CLEAR_INVISIBLE:
|
|
item = get_item_by_noun(noun);
|
|
item->_flags &= ~ITEMF_INVISIBLE;
|
|
break;
|
|
|
|
case OPCODE_CLEAR_WORD:
|
|
item = getItem(instr);
|
|
item->_word = 0;
|
|
break;
|
|
|
|
case OPCODE_CURRENT_OBJECT_NOT_VALID:
|
|
func_set_test_result(func_state, !noun);
|
|
break;
|
|
|
|
case OPCODE_CURRENT_IS_OBJECT:
|
|
item = get_item_by_noun(noun);
|
|
func_set_test_result(func_state, item != nullptr);
|
|
break;
|
|
|
|
case OPCODE_ELSE:
|
|
func_state->_testResult = func_state->_elseResult;
|
|
break;
|
|
|
|
case OPCODE_HAVE_CURRENT_OBJECT:
|
|
item = get_item_by_noun(noun);
|
|
func_set_test_result(func_state, item->_room == ROOM_INVENTORY);
|
|
break;
|
|
|
|
case OPCODE_HAVE_OBJECT:
|
|
item = getItem(instr);
|
|
func_set_test_result(func_state, item->_room == ROOM_INVENTORY);
|
|
break;
|
|
|
|
case OPCODE_IN_ROOM:
|
|
func_set_test_result(func_state, _currentRoom == instr->_operand[0]);
|
|
break;
|
|
|
|
case OPCODE_INVENTORY: {
|
|
uint count = num_objects_in_room(ROOM_INVENTORY);
|
|
if (count == 0) {
|
|
console_println(stringLookup(STRING_INVENTORY_EMPTY).c_str());
|
|
break;
|
|
}
|
|
|
|
console_println(stringLookup(STRING_INVENTORY).c_str());
|
|
for (uint i = 0; i < _items.size(); i++) {
|
|
item = &_items[i];
|
|
if (item->_room == ROOM_INVENTORY)
|
|
g_comprehend->print("%s\n",
|
|
stringLookup(item->_stringDesc).c_str());
|
|
}
|
|
break;
|
|
}
|
|
|
|
case OPCODE_MOVE_DEFAULT:
|
|
// Move in the direction dictated by the current verb
|
|
if (verb - 1 >= NR_DIRECTIONS)
|
|
error("Bad verb %d in move", verb);
|
|
|
|
if (room->_direction[verb - 1])
|
|
move_to(room->_direction[verb - 1]);
|
|
else
|
|
console_println(stringLookup(STRING_CANT_GO).c_str());
|
|
break;
|
|
|
|
case OPCODE_MOVE_OBJECT_TO_CURRENT_ROOM:
|
|
item = getItem(instr);
|
|
move_object(item, _currentRoom);
|
|
break;
|
|
|
|
case OPCODE_MOVE_OBJECT_TO_ROOM:
|
|
item = getItem(instr);
|
|
move_object(item, instr->_operand[1]);
|
|
break;
|
|
|
|
case OPCODE_MOVE_TO_ROOM:
|
|
if (instr->_operand[0] != 0xff)
|
|
move_to(instr->_operand[0]);
|
|
break;
|
|
|
|
case OPCODE_OBJECT_IN_ROOM:
|
|
item = getItem(instr);
|
|
func_set_test_result(func_state, item->_room == instr->_operand[1]);
|
|
break;
|
|
|
|
case OPCODE_OBJECT_IS_NOWHERE:
|
|
item = getItem(instr);
|
|
func_set_test_result(func_state, item->_room == ROOM_NOWHERE);
|
|
break;
|
|
|
|
case OPCODE_OBJECT_PRESENT:
|
|
item = getItem(instr);
|
|
func_set_test_result(func_state, item->_room == _currentRoom);
|
|
break;
|
|
|
|
case OPCODE_OR:
|
|
if (func_state->_orCount) {
|
|
func_state->_orCount += 2;
|
|
} else {
|
|
func_state->_testResult = false;
|
|
func_state->_orCount += 3;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_PRINT:
|
|
console_println(instrStringLookup(instr->_operand[0], instr->_operand[1]).c_str());
|
|
break;
|
|
|
|
case OPCODE_RANDOM_MSG: {
|
|
int msgId = (instr->_operand[2] << 8 | instr->_operand[1]) +
|
|
getRandomNumber(instr->_operand[0] - 1);
|
|
console_println(stringLookup(msgId).c_str());
|
|
break;
|
|
}
|
|
|
|
case OPCODE_REMOVE_OBJECT:
|
|
item = getItem(instr);
|
|
move_object(item, ROOM_NOWHERE);
|
|
break;
|
|
|
|
case OPCODE_SAVE_ACTION:
|
|
// Causes the next sentence inputed to re-use the first word of the current one.
|
|
// As far as I'm aware, this is only used for handling responses to questions
|
|
_nounState = NOUNSTATE_QUERY;
|
|
// fall-through
|
|
|
|
case OPCODE_CLEAR_LINE:
|
|
// Resets the input line, removing any pending further actions that were specified
|
|
Common::fill(&_inputLine[0], &_inputLine[INPUT_LINE_SIZE], 0);
|
|
_inputLineIndex = 0;
|
|
break;
|
|
|
|
case OPCODE_SET_CAN_TAKE:
|
|
item = get_item_by_noun(noun);
|
|
item->_flags |= ITEMF_CAN_TAKE;
|
|
break;
|
|
|
|
case OPCODE_SET_FLAG:
|
|
_flags[instr->_operand[0]] = true;
|
|
break;
|
|
|
|
case OPCODE_SET_FLAG40:
|
|
item = getItem(instr);
|
|
item->_flags |= ITEMF_UNKNOWN;
|
|
break;
|
|
|
|
case OPCODE_SET_INVISIBLE:
|
|
item = get_item_by_noun(noun);
|
|
item->_flags |= ITEMF_INVISIBLE;
|
|
break;
|
|
|
|
case OPCODE_SET_OBJECT_DESCRIPTION:
|
|
item = getItem(instr);
|
|
item->_stringDesc = (instr->_operand[2] << 8) | instr->_operand[1];
|
|
break;
|
|
|
|
case OPCODE_SET_ROOM_DESCRIPTION:
|
|
room = get_room(instr->_operand[0]);
|
|
switch (instr->_operand[2]) {
|
|
case 0x80:
|
|
room->_stringDesc = instr->_operand[1];
|
|
break;
|
|
case 0x81:
|
|
room->_stringDesc = instr->_operand[1] + 0x100;
|
|
break;
|
|
case 0x82:
|
|
room->_stringDesc = instr->_operand[1] + 0x200;
|
|
break;
|
|
default:
|
|
error("Bad string desc %.2x:%.2x\n", instr->_operand[1], instr->_operand[2]);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_SET_ROOM_GRAPHIC:
|
|
room = get_room(instr->_operand[0]);
|
|
room->_graphic = instr->_operand[1];
|
|
if (instr->_operand[0] == _currentRoom)
|
|
_updateFlags |= UPDATE_GRAPHICS;
|
|
break;
|
|
|
|
case OPCODE_SET_STRING_REPLACEMENT1:
|
|
_currentReplaceWord = (instr->_operand[0] & 0x80) - 1;
|
|
break;
|
|
|
|
case OPCODE_SET_STRING_REPLACEMENT2:
|
|
_currentReplaceWord = instr->_operand[0] - 1;
|
|
break;
|
|
|
|
case OPCODE_SET_WORD:
|
|
item = getItem(instr);
|
|
item->_word = instr->_operand[1];
|
|
break;
|
|
|
|
case OPCODE_SPECIAL:
|
|
// Game specific opcode
|
|
_specialOpcode = instr->_operand[0];
|
|
break;
|
|
|
|
case OPCODE_TAKE_CURRENT_OBJECT:
|
|
item = get_item_by_noun(noun);
|
|
if (!item)
|
|
error("Attempt to take object failed\n");
|
|
|
|
move_object(item, ROOM_INVENTORY);
|
|
break;
|
|
|
|
case OPCODE_TAKE_OBJECT:
|
|
item = getItem(instr);
|
|
move_object(item, ROOM_INVENTORY);
|
|
break;
|
|
|
|
case OPCODE_TEST_FLAG:
|
|
func_set_test_result(func_state, _flags[instr->_operand[0]]);
|
|
break;
|
|
|
|
case OPCODE_TEST_ROOM_FLAG:
|
|
func_set_test_result(func_state, room->_flags & instr->_operand[0]);
|
|
break;
|
|
|
|
case OPCODE_TURN_TICK:
|
|
_variables[VAR_TURN_COUNT]++;
|
|
break;
|
|
|
|
case OPCODE_VAR_ADD:
|
|
_variables[instr->_operand[0]] += _variables[instr->_operand[1]];
|
|
break;
|
|
|
|
case OPCODE_VAR_DEC:
|
|
_variables[instr->_operand[0]]--;
|
|
break;
|
|
|
|
case OPCODE_VAR_EQ2:
|
|
func_set_test_result(func_state,
|
|
_variables[instr->_operand[0]] == _variables[instr->_operand[1]]);
|
|
break;
|
|
|
|
case OPCODE_VAR_GT1:
|
|
func_set_test_result(func_state,
|
|
_variables[0] >
|
|
_variables[instr->_operand[0]]);
|
|
break;
|
|
|
|
case OPCODE_VAR_GT2:
|
|
func_set_test_result(func_state, _variables[instr->_operand[0]] >
|
|
_variables[instr->_operand[1]]);
|
|
break;
|
|
|
|
case OPCODE_VAR_GTE1:
|
|
func_set_test_result(func_state,
|
|
_variables[0] >=
|
|
_variables[instr->_operand[0]]);
|
|
break;
|
|
|
|
case OPCODE_VAR_GTE2:
|
|
func_set_test_result(func_state,
|
|
_variables[instr->_operand[0]] >=
|
|
_variables[instr->_operand[1]]);
|
|
break;
|
|
|
|
case OPCODE_VAR_EQ1:
|
|
func_set_test_result(func_state,
|
|
_variables[0] ==
|
|
_variables[instr->_operand[0]]);
|
|
break;
|
|
|
|
case OPCODE_VAR_INC:
|
|
_variables[instr->_operand[0]]++;
|
|
break;
|
|
|
|
case OPCODE_VAR_SUB:
|
|
_variables[instr->_operand[0]] -= _variables[instr->_operand[1]];
|
|
break;
|
|
|
|
default:
|
|
if (instr->_opcode & 0x80) {
|
|
warning("Unhandled command opcode %.2x", opcode);
|
|
} else {
|
|
warning("Unhandled test opcode %.2x - returning false", opcode);
|
|
func_set_test_result(func_state, false);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ComprehendGameOpcodes::func_set_test_result(FunctionState *func_state, bool value) {
|
|
if (func_state->_orCount == 0) {
|
|
/* And */
|
|
if (func_state->_and) {
|
|
if (!value)
|
|
func_state->_testResult = false;
|
|
} else {
|
|
func_state->_testResult = value;
|
|
func_state->_and = true;
|
|
}
|
|
|
|
} else {
|
|
/* Or */
|
|
if (value)
|
|
func_state->_testResult = value;
|
|
}
|
|
}
|
|
|
|
bool ComprehendGameOpcodes::isItemPresent(Item *item) const {
|
|
return item && (
|
|
item->_room == _currentRoom || item->_room == ROOM_INVENTORY
|
|
|| item->_room == ROOM_CONTAINER
|
|
);
|
|
}
|
|
|
|
Item *ComprehendGameOpcodes::getItem(const Instruction *instr) {
|
|
return get_item(instr->_operand[0] - 1);
|
|
}
|
|
|
|
/*-------------------------------------------------------*/
|
|
|
|
ComprehendGameV1::ComprehendGameV1() {
|
|
_opcodeMap[0x01] = OPCODE_HAVE_OBJECT;
|
|
_opcodeMap[0x02] = OPCODE_VAR_GT2;
|
|
_opcodeMap[0x04] = OPCODE_OR;
|
|
_opcodeMap[0x05] = OPCODE_IN_ROOM;
|
|
_opcodeMap[0x06] = OPCODE_VAR_EQ2;
|
|
_opcodeMap[0x08] = OPCODE_CURRENT_IS_OBJECT;
|
|
_opcodeMap[0x09] = OPCODE_OBJECT_PRESENT;
|
|
_opcodeMap[0x0a] = OPCODE_VAR_GTE2;
|
|
_opcodeMap[0x0c] = OPCODE_ELSE;
|
|
_opcodeMap[0x0e] = OPCODE_OBJECT_IN_ROOM;
|
|
_opcodeMap[0x14] = OPCODE_CURRENT_OBJECT_NOT_VALID;
|
|
_opcodeMap[0x18] = OPCODE_INVENTORY_FULL;
|
|
_opcodeMap[0x19] = OPCODE_TEST_FLAG;
|
|
_opcodeMap[0x1d] = OPCODE_CURRENT_OBJECT_IN_ROOM;
|
|
_opcodeMap[0x20] = OPCODE_HAVE_CURRENT_OBJECT;
|
|
_opcodeMap[0x21] = OPCODE_OBJECT_IS_NOT_NOWHERE;
|
|
_opcodeMap[0x24] = OPCODE_CURRENT_OBJECT_PRESENT;
|
|
_opcodeMap[0x25] = OPCODE_VAR_GT1;
|
|
_opcodeMap[0x29] = OPCODE_VAR_EQ1;
|
|
_opcodeMap[0x2d] = OPCODE_VAR_GTE1;
|
|
_opcodeMap[0x31] = OPCODE_TEST_ROOM_FLAG;
|
|
_opcodeMap[0x41] = OPCODE_NOT_HAVE_OBJECT;
|
|
_opcodeMap[0x45] = OPCODE_NOT_IN_ROOM;
|
|
_opcodeMap[0x48] = OPCODE_CURRENT_OBJECT_NOT_PRESENT;
|
|
_opcodeMap[0x49] = OPCODE_OBJECT_NOT_IN_ROOM;
|
|
_opcodeMap[0x4E] = OPCODE_TEST_FALSE;
|
|
_opcodeMap[0x50] = OPCODE_CURRENT_OBJECT_IS_NOWHERE;
|
|
_opcodeMap[0x59] = OPCODE_TEST_NOT_FLAG;
|
|
_opcodeMap[0x5D] = OPCODE_TEST_FALSE;
|
|
_opcodeMap[0x60] = OPCODE_NOT_HAVE_CURRENT_OBJECT;
|
|
_opcodeMap[0x61] = OPCODE_OBJECT_IS_NOWHERE;
|
|
_opcodeMap[0x64] = OPCODE_CURRENT_OBJECT_NOT_IN_ROOM;
|
|
_opcodeMap[0x68] = OPCODE_CURRENT_OBJECT_NOT_TAKEABLE;
|
|
_opcodeMap[0x71] = OPCODE_TEST_NOT_ROOM_FLAG;
|
|
_opcodeMap[0x80] = OPCODE_INVENTORY;
|
|
_opcodeMap[0x81] = OPCODE_TAKE_OBJECT;
|
|
_opcodeMap[0x82] = OPCODE_MOVE_OBJECT_TO_ROOM;
|
|
_opcodeMap[0x83] = OPCODE_RANDOM_MSG;
|
|
_opcodeMap[0x84] = OPCODE_SAVE_ACTION;
|
|
_opcodeMap[0x85] = OPCODE_MOVE_TO_ROOM;
|
|
_opcodeMap[0x86] = OPCODE_VAR_ADD;
|
|
_opcodeMap[0x87] = OPCODE_SET_ROOM_DESCRIPTION;
|
|
_opcodeMap[0x88] = OPCODE_CLEAR_LINE;
|
|
_opcodeMap[0x89] = OPCODE_MOVE_OBJECT_TO_CURRENT_ROOM;
|
|
_opcodeMap[0x8a] = OPCODE_VAR_SUB;
|
|
_opcodeMap[0x8b] = OPCODE_SET_OBJECT_DESCRIPTION;
|
|
_opcodeMap[0x8c] = OPCODE_MOVE_DEFAULT;
|
|
_opcodeMap[0x8d] = OPCODE_SET_CAN_TAKE;
|
|
_opcodeMap[0x8e] = OPCODE_PRINT;
|
|
_opcodeMap[0x91] = OPCODE_CLEAR_CAN_TAKE;
|
|
_opcodeMap[0x95] = OPCODE_REMOVE_OBJECT;
|
|
_opcodeMap[0x99] = OPCODE_SET_FLAG;
|
|
_opcodeMap[0x92] = OPCODE_CALL_FUNC;
|
|
_opcodeMap[0x98] = OPCODE_TURN_TICK;
|
|
_opcodeMap[0x9a] = OPCODE_SET_WORD;
|
|
_opcodeMap[0x9d] = OPCODE_CLEAR_FLAG;
|
|
_opcodeMap[0x9e] = OPCODE_INVENTORY_ROOM;
|
|
_opcodeMap[0xa0] = OPCODE_TAKE_CURRENT_OBJECT;
|
|
_opcodeMap[0xa1] = OPCODE_SPECIAL;
|
|
_opcodeMap[0xa4] = OPCODE_DROP_CURRENT_OBJECT;
|
|
_opcodeMap[0xa2] = OPCODE_SET_ROOM_GRAPHIC;
|
|
_opcodeMap[0xad] = OPCODE_CLEAR_WORD;
|
|
_opcodeMap[0xb0] = OPCODE_REMOVE_CURRENT_OBJECT;
|
|
_opcodeMap[0xb1] = OPCODE_MOVE_DIR;
|
|
_opcodeMap[0xb5] = OPCODE_SET_STRING_REPLACEMENT1;
|
|
_opcodeMap[0xb9] = OPCODE_SET_STRING_REPLACEMENT2;
|
|
_opcodeMap[0xbd] = OPCODE_VAR_INC;
|
|
_opcodeMap[0xc1] = OPCODE_VAR_DEC;
|
|
_opcodeMap[0xc5] = OPCODE_SET_STRING_REPLACEMENT3;
|
|
_opcodeMap[0xc9] = OPCODE_MOVE_CURRENT_OBJECT_TO_ROOM;
|
|
_opcodeMap[0xcd] = OPCODE_CLEAR_INVISIBLE;
|
|
_opcodeMap[0xd1] = OPCODE_SET_INVISIBLE;
|
|
_opcodeMap[0xd5] = OPCODE_CLEAR_FLAG40;
|
|
_opcodeMap[0xd9] = OPCODE_SET_FLAG40;
|
|
}
|
|
|
|
void ComprehendGameV1::execute_opcode(const Instruction *instr, const Sentence *sentence,
|
|
FunctionState *func_state) {
|
|
byte noun = sentence ? sentence->_formattedWords[2] : 0;
|
|
Room *room = get_room(_currentRoom);
|
|
Item *item;
|
|
uint count;
|
|
|
|
switch (_opcodeMap[getOpcode(instr)]) {
|
|
case OPCODE_INVENTORY_FULL:
|
|
item = get_item_by_noun(noun);
|
|
|
|
if (g_debugger->_invLimit)
|
|
func_set_test_result(func_state, _variables[VAR_INVENTORY_WEIGHT] +
|
|
(item->_flags & ITEMF_WEIGHT_MASK) > _variables[VAR_INVENTORY_LIMIT]);
|
|
else
|
|
// Allow for an unlimited number of items in inventory
|
|
func_set_test_result(func_state, false);
|
|
break;
|
|
|
|
case OPCODE_OBJECT_NOT_PRESENT:
|
|
item = getItem(instr);
|
|
func_set_test_result(func_state, !isItemPresent(item));
|
|
break;
|
|
|
|
case OPCODE_SET_STRING_REPLACEMENT3:
|
|
_currentReplaceWord = instr->_operand[0] - 1;
|
|
break;
|
|
|
|
/*--------------------------------------*/
|
|
|
|
case OPCODE_TEST_NOT_ROOM_FLAG:
|
|
func_set_test_result(func_state,
|
|
!(room->_flags & instr->_operand[0]));
|
|
break;
|
|
|
|
case OPCODE_NOT_IN_ROOM:
|
|
func_set_test_result(func_state,
|
|
_currentRoom != instr->_operand[0]);
|
|
break;
|
|
|
|
case OPCODE_OBJECT_NOT_IN_ROOM:
|
|
item = getItem(instr);
|
|
func_set_test_result(func_state, !item || item->_room != _currentRoom);
|
|
break;
|
|
|
|
case OPCODE_CURRENT_OBJECT_NOT_IN_ROOM:
|
|
item = get_item_by_noun(noun);
|
|
func_set_test_result(func_state, !item || item->_room != _currentRoom);
|
|
break;
|
|
|
|
case OPCODE_DESCRIBE_CURRENT_OBJECT:
|
|
/*
|
|
* This opcode is only used in version 2
|
|
* FIXME - unsure what the single operand is for.
|
|
*/
|
|
item = get_item_by_noun(noun);
|
|
g_comprehend->print("%s\n", stringLookup(item->_longString).c_str());
|
|
break;
|
|
|
|
case OPCODE_CURRENT_OBJECT_IN_ROOM: {
|
|
/* FIXME - use common code for these two ops */
|
|
bool test = false;
|
|
|
|
if (noun) {
|
|
for (uint i = 0; i < _items.size(); i++) {
|
|
Item *itemP = &_items[i];
|
|
|
|
if (itemP->_word == noun && itemP->_room == instr->_operand[0]) {
|
|
test = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
func_set_test_result(func_state, test);
|
|
break;
|
|
}
|
|
|
|
case OPCODE_CURRENT_OBJECT_PRESENT:
|
|
item = get_item_by_noun(noun);
|
|
if (item)
|
|
func_set_test_result(func_state,
|
|
item->_room == _currentRoom);
|
|
else
|
|
func_set_test_result(func_state, false);
|
|
break;
|
|
|
|
case OPCODE_NOT_HAVE_CURRENT_OBJECT:
|
|
item = get_item_by_noun(noun);
|
|
func_set_test_result(func_state,
|
|
!item || item->_room != ROOM_INVENTORY);
|
|
break;
|
|
|
|
case OPCODE_NOT_HAVE_OBJECT:
|
|
item = getItem(instr);
|
|
func_set_test_result(func_state,
|
|
item->_room != ROOM_INVENTORY);
|
|
break;
|
|
|
|
case OPCODE_CURRENT_OBJECT_NOT_TAKEABLE:
|
|
item = get_item_by_noun(noun);
|
|
if (!item)
|
|
func_set_test_result(func_state, true);
|
|
else
|
|
func_set_test_result(func_state,
|
|
!(item->_flags & ITEMF_CAN_TAKE));
|
|
break;
|
|
|
|
case OPCODE_CURRENT_OBJECT_IS_NOWHERE:
|
|
item = get_item_by_noun(noun);
|
|
func_set_test_result(func_state, item && item->_room == ROOM_NOWHERE);
|
|
break;
|
|
|
|
case OPCODE_OBJECT_IS_NOT_NOWHERE:
|
|
item = getItem(instr);
|
|
func_set_test_result(func_state, item->_room != ROOM_NOWHERE);
|
|
break;
|
|
|
|
case OPCODE_CURRENT_OBJECT_NOT_PRESENT:
|
|
item = get_item_by_noun(noun);
|
|
func_set_test_result(func_state, !isItemPresent(item));
|
|
break;
|
|
|
|
case OPCODE_REMOVE_CURRENT_OBJECT:
|
|
item = get_item_by_noun(noun);
|
|
move_object(item, ROOM_NOWHERE);
|
|
break;
|
|
|
|
case OPCODE_INVENTORY_ROOM:
|
|
count = num_objects_in_room(instr->_operand[0]);
|
|
if (count == 0) {
|
|
console_println(stringLookup(instr->_operand[1] + 1).c_str());
|
|
break;
|
|
}
|
|
|
|
console_println(stringLookup(instr->_operand[1]).c_str());
|
|
for (uint i = 0; i < _items.size(); i++) {
|
|
item = &_items[i];
|
|
if (item->_room == instr->_operand[0])
|
|
g_comprehend->print("%s\n",
|
|
stringLookup(item->_stringDesc).c_str());
|
|
}
|
|
break;
|
|
|
|
case OPCODE_MOVE_CURRENT_OBJECT_TO_ROOM:
|
|
item = get_item_by_noun(noun);
|
|
if (!item)
|
|
error("Bad current object\n");
|
|
|
|
move_object(item, instr->_operand[0]);
|
|
break;
|
|
|
|
case OPCODE_DROP_OBJECT:
|
|
item = getItem(instr);
|
|
move_object(item, _currentRoom);
|
|
break;
|
|
|
|
case OPCODE_DROP_CURRENT_OBJECT:
|
|
item = get_item_by_noun(noun);
|
|
if (!item)
|
|
error("Attempt to take object failed\n");
|
|
|
|
move_object(item, _currentRoom);
|
|
break;
|
|
|
|
case OPCODE_TEST_NOT_FLAG:
|
|
func_set_test_result(func_state,
|
|
!_flags[instr->_operand[0]]);
|
|
break;
|
|
|
|
case OPCODE_TEST_FALSE:
|
|
// The original had two opcodes mapped to the same code that does
|
|
// a test, but ignores the result, and is always false
|
|
func_set_test_result(func_state, false);
|
|
break;
|
|
|
|
case OPCODE_SET_CURRENT_NOUN_STRING_REPLACEMENT:
|
|
#if 1
|
|
error("TODO: OPCODE_SET_CURRENT_NOUN_STRING_REPLACEMENT");
|
|
#else
|
|
/*
|
|
* FIXME - Not sure what the operand is for,
|
|
* maybe capitalisation?
|
|
*/
|
|
if (noun && (noun->_type & WORD_TYPE_NOUN_PLURAL))
|
|
_currentReplaceWord = 3;
|
|
else if (noun && (noun->_type & WORD_TYPE_FEMALE))
|
|
_currentReplaceWord = 0;
|
|
else if (noun && (noun->_type & WORD_TYPE_MALE))
|
|
_currentReplaceWord = 1;
|
|
else
|
|
_currentReplaceWord = 2;
|
|
#endif
|
|
break;
|
|
|
|
case OPCODE_MOVE_DIR:
|
|
doMovementVerb(instr->_operand[0]);
|
|
break;
|
|
|
|
default:
|
|
ComprehendGameOpcodes::execute_opcode(instr, sentence, func_state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------*/
|
|
|
|
ComprehendGameV2::ComprehendGameV2() {
|
|
_opcodeMap[0x01] = OPCODE_HAVE_OBJECT;
|
|
_opcodeMap[0x02] = OPCODE_VAR_GT2;
|
|
_opcodeMap[0x04] = OPCODE_OR;
|
|
_opcodeMap[0x05] = OPCODE_IN_ROOM;
|
|
_opcodeMap[0x06] = OPCODE_VAR_EQ2;
|
|
_opcodeMap[0x08] = OPCODE_CURRENT_IS_OBJECT;
|
|
_opcodeMap[0x09] = OPCODE_VAR_GT1;
|
|
_opcodeMap[0x0a] = OPCODE_VAR_GTE2;
|
|
_opcodeMap[0x0c] = OPCODE_ELSE;
|
|
_opcodeMap[0x0d] = OPCODE_VAR_EQ1;
|
|
_opcodeMap[0x11] = OPCODE_OBJECT_IS_NOWHERE;
|
|
_opcodeMap[0x14] = OPCODE_CURRENT_OBJECT_NOT_VALID;
|
|
_opcodeMap[0x15] = OPCODE_INVENTORY_FULL_X;
|
|
_opcodeMap[0x19] = OPCODE_TEST_FLAG;
|
|
_opcodeMap[0x1d] = OPCODE_TEST_ROOM_FLAG;
|
|
_opcodeMap[0x20] = OPCODE_HAVE_CURRENT_OBJECT;
|
|
_opcodeMap[0x21] = OPCODE_OBJECT_PRESENT;
|
|
_opcodeMap[0x22] = OPCODE_OBJECT_IN_ROOM;
|
|
_opcodeMap[0x25] = OPCODE_OBJECT_TAKEABLE;
|
|
_opcodeMap[0x29] = OPCODE_INVENTORY_FULL;
|
|
_opcodeMap[0x2d] = OPCODE_OBJECT_CAN_TAKE;
|
|
_opcodeMap[0x80] = OPCODE_INVENTORY;
|
|
_opcodeMap[0x81] = OPCODE_TAKE_OBJECT;
|
|
_opcodeMap[0x83] = OPCODE_RANDOM_MSG;
|
|
_opcodeMap[0x84] = OPCODE_SAVE_ACTION;
|
|
_opcodeMap[0x85] = OPCODE_MOVE_TO_ROOM;
|
|
_opcodeMap[0x86] = OPCODE_VAR_ADD;
|
|
_opcodeMap[0x87] = OPCODE_SET_ROOM_DESCRIPTION;
|
|
_opcodeMap[0x88] = OPCODE_CLEAR_LINE;
|
|
_opcodeMap[0x89] = OPCODE_SPECIAL;
|
|
_opcodeMap[0x8a] = OPCODE_VAR_SUB;
|
|
_opcodeMap[0x8b] = OPCODE_SET_OBJECT_DESCRIPTION;
|
|
_opcodeMap[0x8c] = OPCODE_MOVE_DEFAULT;
|
|
_opcodeMap[0x8e] = OPCODE_PRINT;
|
|
_opcodeMap[0x8f] = OPCODE_SET_OBJECT_LONG_DESCRIPTION;
|
|
_opcodeMap[0x90] = OPCODE_WAIT_KEY;
|
|
_opcodeMap[0x92] = OPCODE_CALL_FUNC;
|
|
_opcodeMap[0x95] = OPCODE_CLEAR_WORD;
|
|
_opcodeMap[0x96] = OPCODE_CALL_FUNC2;
|
|
_opcodeMap[0x98] = OPCODE_TURN_TICK;
|
|
_opcodeMap[0x99] = OPCODE_SET_FLAG;
|
|
_opcodeMap[0x9a] = OPCODE_SET_WORD;
|
|
_opcodeMap[0x9d] = OPCODE_CLEAR_FLAG;
|
|
_opcodeMap[0xa0] = OPCODE_TAKE_CURRENT_OBJECT;
|
|
_opcodeMap[0xa1] = OPCODE_CLEAR_FLAG40;
|
|
_opcodeMap[0xa2] = OPCODE_MOVE_OBJECT_TO_ROOM;
|
|
_opcodeMap[0xa5] = OPCODE_SET_FLAG40;
|
|
_opcodeMap[0xa9] = OPCODE_CLEAR_INVISIBLE;
|
|
_opcodeMap[0xad] = OPCODE_SET_INVISIBLE;
|
|
_opcodeMap[0xc1] = OPCODE_VAR_DEC;
|
|
_opcodeMap[0xc2] = OPCODE_SET_ROOM_GRAPHIC;
|
|
_opcodeMap[0xc5] = OPCODE_SET_STRING_REPLACEMENT3;
|
|
_opcodeMap[0xc9] = OPCODE_SET_STRING_REPLACEMENT1;
|
|
_opcodeMap[0xcd] = OPCODE_SET_STRING_REPLACEMENT2;
|
|
_opcodeMap[0xd1] = OPCODE_MOVE_DIR;
|
|
_opcodeMap[0xd5] = OPCODE_DRAW_ROOM;
|
|
_opcodeMap[0xd9] = OPCODE_DRAW_OBJECT;
|
|
_opcodeMap[0xdd] = OPCODE_VAR_INC;
|
|
_opcodeMap[0xe1] = OPCODE_MOVE_OBJECT_TO_CURRENT_ROOM;
|
|
_opcodeMap[0xe5] = OPCODE_SET_CAN_TAKE;
|
|
_opcodeMap[0xe9] = OPCODE_CLEAR_CAN_TAKE;
|
|
_opcodeMap[0xed] = OPCODE_REMOVE_OBJECT;
|
|
|
|
#if 0
|
|
_opcodeMap[0x9e] = OPCODE_INVENTORY_ROOM;
|
|
_opcodeMap[0xc6] = OPCODE_SET_OBJECT_GRAPHIC;
|
|
_opcodeMap[0xf0] = OPCODE_DROP_CURRENT_OBJECT;
|
|
_opcodeMap[0xfc] = OPCODE_REMOVE_CURRENT_OBJECT;
|
|
#endif
|
|
}
|
|
|
|
void ComprehendGameV2::execute_opcode(const Instruction *instr, const Sentence *sentence,
|
|
FunctionState *func_state) {
|
|
Instruction instrCopy;
|
|
byte noun = sentence ? sentence->_formattedWords[2] : 0;
|
|
Room *room = get_room(_currentRoom);
|
|
Item *item;
|
|
|
|
// In case a single opcode is being executed outside of a function, use a dummy function state
|
|
FunctionState dummyState;
|
|
if (!func_state)
|
|
func_state = &dummyState;
|
|
|
|
if ((instr->_opcode & 0x30) == 0x30) {
|
|
// First operand comes from entered sentence noun, shifting out existing operands
|
|
instrCopy = *instr;
|
|
instrCopy._operand[2] = instrCopy._operand[1];
|
|
instrCopy._operand[1] = instrCopy._operand[0];
|
|
instrCopy._operand[0] = get_item_id(noun) + 1;
|
|
instr = &instrCopy;
|
|
}
|
|
|
|
func_state->_notComparison = (instr->_opcode & 0x40) != 0;
|
|
|
|
switch (_opcodeMap[getOpcode(instr)]) {
|
|
case OPCODE_CLEAR_INVISIBLE:
|
|
item = get_item_by_noun(noun);
|
|
item->_flags &= ~ITEMF_INVISIBLE;
|
|
break;
|
|
|
|
case OPCODE_DRAW_OBJECT:
|
|
g_comprehend->drawItemPicture(instr->_operand[0] - 1);
|
|
break;
|
|
|
|
case OPCODE_DRAW_ROOM:
|
|
g_comprehend->drawLocationPicture(instr->_operand[0] - 1);
|
|
g_comprehend->readChar();
|
|
break;
|
|
|
|
case OPCODE_INVENTORY_FULL:
|
|
item = get_item_by_noun(noun);
|
|
|
|
weighInventory();
|
|
func_set_test_result(func_state, _totalInventoryWeight + (item->_flags & ITEMF_WEIGHT_MASK) >
|
|
_variables[VAR_INVENTORY_LIMIT]);
|
|
break;
|
|
|
|
case OPCODE_INVENTORY_FULL_X:
|
|
item = get_item_by_noun(noun);
|
|
|
|
weighInventory();
|
|
func_set_test_result(func_state, _totalInventoryWeight + (item->_flags & ITEMF_WEIGHT_MASK) >
|
|
_variables[instr->_operand[1]]);
|
|
break;
|
|
|
|
case OPCODE_MOVE_DIR:
|
|
if (room->_direction[instr->_operand[0] - 1])
|
|
move_to(room->_direction[instr->_operand[0] - 1]);
|
|
else
|
|
console_println(stringLookup(STRING_CANT_GO).c_str());
|
|
break;
|
|
|
|
case OPCODE_OBJECT_TAKEABLE:
|
|
// WORKAROUND: Trying to get non-items in OO-Topos
|
|
func_set_test_result(func_state, instr->_operand[0]
|
|
&& (getItem(instr)->_flags & ITEMF_WEIGHT_MASK) != ITEMF_WEIGHT_MASK);
|
|
break;
|
|
|
|
case OPCODE_OBJECT_CAN_TAKE:
|
|
item = getItem(instr);
|
|
func_set_test_result(func_state, item->_flags & ITEMF_CAN_TAKE);
|
|
break;
|
|
|
|
case OPCODE_SET_OBJECT_GRAPHIC:
|
|
item = getItem(instr);
|
|
item->_graphic = instr->_operand[1];
|
|
if (item->_room == _currentRoom)
|
|
_updateFlags |= UPDATE_GRAPHICS;
|
|
break;
|
|
|
|
case OPCODE_SET_OBJECT_LONG_DESCRIPTION:
|
|
item = getItem(instr);
|
|
item->_longString = (instr->_operand[2] << 8) | instr->_operand[1];
|
|
break;
|
|
|
|
case OPCODE_SET_STRING_REPLACEMENT3: {
|
|
int articleNum, bits = _wordFlags;
|
|
for (articleNum = 3; articleNum >= 0; --articleNum, bits <<= 1) {
|
|
if (bits >= 0x100)
|
|
break;
|
|
}
|
|
if (articleNum == -1)
|
|
articleNum = 2;
|
|
|
|
_currentReplaceWord = instr->_operand[0] + articleNum - 1;
|
|
break;
|
|
}
|
|
|
|
case OPCODE_WAIT_KEY:
|
|
console_get_key();
|
|
break;
|
|
|
|
default:
|
|
ComprehendGameOpcodes::execute_opcode(instr, sentence, func_state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
byte ComprehendGameV2::getOpcode(const Instruction *instr) {
|
|
// Special pre-processing for opcodes
|
|
byte opcode = instr->_opcode;
|
|
if (!(opcode & 0x80))
|
|
opcode &= 0x3f;
|
|
if ((opcode & 0x30) == 0x30) {
|
|
opcode = (opcode & ~0x10) + 1;
|
|
}
|
|
|
|
return opcode;
|
|
}
|
|
|
|
void ComprehendGameV2::func_set_test_result(FunctionState *func_state, bool value) {
|
|
ComprehendGameOpcodes::func_set_test_result(func_state, value ^ func_state->_notComparison);
|
|
}
|
|
|
|
|
|
} // namespace Comprehend
|
|
} // namespace Glk
|