scummvm/queen/command.cpp

1597 lines
38 KiB
C++
Raw Normal View History

/* ScummVM - Scumm Interpreter
* Copyright (C) 2003 The ScummVM project
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*
*/
#include "stdafx.h"
#include "queen/command.h"
#include "queen/display.h"
#include "queen/input.h"
#include "queen/graphics.h"
#include "queen/logic.h"
#include "queen/walk.h"
namespace Queen {
void CmdText::clear() {
memset(_command, 0, sizeof(_command));
}
void CmdText::display(uint8 color) {
_graphics->textCurrentColor(color);
_graphics->textSetCentered(COMMAND_Y_POS, _command);
}
void CmdText::displayTemp(uint8 color, bool locked, const Verb& v, const char *name) {
char temp[MAX_COMMAND_LEN];
if (locked) {
sprintf(temp, "%s%s", lockedVerbPrefix, v.name());
}
else {
strcpy(temp, v.name());
}
if (name != NULL) {
strcat(temp, " ");
strcat(temp, name);
}
_graphics->textCurrentColor(color);
_graphics->textSetCentered(COMMAND_Y_POS, temp);
}
void CmdText::displayTemp(uint8 color, const char *name) {
char temp[MAX_COMMAND_LEN];
sprintf(temp, "%s %s", _command, name);
_graphics->textCurrentColor(color);
_graphics->textSetCentered(COMMAND_Y_POS, temp);
}
void CmdText::setVerb(const Verb& v) {
strcpy(_command, v.name());
}
void CmdText::addLinkWord(const Verb& v) {
strcat(_command, " ");
strcat(_command, v.name());
}
void CmdText::addObject(const char *objName) {
strcat(_command, " ");
strcat(_command, objName);
}
bool CmdText::isEmpty() const {
return _command[0] == 0;
}
Command::Command(Logic *l, Graphics *g, Input *i, Walk *w)
: _logic(l), _graphics(g), _input(i), _walk(w) {
_cmdText._graphics = _graphics;
_cmdText.lockedVerbPrefix = _logic->lockedVerbPrefix();
}
void Command::clear(bool clearTexts) {
_cmdText.clear();
if (clearTexts) {
_graphics->textClear(151, 151);
}
_parse = false;
_commandLevel = 1;
_oldVerb = _verb = Verb(VERB_NONE);
_oldNoun = _noun = _selectedNoun = 0;
_currentAction = Verb(VERB_NONE);
_selectedAction = Verb(VERB_NONE);
_subject1 = _subject2 = 0;
}
void Command::executeCurrentAction(bool walk) {
_logic->entryObj(0);
const char *obj1Name = NULL;
const char *obj2Name = NULL;
if (_commandLevel == 2 && _mouseKey == Input::MOUSE_RBUTTON) {
_mouseKey = Input::MOUSE_LBUTTON;
}
// XXX SUBJ1=SUBJECT[1]; SUBJ2=SUBJECT[2];
uint16 objMax = _logic->currentRoomObjMax();
uint16 roomData = _logic->currentRoomData();
if (_mouseKey == Input::MOUSE_RBUTTON && _subject1 != 0) {
// check to see if selecting default command for object/item
if (_subject1 > 0) {
// an object
int16 i = _subject1;
if (_noun > objMax) {
int16 aObj = _logic->currentRoomArea(_noun - objMax)->object;
int16 aObjName = _logic->objectData(aObj)->name;
if (aObjName > 0) {
_noun = objMax;
i = aObj;
}
}
ObjectData *od = _logic->objectData(ABS(i));
obj1Name = _logic->objectName(od->name);
if (_noun == 0 || _noun > objMax || _logic->objectData(i)->name <= 0) {
cleanupCurrentAction();
return;
}
uint16 obj = roomData + _noun;
_verb = findDefault(obj, false);
if (_verb.isNone()) {
// no match made, so command not yet completed, redefine as WALK_TO
_cmdText.setVerb(Verb(VERB_WALK_TO));
_selectedAction = Verb(VERB_WALK_TO);
}
else {
_cmdText.setVerb(_verb);
_selectedAction = _verb;
}
_cmdText.addObject(_logic->objectName(_logic->objectData(obj)->name));
}
else {
// an item
int16 name = _logic->itemData(ABS(_subject1))->name;
obj1Name = _logic->objectName(name);
}
}
// make sure that command is always highlighted when actioned!
_cmdText.display(INK_CMD_SELECT);
_selectedNoun = _noun;
_commandLevel = 1;
// XXX SUBJECT[2]=0;
// get objects names
obj1Name = _logic->objectOrItemName(_subject1);
obj2Name = _logic->objectOrItemName(_subject2);
if (handleBadCommand(walk)) {
cleanupCurrentAction();
return;
}
// get the number of commands associated with Object/Item
uint16 comMax = countAssociatedCommands(_selectedAction, _subject1, _subject2);
if (comMax == 0) {
// no command match was found, so exit
// pass ACTION2 as paramater, as a new Command (and a new ACTION2)
// can be constructed while Joe speaks
executeStandardStuff(_selectedAction, _subject1, _subject2);
cleanupCurrentAction();
return;
}
// process each associated command for the Object, until all done
// or one of the Gamestate tests fails...
int16 cond = 0;
CmdListData *com = &_cmdList[0];
uint16 comId = 0;
uint16 curCommand;
for (curCommand = 1; curCommand <= comMax; ++curCommand) {
++com;
++comId;
// try to find a match for the command in COM_LIST
for (; comId <= _numCmdList; ++comId, ++com) {
if (com->match(_selectedAction, _subject1, _subject2)) {
break;
}
}
// check the Gamestates and set them if necessary
cond = 0;
if (com->setConditions) {
cond = setConditions(comId, (curCommand == comMax));
}
if (cond == -1 && curCommand == comMax) {
// only exit on a condition fail if at last command
// Joe hasnt spoken, so do normal LOOK command
if (_selectedAction.value() == VERB_LOOK_AT) {
// Look At, do standard look at routine
look();
cleanupCurrentAction();
return;
}
}
else if (cond == -2 && curCommand == comMax) {
// only exit on a condition fail if at last command
// Joe has spoken, so skip LOOK command
cleanupCurrentAction();
return;
}
else if (cond >= 0) {
// we've had a successful Gamestate check, so we must now exit
break;
}
}
debug(0, "Command::executeCurrentAction() - cond = %X", cond);
if (com->setAreas) {
setAreas(comId);
}
// Don't grab if action is TALK or WALK
if (_selectedAction.value() != VERB_TALK_TO && _selectedAction.value() != VERB_WALK_TO) {
if (_subject1 > 0) {
_logic->joeGrab(_logic->objectData(_subject1)->state, 0);
}
if (_subject2 > 0) {
_logic->joeGrab(_logic->objectData(_subject2)->state, 0);
}
}
bool cutDone = false;
if (cond > 0) {
// FIXME: this stuff must be seriously re-designed !
// CR 2 - 7/3/95, Because we may be calling a cutaway triggered from walking
// to the pinnacle (which connects to Map room 7), we'll be caught in the
// R_MAP routine until we select a location and then call this very procedure
// again - the result being that COM will be reset. So to avoid that, we'll
// keep a copy of COM until we return from the recursive call...
// Otherwise, all remaining commands will be wiped and not carried out!
uint16 comTempId = comId;
CmdListData *comTemp = com;
const char *desc = _logic->objectTextualDescription(cond);
if (executeIfCutaway(desc)) {
cond = 0;
cutDone = true;
}
comId = comTempId;
com = comTemp;
// check for dialogs before updating Objects
if (executeIfDialog(desc)) {
cond = 0;
}
}
int16 oldImage = 0;
if (_subject1 > 0) {
// an object (not an item)
oldImage = _logic->objectData(_subject1)->image;
}
if (com->setObjects) {
setObjects(comId);
}
if (com->setItems) {
setItems(comId);
}
if (com->imageOrder != 0) {
ObjectData* od = _logic->objectData(_subject1);
// we must update the graphic image of the object
if (com->imageOrder < 0) {
// instead of setting to -1 or -2, flag as negative
if (od->image > 0) {
// make sure that object is not already updated
od->image = -(od->image + 10);
}
}
else {
od->image = com->imageOrder;
}
_logic->roomRefreshObject(_subject1);
}
else {
// this object is not being updated by command list, see if
// it has another image copied to it
if (_subject1 > 0) {
// an object (not an item)
if (_logic->objectData(_subject1)->image != oldImage) {
_logic->roomRefreshObject(_subject1);
}
}
}
// don't play music on an OPEN/CLOSE command - in case the command fails
if (_selectedAction.value() != VERB_OPEN && _selectedAction.value() != VERB_CLOSE) {
// only play song if it's a PLAY BEFORE type
if (com->song > 0) {
// XXX playsong(com->song);
}
}
// do a special hardcoded section
// l.419-452 execute.c
switch (com->specialSection) {
case 1:
// XXX l.428-438
warning("Command::executeCurrentAction() - Journal unimplemented");
return;
case 2:
_logic->joeUseDress(true);
break;
case 3:
_logic->joeUseClothes(true);
break;
case 4:
_logic->joeUseUnderwear();
break;
}
changeObjectState(_selectedAction, _subject1, com->song, cutDone);
if (_selectedAction.value() == VERB_TALK_TO && cond > 0) {
if (executeIfDialog(_logic->objectTextualDescription(cond))) {
cleanupCurrentAction();
return;
}
}
// EXECUTE_EXIT1:
if (cond > 0) {
const char *desc = _logic->objectTextualDescription(cond);
// Joe needs to say something as a result of a Gamestate
// check first to see if it is a cutaway scene!
if (executeIfCutaway(desc)) {
}
else if (executeIfDialog(desc)) {
cleanupCurrentAction();
return;
}
else {
_logic->joeSpeak(cond, true);
}
}
else {
// we've failed commands with nothing to say
if (_selectedAction.value() == VERB_LOOK_AT) {
// Look At, do standard look at routine
look();
cleanupCurrentAction();
return;
}
}
// only play song if it's a PLAY AFTER type
if (com->song > 0) {
// XXX playsong(com->song);
}
clear(true);
cleanupCurrentAction();
}
void Command::updatePlayer() {
if (_input->cutawayRunning()) return;
lookCurrentRoom();
lookCurrentIcon();
if (!_input->keyVerb().isNone()) {
if (_input->keyVerb().isJournal()) {
// XXX queen.c l.348-365
warning("Command::updatePlayer() - Journal not implemented");
}
else if (!_input->keyVerb().isSkipText()) {
_verb = _input->keyVerb();
if (_verb.isInventory()) {
_noun = _selectedNoun = 0;
// Clear old noun and old verb in case we're pointing at an
// object (noun) or item (verb) and we want to use an item
// on it. This was the command will be redisplayed with the
// object/item that the cursor is currently on.
_oldNoun = 0;
_oldVerb = Verb(VERB_NONE);
grabSelectedItem();
}
else {
grabSelectedVerb();
}
_input->clearKeyVerb();
}
}
_mouseKey = _input->mouseButton();
_input->clearMouseButton();
if (_mouseKey > 0) {
grabCurrentSelection();
}
}
void Command::readCommandsFrom(byte *&ptr) {
uint16 i;
// Command List Data
_numCmdList = READ_BE_UINT16(ptr); ptr += 2;
_cmdList = new CmdListData[_numCmdList + 1];
memset(&_cmdList[0], 0, sizeof(CmdListData));
for (i = 1; i <= _numCmdList; i++) {
_cmdList[i].readFrom(ptr);
}
// Command AREA
_numCmdArea = READ_BE_UINT16(ptr); ptr += 2;
_cmdArea = new CmdArea[_numCmdArea + 1];
memset(&_cmdArea[0], 0, sizeof(CmdArea));
for (i = 1; i <= _numCmdArea; i++) {
_cmdArea[i].readFrom(ptr);
}
// Command OBJECT
_numCmdObject = READ_BE_UINT16(ptr); ptr += 2;
_cmdObject = new CmdObject[_numCmdObject + 1];
memset(&_cmdObject[0], 0, sizeof(CmdObject));
for (i = 1; i <= _numCmdObject; i++) {
_cmdObject[i].readFrom(ptr);
}
// Command INVENTORY
_numCmdInventory = READ_BE_UINT16(ptr); ptr += 2;
_cmdInventory = new CmdInventory[_numCmdInventory + 1];
memset(&_cmdInventory[0], 0, sizeof(CmdInventory));
for (i = 1; i <= _numCmdInventory; i++) {
_cmdInventory[i].readFrom(ptr);
}
// Command GAMESTATE
_numCmdGameState = READ_BE_UINT16(ptr); ptr += 2;
_cmdGameState = new CmdGameState[_numCmdGameState + 1];
memset(&_cmdGameState[0], 0, sizeof(CmdGameState));
for (i = 1; i <= _numCmdGameState; i++) {
_cmdGameState[i].readFrom(ptr);
}
}
void Command::grabCurrentSelection() {
_selPosX = _input->mousePosX();
_selPosY = _input->mousePosY();
uint16 zone = _logic->findObjectUnderCursor(_selPosX, _selPosY);
_noun = _logic->findObjectRoomNumber(zone);
_verb = _logic->findVerbUnderCursor(_selPosX, _selPosY);
_selPosX += _logic->display()->horizontalScroll();
debug(0, "Command::grabCurrentSelection() - _noun = %d, _verb = %d", _noun, _verb.value());
if (_verb.isAction()) {
grabSelectedVerb();
}
else if (_verb.isInventory()) {
grabSelectedItem();
}
else if (_noun > 0 && _noun <= _logic->currentRoomObjMax()) {
grabSelectedNoun();
}
else if (_selPosY < ROOM_ZONE_HEIGHT && _verb.isNone()) {
// select without a command, do a WALK
clear(true);
_logic->joeWalk(2);
}
}
void Command::grabSelectedItem() {
// if the NOUN has been selected from screen then it is positive
// Otherwise it has been selected from inventory and is negative
// Set PARSE to TRUE, default FALSE if command half complete
_parse = true;
uint16 item = _logic->findInventoryItem(_verb.inventoryItem());
if (item == 0 || _logic->itemData(item)->name == 0) {
return;
}
// If we've selected via keyboard, and there is no VERB then do
// the ITEMs default, otherwise keep constructing!
if (_mouseKey == Input::MOUSE_LBUTTON ||
(!_input->keyVerb().isNone() && !_verb.isNone())) {
if (_currentAction.isNone()) {
if (!_input->keyVerb().isNone()) {
/* 2 - We've selected via the keyboard, no command is being */
/* constructed, so we shall find the item's default */
_verb = findDefault(item, true);
if (_verb.isNone()) {
// set to Look At
_verb = Verb(VERB_LOOK_AT);
_cmdText.setVerb(Verb(VERB_LOOK_AT));
}
_currentAction = _verb;
}
else {
// Action>0 ONLY if command has been constructed
// Left Mouse Button pressed just do Look At
_verb = Verb(VERB_LOOK_AT);
_currentAction = Verb(VERB_LOOK_AT);
_cmdText.setVerb(Verb(VERB_LOOK_AT));
}
}
_verb = Verb(VERB_NONE);
}
else {
if (_logic->joeWalk() == 1) {
_cmdText.clear();
_commandLevel = 1;
_logic->joeWalk(0);
_currentAction = Verb(VERB_NONE);
lookCurrentIcon();
}
if (!_defaultVerb.isNone()) {
alterDefault(_defaultVerb, true);
_defaultVerb = Verb(VERB_NONE);
clear(true);
return;
}
if (_cmdText.isEmpty()) {
_verb = Verb(VERB_LOOK_AT);
_currentAction = Verb(VERB_LOOK_AT);
_cmdText.setVerb(Verb(VERB_LOOK_AT));
}
else {
if (_commandLevel == 2 && _parse) {
_verb = _currentAction;
}
else {
_verb = findDefault(item, true);
}
if (!_verb.isNone()) {
// No match made, so command not yet completed. Redefine as LOOK AT
_verb = Verb(VERB_LOOK_AT);
_cmdText.setVerb(Verb(VERB_LOOK_AT));
}
_currentAction = _verb;
}
}
if (_currentAction.isTwoLevelsCommand() && _commandLevel == 1) {
_parse = false;
}
if (!_currentAction.isNone()) {
_cmdText.addObject(_logic->objectName(_logic->itemData(item)->name));
}
switch (_commandLevel) {
case 1:
_subject1 = -item;
break;
case 2:
_subject2 = -item;
break;
}
if (_currentAction.value() == VERB_USE && _commandLevel == 1) {
if (State::findUse(_logic->itemData(item)->state) == STATE_USE_ON) {
_cmdText.addLinkWord(Verb(VERB_PREP_WITH));
_commandLevel = 2;
}
else {
_parse = true;
}
_cmdText.display(INK_CMD_NORMAL);
}
else if (_currentAction.value() == VERB_GIVE && _commandLevel == 1) {
_cmdText.addLinkWord(Verb(VERB_PREP_TO));
_commandLevel = 2;
_cmdText.display(INK_CMD_NORMAL);
}
else {
_cmdText.display(INK_CMD_SELECT);
}
if (_parse) {
_verb = Verb(VERB_NONE);
_logic->joeWalk(2); // set JOEWALK flag to perform EXECUTE_ACTION procedure
_selectedAction = _currentAction;
_currentAction = Verb(VERB_NONE);
}
}
void Command::grabSelectedNoun() {
// if the NOUN has been selected from screen then it is positive
// otherwise it has been selected from inventory and is negative
// set PARSE to TRUE, default FALSE if command half complete
// click object without a command, if DEFAULT then
// do that, otherwise do a WALK!
uint16 objNum = _logic->currentRoomData() + _noun;
int16 objName = _logic->objectData(objNum)->name;
if (objName < 0) {
// selected a turned off object, so just walk
_noun = 0;
clear(true);
_logic->joeWalk(2);
return;
}
if (_verb.isNone()) {
if (_mouseKey == Input::MOUSE_LBUTTON) {
if ((_commandLevel != 2 && _currentAction.isNone()) ||
(_commandLevel == 2 && _parse)) {
// action2 > 0 only if command has been constructed
// lmb pressed, just walk
_verb = Verb(VERB_WALK_TO);
_currentAction = Verb(VERB_WALK_TO);
_cmdText.setVerb(Verb(VERB_WALK_TO));
}
}
else if (_mouseKey == Input::MOUSE_RBUTTON) {
// rmb pressed, do default if one exists
if (!_defaultVerb.isNone()) {
// change default of command
alterDefault(_defaultVerb, false);
_defaultVerb = Verb(VERB_NONE);
clear(true);
return;
}
if (_cmdText.isEmpty()) {
// Ensures that Right Mkey will select correct default
_verb = findDefault(objNum, false);
if (!_verb.isNone()) {
// no match made, redefine as Walk To
_selectedAction = Verb(VERB_WALK_TO);
}
else {
_selectedAction = _verb;
}
_cmdText.setVerb(_selectedAction);
_cmdText.addObject(_logic->objectName(_logic->objectData(objNum)->name));
}
else {
_verb = Verb(VERB_NONE);
if ((_commandLevel == 2 && !_parse) || !_currentAction.isNone()) {
_verb = _currentAction;
}
else {
_verb = findDefault(objNum, false);
}
if (_verb.value() == VERB_NONE) {
_currentAction = Verb(VERB_WALK_TO);
_cmdText.setVerb(Verb(VERB_WALK_TO));
}
else {
_currentAction = _verb;
}
_verb = Verb(VERB_NONE);
}
}
}
_selectedNoun = 0;
if (_currentAction.isTwoLevelsCommand() && _commandLevel == 1) {
// command not fully constructed
_parse = false;
}
else {
_parse = true;
}
if (!_currentAction.isNone()) {
_cmdText.addObject(_logic->objectName(objName));
}
switch (_commandLevel) {
case 1:
_subject1 = objNum;
break;
case 2:
_subject2 = objNum;
break;
}
// if first noun and it's a 2 level command then set up action word
if (_currentAction.value() == VERB_USE && _commandLevel == 1) {
if (State::findUse(_logic->objectData(objNum)->state) == STATE_USE_ON) {
_cmdText.addLinkWord(Verb(VERB_PREP_WITH));
_commandLevel = 2;
}
else {
// object does not support 2nd level
_parse = true;
}
_cmdText.display(INK_CMD_NORMAL);
}
else if (_currentAction.value() == VERB_GIVE && _commandLevel == 1) {
_cmdText.addLinkWord(Verb(VERB_PREP_TO));
_cmdText.display(INK_CMD_NORMAL);
}
else {
_cmdText.display(INK_CMD_SELECT);
}
if (_parse) {
_verb = Verb(VERB_NONE);
_logic->joeWalk(2); // set JOEWALK flag to perform EXECUTE_ACTION procedure
_selectedAction = _currentAction;
_currentAction = Verb(VERB_NONE);
}
}
void Command::grabSelectedVerb() {
if (_verb.isScrollInventory()) {
// move through inventory (by four if right mouse button)
uint16 scroll = _mouseKey == Input::MOUSE_RBUTTON ? 4 : 1;
_logic->inventoryScroll(scroll, _verb.value() == VERB_SCROLL_UP);
}
else if (_verb.isPanelCommand() || _verb.value() == VERB_WALK_TO) {
_currentAction = _verb;
_subject1 = 0;
_subject2 = 0;
// if right mouse key selected, then store command VERB
if (_mouseKey == Input::MOUSE_RBUTTON) {
_defaultVerb = _verb;
_cmdText.displayTemp(11, true, _verb);
}
else {
_defaultVerb = Verb(VERB_NONE);
if (_logic->joeWalk() == 1 && !_verb.isNone()) {
_logic->joeWalk(0);
}
_commandLevel = 1;
_oldVerb = Verb(VERB_NONE);
_oldNoun = 0;
_cmdText.setVerb(_verb);
_cmdText.display(INK_CMD_NORMAL);
}
}
}
bool Command::executeIfCutaway(const char *description) {
warning("Command::executeIfCutaway(%s) unimplemented", description);
if(scumm_stricmp(description + strlen(description) - 4, ".cut") == 0) {
/* XXX
CUTAWAY(description);
strcpy(Kstr,Paramstr);
while(Kstr[0]) {
CUTAWAY(Kstr);
strcpy(Kstr,Paramstr);
}*/
return true;
}
return false;
}
bool Command::executeIfDialog(const char *description) {
warning("Command::executeIfDialog(%s) unimplemented", description);
if(scumm_stricmp(description + strlen(description) - 4, ".dog") == 0) {
/* XXX
talk(Kstr);
strcpy(Kstr,Paramstr);
while(Kstr[0]) {
CUTAWAY(Kstr);
strcpy(Kstr,Paramstr);
}*/
return true;
}
return false;
}
uint16 Command::countAssociatedCommands(const Verb& verb, int16 subj1, int16 subj2) {
// l.145-150 execute.c
uint16 comMax = 0;
CmdListData *cmdList = &_cmdList[1];
uint16 i;
for (i = 1; i <= _numCmdList; ++i, ++cmdList) {
if (cmdList->match(verb, subj1, subj2)) {
++comMax;
}
}
return comMax;
}
bool Command::handleBadCommand(bool walk) {
// l.96-141 execute.c
uint16 objMax = _logic->currentRoomObjMax();
uint16 roomData = _logic->roomData(_logic->currentRoom());
// select without a command or WALK TO ; do a WALK
if ((_selectedAction.value() == VERB_WALK_TO || _selectedAction.isNone()) &&
(_selectedNoun > objMax || _selectedNoun == 0)) {
if (_selectedAction.isNone()) {
_graphics->textClear(151, 151);
}
_walk->joeMove(0, _selPosX, _selPosY, false); // XXX inCutaway parameter
return true;
}
// check to see if one of the objects is hidden
if (_subject1 > 0 && _logic->objectData(_subject1)->name <= 0) {
return true;
}
if (_selectedAction.value() == VERB_GIVE && _subject2 > 0 && _logic->objectData(_subject2)->name <= 0) {
return true;
}
// check for USE command on exists
if (_selectedAction.value() == VERB_USE && _subject1 > 0 && _logic->objectData(_subject1)->entryObj > 0) {
_selectedAction = Verb(VERB_WALK_TO);
}
if (_selectedNoun > 0 && _selectedNoun <= objMax) {
int16 p = _logic->joeWalkTo(_selPosX, _selPosY, walk);
if (p != 0) {
return true;
}
if (_selectedAction.value() == VERB_WALK_TO && _logic->objectData(roomData + _selectedNoun)->entryObj < 0) {
return true;
}
}
return false;
}
void Command::executeStandardStuff(const Verb& action, int16 subj1, int16 subj2) {
// l.158-272 execute.c
uint16 k;
switch (action.value()) {
case VERB_LOOK_AT:
look();
break;
case VERB_OPEN:
// 'it doesn't seem to open'
_logic->joeSpeak(1);
break;
case VERB_USE:
if (subj1 < 0) {
k = _logic->itemData(ABS(subj1))->sfxDescription;
if (k > 0) {
_logic->joeSpeak(k, true);
}
else {
_logic->joeSpeak(2);
}
}
else {
_logic->joeSpeak(1);
}
break;
case VERB_TALK_TO:
_logic->joeSpeak(24 + Logic::randomizer.getRandomNumber(2));
break;
case VERB_CLOSE:
_logic->joeSpeak(2);
break;
case 4: // weird, isn't it ? l.193 execute.c
case VERB_MOVE:
// 'I can't move it'
if (subj1 > 0) {
int16 img = _logic->objectData(subj1)->image;
if (img == -4 || img == -3) {
_logic->joeSpeak(18);
}
else {
_logic->joeSpeak(3);
}
}
else {
_logic->joeSpeak(3);
}
break;
case VERB_GIVE:
// 'I can't give the subj1 to subj2'
if (subj1 < 0) {
k = 11;
if (subj2 > 0) {
int16 img = _logic->objectData(subj2)->image;
if (img == -4 || img == -3) {
_logic->joeSpeak(27 + Logic::randomizer.getRandomNumber(2));
}
}
else {
_logic->joeSpeak(11);
}
}
else {
_logic->joeSpeak(12);
}
break;
case VERB_PICK_UP:
if (subj1 < 0) {
_logic->joeSpeak(14);
}
else {
int16 img = _logic->objectData(subj2)->image;
if (img == -4 || img == -3) {
// Trying to get a person
_logic->joeSpeak(20);
}
else {
switch(Logic::randomizer.getRandomNumber(3)) {
case 0:
// 'I can't pick that up'
_logic->joeSpeak(5);
break;
case 1:
// 'I don't think I need that'
_logic->joeSpeak(6);
break;
case 2:
// 'I'd rather leave it here'
_logic->joeSpeak(7);
break;
case 3:
// 'I don't think I'd have any use for that'
_logic->joeSpeak(8);
break;
}
}
}
break;
default:
break;
}
}
void Command::changeObjectState(const Verb& action, int16 obj, int16 song, bool cutDone) {
// l.456-533 execute.c
ObjectData *objData = _logic->objectData(obj);
if (action.value() == VERB_OPEN && !cutDone) {
if (State::findOn(objData->state) == STATE_ON_ON) {
State::alterOn(&objData->state, STATE_ON_OFF);
State::alterDefaultVerb(&objData->state, Verb(VERB_NONE));
// play music if it exists... (or SFX for open/close door)
if (song != 0) {
// XXX playsong(abs(song));
}
if (objData->entryObj != 0) {
// if it's a door, then update door that it links to
openOrCloseAssociatedObject(action, objData->entryObj);
objData->entryObj = ABS(objData->entryObj);
}
}
else {
// 'it's already open !'
_logic->joeSpeak(9);
}
}
else if (action.value() == VERB_CLOSE && !cutDone) {
if (State::findOn(objData->state) == STATE_ON_OFF) {
State::alterOn(&objData->state, STATE_ON_ON);
State::alterDefaultVerb(&objData->state, Verb(VERB_OPEN));
// play music if it exists... (or SFX for open/close door)
if (song != 0) {
// XXX playsong(abs(song));
}
if (objData->entryObj != 0) {
// if it's a door, then update door that it links to
openOrCloseAssociatedObject(action, objData->entryObj);
objData->entryObj = -ABS(objData->entryObj);
}
}
else {
// 'it's already closed !'
_logic->joeSpeak(10);
}
}
else if (action.value() == VERB_MOVE) {
State::alterOn(&objData->state, STATE_ON_OFF);
}
}
void Command::cleanupCurrentAction() {
// l.595-597 execute.c
_logic->joeFace();
_oldNoun = 0;
_oldVerb = Verb(VERB_NONE);
}
Verb Command::findDefault(uint16 obj, bool itemType) {
uint16 s = itemType ? _logic->itemData(obj)->state : _logic->objectData(obj)->state;
return State::findDefaultVerb(s);
}
void Command::alterDefault(const Verb& def, bool itemType) {
uint16 *newDefaultState = 0;
const char *name = NULL;
_noun = _logic->findObjectUnderCursor(_selPosX, _selPosY);
if (!itemType) {
if (_noun == 0) {
return;
}
uint16 i = _logic->findObjectGlobalNumber(_noun);
ObjectData *od = _logic->objectData(i);
if (od->name < 0) {
return;
}
newDefaultState = &od->state;
name = _logic->objectTextualDescription(od->name);
}
else {
uint16 item = _logic->findInventoryItem(_verb.inventoryItem());
if (item == 0 || _logic->itemData(item)->name == 0) {
return;
}
ItemData *id = _logic->itemData(item);
newDefaultState = &id->state;
name = _logic->objectTextualDescription(id->name);
}
State::alterDefaultVerb(newDefaultState, def);
if (_noun == 0) {
_cmdText.clear();
}
else {
_cmdText.setVerb(def.isNone() ? Verb(VERB_WALK_TO) : def);
}
_cmdText.displayTemp(INK_CMD_NORMAL, name);
_oldNoun = _noun;
}
void Command::openOrCloseAssociatedObject(const Verb& action, int16 otherObj) {
CmdListData *cmdList = &_cmdList[1];
uint16 com = 0;
uint16 i;
for (i = 1; i <= _numCmdList && com != 0; ++i, ++cmdList) {
if (cmdList->match(action, otherObj, 0)) {
if (cmdList->setConditions) {
CmdGameState *cmdGs = _cmdGameState;
// FIXME: weird loop...
uint16 j;
for (j = 1; j <= _numCmdGameState; ++j) {
if (cmdGs[j].id == i && cmdGs[i].gameStateSlot > 0) {
if (_logic->gameState(cmdGs[i].gameStateSlot) == cmdGs[i].gameStateValue) {
com = i;
break;
}
}
}
}
else {
com = i;
break;
}
}
}
if (com != 0) {
cmdList = &_cmdList[com];
ObjectData *objData = _logic->objectData(otherObj);
if (cmdList->imageOrder != 0) {
// update the graphic image of object
objData->image = cmdList->imageOrder;
}
if (action.value() == VERB_OPEN) {
if (State::findOn(objData->state) == STATE_ON_ON) {
State::alterOn(&objData->state, STATE_ON_OFF);
State::alterDefaultVerb(&objData->state, Verb(VERB_NONE));
objData->entryObj = ABS(objData->entryObj);
}
}
else if (action.value() == VERB_CLOSE) {
if (State::findOn(objData->state) == STATE_ON_OFF) {
State::alterOn(&objData->state, STATE_ON_ON);
State::alterDefaultVerb(&objData->state, Verb(VERB_OPEN));
objData->entryObj = -ABS(objData->entryObj);
}
}
}
}
int16 Command::setConditions(uint16 command, bool lastCmd) {
debug(9, "Command::setConditions(%d, %d)", command, lastCmd);
// Test conditions, if FAIL write && exit, Return -1
// if(Joe speaks before he returns, -2 is returned
// This way a -1 return will allow Joe to speak normal description
uint16 temp[21];
memset(temp, 0, sizeof(temp));
uint16 tempInd = 0;
int16 ret = 0;
uint16 i;
CmdGameState *cmdGs = &_cmdGameState[1];
for (i = 1; i <= _numCmdGameState; ++i, ++cmdGs) {
if (cmdGs->id == command) {
if (cmdGs->gameStateSlot > 0) {
if (_logic->gameState(cmdGs->gameStateSlot) != cmdGs->gameStateValue) {
// failed test
ret = i;
break;
}
}
else {
temp[tempInd] = i;
++tempInd;
}
}
}
if (ret > 0) {
// we've failed, so see if we need to make Joe speak
cmdGs = &_cmdGameState[ret];
if (cmdGs->gameStateValue > 0 && lastCmd) {
// check to see if fail state is in fact a cutaway
const char *objDesc = _logic->objectTextualDescription(cmdGs->speakValue);
if (!executeIfCutaway(objDesc) && !executeIfDialog(objDesc)) {
_logic->joeSpeak(cmdGs->speakValue, true);
}
ret = -2;
}
else {
ret = -1;
}
}
else {
ret = 0;
// all tests were okay, now set gamestates
for (i = 0; i < tempInd; ++i) {
cmdGs = &_cmdGameState[temp[i]];
_logic->gameState(ABS(cmdGs->gameStateSlot), cmdGs->gameStateValue);
// set return value for Joe to say something
ret = cmdGs->speakValue;
}
}
return ret;
}
void Command::setAreas(uint16 command) {
debug(9, "Command::setAreas(%d)", command);
CmdArea *cmdArea = &_cmdArea[1];
uint16 i;
for (i = 1; i <= _numCmdArea; ++i, ++cmdArea) {
if (cmdArea->id == command) {
uint16 areaNum = ABS(cmdArea->area);
Area *area = _logic->area(cmdArea->room, areaNum);
if (cmdArea->area > 0) {
// turn on area
area->mapNeighbours = ABS(area->mapNeighbours);
}
else {
// turn off area
area->mapNeighbours = -ABS(area->mapNeighbours);
}
}
}
}
void Command::setObjects(uint16 command) {
debug(9, "Command::setObjects(%d)", command);
CmdObject *cmdObj = &_cmdObject[1];
uint16 i;
for (i = 1; i <= _numCmdObject; ++i, ++cmdObj) {
if (cmdObj->id == command) {
// found an object
uint16 dstObj = ABS(cmdObj->dstObj);
ObjectData *objData = _logic->objectData(dstObj);
if (cmdObj->dstObj > 0) {
// show the object
objData->name = ABS(objData->name);
// test that the object has not already been deleted
// by checking if it is not equal to zero
if (cmdObj->srcObj == -1 && objData->name != 0) {
// delete object by setting its name to 0 and
// turning off graphic image
objData->name = 0;
if (objData->room == _logic->currentRoom()) {
if (dstObj != _subject1) {
// if the new object we have updated is on screen and is not the
// current object, then we can update. This is because we turn
// current object off ourselves by COM_LIST(com, 8)
if (objData->image != -3 && objData->image != -4) {
// it is a normal object (not a person)
// turn the graphic image off for the object
objData->image = -(objData->image + 10);
}
}
// invalidate object area
uint16 objZone = dstObj - _logic->currentRoomData();
_logic->zoneSet(ZONE_ROOM, objZone, 0, 0, 1, 1);
}
}
if (cmdObj->srcObj > 0) {
// copy data from dummy object to object
int16 image1 = objData->image;
int16 image2 = _logic->objectData(cmdObj->srcObj)->image;
_logic->objectCopy(cmdObj->srcObj, dstObj);
if (image1 != 0 && image2 == 0 && objData->room == _logic->currentRoom()) {
uint16 bobNum = _logic->findBob(dstObj);
if (bobNum != 0) {
_graphics->bobClear(bobNum);
}
}
}
if (dstObj != _subject1) {
// if the new object we have updated is on screen and
// is not current object then update it
_logic->roomRefreshObject(dstObj);
}
}
else {
// hide the object
if (objData->name > 0) {
objData->name = -objData->name;
// may need to turn BOBs off for objects to be hidden on current
// screen ! if the new object we have updated is on screen and
// is not current object then update it
_logic->roomRefreshObject(dstObj);
}
}
}
}
}
void Command::setItems(uint16 command) {
debug(9, "Command::setItems(%d)", command);
CmdInventory *cmdInv = &_cmdInventory[1];
ItemData *items = _logic->itemData(0);
uint16 i;
for (i = 1; i <= _numCmdInventory; ++i, ++cmdInv) {
if (cmdInv->id == command) {
uint16 dstItem = ABS(cmdInv->dstItem);
// found an item
if (cmdInv->dstItem > 0) {
// add item to inventory
if (cmdInv->srcItem > 0) {
// copy data from source item to item
items[dstItem] = items[cmdInv->srcItem];
// enable it
items[dstItem].name = ABS(items[dstItem].name);
}
_logic->inventoryInsertItem(cmdInv->dstItem);
}
else {
// delete item
if (items[dstItem].name > 0) {
_logic->inventoryDeleteItem(dstItem);
}
if (cmdInv->srcItem > 0) {
// copy data from source item to item
items[dstItem] = items[cmdInv->srcItem];
// disable it
items[dstItem].name = -ABS(items[dstItem].name);
}
}
}
}
}
uint16 Command::nextObjectDescription(ObjectDescription* objDesc, uint16 firstDesc) {
// l.69-103 select.c
uint16 i;
uint16 diff = objDesc->lastDescription - firstDesc;
debug(0, "Command::updateNextDescription() - diff = %d, type = %d", diff, objDesc->type);
switch (objDesc->type) {
case 0:
// random type, start with first description
if (objDesc->lastSeenNumber == 0) {
// first time look at called
objDesc->lastSeenNumber = firstDesc;
}
else {
// already displayed first, do a random
i = objDesc->lastSeenNumber;
while (i == objDesc->lastSeenNumber) {
i = firstDesc + Logic::randomizer.getRandomNumber(diff);
}
objDesc->lastSeenNumber = i;
}
break;
case 1:
i = objDesc->lastSeenNumber;
while (i == objDesc->lastSeenNumber) {
i = firstDesc + Logic::randomizer.getRandomNumber(diff);
}
objDesc->lastSeenNumber = i;
break;
case 2:
// sequential, but loop
++objDesc->lastSeenNumber;
if (objDesc->lastSeenNumber > objDesc->lastDescription) {
objDesc->lastSeenNumber = firstDesc;
}
break;
case 3:
// sequential without looping
if (objDesc->lastSeenNumber != objDesc->lastDescription) {
++objDesc->lastSeenNumber;
}
break;
}
return objDesc->lastSeenNumber;
}
void Command::look() {
if (_selectedNoun > 0 && _selectedNoun <= _logic->currentRoomObjMax()) {
uint16 k = _logic->currentRoomData();
if (_logic->objectData(k + _selectedNoun)->entryObj == 0) {
if (_logic->joeWalkTo(_selPosX, _selPosY, true) == -2) {
// 'I can't get close enough to have a look.'
_logic->joeSpeak(13);
}
}
}
// if object type and disabled, don't look
if (_subject1 > 0 && _logic->objectData(_subject1)->name <= 0) {
return;
}
uint16 desc;
if (_subject1 < 0) {
desc = _logic->itemData(ABS(_subject1))->description;
}
else {
desc = _logic->objectData(_subject1)->description;
}
debug(0, "Command::look() - desc = %X, _subject1 = %X", desc, _subject1);
// check to see if the object/item has a series of description
ObjectDescription *objDesc = _logic->objectDescription(1);
uint16 i;
for (i = 1; i <= _logic->objectDescriptionCount(); ++i, ++objDesc) {
if (objDesc->object == _subject1) {
desc = nextObjectDescription(objDesc, desc);
break;
}
}
_logic->joeSpeak(desc, true);
_logic->joeFace();
}
void Command::lookCurrentItem() {
if (_verb.isInventory()) {
uint16 item = _logic->findInventoryItem(_verb.inventoryItem());
if (item != 0) {
ItemData *itemData = _logic->itemData(item);
const char *name = _logic->objectName(itemData->name);
if (_currentAction.isNone()) {
Verb v = State::findDefaultVerb(itemData->state);
_cmdText.setVerb(v.isNone() ? Verb(VERB_LOOK_AT) : v);
}
if (!_defaultVerb.isNone()) {
_cmdText.displayTemp(INK_CMD_LOCK, true, _defaultVerb, name);
}
else {
_cmdText.displayTemp(INK_CMD_NORMAL, name);
}
_oldVerb = _verb;
}
}
}
void Command::lookCurrentRoom() {
_noun = _logic->findObjectUnderCursor(_input->mousePosX(), _input->mousePosY());
if (_logic->joeWalk() == 1) {
return;
}
int16 aObjName = 0;
uint16 k = _logic->currentRoomData();
int16 i = 0;
if (_noun > _logic->currentRoomObjMax()) {
uint16 obj = _logic->currentRoomArea(_noun - _logic->currentRoomObjMax())->object;
aObjName = _logic->objectData(obj)->name;
if (aObjName > 0) {
i = aObjName;
_noun = obj - k;
}
}
else {
i = _logic->objectData(k + _noun)->name;
}
if (_oldNoun == _noun) {
return;
}
// if pointing at an Area then exit
// if the AREA is linked to an object, then dont exit. Find
// the object its linked to && store in AOBJ
if (_noun > _logic->currentRoomObjMax() && aObjName <= 0) {
if (_oldNoun != 0) {
if (!_defaultVerb.isNone()) {
_cmdText.displayTemp(INK_CMD_LOCK, true, _defaultVerb);
}
else if (!_currentAction.isNone()) {
_cmdText.display(INK_CMD_NORMAL);
}
_oldNoun = 0;
return;
}
}
// cyx: useless as (_oldNoun==_noun) already tested
// if (i <= 0 && _oldNoun == _noun) { return; }
if (i <= 0) {
_oldNoun = _noun;
_graphics->textClear(151, 151);
if (!_defaultVerb.isNone()) {
_cmdText.displayTemp(INK_CMD_LOCK, true, _defaultVerb);
}
else if (!_currentAction.isNone()) {
_cmdText.display(INK_CMD_NORMAL);
}
return;
}
// if no command yet selected, then use DEFAULT command, if any
if (_currentAction.isNone()) {
Verb v = State::findDefaultVerb(_logic->objectData(k + _noun)->state);
_cmdText.setVerb(v.isNone() ? Verb(VERB_WALK_TO) : v);
if (_noun == 0) {
_cmdText.clear();
}
}
const char *objName = "";
if (_noun > 0) {
objName = _logic->objectName(i);
}
if (!_defaultVerb.isNone()) {
_cmdText.displayTemp(INK_CMD_LOCK, true, _defaultVerb, objName);
}
else {
_cmdText.displayTemp(INK_CMD_NORMAL, objName);
}
_oldNoun = _noun;
}
void Command::lookCurrentIcon() {
_verb = _logic->findVerbUnderCursor(_input->mousePosX(), _input->mousePosY());
if (_verb != _oldVerb && _logic->joeWalk() != 1) {
if (_currentAction.isNone()) {
_cmdText.clear();
}
_graphics->textClear(151, 151);
lookCurrentItem();
// ensure that registers when move to top screen
if (_noun > 0) {
_oldNoun = -1;
}
_oldVerb = _verb;
if (_verb.isPanelCommand() || _verb.value() == VERB_WALK_TO) {
if (_verb.isNone()) {
_cmdText.display(INK_CMD_NORMAL);
}
else {
_cmdText.displayTemp(INK_CMD_NORMAL, false, _verb);
}
}
}
}
}