Logo Search packages:      
Sourcecode: scummvm version File versions

command.cpp

/* ScummVM - Scumm Interpreter
 * Copyright (C) 2003-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL: https://svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-9-1/engines/queen/command.cpp $
 * $Id: command.cpp 22785 2006-05-30 20:46:11Z cyx $
 *
 */

#include "common/stdafx.h"
#include "queen/command.h"

#include "queen/display.h"
#include "queen/input.h"
#include "queen/graphics.h"
#include "queen/grid.h"
#include "queen/logic.h"
#include "queen/queen.h"
#include "queen/resource.h"
#include "queen/sound.h"
#include "queen/state.h"
#include "queen/walk.h"

namespace Queen {

CmdText::CmdText(bool reversed, uint8 y, QueenEngine *vm)
      : _isReversed(reversed), _y(y), _vm(vm) {
      clear();
}

void CmdText::clear() {
      memset(_command, 0, sizeof(_command));
}

void CmdText::display(uint8 color) {
      _vm->display()->textCurrentColor(color);
      _vm->display()->setTextCentered(_y, _command, false);
}

void CmdText::displayTemp(uint8 color, Verb v, const char *name, bool outlined) {
      char temp[MAX_COMMAND_LEN] = "";
      if (_isReversed) {
            if (name != NULL)
                  sprintf(temp, "%s ", name);
            strcat(temp, _vm->logic()->verbName(v));
      } else {
            strcpy(temp, _vm->logic()->verbName(v));
            if (name != NULL) {
                  strcat(temp, " ");
                  strcat(temp, name);
            }
      }
      _vm->display()->textCurrentColor(color);
      _vm->display()->setTextCentered(_y, temp, outlined);
}

void CmdText::displayTemp(uint8 color, const char *name, bool outlined) {
      char temp[MAX_COMMAND_LEN];
      if (_isReversed)
            sprintf(temp, "%s %s", name, _command);
      else
            sprintf(temp, "%s %s", _command, name);
      _vm->display()->textCurrentColor(color);
      _vm->display()->setTextCentered(_y, temp, outlined);
}

void CmdText::setVerb(Verb v) {
      strcpy(_command, _vm->logic()->verbName(v));
}

void CmdText::addLinkWord(Verb v) {
      if (_isReversed) {
            char temp[MAX_COMMAND_LEN];

            strcpy(temp, _command);
            strcpy(_command, _vm->logic()->verbName(v));
            strcat(_command, " ");
            strcat(_command, temp);
      } else {
            strcat(_command, " ");
            strcat(_command, _vm->logic()->verbName(v));
      }
}

void CmdText::addObject(const char *objName) {
      if (_isReversed) {
            char temp[MAX_COMMAND_LEN];

            strcpy(temp, _command);
            strcpy(_command, objName);
            strcat(_command, " ");
            strcat(_command, temp);
      } else {
            strcat(_command, " ");
            strcat(_command, objName);
      }
}

bool CmdText::isEmpty() const {
      return _command[0] == 0;
}

void CmdState::init() {
      commandLevel = 1;
      oldVerb = verb = action = VERB_NONE;
      oldNoun = noun = subject[0] = subject[1] = 0;

      selAction = VERB_NONE;
      selNoun = 0;
}

Command::Command(QueenEngine *vm)
      : _cmdList(NULL), _cmdArea(NULL), _cmdObject(NULL), _cmdInventory(NULL), _cmdGameState(NULL),
      _cmdText((vm->resource()->getLanguage() == HEBREW), CmdText::COMMAND_Y_POS, vm), _vm(vm) {
}

Command::~Command() {
      delete[] _cmdList;
      delete[] _cmdArea;
      delete[] _cmdObject;
      delete[] _cmdInventory;
      delete[] _cmdGameState;
}

void Command::clear(bool clearTexts) {
      debug(6, "Command::clear(%d)", clearTexts);
      _cmdText.clear();
      if (clearTexts) {
            _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
      }
      _parse = false;
      _state.init();
}

void Command::executeCurrentAction() {
      _vm->logic()->entryObj(0);

      if (_mouseKey == Input::MOUSE_RBUTTON && _state.subject[0] > 0) {

            ObjectData *od = _vm->logic()->objectData(_state.subject[0]);
            if (od == NULL || od->name <= 0) {
                  cleanupCurrentAction();
                  return;
            }

            _state.verb = State::findDefaultVerb(od->state);
            _state.selAction = (_state.verb == VERB_NONE) ? VERB_WALK_TO : _state.verb;
            _cmdText.setVerb(_state.selAction);
            _cmdText.addObject(_vm->logic()->objectName(od->name));
      }

      // always highlight the current command when actioned
      _cmdText.display(INK_CMD_SELECT);

      _state.selNoun = _state.noun;
      _state.commandLevel = 1;

      if (handleWrongAction()) {
            cleanupCurrentAction();
            return;
      }

      // get the commands associated with this object/item
      uint16 comMax = 0;
      uint16 matchingCmds[MAX_MATCHING_CMDS];
      CmdListData *cmdList = &_cmdList[1];
      uint16 i;
      for (i = 1; i <= _numCmdList; ++i, ++cmdList) {
            if (cmdList->match(_state.selAction, _state.subject[0], _state.subject[1])) {
                  assert(comMax < MAX_MATCHING_CMDS);
                  matchingCmds[comMax] = i;
                  ++comMax;
            }
      }

      debug(6, "Command::executeCurrentAction() - comMax=%d subj1=%X subj2=%X", comMax, _state.subject[0], _state.subject[1]);

      if (comMax == 0) {
            sayInvalidAction(_state.selAction, _state.subject[0], _state.subject[1]);
            clear(true);
            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;
      for (i = 1; i <= comMax; ++i) {

            comId = matchingCmds[i - 1];

            // WORKAROUND bug #1497280: This command is triggered in room 56 (the
            // room with two waterfalls in the maze part of the game) if the user
            // tries to walk through the left waterfall (object 423).
            //
            // Normally, this would move Joe to room 101 on the upper level and
            // start a cutscene. Joe would notice that Yan has been trapped (on
            // the lower level of the same room). The problem would then appear :
            // Joe is stuck behind the waterfall due to a walkbox issue. We could
            // fix the walkbox issue, but then Joe would walk through the waterfall
            // which wouldn't look that nice, graphically.
            //          
            // Since this command isn't necessary to complete the game and doesn't
            // really makes sense here, we just skip it for now. The same cutscene
            // is already played in command 648, so the user don't miss anything
            // from the story/experience pov.
            //
            // Note: this happens with the original engine, too.

            if (comId == 649) {
                  continue;
            }
                        
            com = &_cmdList[comId];

            // check the Gamestates and set them if necessary
            cond = 0;
            if (com->setConditions) {
                  cond = setConditions(comId, (i == comMax));
            }

            if (cond == -1 && i == comMax) {
                  // only exit on a condition fail if at last command
                  // Joe hasnt spoken, so do normal LOOK command
                  break;
            } else if (cond == -2 && i == 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
                  cond = executeCommand(comId, cond);
                  break;
            }
      }

      if (_state.selAction == VERB_USE_JOURNAL) {
            clear(true);
      } else {
            if (cond <= 0 && _state.selAction == VERB_LOOK_AT) {
                  lookAtSelectedObject();
            } else {
                  // only play song if it's a PLAY AFTER type
                  if (com->song < 0) {
                        _vm->sound()->playSong(-com->song);
                  }
                  clear(true);
            }
            cleanupCurrentAction();
      }
}

void Command::updatePlayer() {
      if (_vm->logic()->joeWalk() != JWM_MOVE) {
            int16 cx = _vm->input()->mousePosX();
            int16 cy = _vm->input()->mousePosY();
            lookForCurrentObject(cx, cy);
            lookForCurrentIcon(cx, cy);
      }

      if (_vm->input()->keyVerb() != VERB_NONE) {
            if (_vm->input()->keyVerb() == VERB_USE_JOURNAL) {
                  _vm->logic()->useJournal();
            } else if (_vm->input()->keyVerb() != VERB_SKIP_TEXT) {
                  _state.verb = _vm->input()->keyVerb();
                  if (isVerbInv(_state.verb)) {
                        _state.noun = _state.selNoun = 0;
                        _state.oldNoun = 0;
                        _state.oldVerb = VERB_NONE;
                        grabSelectedItem();
                  } else {
                        grabSelectedVerb();
                  }
            }
            _vm->input()->clearKeyVerb();
      }

      _mouseKey = _vm->input()->mouseButton();
      _vm->input()->clearMouseButton();
      if (_mouseKey > 0) {
            grabCurrentSelection();
      }
}

void Command::readCommandsFrom(byte *&ptr) {
      uint16 i;

      _numCmdList = READ_BE_UINT16(ptr); ptr += 2;
      _cmdList = new CmdListData[_numCmdList + 1];
      if (_numCmdList == 0) {
            _cmdList[0].readFromBE(ptr);
      } else {
            memset(&_cmdList[0], 0, sizeof(CmdListData));
            for (i = 1; i <= _numCmdList; i++) {
                  _cmdList[i].readFromBE(ptr);
            }
      }

      _numCmdArea = READ_BE_UINT16(ptr); ptr += 2;
      _cmdArea = new CmdArea[_numCmdArea + 1];
      if (_numCmdArea == 0) {
            _cmdArea[0].readFromBE(ptr);
      } else {
            memset(&_cmdArea[0], 0, sizeof(CmdArea));
            for (i = 1; i <= _numCmdArea; i++) {
                  _cmdArea[i].readFromBE(ptr);
            }
      }

      _numCmdObject = READ_BE_UINT16(ptr); ptr += 2;
      _cmdObject = new CmdObject[_numCmdObject + 1];
      if (_numCmdObject == 0) {
            _cmdObject[0].readFromBE(ptr);
      } else {
            memset(&_cmdObject[0], 0, sizeof(CmdObject));
            for (i = 1; i <= _numCmdObject; i++) {
                  _cmdObject[i].readFromBE(ptr);
            }
      }

      _numCmdInventory = READ_BE_UINT16(ptr);   ptr += 2;
      _cmdInventory = new CmdInventory[_numCmdInventory + 1];
      if (_numCmdInventory == 0) {
            _cmdInventory[0].readFromBE(ptr);
      } else {
            memset(&_cmdInventory[0], 0, sizeof(CmdInventory));
            for (i = 1; i <= _numCmdInventory; i++) {
                  _cmdInventory[i].readFromBE(ptr);
            }
      }

      _numCmdGameState = READ_BE_UINT16(ptr);   ptr += 2;
      _cmdGameState = new CmdGameState[_numCmdGameState + 1];
      if (_numCmdGameState == 0) {
            _cmdGameState[0].readFromBE(ptr);
      } else {
            memset(&_cmdGameState[0], 0, sizeof(CmdGameState));
            for (i = 1; i <= _numCmdGameState; i++) {
                  _cmdGameState[i].readFromBE(ptr);
            }
      }
}

ObjectData *Command::findObjectData(uint16 objRoomNum) const {
      ObjectData *od = NULL;
      if (objRoomNum != 0) {
            objRoomNum += _vm->logic()->currentRoomData();
            od = _vm->logic()->objectData(objRoomNum);
      }
      return od;
}

ItemData *Command::findItemData(Verb invNum) const {
      ItemData *id = NULL;
      uint16 itNum = _vm->logic()->findInventoryItem(invNum - VERB_INV_FIRST);
      if (itNum != 0) {
            id = _vm->logic()->itemData(itNum);
      }
      return id;
}

int16 Command::executeCommand(uint16 comId, int16 condResult) {
      // execute.c l.313-452
      debug(6, "Command::executeCommand() - cond = %X, com = %X", condResult, comId);

      CmdListData *com = &_cmdList[comId];

      if (com->setAreas) {
            setAreas(comId);
      }

      // don't try to grab if action is TALK or WALK
      if (_state.selAction != VERB_TALK_TO && _state.selAction != VERB_WALK_TO) {
            int i;
            for  (i = 0; i < 2; ++i) {
                  int16 obj = _state.subject[i];
                  if (obj > 0) {
                        _vm->logic()->joeGrab(State::findGrab(_vm->logic()->objectData(obj)->state));
                  }
            }
      }

      bool cutDone = false;
      if (condResult > 0) {
            // check for cutaway/dialogs before updating Objects
            const char *desc = _vm->logic()->objectTextualDescription(condResult);
            if (executeIfCutaway(desc)) {
                  condResult = 0;
                  cutDone = true;
            } else if (executeIfDialog(desc)) {
                  condResult = 0;
            }
      }

      int16 oldImage = 0;
      if (_state.subject[0] > 0) {
            // an object (not an item)
            oldImage = _vm->logic()->objectData(_state.subject[0])->image;
      }

      if (com->setObjects) {
            setObjects(comId);
      }

      if (com->setItems) {
            setItems(comId);
      }

      if (com->imageOrder != 0 && _state.subject[0] > 0) {
            ObjectData *od = _vm->logic()->objectData(_state.subject[0]);
            // 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;
            }
            _vm->graphics()->refreshObject(_state.subject[0]);
      } else {
            // this object is not being updated by command list, see if
            // it has another image copied to it
            if (_state.subject[0] > 0) {
                  // an object (not an item)
                  if (_vm->logic()->objectData(_state.subject[0])->image != oldImage) {
                        _vm->graphics()->refreshObject(_state.subject[0]);
                  }
            }
      }

      // don't play music on an OPEN/CLOSE command - in case the command fails
      if (_state.selAction != VERB_NONE &&
            _state.selAction != VERB_OPEN &&
            _state.selAction != VERB_CLOSE) {
            // only play song if it's a PLAY BEFORE type
            if (com->song > 0) {
                  _vm->sound()->playSong(com->song);
            }
      }

      // do a special hardcoded section
      // l.419-452 execute.c
      switch (com->specialSection) {
      case 1:
            _vm->logic()->useJournal();
            _state.selAction = VERB_USE_JOURNAL;
            return condResult;
      case 2:
            _vm->logic()->joeUseDress(true);
            break;
      case 3:
            _vm->logic()->joeUseClothes(true);
            break;
      case 4:
            _vm->logic()->joeUseUnderwear();
            break;
      }

      if (_state.subject[0] > 0)
            changeObjectState(_state.selAction, _state.subject[0], com->song, cutDone);

      if (condResult > 0) {
            _vm->logic()->makeJoeSpeak(condResult, true);
      }
      return condResult;
}

int16 Command::makeJoeWalkTo(int16 x, int16 y, int16 objNum, Verb v, bool mustWalk) {
      // Check to see if object is actually an exit to another
      // room. If so, then set up new room
      ObjectData *objData = _vm->logic()->objectData(objNum);
      if (objData->x != 0 || objData->y != 0) {
            x = objData->x;
            y = objData->y;
      }
      if (v == VERB_WALK_TO) {
            _vm->logic()->entryObj(objData->entryObj);
            if (objData->entryObj > 0) {
                  _vm->logic()->newRoom(_vm->logic()->objectData(objData->entryObj)->room);
                  // because this is an exit object, see if there is
                  // a walk off point and set (x,y) accordingly
                  WalkOffData *wod = _vm->logic()->walkOffPointForObject(objNum);
                  if (wod != NULL) {
                        x = wod->x;
                        y = wod->y;
                  }
            }
      } else {
            _vm->logic()->entryObj(0);
            _vm->logic()->newRoom(0);
      }

      debug(6, "Command::makeJoeWalkTo() - x=%d y=%d newRoom=%d", x, y, _vm->logic()->newRoom());

      int16 p = 0;
      if (mustWalk) {
            // determine which way for Joe to face Object
            uint16 facing = State::findDirection(objData->state);
            BobSlot *bobJoe = _vm->graphics()->bob(0);
            if (x == bobJoe->x && y == bobJoe->y) {
                  _vm->logic()->joeFacing(facing);
                  _vm->logic()->joeFace();
            } else {
                  p = _vm->walk()->moveJoe(facing, x, y, false);
                  if (p != 0) {
                        _vm->logic()->newRoom(0); // cancel makeJoeWalkTo, that should be equivalent to cr10 fix
                  }
            }
      }
      return p;
}

void Command::grabCurrentSelection() {
      _selPosX = _vm->input()->mousePosX();
      _selPosY = _vm->input()->mousePosY();

      uint16 zone = _vm->grid()->findObjectUnderCursor(_selPosX, _selPosY);
      _state.noun = _vm->grid()->findObjectNumber(zone);
      _state.verb = _vm->grid()->findVerbUnderCursor(_selPosX, _selPosY);

      _selPosX += _vm->display()->horizontalScroll();

      if (isVerbAction(_state.verb) || isVerbInvScroll(_state.verb)) {
            grabSelectedVerb();
      } else if (isVerbInv(_state.verb)) {
            grabSelectedItem();
      } else if (_state.noun != 0) {
            grabSelectedNoun();
      } else if (_selPosY < ROOM_ZONE_HEIGHT && _state.verb == VERB_NONE) {
            // select without a command, do a WALK
            clear(true);
            _vm->logic()->joeWalk(JWM_EXECUTE);
      }
}

void Command::grabSelectedObject(int16 objNum, uint16 objState, uint16 objName) {
      if (_state.action != VERB_NONE) {
            _cmdText.addObject(_vm->logic()->objectName(objName));
      }

      _state.subject[_state.commandLevel - 1] = objNum;

      // if first noun and it's a 2 level command then set up action word
      if (_state.action == VERB_USE && _state.commandLevel == 1) {
            if (State::findUse(objState) == STATE_USE_ON) {
                  // object supports 2 levels, command not fully constructed
                  _state.commandLevel = 2;
                  _cmdText.addLinkWord(VERB_PREP_WITH);
                  _cmdText.display(INK_CMD_NORMAL);
                  _parse = false;
            } else {
                  _parse = true;
            }
      } else if (_state.action == VERB_GIVE && _state.commandLevel == 1) {
            // command not fully constructed
            _state.commandLevel = 2;
            _cmdText.addLinkWord(VERB_PREP_TO);
            _cmdText.display(INK_CMD_NORMAL);
            _parse = false;
      } else {
            _parse = true;
      }

      if (_parse) {
            _state.verb = VERB_NONE;
            _vm->logic()->joeWalk(JWM_EXECUTE);
            _state.selAction = _state.action;
            _state.action = VERB_NONE;
      }
}

void Command::grabSelectedItem() {
      ItemData *id = findItemData(_state.verb);
      if (id == NULL || id->name <= 0) {
            return;
      }

      int16 item = _vm->logic()->findInventoryItem(_state.verb - VERB_INV_FIRST);

      // If we've selected via keyboard, and there is no VERB then do
      // the ITEMs default, otherwise keep constructing!

      if (_mouseKey == Input::MOUSE_LBUTTON ||
            (_vm->input()->keyVerb() != VERB_NONE && _state.verb != VERB_NONE)) {
            if (_state.action == VERB_NONE) {
                  if (_vm->input()->keyVerb() != VERB_NONE) {
                        // We've selected via the keyboard, no command is being
                        // constructed, so we shall find the item's default
                        _state.verb = State::findDefaultVerb(id->state);
                        if (_state.verb == VERB_NONE) {
                              // set to Look At
                              _state.verb = VERB_LOOK_AT;
                              _cmdText.setVerb(VERB_LOOK_AT);
                        }
                        _state.action = _state.verb;
                  } else {
                        // Action>0 ONLY if command has been constructed
                        // Left Mouse Button pressed just do Look At
                        _state.action = VERB_LOOK_AT;
                        _cmdText.setVerb(VERB_LOOK_AT);
                  }
            }
            _state.verb = VERB_NONE;
      } else {
            if (_cmdText.isEmpty()) {
                  _state.verb = VERB_LOOK_AT;
                  _state.action = VERB_LOOK_AT;
                  _cmdText.setVerb(VERB_LOOK_AT);
            } else {
                  if (_state.commandLevel == 2 && _parse)
                        _state.verb = _state.action;
                  else
                        _state.verb = State::findDefaultVerb(id->state);
                  if (_state.verb == VERB_NONE) {
                        // No match made, so command not yet completed. Redefine as LOOK AT
                        _state.action = VERB_LOOK_AT;
                        _cmdText.setVerb(VERB_LOOK_AT);
                  } else {
                        _state.action = _state.verb;
                  }
                  _state.verb = VERB_NONE;
            }
      }

      grabSelectedObject(-item, id->state, id->name);
}

void Command::grabSelectedNoun() {
      ObjectData *od = findObjectData(_state.noun);
      if (od == NULL || od->name <= 0) {
            // selected a turned off object, so just walk
            clear(true);
            _state.noun = 0;
            _vm->logic()->joeWalk(JWM_EXECUTE);
            return;
      }

      if (_state.verb == VERB_NONE) {
            if (_mouseKey == Input::MOUSE_LBUTTON) {
                  if ((_state.commandLevel != 2 && _state.action == VERB_NONE) ||
                        (_state.commandLevel == 2 && _parse)) {
                              _state.verb = VERB_WALK_TO;
                              _state.action = VERB_WALK_TO;
                              _cmdText.setVerb(VERB_WALK_TO);
                  }
            } else if (_mouseKey == Input::MOUSE_RBUTTON) {
                  if (_cmdText.isEmpty()) {
                        _state.verb = State::findDefaultVerb(od->state);
                        _state.selAction = (_state.verb == VERB_NONE) ? VERB_WALK_TO : _state.verb;
                        _cmdText.setVerb(_state.selAction);
                        _cmdText.addObject(_vm->logic()->objectName(od->name));
                  } else {
                        if ((_state.commandLevel == 2 && !_parse) || _state.action != VERB_NONE) {
                              _state.verb = _state.action;
                        } else {
                              _state.verb = State::findDefaultVerb(od->state);
                        }
                        _state.action = (_state.verb == VERB_NONE) ? VERB_WALK_TO : _state.verb;
                        _state.verb = VERB_NONE;
                  }
            }
      }

      _state.selNoun = 0;
      int16 objNum = _vm->logic()->currentRoomData() + _state.noun;
      grabSelectedObject(objNum, od->state, od->name);
}

void Command::grabSelectedVerb() {
      if (isVerbInvScroll(_state.verb)) {
            // move through inventory (by four if right mouse button)
            uint16 scroll = (_mouseKey == Input::MOUSE_RBUTTON) ? 4 : 1;
            _vm->logic()->inventoryScroll(scroll, _state.verb == VERB_SCROLL_UP);
      } else {
            _state.action = _state.verb;
            _state.subject[0] = 0;
            _state.subject[1] = 0;

            if (_vm->logic()->joeWalk() == JWM_MOVE && _state.verb != VERB_NONE) {
                  _vm->logic()->joeWalk(JWM_NORMAL);
            }
            _state.commandLevel = 1;
            _state.oldVerb = VERB_NONE;
            _state.oldNoun = 0;
            _cmdText.setVerb(_state.verb);
            _cmdText.display(INK_CMD_NORMAL);
      }
}

bool Command::executeIfCutaway(const char *description) {
      if (strlen(description) > 4 &&
            scumm_stricmp(description + strlen(description) - 4, ".cut") == 0) {

            _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);

            char nextCutaway[20];
            memset(nextCutaway, 0, sizeof(nextCutaway));
            _vm->logic()->playCutaway(description, nextCutaway);
            while (nextCutaway[0] != '\0') {
                  _vm->logic()->playCutaway(nextCutaway, nextCutaway);
            }
            return true;
      }
      return false;
}

bool Command::executeIfDialog(const char *description) {
      if (strlen(description) > 4 &&
            scumm_stricmp(description + strlen(description) - 4, ".dog") == 0) {

            _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);

            char cutaway[20];
            memset(cutaway, 0, sizeof(cutaway));
            _vm->logic()->startDialogue(description, _state.selNoun, cutaway);

            while (cutaway[0] != '\0') {
                  char currentCutaway[20];
                  strcpy(currentCutaway, cutaway);
                  _vm->logic()->playCutaway(currentCutaway, cutaway);
            }
            return true;
      }
      return false;
}

bool Command::handleWrongAction() {
      // l.96-141 execute.c
      uint16 objMax = _vm->grid()->objMax(_vm->logic()->currentRoom());
      uint16 roomData = _vm->logic()->currentRoomData();

      // select without a command or WALK TO ; do a WALK
      if ((_state.selAction == VERB_WALK_TO || _state.selAction == VERB_NONE) &&
            (_state.selNoun > objMax || _state.selNoun == 0)) {
            if (_state.selAction == VERB_NONE) {
                  _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
            }
            _vm->walk()->moveJoe(0, _selPosX, _selPosY, false);
            return true;
      }

      // check to see if one of the objects is hidden
      int i;
      for (i = 0; i < 2; ++i) {
            int16 obj = _state.subject[i];
            if (obj > 0 && _vm->logic()->objectData(obj)->name <= 0) {
                  return true;
            }
      }

      // check for USE command on exists
      if (_state.selAction == VERB_USE &&
            _state.subject[0] > 0 && _vm->logic()->objectData(_state.subject[0])->entryObj > 0) {
            _state.selAction = VERB_WALK_TO;
      }

      if (_state.selNoun > 0 && _state.selNoun <= objMax) {
            uint16 objNum = roomData + _state.selNoun;
            if (makeJoeWalkTo(_selPosX, _selPosY, objNum, _state.selAction, true) != 0) {
                  return true;
            }
            if (_state.selAction == VERB_WALK_TO && _vm->logic()->objectData(objNum)->entryObj < 0) {
                  return true;
            }
      }
      return false;
}

void Command::sayInvalidAction(Verb action, int16 subj1, int16 subj2) {
      // l.158-272 execute.c
      switch (action) {

      case VERB_LOOK_AT:
            lookAtSelectedObject();
            break;

      case VERB_OPEN:
            // 'it doesn't seem to open'
            _vm->logic()->makeJoeSpeak(1);
            break;

      case VERB_USE:
            if (subj1 < 0) {
                  uint16 k = _vm->logic()->itemData(-subj1)->sfxDescription;
                  if (k > 0) {
                        _vm->logic()->makeJoeSpeak(k, true);
                  } else {
                        _vm->logic()->makeJoeSpeak(2);
                  }
            } else {
                  _vm->logic()->makeJoeSpeak(2);
            }
            break;

      case VERB_TALK_TO:
            _vm->logic()->makeJoeSpeak(24 + _vm->randomizer.getRandomNumber(2));
            break;

      case VERB_CLOSE:
            _vm->logic()->makeJoeSpeak(2);
            break;

      case VERB_MOVE:
            // 'I can't move it'
            if (subj1 > 0) {
                  int16 img = _vm->logic()->objectData(subj1)->image;
                  if (img == -4 || img == -3) {
                        _vm->logic()->makeJoeSpeak(18);
                  } else {
                        _vm->logic()->makeJoeSpeak(3);
                  }
            } else {
                  _vm->logic()->makeJoeSpeak(3);
            }
            break;

      case VERB_GIVE:
            // 'I can't give the subj1 to subj2'
            if (subj1 < 0) {
                  if (subj2 > 0) {
                        int16 img = _vm->logic()->objectData(subj2)->image;
                        if (img == -4 || img == -3) {
                              _vm->logic()->makeJoeSpeak(27 + _vm->randomizer.getRandomNumber(2));
                        }
                  } else {
                        _vm->logic()->makeJoeSpeak(11);
                  }
            } else {
                  _vm->logic()->makeJoeSpeak(12);
            }
            break;

      case VERB_PICK_UP:
            if (subj1 < 0) {
                  _vm->logic()->makeJoeSpeak(14);
            } else {
                  int16 img = _vm->logic()->objectData(subj1)->image;
                  if (img == -4 || img == -3) {
                        // Trying to get a person
                        _vm->logic()->makeJoeSpeak(20);
                  } else {
                        // 5 : 'I can't pick that up'
                        // 6 : 'I don't think I need that'
                        // 7 : 'I'd rather leave it here'
                        // 8 : 'I don't think I'd have any use for that'
                        _vm->logic()->makeJoeSpeak(5 + _vm->randomizer.getRandomNumber(3));
                  }
            }
            break;

      default:
            break;
      }
}

void Command::changeObjectState(Verb action, int16 obj, int16 song, bool cutDone) {
      // l.456-533 execute.c
      ObjectData *objData = _vm->logic()->objectData(obj);

      if (action == VERB_OPEN && !cutDone) {
            if (State::findOn(objData->state) == STATE_ON_ON) {
                  State::alterOn(&objData->state, STATE_ON_OFF);
                  State::alterDefaultVerb(&objData->state, VERB_NONE);

                  // play music if it exists... (or SFX for open/close door)
                  if (song != 0) {
                        _vm->sound()->playSong(ABS(song));
                  }

                  if (objData->entryObj != 0) {
                        // if it's a door, then update door that it links to
                        openOrCloseAssociatedObject(action, ABS(objData->entryObj));
                        objData->entryObj = ABS(objData->entryObj);
                  }
            } else {
                  // 'it's already open !'
                  _vm->logic()->makeJoeSpeak(9);
            }
      } else if (action == VERB_CLOSE && !cutDone) {
            if (State::findOn(objData->state) == STATE_ON_OFF) {
                  State::alterOn(&objData->state, STATE_ON_ON);
                  State::alterDefaultVerb(&objData->state, VERB_OPEN);

                  // play music if it exists... (or SFX for open/close door)
                  if (song != 0) {
                        _vm->sound()->playSong(ABS(song));
                  }

                  if (objData->entryObj != 0) {
                        // if it's a door, then update door that it links to
                        openOrCloseAssociatedObject(action, ABS(objData->entryObj));
                        objData->entryObj = -ABS(objData->entryObj);
                  }
            } else {
                  // 'it's already closed !'
                  _vm->logic()->makeJoeSpeak(10);
            }
      } else if (action == VERB_MOVE) {
            State::alterOn(&objData->state, STATE_ON_OFF);
      }
}

void Command::cleanupCurrentAction() {
      // l.595-597 execute.c
      _vm->logic()->joeFace();
      _state.oldNoun = 0;
      _state.oldVerb = VERB_NONE;
}

void Command::openOrCloseAssociatedObject(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;
                        uint16 j;
                        for (j = 1; j <= _numCmdGameState; ++j) {
                              if (cmdGs[j].id == i && cmdGs[j].gameStateSlot > 0) {
                                    if (_vm->logic()->gameState(cmdGs[j].gameStateSlot) == cmdGs[j].gameStateValue) {
                                          com = i;
                                          break;
                                    }
                              }
                        }
                  } else {
                        com = i;
                        break;
                  }
            }
      }

      if (com != 0) {

            debug(6, "Command::openOrCloseAssociatedObject() com=%X", com);

            cmdList = &_cmdList[com];
            ObjectData *objData = _vm->logic()->objectData(otherObj);

            if (cmdList->imageOrder != 0) {
                  objData->image = cmdList->imageOrder;
            }

            if (action == VERB_OPEN) {
                  if (State::findOn(objData->state) == STATE_ON_ON) {
                        State::alterOn(&objData->state, STATE_ON_OFF);
                        State::alterDefaultVerb(&objData->state, VERB_NONE);
                        objData->entryObj = ABS(objData->entryObj);
                  }
            } else if (action == VERB_CLOSE) {
                  if (State::findOn(objData->state) == STATE_ON_OFF) {
                        State::alterOn(&objData->state, STATE_ON_ON);
                        State::alterDefaultVerb(&objData->state, VERB_OPEN);
                        objData->entryObj = -ABS(objData->entryObj);
                  }
            }
      }
}

int16 Command::setConditions(uint16 command, bool lastCmd) {
      debug(9, "Command::setConditions(%d, %d)", command, lastCmd);

      int16 ret = 0;
      uint16 cmdState[21];
      memset(cmdState, 0, sizeof(cmdState));
      uint16 cmdStateCount = 0;
      uint16 i;
      CmdGameState *cmdGs = &_cmdGameState[1];
      for (i = 1; i <= _numCmdGameState; ++i, ++cmdGs) {
            if (cmdGs->id == command) {
                  if (cmdGs->gameStateSlot > 0) {
                        if (_vm->logic()->gameState(cmdGs->gameStateSlot) != cmdGs->gameStateValue) {
                              debug(6, "Command::setConditions() - GS[%d] == %d (should be %d)", cmdGs->gameStateSlot, _vm->logic()->gameState(cmdGs->gameStateSlot), cmdGs->gameStateValue);
                              // failed test
                              ret = i;
                              break;
                        }
                  } else {
                        cmdState[cmdStateCount] = i;
                        ++cmdStateCount;
                  }
            }
      }

      if (ret > 0) {
            // we've failed, so see if we need to make Joe speak
            cmdGs = &_cmdGameState[ret];
            if (cmdGs->speakValue > 0 && lastCmd) {
                  // check to see if fail state is in fact a cutaway
                  const char *objDesc = _vm->logic()->objectTextualDescription(cmdGs->speakValue);
                  if (!executeIfCutaway(objDesc) && !executeIfDialog(objDesc)) {
                        _vm->logic()->makeJoeSpeak(cmdGs->speakValue, true);
                  }
                  ret = -2;
            } else {
                  // return -1 so Joe will be able to speak a normal description
                  ret = -1;
            }
      } else {
            ret = 0;
            // all tests were okay, now set gamestates
            for (i = 0; i < cmdStateCount; ++i) {
                  cmdGs = &_cmdGameState[cmdState[i]];
                  _vm->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];
      for (uint16 i = 1; i <= _numCmdArea; ++i, ++cmdArea) {
            if (cmdArea->id == command) {
                  uint16 areaNum = ABS(cmdArea->area);
                  Area *area = _vm->grid()->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];
      for (uint16 i = 1; i <= _numCmdObject; ++i, ++cmdObj) {
            if (cmdObj->id == command) {

                  // found an object
                  uint16 dstObj = ABS(cmdObj->dstObj);
                  ObjectData *objData = _vm->logic()->objectData(dstObj);

                  debug(6, "Command::setObjects() - dstObj=%X srcObj=%X _state.subject[0]=%X", cmdObj->dstObj, cmdObj->srcObj, _state.subject[0]);

                  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 == _vm->logic()->currentRoom()) {
                                    if (dstObj != _state.subject[0]) {
                                          // 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 - _vm->logic()->currentRoomData();
                                    _vm->grid()->setZone(GS_ROOM, objZone, 0, 0, 1, 1);
                              }
                        }

                        if (cmdObj->srcObj > 0) {
                              // copy data from dummy object to object
                              int16 image1 = objData->image;
                              int16 image2 = _vm->logic()->objectData(cmdObj->srcObj)->image;
                              _vm->logic()->objectCopy(cmdObj->srcObj, dstObj);
                              if (image1 != 0 && image2 == 0 && objData->room == _vm->logic()->currentRoom()) {
                                    uint16 bobNum = _vm->logic()->findBob(dstObj);
                                    if (bobNum != 0) {
                                          _vm->graphics()->bob(bobNum)->clear();
                                    }
                              }
                        }

                        if (dstObj != _state.subject[0]) {
                              // if the new object we have updated is on screen and
                              // is not current object then update it
                              _vm->graphics()->refreshObject(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
                              _vm->graphics()->refreshObject(dstObj);
                        }
                  }
            }
      }
}

void Command::setItems(uint16 command) {
      debug(9, "Command::setItems(%d)", command);

      ItemData *items = _vm->logic()->itemData(0);
      CmdInventory *cmdInv = &_cmdInventory[1];
      for (uint16 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, then enable it
                              items[dstItem] = items[cmdInv->srcItem];
                              items[dstItem].name = ABS(items[dstItem].name);
                        }
                        _vm->logic()->inventoryInsertItem(cmdInv->dstItem);
                  } else {
                        // delete item
                        if (items[dstItem].name > 0) {
                              _vm->logic()->inventoryDeleteItem(dstItem);
                        }
                        if (cmdInv->srcItem > 0) {
                              // copy data from source item to item, then disable it
                              items[dstItem] = items[cmdInv->srcItem];
                              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(6, "Command::nextObjectDescription() - 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;
                  break;
            }
            // already displayed first, do a random
      case 1:
            i = objDesc->lastSeenNumber;
            while (i == objDesc->lastSeenNumber) {
                  i = firstDesc + _vm->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::lookAtSelectedObject() {
      uint16 desc;
      if (_state.subject[0] < 0) {
            desc = _vm->logic()->itemData(-_state.subject[0])->description;
      } else {
            ObjectData *objData = _vm->logic()->objectData(_state.subject[0]);
            if (objData->name <= 0) {
                  return;
            }
            desc = objData->description;
      }

      debug(6, "Command::lookAtSelectedObject() - desc = %X, _state.subject[0] = %X", desc, _state.subject[0]);

      // check to see if the object/item has a series of description
      ObjectDescription *objDesc = _vm->logic()->objectDescription(1);
      uint16 i;
      for (i = 1; i <= _vm->logic()->objectDescriptionCount(); ++i, ++objDesc) {
            if (objDesc->object == _state.subject[0]) {
                  desc = nextObjectDescription(objDesc, desc);
                  break;
            }
      }

      _vm->logic()->makeJoeSpeak(desc, true);
      _vm->logic()->joeFace();
}

void Command::lookForCurrentObject(int16 cx, int16 cy) {
      uint16 obj = _vm->grid()->findObjectUnderCursor(cx, cy);
      _state.noun = _vm->grid()->findObjectNumber(obj);

      if (_state.oldNoun == _state.noun) {
            return;
      }

      ObjectData *od = findObjectData(_state.noun);
      if (od == NULL || od->name <= 0) {
            _state.oldNoun = _state.noun;
            _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
            if (_state.action != VERB_NONE) {
                  _cmdText.display(INK_CMD_NORMAL);
            }
            return;
      }

      // if no command yet selected, then use DEFAULT command, if any
      if (_state.action == VERB_NONE) {
            Verb v = State::findDefaultVerb(od->state);
            _cmdText.setVerb((v == VERB_NONE) ? VERB_WALK_TO : v);
            if (_state.noun == 0) {
                  _cmdText.clear();
            }
      }
      const char *name = _vm->logic()->objectName(od->name);
      _cmdText.displayTemp(INK_CMD_NORMAL, name);
      _state.oldNoun = _state.noun;
}

void Command::lookForCurrentIcon(int16 cx, int16 cy) {
      _state.verb = _vm->grid()->findVerbUnderCursor(cx, cy);
      if (_state.oldVerb != _state.verb) {

            if (_state.action == VERB_NONE) {
                  _cmdText.clear();
            }
            _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);

            if (isVerbInv(_state.verb)) {
                  ItemData *id = findItemData(_state.verb);
                  if (id != NULL && id->name > 0) {
                        if (_state.action == VERB_NONE) {
                              Verb v = State::findDefaultVerb(id->state);
                              _cmdText.setVerb((v == VERB_NONE) ? VERB_LOOK_AT : v);
                        }
                        const char *name = _vm->logic()->objectName(id->name);
                        _cmdText.displayTemp(INK_CMD_NORMAL, name);
                  }
            } else if (isVerbAction(_state.verb)) {
                  _cmdText.displayTemp(INK_CMD_NORMAL, _state.verb);
            } else if (_state.verb == VERB_NONE) {
                  _cmdText.display(INK_CMD_NORMAL);
            }
            _state.oldVerb = _state.verb;
      }
}

} // End of namespace Queen

Generated by  Doxygen 1.6.0   Back to index