Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

script_v2.cpp

/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-11-1/engines/scumm/script_v2.cpp $
 * $Id: script_v2.cpp 30944 2008-02-23 22:50:18Z sev $
 *
 */


#include "scumm/actor.h"
#include "scumm/charset.h"
#include "scumm/intern.h"
#include "scumm/object.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
#include "scumm/util.h"
#include "scumm/verbs.h"

namespace Scumm {

#define OPCODE(x) _OPCODE(ScummEngine_v2, x)

void ScummEngine_v2::setupOpcodes() {
      static const OpcodeEntryV2 opcodes[256] = {
            /* 00 */
            OPCODE(o5_stopObjectCode),
            OPCODE(o2_putActor),
            OPCODE(o5_startMusic),
            OPCODE(o5_getActorRoom),
            /* 04 */
            OPCODE(o2_isGreaterEqual),
            OPCODE(o2_drawObject),
            OPCODE(o2_getActorElevation),
            OPCODE(o2_setState08),
            /* 08 */
            OPCODE(o5_isNotEqual),
            OPCODE(o5_faceActor),
            OPCODE(o2_assignVarWordIndirect),
            OPCODE(o2_setObjPreposition),
            /* 0C */
            OPCODE(o2_resourceRoutines),
            OPCODE(o5_walkActorToActor),
            OPCODE(o2_putActorAtObject),
            OPCODE(o2_ifNotState08),
            /* 10 */
            OPCODE(o5_getObjectOwner),
            OPCODE(o5_animateActor),
            OPCODE(o2_panCameraTo),
            OPCODE(o2_actorOps),
            /* 14 */
            OPCODE(o5_print),
            OPCODE(o2_actorFromPos),
            OPCODE(o5_getRandomNr),
            OPCODE(o2_clearState02),
            /* 18 */
            OPCODE(o5_jumpRelative),
            OPCODE(o2_doSentence),
            OPCODE(o5_move),
            OPCODE(o2_setBitVar),
            /* 1C */
            OPCODE(o5_startSound),
            OPCODE(o2_ifClassOfIs),
            OPCODE(o2_walkActorTo),
            OPCODE(o2_ifState02),
            /* 20 */
            OPCODE(o5_stopMusic),
            OPCODE(o2_putActor),
            OPCODE(o5_saveLoadGame),
            OPCODE(o2_getActorY),
            /* 24 */
            OPCODE(o2_loadRoomWithEgo),
            OPCODE(o2_drawObject),
            OPCODE(o5_setVarRange),
            OPCODE(o2_setState04),
            /* 28 */
            OPCODE(o5_equalZero),
            OPCODE(o2_setOwnerOf),
            OPCODE(o2_addIndirect),
            OPCODE(o5_delayVariable),
            /* 2C */
            OPCODE(o2_assignVarByte),
            OPCODE(o2_putActorInRoom),
            OPCODE(o2_delay),
            OPCODE(o2_ifNotState04),
            /* 30 */
            OPCODE(o2_setBoxFlags),
            OPCODE(o2_getBitVar),
            OPCODE(o2_setCameraAt),
            OPCODE(o2_roomOps),
            /* 34 */
            OPCODE(o5_getDist),
            OPCODE(o2_findObject),
            OPCODE(o2_walkActorToObject),
            OPCODE(o2_setState01),
            /* 38 */
            OPCODE(o2_isLessEqual),
            OPCODE(o2_doSentence),
            OPCODE(o2_subtract),
            OPCODE(o2_waitForActor),
            /* 3C */
            OPCODE(o5_stopSound),
            OPCODE(o2_setActorElevation),
            OPCODE(o2_walkActorTo),
            OPCODE(o2_ifNotState01),
            /* 40 */
            OPCODE(o2_cutscene),
            OPCODE(o2_putActor),
            OPCODE(o2_startScript),
            OPCODE(o2_getActorX),
            /* 44 */
            OPCODE(o2_isLess),
            OPCODE(o2_drawObject),
            OPCODE(o5_increment),
            OPCODE(o2_clearState08),
            /* 48 */
            OPCODE(o5_isEqual),
            OPCODE(o5_faceActor),
            OPCODE(o2_chainScript),
            OPCODE(o2_setObjPreposition),
            /* 4C */
            OPCODE(o2_waitForSentence),
            OPCODE(o5_walkActorToActor),
            OPCODE(o2_putActorAtObject),
            OPCODE(o2_ifState08),
            /* 50 */
            OPCODE(o2_pickupObject),
            OPCODE(o5_animateActor),
            OPCODE(o5_actorFollowCamera),
            OPCODE(o2_actorOps),
            /* 54 */
            OPCODE(o5_setObjectName),
            OPCODE(o2_actorFromPos),
            OPCODE(o5_getActorMoving),
            OPCODE(o2_setState02),
            /* 58 */
            OPCODE(o2_beginOverride),
            OPCODE(o2_doSentence),
            OPCODE(o2_add),
            OPCODE(o2_setBitVar),
            /* 5C */
            OPCODE(o2_dummy),
            OPCODE(o2_ifClassOfIs),
            OPCODE(o2_walkActorTo),
            OPCODE(o2_ifNotState02),
            /* 60 */
            OPCODE(o2_cursorCommand),
            OPCODE(o2_putActor),
            OPCODE(o2_stopScript),
            OPCODE(o5_getActorFacing),
            /* 64 */
            OPCODE(o2_loadRoomWithEgo),
            OPCODE(o2_drawObject),
            OPCODE(o5_getClosestObjActor),
            OPCODE(o2_clearState04),
            /* 68 */
            OPCODE(o5_isScriptRunning),
            OPCODE(o2_setOwnerOf),
            OPCODE(o2_subIndirect),
            OPCODE(o2_dummy),
            /* 6C */
            OPCODE(o2_getObjPreposition),
            OPCODE(o2_putActorInRoom),
            OPCODE(o2_dummy),
            OPCODE(o2_ifState04),
            /* 70 */
            OPCODE(o2_lights),
            OPCODE(o5_getActorCostume),
            OPCODE(o5_loadRoom),
            OPCODE(o2_roomOps),
            /* 74 */
            OPCODE(o5_getDist),
            OPCODE(o2_findObject),
            OPCODE(o2_walkActorToObject),
            OPCODE(o2_clearState01),
            /* 78 */
            OPCODE(o2_isGreater),
            OPCODE(o2_doSentence),
            OPCODE(o2_verbOps),
            OPCODE(o2_getActorWalkBox),
            /* 7C */
            OPCODE(o5_isSoundRunning),
            OPCODE(o2_setActorElevation),
            OPCODE(o2_walkActorTo),
            OPCODE(o2_ifState01),
            /* 80 */
            OPCODE(o5_breakHere),
            OPCODE(o2_putActor),
            OPCODE(o5_startMusic),
            OPCODE(o5_getActorRoom),
            /* 84 */
            OPCODE(o2_isGreaterEqual),
            OPCODE(o2_drawObject),
            OPCODE(o2_getActorElevation),
            OPCODE(o2_setState08),
            /* 88 */
            OPCODE(o5_isNotEqual),
            OPCODE(o5_faceActor),
            OPCODE(o2_assignVarWordIndirect),
            OPCODE(o2_setObjPreposition),
            /* 8C */
            OPCODE(o2_resourceRoutines),
            OPCODE(o5_walkActorToActor),
            OPCODE(o2_putActorAtObject),
            OPCODE(o2_ifNotState08),
            /* 90 */
            OPCODE(o5_getObjectOwner),
            OPCODE(o5_animateActor),
            OPCODE(o2_panCameraTo),
            OPCODE(o2_actorOps),
            /* 94 */
            OPCODE(o5_print),
            OPCODE(o2_actorFromPos),
            OPCODE(o5_getRandomNr),
            OPCODE(o2_clearState02),
            /* 98 */
            OPCODE(o2_restart),
            OPCODE(o2_doSentence),
            OPCODE(o5_move),
            OPCODE(o2_setBitVar),
            /* 9C */
            OPCODE(o5_startSound),
            OPCODE(o2_ifClassOfIs),
            OPCODE(o2_walkActorTo),
            OPCODE(o2_ifState02),
            /* A0 */
            OPCODE(o5_stopObjectCode),
            OPCODE(o2_putActor),
            OPCODE(o5_saveLoadGame),
            OPCODE(o2_getActorY),
            /* A4 */
            OPCODE(o2_loadRoomWithEgo),
            OPCODE(o2_drawObject),
            OPCODE(o5_setVarRange),
            OPCODE(o2_setState04),
            /* A8 */
            OPCODE(o5_notEqualZero),
            OPCODE(o2_setOwnerOf),
            OPCODE(o2_addIndirect),
            OPCODE(o2_switchCostumeSet),
            /* AC */
            OPCODE(o2_drawSentence),
            OPCODE(o2_putActorInRoom),
            OPCODE(o2_waitForMessage),
            OPCODE(o2_ifNotState04),
            /* B0 */
            OPCODE(o2_setBoxFlags),
            OPCODE(o2_getBitVar),
            OPCODE(o2_setCameraAt),
            OPCODE(o2_roomOps),
            /* B4 */
            OPCODE(o5_getDist),
            OPCODE(o2_findObject),
            OPCODE(o2_walkActorToObject),
            OPCODE(o2_setState01),
            /* B8 */
            OPCODE(o2_isLessEqual),
            OPCODE(o2_doSentence),
            OPCODE(o2_subtract),
            OPCODE(o2_waitForActor),
            /* BC */
            OPCODE(o5_stopSound),
            OPCODE(o2_setActorElevation),
            OPCODE(o2_walkActorTo),
            OPCODE(o2_ifNotState01),
            /* C0 */
            OPCODE(o2_endCutscene),
            OPCODE(o2_putActor),
            OPCODE(o2_startScript),
            OPCODE(o2_getActorX),
            /* C4 */
            OPCODE(o2_isLess),
            OPCODE(o2_drawObject),
            OPCODE(o5_decrement),
            OPCODE(o2_clearState08),
            /* C8 */
            OPCODE(o5_isEqual),
            OPCODE(o5_faceActor),
            OPCODE(o2_chainScript),
            OPCODE(o2_setObjPreposition),
            /* CC */
            OPCODE(o5_pseudoRoom),
            OPCODE(o5_walkActorToActor),
            OPCODE(o2_putActorAtObject),
            OPCODE(o2_ifState08),
            /* D0 */
            OPCODE(o2_pickupObject),
            OPCODE(o5_animateActor),
            OPCODE(o5_actorFollowCamera),
            OPCODE(o2_actorOps),
            /* D4 */
            OPCODE(o5_setObjectName),
            OPCODE(o2_actorFromPos),
            OPCODE(o5_getActorMoving),
            OPCODE(o2_setState02),
            /* D8 */
            OPCODE(o5_printEgo),
            OPCODE(o2_doSentence),
            OPCODE(o2_add),
            OPCODE(o2_setBitVar),
            /* DC */
            OPCODE(o2_dummy),
            OPCODE(o2_ifClassOfIs),
            OPCODE(o2_walkActorTo),
            OPCODE(o2_ifNotState02),
            /* E0 */
            OPCODE(o2_cursorCommand),
            OPCODE(o2_putActor),
            OPCODE(o2_stopScript),
            OPCODE(o5_getActorFacing),
            /* E4 */
            OPCODE(o2_loadRoomWithEgo),
            OPCODE(o2_drawObject),
            OPCODE(o5_getClosestObjActor),
            OPCODE(o2_clearState04),
            /* E8 */
            OPCODE(o5_isScriptRunning),
            OPCODE(o2_setOwnerOf),
            OPCODE(o2_subIndirect),
            OPCODE(o2_dummy),
            /* EC */
            OPCODE(o2_getObjPreposition),
            OPCODE(o2_putActorInRoom),
            OPCODE(o2_dummy),
            OPCODE(o2_ifState04),
            /* F0 */
            OPCODE(o2_lights),
            OPCODE(o5_getActorCostume),
            OPCODE(o5_loadRoom),
            OPCODE(o2_roomOps),
            /* F4 */
            OPCODE(o5_getDist),
            OPCODE(o2_findObject),
            OPCODE(o2_walkActorToObject),
            OPCODE(o2_clearState01),
            /* F8 */
            OPCODE(o2_isGreater),
            OPCODE(o2_doSentence),
            OPCODE(o2_verbOps),
            OPCODE(o2_getActorWalkBox),
            /* FC */
            OPCODE(o5_isSoundRunning),
            OPCODE(o2_setActorElevation),
            OPCODE(o2_walkActorTo),
            OPCODE(o2_ifState01)
      };

      _opcodesV2 = opcodes;
}

#define SENTENCE_SCRIPT 2

#define PARAM_1 0x80
#define PARAM_2 0x40
#define PARAM_3 0x20

void ScummEngine_v2::executeOpcode(byte i) {
      OpcodeProcV2 op = _opcodesV2[i].proc;
      (this->*op) ();
}

const char *ScummEngine_v2::getOpcodeDesc(byte i) {
      return _opcodesV2[i].desc;
}

int ScummEngine_v2::getVar() {
      return readVar(fetchScriptByte());
}

void ScummEngine_v2::decodeParseString() {
      byte buffer[512];
      byte *ptr = buffer;
      byte c;
      bool insertSpace = false;

      while ((c = fetchScriptByte())) {

            insertSpace = (c & 0x80) != 0;
            c &= 0x7f;

            if (c < 8) {
                  // Special codes as seen in CHARSET_1 etc. My guess is that they
                  // have a similar function as the corresponding embedded stuff in modern
                  // games. Hence for now we convert them to the modern format.
                  // This might allow us to reuse the existing code.
                  *ptr++ = 0xFF;
                  *ptr++ = c;
                  if (c > 3) {
                        *ptr++ = fetchScriptByte();
                        *ptr++ = 0;
                  }
            } else
                  *ptr++ = c;

            if (insertSpace)
                  *ptr++ = ' ';

      }
      *ptr = 0;

      int textSlot = 0;
      _string[textSlot].xpos = 0;
      _string[textSlot].ypos = 0;
      _string[textSlot].right = _screenWidth - 1;
      _string[textSlot].center = false;
      _string[textSlot].overhead = false;

      if (_game.id == GID_MANIAC && _actorToPrintStrFor == 0xFF) {
            if (_game.platform == Common::kPlatformC64) {
                  _string[textSlot].color = 14;
            } else if (_game.features & GF_DEMO) {
                  _string[textSlot].color = (_game.version == 2) ? 15 : 1;
            }
      }

      actorTalk(buffer);
}

int ScummEngine_v2::readVar(uint var) {
      if (var >= 14 && var <= 16)
            var = _scummVars[var];

      assertRange(0, var, _numVariables - 1, "variable (reading)");
      debugC(DEBUG_VARS, "readvar(%d) = %d", var, _scummVars[var]);
      return _scummVars[var];
}

void ScummEngine_v2::writeVar(uint var, int value) {
      assertRange(0, var, _numVariables - 1, "variable (writing)");
      debugC(DEBUG_VARS, "writeVar(%d) = %d", var, value);

      if (VAR_CUTSCENEEXIT_KEY != 0xFF && var == VAR_CUTSCENEEXIT_KEY) {
            // Remap the cutscene exit key in earlier games
            if (value == 4 || value == 13 || value == 64)
                  value = 27;
      }

      _scummVars[var] = value;
}

void ScummEngine_v2::getResultPosIndirect() {
      _resultVarNumber = _scummVars[fetchScriptByte()];
}

void ScummEngine_v2::getResultPos() {
      _resultVarNumber = fetchScriptByte();
}

void ScummEngine_v2::setStateCommon(byte type) {
      int obj = getVarOrDirectWord(PARAM_1);
      putState(obj, getState(obj) | type);
}

void ScummEngine_v2::clearStateCommon(byte type) {
      int obj = getVarOrDirectWord(PARAM_1);
      putState(obj, getState(obj) & ~type);
}

void ScummEngine_v2::o2_setState08() {
      int obj = getVarOrDirectWord(PARAM_1);
      putState(obj, getState(obj) | kObjectState_08);
      markObjectRectAsDirty(obj);
      clearDrawObjectQueue();
}

void ScummEngine_v2::o2_clearState08() {
      int obj = getVarOrDirectWord(PARAM_1);
      putState(obj, getState(obj) & ~kObjectState_08);
      markObjectRectAsDirty(obj);
      clearDrawObjectQueue();
}

void ScummEngine_v2::o2_setState04() {
      setStateCommon(kObjectStateLocked);
}

void ScummEngine_v2::o2_clearState04() {
      clearStateCommon(kObjectStateLocked);
}

void ScummEngine_v2::o2_setState02() {
      setStateCommon(kObjectStateUntouchable);
}

void ScummEngine_v2::o2_clearState02() {
      clearStateCommon(kObjectStateUntouchable);
}

void ScummEngine_v2::o2_setState01() {
      setStateCommon(kObjectStatePickupable);
}

void ScummEngine_v2::o2_clearState01() {
      clearStateCommon(kObjectStatePickupable);
}

void ScummEngine_v2::o2_assignVarWordIndirect() {
      getResultPosIndirect();
      setResult(getVarOrDirectWord(PARAM_1));
}

void ScummEngine_v2::o2_assignVarByte() {
      getResultPos();
      setResult(fetchScriptByte());
}

void ScummEngine_v2::o2_setObjPreposition() {
      int obj = getVarOrDirectWord(PARAM_1);
      int unk = fetchScriptByte();

      if (_game.platform == Common::kPlatformNES)
            return;

      if (whereIsObject(obj) != WIO_NOT_FOUND) {
            // FIXME: this might not work properly the moment we save and restore the game.
            byte *ptr = getOBCDFromObject(obj) + 12;
            *ptr &= 0x1F;
            *ptr |= unk << 5;
      }
}

void ScummEngine_v2::o2_getObjPreposition() {
      getResultPos();
      int obj = getVarOrDirectWord(PARAM_1);

      if (whereIsObject(obj) != WIO_NOT_FOUND) {
            byte *ptr = getOBCDFromObject(obj) + 12;
            setResult(*ptr >> 5);
      } else {
            setResult(0xFF);
      }
}

void ScummEngine_v2::o2_setBitVar() {
      int var = fetchScriptWord();
      byte a = getVarOrDirectByte(PARAM_1);

      int bit_var = var + a;
      int bit_offset = bit_var & 0x0f;
      bit_var >>= 4;

      if (getVarOrDirectByte(PARAM_2))
            _scummVars[bit_var] |= (1 << bit_offset);
      else
            _scummVars[bit_var] &= ~(1 << bit_offset);

}

void ScummEngine_v2::o2_getBitVar() {
      getResultPos();
      int var = fetchScriptWord();
      byte a = getVarOrDirectByte(PARAM_1);

      int bit_var = var + a;
      int bit_offset = bit_var & 0x0f;
      bit_var >>= 4;

      setResult((_scummVars[bit_var] & (1 << bit_offset)) ? 1 : 0);
}

void ScummEngine_v2::ifStateCommon(byte type) {
      int obj = getVarOrDirectWord(PARAM_1);

      if ((getState(obj) & type) != 0)
            ignoreScriptWord();
      else
            o5_jumpRelative();
}

void ScummEngine_v2::ifNotStateCommon(byte type) {
      int obj = getVarOrDirectWord(PARAM_1);

      if ((getState(obj) & type) == 0)
            ignoreScriptWord();
      else
            o5_jumpRelative();
}

void ScummEngine_v2::o2_ifState08() {
      ifStateCommon(kObjectState_08);
}

void ScummEngine_v2::o2_ifNotState08() {
      ifNotStateCommon(kObjectState_08);
}

void ScummEngine_v2::o2_ifState04() {
      ifStateCommon(kObjectStateLocked);
}

void ScummEngine_v2::o2_ifNotState04() {
      ifNotStateCommon(kObjectStateLocked);
}

void ScummEngine_v2::o2_ifState02() {
      ifStateCommon(kObjectStateUntouchable);
}

void ScummEngine_v2::o2_ifNotState02() {
      ifNotStateCommon(kObjectStateUntouchable);
}

void ScummEngine_v2::o2_ifState01() {
      ifStateCommon(kObjectStatePickupable);
}

void ScummEngine_v2::o2_ifNotState01() {
      ifNotStateCommon(kObjectStatePickupable);
}

void ScummEngine_v2::o2_addIndirect() {
      int a;
      getResultPosIndirect();
      a = getVarOrDirectWord(PARAM_1);
      _scummVars[_resultVarNumber] += a;
}

void ScummEngine_v2::o2_subIndirect() {
      int a;
      getResultPosIndirect();
      a = getVarOrDirectWord(PARAM_1);
      _scummVars[_resultVarNumber] -= a;
}

void ScummEngine_v2::o2_add() {
      int a;
      getResultPos();
      a = getVarOrDirectWord(PARAM_1);
      _scummVars[_resultVarNumber] += a;
}

void ScummEngine_v2::o2_subtract() {
      int a;
      getResultPos();
      a = getVarOrDirectWord(PARAM_1);
      _scummVars[_resultVarNumber] -= a;
}

void ScummEngine_v2::o2_waitForActor() {
      Actor *a = derefActor(getVarOrDirectByte(PARAM_1), "o2_waitForActor");
      if (a->_moving) {
            _scriptPointer -= 2;
            o5_breakHere();
      }
}

void ScummEngine_v2::o2_waitForMessage() {

      if (VAR(VAR_HAVE_MSG)) {
            _scriptPointer--;
            o5_breakHere();
      }
}

void ScummEngine_v2::o2_waitForSentence() {
      if (!_sentenceNum && !isScriptInUse(SENTENCE_SCRIPT))
            return;

      _scriptPointer--;
      o5_breakHere();
}

void ScummEngine_v2::o2_actorOps() {
      int act = getVarOrDirectByte(PARAM_1);
      int arg = getVarOrDirectByte(PARAM_2);
      Actor *a;
      int i;

      _opcode = fetchScriptByte();
      if (act == 0 && _opcode == 5) {
            // This case happens in the Zak/MM bootscripts, to set the default talk color (9).
            _string[0].color = arg;
            return;
      }

      a = derefActor(act, "actorOps");

      switch (_opcode) {
      case 1:     // SO_SOUND
            a->_sound[0] = arg;
            break;
      case 2:           // SO_PALETTE
            if (_game.version == 1)
                  i = act;
            else
                  i = fetchScriptByte();

            a->setPalette(i, arg);
            break;
      case 3:           // SO_ACTOR_NAME
            loadPtrToResource(rtActorName, a->_number, NULL);
            break;
      case 4:           // SO_COSTUME
            a->setActorCostume(arg);
            break;
      case 5:           // SO_TALK_COLOR
            if (_game.id == GID_MANIAC && _game.version == 2 && (_game.features & GF_DEMO) && arg == 1)
                  a->_talkColor = 15;
            else
                  a->_talkColor = arg;
            break;
      default:
            error("o2_actorOps: opcode %d not yet supported", _opcode);
      }
}

void ScummEngine_v2::o2_restart() {
      restart();
}

void ScummEngine_v2::o2_drawObject() {
      int obj, idx, i;
      ObjectData *od;
      uint16 x, y, w, h;
      int xpos, ypos;

      obj = getVarOrDirectWord(PARAM_1);
      xpos = getVarOrDirectByte(PARAM_2);
      ypos = getVarOrDirectByte(PARAM_3);

      idx = getObjectIndex(obj);
      if (idx == -1)
            return;

      od = &_objs[idx];
      if (xpos != 0xFF) {
            od->walk_x += (xpos * 8) - od->x_pos;
            od->x_pos = xpos * 8;
            od->walk_y += (ypos * 8) - od->y_pos;
            od->y_pos = ypos * 8;
      }
      addObjectToDrawQue(idx);

      x = od->x_pos;
      y = od->y_pos;
      w = od->width;
      h = od->height;

      i = _numLocalObjects;
      while (i--) {
            if (_objs[i].obj_nr && _objs[i].x_pos == x && _objs[i].y_pos == y && _objs[i].width == w && _objs[i].height == h)
                  putState(_objs[i].obj_nr, getState(_objs[i].obj_nr) & ~kObjectState_08);
      }

      putState(obj, getState(od->obj_nr) | kObjectState_08);
}

void ScummEngine_v2::o2_resourceRoutines() {
      const ResTypes resTypes[] = {
            rtNumTypes, // Invalid
            rtNumTypes, // Invalid
            rtCostume,
            rtRoom,
            rtNumTypes, // Invalid
            rtScript,
            rtSound
      };
      int resid = getVarOrDirectByte(PARAM_1);
      int opcode = fetchScriptByte();

      ResTypes type = rtNumTypes;
      if (0 <= (opcode >> 4) && (opcode >> 4) < (int)ARRAYSIZE(resTypes))
            type = resTypes[opcode >> 4];

      if ((opcode & 0x0f) == 0 || type == rtNumTypes)
            return;

      // HACK V2 Maniac Mansion tries to load an invalid sound resource in demo script.
      if (_game.id == GID_MANIAC && _game.version == 2 && vm.slot[_currentScript].number == 9 && type == rtSound && resid == 1)
            return;

      if ((opcode & 0x0f) == 1) {
            ensureResourceLoaded(type, resid);
      } else {
            if (opcode & 1)
                  _res->lock(type, resid);
            else
                  _res->unlock(type, resid);
      }
}

void ScummEngine_v2::o2_verbOps() {
      int verb = fetchScriptByte();
      int slot, state;

      switch (verb) {
      case 0:           // SO_DELETE_VERBS
            slot = getVarOrDirectByte(PARAM_1) + 1;
            assert(0 < slot && slot < _numVerbs);
            killVerb(slot);
            break;

      case 0xFF:  // Verb On/Off
            verb = fetchScriptByte();
            state = fetchScriptByte();
            slot = getVerbSlot(verb, 0);
            _verbs[slot].curmode = state;
            break;

      default: {  // New Verb
            int x = fetchScriptByte() * 8;
            int y = fetchScriptByte() * 8;
            slot = getVarOrDirectByte(PARAM_1) + 1;
            int prep = fetchScriptByte(); // Only used in V1?
            // V1 Maniac verbs are relative to the 'verb area' - under the sentence
            if (_game.platform == Common::kPlatformNES)
                  x += 8;
            else if ((_game.id == GID_MANIAC) && (_game.version == 1))
                  y += 8;

            VerbSlot *vs;
            assert(0 < slot && slot < _numVerbs);

            vs = &_verbs[slot];
            vs->verbid = verb;
            if (_game.platform == Common::kPlatformNES) {
                  vs->color = 1;
                  vs->hicolor = 1;
                  vs->dimcolor = 1;
            } else if (_game.version == 1) {
                  vs->color = (_game.id == GID_MANIAC && (_game.features & GF_DEMO)) ? 16 : 5;
                  vs->hicolor = 7;
                  vs->dimcolor = 11;
            } else {
                  vs->color = (_game.id == GID_MANIAC && (_game.features & GF_DEMO)) ? 13 : 2;
                  vs->hicolor = 14;
                  vs->dimcolor = 8;
            }
            vs->type = kTextVerbType;
            vs->charset_nr = _string[0]._default.charset;
            vs->curmode = 1;
            vs->saveid = 0;
            vs->key = 0;
            vs->center = 0;
            vs->imgindex = 0;
            vs->prep = prep;

            vs->curRect.left = x;
            vs->curRect.top = y;

            // FIXME: these keyboard map depends on the language of the game.
            // E.g. a german keyboard has 'z' and 'y' swapped, while a french
            // keyboard starts with "azerty", etc.
            if (_game.platform == Common::kPlatformNES) {
                  static const char keyboard[] = {
                              'q','w','e','r',
                              'a','s','d','f',
                              'z','x','c','v'
                        };
                  if (1 <= slot && slot <= ARRAYSIZE(keyboard))
                        vs->key = keyboard[slot - 1];
            } else {
                  static const char keyboard[] = {
                              'q','w','e','r','t',
                              'a','s','d','f','g',
                              'z','x','c','v','b'
                        };
                  if (1 <= slot && slot <= ARRAYSIZE(keyboard))
                        vs->key = keyboard[slot - 1];
            }

            // It follows the verb name
            loadPtrToResource(rtVerb, slot, NULL);
            }
            break;
      }

      // Force redraw of the modified verb slot
      drawVerb(slot, 0);
      verbMouseOver(0);
}

void ScummEngine_v2::o2_doSentence() {
      int a;
      SentenceTab *st;

      a = getVarOrDirectByte(PARAM_1);
      if (a == 0xFC) {
            _sentenceNum = 0;
            stopScript(SENTENCE_SCRIPT);
            return;
      }
      if (a == 0xFB) {
            resetSentence();
            return;
      }

      assert(_sentenceNum < NUM_SENTENCE);
      st = &_sentence[_sentenceNum++];

      st->verb = a;
      st->objectA = getVarOrDirectWord(PARAM_2);
      st->objectB = getVarOrDirectWord(PARAM_3);
      st->preposition = (st->objectB != 0);
      st->freezeCount = 0;

      // Execute or print the sentence
      _opcode = fetchScriptByte();
      switch (_opcode) {
      case 0:
            // Do nothing (besides setting up the sentence above)
            break;
      case 1:
            // Execute the sentence
            _sentenceNum--;

            if (st->verb == 254) {
                  ScummEngine::stopObjectScript(st->objectA);
            } else {
                  bool isBackgroundScript;
                  bool isSpecialVerb;
                  if (st->verb != 253 && st->verb != 250) {
                        VAR(VAR_ACTIVE_VERB) = st->verb;
                        VAR(VAR_ACTIVE_OBJECT1) = st->objectA;
                        VAR(VAR_ACTIVE_OBJECT2) = st->objectB;

                        isBackgroundScript = false;
                        isSpecialVerb = false;
                  } else {
                        isBackgroundScript = (st->verb == 250);
                        isSpecialVerb = true;
                        st->verb = 253;
                  }

                  // Check if an object script for this object is already running. If
                  // so, reuse its script slot. Note that we abuse two script flags:
                  // freezeResistant and recursive. We use them to track two
                  // script flags used in V1/V2 games. The main reason we do it this
                  // ugly evil way is to avoid having to introduce yet another save
                  // game revision.
                  int slot = -1;
                  ScriptSlot *ss;
                  int i;

                  ss = vm.slot;
                  for (i = 0; i < NUM_SCRIPT_SLOT; i++, ss++) {
                        if (st->objectA == ss->number &&
                              ss->freezeResistant == isBackgroundScript &&
                              ss->recursive == isSpecialVerb &&
                              (ss->where == WIO_ROOM || ss->where == WIO_INVENTORY || ss->where == WIO_FLOBJECT)) {
                              slot = i;
                              break;
                        }
                  }

                  runObjectScript(st->objectA, st->verb, isBackgroundScript, isSpecialVerb, NULL, slot);
            }
            break;
      case 2:
            // Print the sentence
            _sentenceNum--;

            VAR(VAR_SENTENCE_VERB) = st->verb;
            VAR(VAR_SENTENCE_OBJECT1) = st->objectA;
            VAR(VAR_SENTENCE_OBJECT2) = st->objectB;

            o2_drawSentence();
            break;
      default:
            error("o2_doSentence: unknown subopcode %d", _opcode);
      }
}

void ScummEngine_v2::o2_drawSentence() {
      Common::Rect sentenceline;
      const byte *temp;
      int slot = getVerbSlot(VAR(VAR_SENTENCE_VERB), 0);

      if (!((_userState & 32) || (_game.platform == Common::kPlatformNES && _userState & 0xe0)))
            return;

      if (getResourceAddress(rtVerb, slot))
            strcpy(_sentenceBuf, (char*)getResourceAddress(rtVerb, slot));
      else
            return;

      if (VAR(VAR_SENTENCE_OBJECT1) > 0) {
            temp = getObjOrActorName(VAR(VAR_SENTENCE_OBJECT1));
            if (temp) {
                  strcat(_sentenceBuf, " ");
                  strcat(_sentenceBuf, (const char*)temp);
            }

            // For V1 games, the engine must compute the preposition.
            // In all other Scumm versions, this is done by the sentence script.
            if ((_game.id == GID_MANIAC && _game.version == 1 && !(_game.platform == Common::kPlatformNES)) && (VAR(VAR_SENTENCE_PREPOSITION) == 0)) {
                  if (_verbs[slot].prep == 0xFF) {
                        byte *ptr = getOBCDFromObject(VAR(VAR_SENTENCE_OBJECT1));
                        assert(ptr);
                        VAR(VAR_SENTENCE_PREPOSITION) = (*(ptr + 12) >> 5);
                  } else
                        VAR(VAR_SENTENCE_PREPOSITION) = _verbs[slot].prep;
            }
      }

      if (0 < VAR(VAR_SENTENCE_PREPOSITION) && VAR(VAR_SENTENCE_PREPOSITION) <= 4) {
            // The prepositions, like the fonts, were hard code in the engine. Thus
            // we have to do that, too, and provde localized versions for all the
            // languages MM/Zak are available in.
            const char *prepositions[][5] = {
                  { " ", " in", " with", " on", " to" },   // English
                  { " ", " mit", " mit", " mit", " zu" },  // German
                  { " ", " dans", " avec", " sur", " <" }, // French
                  { " ", " in", " con", " su", " a" },     // Italian
                  { " ", " en", " con", " en", " a" },     // Spanish
                  };
            int lang;
            switch (_language) {
            case Common::DE_DEU:
                  lang = 1;
                  break;
            case Common::FR_FRA:
                  lang = 2;
                  break;
            case Common::IT_ITA:
                  lang = 3;
                  break;
            case Common::ES_ESP:
                  lang = 4;
                  break;
            default:
                  lang = 0;   // Default to english
            }

            if (_game.platform == Common::kPlatformNES) {
                  strcat(_sentenceBuf, (const char *)(getResourceAddress(rtCostume, 78) + VAR(VAR_SENTENCE_PREPOSITION) * 8 + 2));
            } else
                  strcat(_sentenceBuf, prepositions[lang][VAR(VAR_SENTENCE_PREPOSITION)]);
      }

      if (VAR(VAR_SENTENCE_OBJECT2) > 0) {
            temp = getObjOrActorName(VAR(VAR_SENTENCE_OBJECT2));
            if (temp) {
                  strcat(_sentenceBuf, " ");
                  strcat(_sentenceBuf, (const char*)temp);
            }
      }

      _string[2].charset = 1;
      _string[2].ypos = _virtscr[kVerbVirtScreen].topline;
      _string[2].xpos = 0;
      _string[2].right = _virtscr[kVerbVirtScreen].w - 1;
      if (_game.platform == Common::kPlatformNES) {
            _string[2].xpos = 16;
            _string[2].color = 0;
      } else if (_game.version == 1)
            _string[2].color = 16;
      else
            _string[2].color = 13;

      byte string[80];
      char *ptr = _sentenceBuf;
      int i = 0, len = 0;

      // Maximum length of printable characters
      int maxChars = (_game.platform == Common::kPlatformNES) ? 60 : 40;
      while (*ptr) {
            if (*ptr != '@')
                  len++;
            if (len > maxChars) {
                  break;
            }

            string[i++] = *ptr++;

            if (_game.platform == Common::kPlatformNES && len == 30) {
                  string[i++] = 0xFF;
                  string[i++] = 8;
            }
      }
      string[i] = 0;

      if (_game.platform == Common::kPlatformNES) {
            sentenceline.top = _virtscr[kVerbVirtScreen].topline;
            sentenceline.bottom = _virtscr[kVerbVirtScreen].topline + 16;
            sentenceline.left = 16;
            sentenceline.right = _virtscr[kVerbVirtScreen].w - 1;
      } else {
            sentenceline.top = _virtscr[kVerbVirtScreen].topline;
            sentenceline.bottom = _virtscr[kVerbVirtScreen].topline + 8;
            sentenceline.left = 0;
            sentenceline.right = _virtscr[kVerbVirtScreen].w - 1;
      }
      restoreBackground(sentenceline);

      drawString(2, (byte*)string);
}

void ScummEngine_v2::o2_ifClassOfIs() {
      int obj = getVarOrDirectWord(PARAM_1);
      int clsop = getVarOrDirectByte(PARAM_2);
      byte *obcd = getOBCDFromObject(obj);

      if (obcd == 0) {
            o5_jumpRelative();
            return;
      }

      byte cls = *(obcd + 6);
      if ((cls & clsop) != clsop) {
            o5_jumpRelative();
            return;
      }
      ignoreScriptWord();
}

void ScummEngine_v2::o2_walkActorTo() {
      int x, y;
      Actor *a;

      int act = getVarOrDirectByte(PARAM_1);

      // WORKAROUND bug #1252606
      if (_game.id == GID_ZAK && _game.version == 1 && vm.slot[_currentScript].number == 115 && act == 249) {
            act = VAR(VAR_EGO);
      }

      a = derefActor(act, "o2_walkActorTo");

      x = getVarOrDirectByte(PARAM_2);
      y = getVarOrDirectByte(PARAM_3);

      a->startWalkActor(x, y, -1);
}

void ScummEngine_v2::o2_putActor() {
      int act = getVarOrDirectByte(PARAM_1);
      int x, y;
      Actor *a;

      a = derefActor(act, "o2_putActor");

      x = getVarOrDirectByte(PARAM_2);
      y = getVarOrDirectByte(PARAM_3);

      if (_game.id == GID_MANIAC && _game.version <= 1 && _game.platform != Common::kPlatformNES)
            a->setFacing(180);

      a->putActor(x, y);
}

void ScummEngine_v2::o2_startScript() {
      int script = getVarOrDirectByte(PARAM_1);

      if (!_copyProtection) {
            // The enhanced version of Zak McKracken included in the
            // SelectWare Classic Collection bundle used CD check instead
            // of the usual key code check at airports.
            if ((_game.id == GID_ZAK) && (script == 15) && (_roomResource == 45))
                  return;
      }

      runScript(script, 0, 0, 0);
}

void ScummEngine_v2::o2_stopScript() {
      int script;

      script = getVarOrDirectByte(PARAM_1);

      if (_game.id == GID_MANIAC && _roomResource == 26 && vm.slot[_currentScript].number == 10001) {
      // FIXME: Nasty hack for bug #915575
      // Don't let the exit script for room 26 stop the script (116), when
      // switching to the dungeon (script 89)
            if ((script == 116) && isScriptRunning(89))
                  return;
      }

      if (script == 0)
            script = vm.slot[_currentScript].number;

      if (_currentScript != 0 && vm.slot[_currentScript].number == script)
            stopObjectCode();
      else
            stopScript(script);
}

void ScummEngine_v2::o2_panCameraTo() {
      panCameraTo(getVarOrDirectByte(PARAM_1) * V12_X_MULTIPLIER, 0);
}

void ScummEngine_v2::o2_walkActorToObject() {
      int obj;
      Actor *a;

      a = derefActor(getVarOrDirectByte(PARAM_1), "o2_walkActorToObject");
      obj = getVarOrDirectWord(PARAM_2);
      if (whereIsObject(obj) != WIO_NOT_FOUND) {
            int x, y, dir;
            getObjectXYPos(obj, x, y, dir);
            AdjustBoxResult r = a->adjustXYToBeInBox(x, y);
            x = r.x;
            y = r.y;
            a->startWalkActor(x, y, dir);
      }
}

void ScummEngine_v2::o2_putActorAtObject() {
      int obj, x, y;
      Actor *a;

      a = derefActor(getVarOrDirectByte(PARAM_1), "o2_putActorAtObject");
      obj = getVarOrDirectWord(PARAM_2);
      if (whereIsObject(obj) != WIO_NOT_FOUND) {
            getObjectXYPos(obj, x, y);
            AdjustBoxResult r = a->adjustXYToBeInBox(x, y);
            x = r.x;
            y = r.y;
      } else {
            x = 30;
            y = 60;
      }

      a->putActor(x, y);
}

void ScummEngine_v2::o2_putActorInRoom() {
      Actor *a;
      int act = getVarOrDirectByte(PARAM_1);
      int room = getVarOrDirectByte(PARAM_2);

      a = derefActor(act, "o2_putActorInRoom");

      a->_room = room;
      if (!room) {
            if (_game.id == GID_MANIAC && _game.version <= 1 && _game.platform != Common::kPlatformNES)
                  a->setFacing(180);

            a->putActor(0, 0, 0);
      }
}

void ScummEngine_v2::o2_getActorElevation() {
      getResultPos();
      int act = getVarOrDirectByte(PARAM_1);
      Actor *a = derefActor(act, "o2_getActorElevation");
      setResult(a->getElevation());
}

void ScummEngine_v2::o2_setActorElevation() {
      int act = getVarOrDirectByte(PARAM_1);
      int elevation = (int8)getVarOrDirectByte(PARAM_2);

      Actor *a = derefActor(act, "o2_setActorElevation");
      a->setElevation(elevation);
}

void ScummEngine_v2::o2_actorFromPos() {
      int x, y;
      getResultPos();
      x = getVarOrDirectByte(PARAM_1) * V12_X_MULTIPLIER;
      y = getVarOrDirectByte(PARAM_2) * V12_Y_MULTIPLIER;
      setResult(getActorFromPos(x, y));
}

void ScummEngine_v2::o2_findObject() {
      int obj;
      getResultPos();
      int x = getVarOrDirectByte(PARAM_1) * V12_X_MULTIPLIER;
      int y = getVarOrDirectByte(PARAM_2) * V12_Y_MULTIPLIER;
      obj = findObject(x, y);
      if (obj == 0 && (_game.platform == Common::kPlatformNES) && (_userState & 0x40)) {
            if (_mouseOverBoxV2 >= 0 && _mouseOverBoxV2 < 4)
                  obj = findInventory(VAR(VAR_EGO), _mouseOverBoxV2 + _inventoryOffset + 1);
      }
      setResult(obj);
}

void ScummEngine_v2::o2_getActorX() {
      int a;
      getResultPos();

      a = getVarOrDirectByte(PARAM_1);
      setResult(getObjX(a));
}

void ScummEngine_v2::o2_getActorY() {
      int a;
      getResultPos();

      a = getVarOrDirectByte(PARAM_1);
      setResult(getObjY(a));
}

void ScummEngine_v2::o2_isGreater() {
      uint16 a = getVar();
      uint16 b = getVarOrDirectWord(PARAM_1);
      if (b > a)
            ignoreScriptWord();
      else
            o5_jumpRelative();
}

void ScummEngine_v2::o2_isGreaterEqual() {
      uint16 a = getVar();
      uint16 b = getVarOrDirectWord(PARAM_1);
      if (b >= a)
            ignoreScriptWord();
      else
            o5_jumpRelative();
}

void ScummEngine_v2::o2_isLess() {
      uint16 a = getVar();
      uint16 b = getVarOrDirectWord(PARAM_1);

      if (b < a)
            ignoreScriptWord();
      else
            o5_jumpRelative();
}

void ScummEngine_v2::o2_isLessEqual() {
      uint16 a = getVar();
      uint16 b = getVarOrDirectWord(PARAM_1);
      if (b <= a)
            ignoreScriptWord();
      else
            o5_jumpRelative();
}

void ScummEngine_v2::o2_lights() {
      int a, b, c;

      a = getVarOrDirectByte(PARAM_1);
      b = fetchScriptByte();
      c = fetchScriptByte();

      if (c == 0) {
            if (_game.id == GID_MANIAC && _game.version == 1 && !(_game.platform == Common::kPlatformNES)) {
                  // Convert older light mode values into
                  // equivalent values of later games.
                  // 0 Darkness
                  // 1 Flashlight
                  // 2 Lighted area
                  if (a == 2)
                        VAR(VAR_CURRENT_LIGHTS) = 11;
                  else if (a == 1)
                        VAR(VAR_CURRENT_LIGHTS) = 4;
                  else
                        VAR(VAR_CURRENT_LIGHTS) = 0;
            } else
                  VAR(VAR_CURRENT_LIGHTS) = a;
      } else if (c == 1) {
            _flashlight.xStrips = a;
            _flashlight.yStrips = b;
      }
      _fullRedraw = true;
}

void ScummEngine_v2::o2_loadRoomWithEgo() {
      Actor *a;
      int obj, room, x, y, x2, y2, dir;

      obj = getVarOrDirectWord(PARAM_1);
      room = getVarOrDirectByte(PARAM_2);

      a = derefActor(VAR(VAR_EGO), "o2_loadRoomWithEgo");

      a->putActor(0, 0, room);
      _egoPositioned = false;

      x = (int8)fetchScriptByte();
      y = (int8)fetchScriptByte();

      startScene(a->_room, a, obj);

      getObjectXYPos(obj, x2, y2, dir);
      AdjustBoxResult r = a->adjustXYToBeInBox(x2, y2);
      x2 = r.x;
      y2 = r.y;
      a->putActor(x2, y2, _currentRoom);
      a->setDirection(dir + 180);

      camera._dest.x = camera._cur.x = a->getPos().x;
      setCameraAt(a->getPos().x, a->getPos().y);
      setCameraFollows(a);

      _fullRedraw = true;

      resetSentence();

      if (x >= 0 && y >= 0) {
            a->startWalkActor(x, y, -1);
      }
      runScript(5, 0, 0, 0);
}

void ScummEngine_v2::o2_setOwnerOf() {
      int obj, owner;

      obj = getVarOrDirectWord(PARAM_1);
      owner = getVarOrDirectByte(PARAM_2);

      setOwnerOf(obj, owner);
}

void ScummEngine_v2::o2_delay() {
      int delay = fetchScriptByte();
      delay |= fetchScriptByte() << 8;
      delay |= fetchScriptByte() << 16;
      delay = 0xFFFFFF - delay;

      vm.slot[_currentScript].delay = delay;
      vm.slot[_currentScript].status = ssPaused;
      o5_breakHere();
}

void ScummEngine_v2::o2_setBoxFlags() {
      int a, b;

      a = getVarOrDirectByte(PARAM_1);
      b = fetchScriptByte();
      setBoxFlags(a, b);
}

void ScummEngine_v2::o2_setCameraAt() {
      setCameraAtEx(getVarOrDirectByte(PARAM_1) * V12_X_MULTIPLIER);
}

void ScummEngine_v2::o2_roomOps() {
      int a = getVarOrDirectByte(PARAM_1);
      int b = getVarOrDirectByte(PARAM_2);

      _opcode = fetchScriptByte();
      switch (_opcode & 0x1F) {
      case 1:                 // SO_ROOM_SCROLL
            a *= 8;
            b *= 8;
            if (a < (_screenWidth / 2))
                  a = (_screenWidth / 2);
            if (b < (_screenWidth / 2))
                  b = (_screenWidth / 2);
            if (a > _roomWidth - (_screenWidth / 2))
                  a = _roomWidth - (_screenWidth / 2);
            if (b > _roomWidth - (_screenWidth / 2))
                  b = _roomWidth - (_screenWidth / 2);
            VAR(VAR_CAMERA_MIN_X) = a;
            VAR(VAR_CAMERA_MAX_X) = b;
            break;
      case 2:                 // SO_ROOM_COLOR
            if (_game.version == 1) {
                  // V1 zak needs to know when room color is changed
                  _roomPalette[0] = 255;
                  _roomPalette[1] = a;
                  _roomPalette[2] = b;
            } else {
                  _roomPalette[b] = a;
            }
            _fullRedraw = true;
            break;
      }
}

void ScummEngine_v2::o2_cutscene() {
      vm.cutSceneData[0] = _userState | (_userPut ? 16 : 0);
      vm.cutSceneData[1] = (int16)VAR(VAR_CURSORSTATE);
      vm.cutSceneData[2] = _currentRoom;
      vm.cutSceneData[3] = camera._mode;

      VAR(VAR_CURSORSTATE) = 200;

      // Hide inventory, freeze scripts, hide cursor
      setUserState(15);

      _sentenceNum = 0;
      stopScript(SENTENCE_SCRIPT);
      resetSentence();

      vm.cutScenePtr[0] = 0;
}

void ScummEngine_v2::o2_endCutscene() {
      vm.cutSceneStackPointer = 0;

      VAR(VAR_OVERRIDE) = 0;
      vm.cutSceneScript[0] = 0;
      vm.cutScenePtr[0] = 0;

      VAR(VAR_CURSORSTATE) = vm.cutSceneData[1];

      // Reset user state to values before cutscene
      setUserState(vm.cutSceneData[0] | 7);

      if ((_game.id == GID_MANIAC) && !(_game.platform == Common::kPlatformNES)) {
            camera._mode = (byte) vm.cutSceneData[3];
            if (camera._mode == kFollowActorCameraMode) {
                  actorFollowCamera(VAR(VAR_EGO));
            } else if (vm.cutSceneData[2] != _currentRoom) {
                  startScene(vm.cutSceneData[2], 0, 0);
            }
      } else {
            actorFollowCamera(VAR(VAR_EGO));
      }
}

void ScummEngine_v2::o2_beginOverride() {
      vm.cutScenePtr[0] = _scriptPointer - _scriptOrgPointer;
      vm.cutSceneScript[0] = _currentScript;

      // Skip the jump instruction following the override instruction
      fetchScriptByte();
      fetchScriptWord();
}

void ScummEngine_v2::o2_chainScript() {
      int data = getVarOrDirectByte(PARAM_1);
      stopScript(vm.slot[_currentScript].number);
      _currentScript = 0xFF;
      runScript(data, 0, 0, 0);
}

void ScummEngine_v2::o2_pickupObject() {
      int obj = getVarOrDirectWord(PARAM_1);

      if (obj < 1) {
            error("pickupObject received invalid index %d (script %d)", obj, vm.slot[_currentScript].number);
      }

      if (getObjectIndex(obj) == -1)
            return;

      if (whereIsObject(obj) == WIO_INVENTORY)  /* Don't take an */
            return;                                                                 /* object twice */

      addObjectToInventory(obj, _roomResource);
      markObjectRectAsDirty(obj);
      putOwner(obj, VAR(VAR_EGO));
      putState(obj, getState(obj) | kObjectState_08 | kObjectStateUntouchable);
      clearDrawObjectQueue();

      runInventoryScript(1);
      if (_game.platform == Common::kPlatformNES)
            _sound->addSoundToQueue(51);  // play 'pickup' sound
}

void ScummEngine_v2::o2_cursorCommand() { // TODO: Define the magic numbers
      uint16 cmd = getVarOrDirectWord(PARAM_1);
      byte state = cmd >> 8;

      if (cmd & 0xFF) {
            VAR(VAR_CURSORSTATE) = cmd & 0xFF;
      }

      setUserState(state);
}

void ScummEngine_v2::setUserState(byte state) {
      if (state & 4) {                                // Userface
            if (_game.platform == Common::kPlatformNES)
                  _userState = (_userState & ~0xE0) | (state & 0xE0);
            else
                  _userState = state & (32 | 64 | 128);
      }

      if (state & 1) {                                // Freeze
            if (state & 8)
                  freezeScripts(0);
            else
                  unfreezeScripts();
      }

      if (state & 2) {                                // Cursor Show/Hide
            if (_game.platform == Common::kPlatformNES)
                  _userState = (_userState & ~0x10) | (state & 0x10);
            if (state & 16) {
                  _userPut = 1;
                  _cursor.state = 1;
            } else {
                  _userPut = 0;
                  _cursor.state = 0;
            }
      }

      // Hide all verbs and inventory
      Common::Rect rect;
      rect.top = _virtscr[kVerbVirtScreen].topline;
      rect.bottom = _virtscr[kVerbVirtScreen].topline + 8 * 88;
      rect.right = _virtscr[kVerbVirtScreen].w - 1;
      if (_game.platform == Common::kPlatformNES) {
            rect.left = 16;
      } else {
            rect.left = 0;
      }
      restoreBackground(rect);

      // Draw all verbs and inventory
      redrawVerbs();
      runInventoryScript(1);
}

void ScummEngine_v2::o2_getActorWalkBox() {
      Actor *a;
      getResultPos();
      a = derefActor(getVarOrDirectByte(PARAM_1), "o2_getActorWalkbox");
      setResult(a->_walkbox);
}

void ScummEngine_v2::o2_dummy() {
      // Opcode 238 is used in maniac and zak but has no purpose
      if (_opcode != 238)
            warning("o2_dummy invoked (opcode %d)", _opcode);
}

void ScummEngine_v2::o2_switchCostumeSet() {
      // NES version of maniac uses this to switch between the two
      // groups of costumes it has
      if (_game.platform == Common::kPlatformNES)
            NES_loadCostumeSet(fetchScriptByte());
      else if (_game.platform == Common::kPlatformC64)
            fetchScriptByte();
      else
            o2_dummy();
}

void ScummEngine_v2::resetSentence() {
      VAR(VAR_SENTENCE_VERB) = VAR(VAR_BACKUP_VERB);
      VAR(VAR_SENTENCE_OBJECT1) = 0;
      VAR(VAR_SENTENCE_OBJECT2) = 0;
      VAR(VAR_SENTENCE_PREPOSITION) = 0;
}

void ScummEngine_v2::runInventoryScript(int i) {
      redrawV2Inventory();
}

#undef PARAM_1
#undef PARAM_2
#undef PARAM_3

} // End of namespace Scumm

Generated by  Doxygen 1.6.0   Back to index