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

logic.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/logic.cpp $
 * $Id: logic.cpp 20975 2006-02-28 22:20:13Z cyx $
 *
 */

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

#include "common/config-manager.h"
#include "queen/bankman.h"
#include "queen/command.h"
#include "queen/credits.h"
#include "queen/cutaway.h"
#include "queen/debug.h"
#include "queen/defs.h"
#include "queen/display.h"
#include "queen/graphics.h"
#include "queen/grid.h"
#include "queen/input.h"
#include "queen/journal.h"
#include "queen/queen.h"
#include "queen/resource.h"
#include "queen/sound.h"
#include "queen/state.h"
#include "queen/talk.h"
#include "queen/walk.h"

namespace Queen {

static Common::String trim(const Common::String &s) {
      const char *p;

      p = s.c_str();
      while (*p == ' ') ++p;
      int start = p - s.c_str();

      p = s.c_str() + s.size() - 1;
      while (p != s.c_str() && *p == ' ') --p;
      int end = p - s.c_str();

      return Common::String(s.c_str() + start, end - start + 1);
}

Logic::Logic(QueenEngine *vm)
      : _credits(NULL), _objectData(NULL), _roomData(NULL), _sfxName(NULL),
      _itemData(NULL), _graphicData(NULL), _walkOffData(NULL), _objectDescription(NULL),
      _furnitureData(NULL), _actorData(NULL), _graphicAnim(NULL), _vm(vm) {
      _joe.x = _joe.y = 0;
      _joe.scale = 100;
      _joe.walk = JWM_NORMAL;
      memset(_gameState, 0, sizeof(_gameState));
      memset(_talkSelected, 0, sizeof(_talkSelected));
      _puzzleAttemptCount = 0;
      _journal = new Journal(vm);
      _scene = 0;
      initialise();
}

Logic::~Logic() {
      delete _journal;
      delete _credits;
      delete[] _objectData;
      delete[] _roomData;
      delete[] _sfxName;
      delete[] _itemData;
      delete[] _graphicData;
      delete[] _walkOffData;
      delete[] _objectDescription;
      delete[] _furnitureData;
      delete[] _actorData;
      delete[] _graphicAnim;
}

void Logic::initialise() {
      int16 i;

      uint8 *jas = _vm->resource()->loadFile("QUEEN.JAS", 20);
      uint8 *ptr = jas;

      _numRooms = READ_BE_UINT16(ptr); ptr += 2;
      _numNames = READ_BE_UINT16(ptr); ptr += 2;
      _numObjects = READ_BE_UINT16(ptr); ptr += 2;
      _numDescriptions = READ_BE_UINT16(ptr); ptr += 2;

      _objectData = new ObjectData[_numObjects + 1];
      memset(&_objectData[0], 0, sizeof(ObjectData));
      for (i = 1; i <= _numObjects; i++) {
            _objectData[i].readFromBE(ptr);
      }

      _roomData = new uint16[_numRooms + 2];
      _roomData[0] = 0;
      for (i = 1; i <= (_numRooms + 1); i++) {
            _roomData[i] = READ_BE_UINT16(ptr); ptr += 2;
      }
      _roomData[_numRooms + 1] = _numObjects;

      if (_vm->resource()->isDemo()) {
            _sfxName = NULL;
      } else {
            _sfxName = new uint16[_numRooms + 1];
            _sfxName[0] = 0;
            for (i = 1; i <= _numRooms; i++) {
                  _sfxName[i] = READ_BE_UINT16(ptr); ptr += 2;
            }
      }

      _numItems = READ_BE_UINT16(ptr); ptr += 2;
      _itemData = new ItemData[_numItems + 1];
      memset(&_itemData[0], 0, sizeof(ItemData));
      for (i = 1; i <= _numItems; i++) {
            _itemData[i].readFromBE(ptr);
      }

      _numGraphics = READ_BE_UINT16(ptr); ptr += 2;
      _graphicData = new GraphicData[_numGraphics + 1];
      memset(&_graphicData[0], 0, sizeof(GraphicData));
      for (i = 1; i <= _numGraphics; i++) {
            _graphicData[i].readFromBE(ptr);
      }

      _vm->grid()->readDataFrom(_numObjects, _numRooms, ptr);

      _numWalkOffs = READ_BE_UINT16(ptr); ptr += 2;
      _walkOffData = new WalkOffData[_numWalkOffs + 1];
      memset(&_walkOffData[0], 0, sizeof(WalkOffData));
      for (i = 1; i <= _numWalkOffs; i++) {
            _walkOffData[i].readFromBE(ptr);
      }

      _numObjDesc = READ_BE_UINT16(ptr); ptr += 2;
      _objectDescription = new ObjectDescription[_numObjDesc + 1];
      memset(&_objectDescription[0], 0, sizeof(ObjectDescription));
      for (i = 1; i <= _numObjDesc; i++) {
            _objectDescription[i].readFromBE(ptr);
      }

      _vm->command()->readCommandsFrom(ptr);

      _entryObj = READ_BE_UINT16(ptr); ptr += 2;

      _numFurniture = READ_BE_UINT16(ptr); ptr += 2;
      _furnitureData = new FurnitureData[_numFurniture + 1];
      memset(&_furnitureData[0], 0, sizeof(FurnitureData));
      for (i = 1; i <= _numFurniture; i++) {
            _furnitureData[i].readFromBE(ptr);
      }

      // Actors
      _numActors = READ_BE_UINT16(ptr); ptr += 2;
      _numAAnim = READ_BE_UINT16(ptr); ptr += 2;
      _numAName = READ_BE_UINT16(ptr); ptr += 2;
      _numAFile = READ_BE_UINT16(ptr); ptr += 2;

      _actorData = new ActorData[_numActors + 1];
      memset(&_actorData[0], 0, sizeof(ActorData));
      for (i = 1; i <= _numActors; i++) {
            _actorData[i].readFromBE(ptr);
      }

      _numGraphicAnim = READ_BE_UINT16(ptr); ptr += 2;

      _graphicAnim = new GraphicAnim[_numGraphicAnim + 1];
      if (_numGraphicAnim == 0) {
            _graphicAnim[0].readFromBE(ptr);
      } else {
            memset(&_graphicAnim[0], 0, sizeof(GraphicAnim));
            for (i = 1; i <= _numGraphicAnim; i++) {
                  _graphicAnim[i].readFromBE(ptr);
            }
      }

      _currentRoom = _objectData[_entryObj].room;
      _entryObj = 0;

      if (memcmp(ptr, _vm->resource()->JASVersion(), 5) != 0) {
            warning("Unexpected queen.jas file format");
      }

      delete[] jas;

      uint32 size;
      char *buf = (char *)_vm->resource()->loadFile("QUEEN2.JAS", 0, &size);
      LineReader queen2jas(buf, size);

      _objDescription.push_back("");
      for (i = 1; i <= _numDescriptions; i++) {
            _objDescription.push_back(queen2jas.nextLine());
      }

      // Patch for German text bug
      if (_vm->resource()->getLanguage() == GERMAN) {
            _objDescription[296] = "Es bringt nicht viel, das festzubinden.";
      }

      _objName.push_back("");
      for (i = 1; i <= _numNames; i++) {
            _objName.push_back(queen2jas.nextLine());
      }

      _roomName.push_back("");
      for (i = 1; i <= _numRooms; i++) {
            _roomName.push_back(queen2jas.nextLine());
      }

      _verbName.push_back("");
      for (i = 1; i <= 12; i++) {
            _verbName.push_back(queen2jas.nextLine());
      }

      _joeResponse.push_back("");
      for (i = 1; i <= JOE_RESPONSE_MAX; i++) {
            _joeResponse.push_back(queen2jas.nextLine());
      }

      // Spanish version adds some space characters (0x20) at the beginning
      // and the end of the journal button captions. As the engine computes
      // the text width to center it, we need to trim those strings.
      if (_vm->resource()->getLanguage() == SPANISH) {
            for (i = 30; i <= 35; i++) {
                  _joeResponse[i] = trim(_joeResponse[i]);
            }
      }

      _aAnim.push_back("");
      for (i = 1; i <= _numAAnim; i++) {
            _aAnim.push_back(queen2jas.nextLine());
      }

      _aName.push_back("");
      for (i = 1; i <= _numAName; i++) {
            _aName.push_back(queen2jas.nextLine());
      }

      _aFile.push_back("");
      for (i = 1; i <= _numAFile; i++) {
            _aFile.push_back(queen2jas.nextLine());
      }
}

void Logic::start() {
      _vm->command()->clear(false);
      _vm->display()->setupPanel();
      _vm->graphics()->unpackControlBank();
      _vm->graphics()->setupMouseCursor();
      setupJoe();
      _vm->grid()->setupPanel();
      inventorySetup();

      _oldRoom = 0;
      _newRoom = _currentRoom;
}

ObjectData* Logic::objectData(int index) const {
      assert(index >= 0 && index <= _numObjects);
      return &_objectData[index];
}

uint16 Logic::findBob(uint16 obj) const {
      assert(obj <= _numObjects);

      uint16 room = _objectData[obj].room;
      assert(room <= _numRooms);

      uint16 bobnum = 0;
      int16 img = _objectData[obj].image;
      if (img != 0) {
            if (img == -3 || img == -4) {
                  // a person object
                  bobnum = findPersonNumber(obj, room);
            } else {
                  uint16 bobtype = 0; // 1 for animated, 0 for static

                  if (img <= -10) {
                        // object has been turned off, but the image order hasn't been updated
                        if (_graphicData[-(img + 10)].lastFrame != 0) {
                              bobtype = 1;
                        }
                  } else if (img == -2) {
                        // -1 static, -2 animated
                        bobtype = 1;
                  } else if (img > 0) {
                        if (_graphicData[img].lastFrame != 0) {
                              bobtype = 1;
                        }
                  }

                  uint16 idxAnimated = 0;
                  uint16 idxStatic = 0;
                  for (uint16 i = _roomData[room] + 1; i <= obj; ++i) {
                        img = _objectData[i].image;
                        if (img <= -10) {
                              if (_graphicData[-(img + 10)].lastFrame != 0) {
                                    ++idxAnimated;
                              } else {
                                    ++idxStatic;
                              }
                        } else if (img > 0) {
                              if (img > 5000) {
                                    img -= 5000;
                              }

                              assert (img <= _numGraphics);

                              if (_graphicData[img].lastFrame != 0) {
                                    ++idxAnimated;
                              } else {
                                    ++idxStatic;
                              }
                        } else if (img == -1) {
                              ++idxStatic;
                        } else if (img == -2) {
                              ++idxAnimated;
                        }
                  }
                  if (bobtype == 0) {
                        // static bob
                        if (idxStatic > 0) {
                              bobnum = 19 + _vm->graphics()->numStaticFurniture() + idxStatic;
                        }
                  } else {
                        // animated bob
                        if (idxAnimated > 0) {
                              bobnum = 4 + _vm->graphics()->numAnimatedFurniture() + idxAnimated;
                        }
                  }
            }
      }
      return bobnum;
}

uint16 Logic::findFrame(uint16 obj) const {
      uint16 framenum = 0;
      uint16 room = _objectData[obj].room;
      int16 img = _objectData[obj].image;
      if (img == -3 || img == -4) {
            uint16 bobnum = findPersonNumber(obj, room);
            if (bobnum <= 3) {
                  framenum = 31 + bobnum;
            }
      } else {
            uint16 idx = 0;
            for (uint16 i = _roomData[room] + 1; i < obj; ++i) {
                  img = _objectData[i].image;
                  if (img <= -10) {
                        const GraphicData* pgd = &_graphicData[-(img + 10)];
                        if (pgd->lastFrame != 0) {
                              // skip all the frames of the animation
                              idx += ABS(pgd->lastFrame) - pgd->firstFrame + 1;
                        } else {
                              // static bob, skip one frame
                              ++idx;
                        }
                  } else if (img == -1) {
                        ++idx;
                  } else if (img > 0) {
                        if (img > 5000) {
                              img -= 5000;
                        }
                        const GraphicData* pgd = &_graphicData[img];
                        uint16 lastFrame = ABS(pgd->lastFrame);
                        if (pgd->firstFrame < 0) {
                              idx += lastFrame;
                        } else if (lastFrame != 0) {
                              idx += (lastFrame - pgd->firstFrame) + 1;
                        } else {
                              ++idx;
                        }
                  }
            }

            img = _objectData[obj].image;
            if (img <= -10) {
                  const GraphicData* pgd = &_graphicData[-(img + 10)];
                  if (pgd->lastFrame != 0) {
                        idx += ABS(pgd->lastFrame) - pgd->firstFrame + 1;
                  } else {
                        ++idx;
                  }
            } else if (img == -1 || img > 0) {
                  ++idx;
            }

            // calculate only if there are person frames
            if (idx > 0) {
                  framenum = FRAMES_JOE + _vm->graphics()->numFurnitureFrames() + idx;
            }
      }
      return framenum;
}

uint16 Logic::objectForPerson(uint16 bobNum) const {
      uint16 bobcur = 0;
      // first object number in the room
      uint16 cur = currentRoomData() + 1;
      // last object number in the room
      uint16 last = _roomData[_currentRoom + 1];
      for (; cur <= last; ++cur) {
            int16 image = _objectData[cur].image;
            if (image == -3 || image == -4) {
                  // the object is a bob
                  ++bobcur;
            }
            if (bobcur == bobNum) {
                  return cur;
            }
      }
      return 0;
}

WalkOffData *Logic::walkOffPointForObject(int16 obj) const {
      for (uint16 i = 1; i <= _numWalkOffs; ++i) {
            if (_walkOffData[i].entryObj == obj) {
                  return &_walkOffData[i];
            }
      }
      return NULL;
}

void Logic::joeWalk(JoeWalkMode walking) {
      _joe.walk = walking;
      // Do this so that Input doesn't need to know the walk value
      _vm->input()->dialogueRunning(JWM_SPEAK == walking);
}

int16 Logic::gameState(int index) const {
      assert(index >= 0 && index < GAME_STATE_COUNT);
      return _gameState[index];
}

void Logic::gameState(int index, int16 newValue) {
      assert(index >= 0 && index < GAME_STATE_COUNT);
      debug(8, "Logic::gameState() [%d] = %d", index, newValue);
      _gameState[index] = newValue;
}

const char *Logic::roomName(uint16 roomNum) const {
      assert(roomNum >= 1 && roomNum <= _numRooms);
      return _roomName[roomNum].c_str();
}

const char *Logic::objectName(uint16 objNum) const {
      assert(objNum >= 1 && objNum <= _numNames);
      return _objName[objNum].c_str();
}

const char *Logic::objectTextualDescription(uint16 objNum) const {
      assert(objNum >= 1 && objNum <= _numDescriptions);
      return _objDescription[objNum].c_str();
}

const char *Logic::joeResponse(int i) const {
      assert(i >= 1 && i <= JOE_RESPONSE_MAX);
      return _joeResponse[i].c_str();
}

const char *Logic::verbName(Verb v) const {
      assert(v >= 0 && v <= 12);
      return _verbName[v].c_str();
}

void Logic::eraseRoom() {
      _vm->bankMan()->eraseFrames(false);
      _vm->bankMan()->close(15);
      _vm->bankMan()->close(11);
      _vm->bankMan()->close(10);
      _vm->bankMan()->close(12);

      _vm->display()->palFadeOut(_currentRoom);

      // invalidates all persons animations
      _vm->graphics()->clearPersonFrames();
      _vm->graphics()->eraseAllAnims();

      uint16 cur = _roomData[_oldRoom] + 1;
      uint16 last = _roomData[_oldRoom + 1];
      for (; cur <= last; ++cur) {
            ObjectData *pod = &_objectData[cur];
            if (pod->name == 0) {
                  // object has been deleted, invalidate image
                  pod->image = 0;
            } else if (pod->image > -4000 && pod->image <= -10) {
                  if (_graphicData[ABS(pod->image + 10)].lastFrame == 0) {
                        // static Bob
                        pod->image = -1;
                  } else {
                        // animated Bob
                        pod->image = -2;
                  }
            }
      }
}

void Logic::setupRoom(const char *room, int comPanel, bool inCutaway) {
      // load backdrop image, init dynalum, setup colors
      _vm->display()->setupNewRoom(room, _currentRoom);

      // setup graphics to enter fullscreen/panel mode
      _vm->display()->screenMode(comPanel, inCutaway);

      _vm->grid()->setupNewRoom(_currentRoom, _roomData[_currentRoom]);

      int16 furn[9];
      uint16 furnTot = 0;
      for (uint16 i = 1; i <= _numFurniture; ++i) {
            if (_furnitureData[i].room == _currentRoom) {
                  ++furnTot;
                  furn[furnTot] = _furnitureData[i].objNum;
            }
      }
      _vm->graphics()->setupNewRoom(room, _currentRoom, furn, furnTot);

      _vm->display()->forceFullRefresh();
}

void Logic::displayRoom(uint16 room, RoomDisplayMode mode, uint16 scale, int comPanel, bool inCutaway) {
      debug(6, "Logic::displayRoom(%d, %d, %d, %d, %d)", room, mode, scale, comPanel, inCutaway);

      eraseRoom();

      if (_credits)
            _credits->nextRoom();

      setupRoom(roomName(room), comPanel, inCutaway);
      if (mode != RDM_FADE_NOJOE) {
            setupJoeInRoom(mode != RDM_FADE_JOE_XY, scale);
      }
      if (mode != RDM_NOFADE_JOE) {
            _vm->update();
            BobSlot *joe = _vm->graphics()->bob(0);
            _vm->display()->palFadeIn(_currentRoom, joe->active, joe->x, joe->y);
      }
      if (mode != RDM_FADE_NOJOE && joeX() != 0 && joeY() != 0) {
            int16 jx = joeX();
            int16 jy = joeY();
            joePos(0, 0);
            _vm->walk()->moveJoe(0, jx, jy, inCutaway);
      }
}

ActorData *Logic::findActor(uint16 noun, const char *name) const {
      uint16 obj = currentRoomData() + noun;
      int16 img = objectData(obj)->image;
      if (img != -3 && img != -4) {
            warning("Logic::findActor() - Object %d is not a person", obj);
            return NULL;
      }

      // search Bob number for the person
      uint16 bobNum = findPersonNumber(obj, _currentRoom);

      // search for a matching actor
      if (bobNum > 0) {
            for (uint16 i = 1; i <= _numActors; ++i) {
                  ActorData *pad = &_actorData[i];
                  if (pad->room == _currentRoom && gameState(pad->gsSlot) == pad->gsValue) {
                        if (bobNum == pad->bobNum || (name && _aName[pad->name] == name)) {
                              return pad;
                        }
                  }
            }
      }
      return NULL;
}

bool Logic::initPerson(uint16 noun, const char *actorName, bool loadBank, Person *pp) {
      const ActorData *pad = findActor(noun, actorName);
      if (pad != NULL) {
            pp->actor = pad;
            pp->name = _aName[pad->name].c_str();
            if (pad->anim != 0) {
                  pp->anim = _aAnim[pad->anim].c_str();
            } else {
                  pp->anim = NULL;
            }
            if (loadBank && pad->file != 0) {
                  _vm->bankMan()->load(_aFile[pad->file].c_str(), pad->bankNum);
                  // if there is no valid actor file (ie pad->file is 0), the person
                  // data is already loaded as it is included in objects room bank (.bbk)
            }
            pp->bobFrame = 31 + pp->actor->bobNum;
      }
      return pad != NULL;
}

uint16 Logic::findPersonNumber(uint16 obj, uint16 room) const {
      uint16 num = 0;
      for (uint16 i = _roomData[room] + 1; i <= obj; ++i) {
            int16 img = _objectData[i].image;
            if (img == -3 || img == -4) {
                  ++num;
            }
      }
      return num;
}

void Logic::loadJoeBanks(const char *animBank, const char *standBank) {
      _vm->bankMan()->load(animBank, 13);
      for (int i = 11; i < 31; ++i) {
            _vm->bankMan()->unpack(i - 10, i, 13);
      }
      _vm->bankMan()->close(13);

      _vm->bankMan()->load(standBank, 7);
      _vm->bankMan()->unpack(1, 35, 7);
      _vm->bankMan()->unpack(3, 36, 7);
      _vm->bankMan()->unpack(5, 37, 7);
}

void Logic::setupJoe() {
      loadJoeBanks("joe_a.BBK", "joe_b.BBK");
      joePrevFacing(DIR_FRONT);
      joeFacing(DIR_FRONT);
}

void Logic::setupJoeInRoom(bool autoPosition, uint16 scale) {
      debug(9, "Logic::setupJoeInRoom(%d, %d) joe.x=%d joe.y=%d", autoPosition, scale, _joe.x, _joe.y);

      int16 oldx, oldy;
      if (!autoPosition || joeX() != 0 || joeY() != 0) {
            oldx = joeX();
            oldy = joeY();
            joePos(0, 0);
      } else {
            const ObjectData *pod = objectData(_entryObj);
            // find the walk off point for the entry object and make
            // Joe walking to that point
            const WalkOffData *pwo = walkOffPointForObject(_entryObj);
            if (pwo != NULL) {
                  oldx = pwo->x;
                  oldy = pwo->y;
                  // entryObj has a walk off point, then walk from there to object x,y
                  joePos(pod->x, pod->y);
            } else {
                  // no walk off point, use object position
                  oldx = pod->x;
                  oldy = pod->y;
                  joePos(0, 0);
            }
      }

      debug(6, "Logic::setupJoeInRoom() - oldx=%d, oldy=%d scale=%d", oldx, oldy, scale);

      if (scale > 0 && scale < 100) {
            joeScale(scale);
      } else {
            uint16 a = _vm->grid()->findAreaForPos(GS_ROOM, oldx, oldy);
            if (a > 0) {
                  joeScale(_vm->grid()->area(_currentRoom, a)->calcScale(oldy));
            } else {
                  joeScale(100);
            }
      }

      if (joeCutFacing() > 0) {
            joeFacing(joeCutFacing());
            joeCutFacing(0);
      } else {
            // check to see which way Joe entered room
            const ObjectData *pod = objectData(_entryObj);
            switch (State::findDirection(pod->state)) {
            case DIR_BACK:
                  joeFacing(DIR_FRONT);
                  break;
            case DIR_FRONT:
                  joeFacing(DIR_BACK);
                  break;
            case DIR_LEFT:
                  joeFacing(DIR_RIGHT);
                  break;
            case DIR_RIGHT:
                  joeFacing(DIR_LEFT);
                  break;
            }
      }
      joePrevFacing(joeFacing());

      BobSlot *pbs = _vm->graphics()->bob(0);
      pbs->scale = joeScale();

      if (_currentRoom == 108) {
            _vm->graphics()->putCameraOnBob(-1);
            _vm->bankMan()->load("joe_e.act", 7);
            _vm->bankMan()->unpack(2, 31, 7);

            _vm->display()->horizontalScroll(320);

            joeFacing(DIR_RIGHT);
            joeCutFacing(DIR_RIGHT);
            joePrevFacing(DIR_RIGHT);
      }

      joeFace();
      pbs->curPos(oldx, oldy);
      pbs->frameNum = 31;
}

uint16 Logic::joeFace() {
      debug(9, "Logic::joeFace() - curFace = %d, prevFace = %d", _joe.facing, _joe.prevFacing);
      BobSlot *pbs = _vm->graphics()->bob(0);
      uint16 frame;
      if (_currentRoom == 108) {
            frame = 1;
      } else {
            frame = 35;
            if (joeFacing() == DIR_FRONT) {
                  if (joePrevFacing() == DIR_BACK) {
                        pbs->frameNum = 35;
                        _vm->update();
                  }
                  frame = 36;
            } else if (joeFacing() == DIR_BACK) {
                  if (joePrevFacing() == DIR_FRONT) {
                        pbs->frameNum = 35;
                        _vm->update();
                  }
                  frame = 37;
            } else if ((joeFacing() == DIR_LEFT && joePrevFacing() == DIR_RIGHT)
                  ||  (joeFacing() == DIR_RIGHT && joePrevFacing() == DIR_LEFT)) {
                  pbs->frameNum = 36;
                  _vm->update();
            }
            pbs->frameNum = frame;
            pbs->scale = joeScale();
            pbs->xflip = (joeFacing() == DIR_LEFT);
            _vm->update();
            joePrevFacing(joeFacing());
            switch (frame) {
            case 35:
                  frame = 1;
                  break;
            case 36:
                  frame = 3;
                  break;
            case 37:
                  frame = 5;
                  break;
            }
      }
      pbs->frameNum = 31;
      _vm->bankMan()->unpack(frame, pbs->frameNum, 7);
      return frame;
}

void Logic::joeGrab(int16 grabState) {
      uint16 frame = 0;
      BobSlot *bobJoe = _vm->graphics()->bob(0);

      switch (grabState) {
      case STATE_GRAB_NONE:
            break;
      case STATE_GRAB_MID:
            if (joeFacing() == DIR_BACK) {
                  frame = 6;
            } else if (joeFacing() == DIR_FRONT) {
                  frame = 4;
            } else {
                  frame = 2;
            }
            break;
      case STATE_GRAB_DOWN:
            if (joeFacing() == DIR_BACK) {
                  frame = 9;
            } else {
                  frame = 8;
            }
            break;
      case STATE_GRAB_UP:
            // turn back
            _vm->bankMan()->unpack(5, 31, 7);
            bobJoe->xflip = (joeFacing() == DIR_LEFT);
            bobJoe->scale = joeScale();
            _vm->update();
            // grab up
            _vm->bankMan()->unpack(7, 31, 7);
            bobJoe->xflip = (joeFacing() == DIR_LEFT);
            bobJoe->scale = joeScale();
            _vm->update();
            // turn back
            frame = 7;
            break;
      }

      if (frame != 0) {
            _vm->bankMan()->unpack(frame, 31, 7);
            bobJoe->xflip = (joeFacing() == DIR_LEFT);
            bobJoe->scale = joeScale();
            _vm->update();

            // extra delay for grab down
            if (grabState == STATE_GRAB_DOWN) {
                  _vm->update();
                  _vm->update();
            }
      }
}

void Logic::joeUseDress(bool showCut) {
      if (showCut) {
            joeFacing(DIR_FRONT);
            joeFace();
            if (gameState(VAR_JOE_DRESSING_MODE) == 0) {
                  playCutaway("cdres.CUT");
                  inventoryInsertItem(ITEM_CLOTHES);
            } else {
                  playCutaway("cudrs.CUT");
            }
      }
      _vm->display()->palSetJoeDress();
      loadJoeBanks("JoeD_A.BBK", "JoeD_B.BBK");
      inventoryDeleteItem(ITEM_DRESS);
      gameState(VAR_JOE_DRESSING_MODE, 2);
}

void Logic::joeUseClothes(bool showCut) {
      if (showCut) {
            joeFacing(DIR_FRONT);
            joeFace();
            playCutaway("cdclo.CUT");
            inventoryInsertItem(ITEM_DRESS);
      }
      _vm->display()->palSetJoeNormal();
      loadJoeBanks("Joe_A.BBK", "Joe_B.BBK");
      inventoryDeleteItem(ITEM_CLOTHES);
      gameState(VAR_JOE_DRESSING_MODE, 0);
}

void Logic::joeUseUnderwear() {
      _vm->display()->palSetJoeNormal();
      loadJoeBanks("JoeU_A.BBK", "JoeU_B.BBK");
      gameState(VAR_JOE_DRESSING_MODE, 1);
}

void Logic::makePersonSpeak(const char *sentence, Person *person, const char *voiceFilePrefix) {
      _vm->command()->clear(false);
      Talk::speak(sentence, person, voiceFilePrefix, _vm);
}

void Logic::startDialogue(const char *dlgFile, int personInRoom, char *cutaway) {
      ObjectData *data = objectData(_roomData[_currentRoom] + personInRoom);
      if (data->name > 0 && data->entryObj <= 0) {
            if (State::findTalk(data->state) == STATE_TALK_MUTE) {
                  // 'I can't talk to that'
                  makeJoeSpeak(24 + _vm->randomizer.getRandomNumber(2));
            } else {
                  char cutawayFile[20];
                  if (cutaway == NULL) {
                        cutaway = cutawayFile;
                  }
                  _vm->display()->fullscreen(true);
                  Talk::talk(dlgFile, personInRoom, cutaway, _vm);
                  if (!cutaway[0]) {
                        _vm->display()->fullscreen(false);
                  }
            }
      }
}

void Logic::playCutaway(const char *cutFile, char *next) {
      char nextFile[20];
      if (next == NULL) {
            next = nextFile;
      }
      _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
      Cutaway::run(cutFile, next, _vm);
}

void Logic::makeJoeSpeak(uint16 descNum, bool objectType) {
      const char *text = objectType ? _objDescription[descNum].c_str() : _joeResponse[descNum].c_str();
      if (objectType) {
            descNum += JOE_RESPONSE_MAX;
      }
      char descFilePrefix[10];
      sprintf(descFilePrefix, "JOE%04i", descNum);
      makePersonSpeak(text, NULL, descFilePrefix);
}

uint16 Logic::findInventoryItem(int invSlot) const {
      // queen.c l.3894-3898
      if (invSlot >= 0 && invSlot < 4) {
            return _inventoryItem[invSlot];
      }
      return 0;
}

void Logic::inventorySetup() {
      _vm->bankMan()->load("objects.BBK", 14);
      if (_vm->resource()->isInterview()) {
            _inventoryItem[0] = 1;
            _inventoryItem[1] = 2;
            _inventoryItem[2] = 3;
            _inventoryItem[3] = 4;
      } else {
            _inventoryItem[0] = ITEM_BAT;
            _inventoryItem[1] = ITEM_JOURNAL;
            _inventoryItem[2] = ITEM_NONE;
            _inventoryItem[3] = ITEM_NONE;
      }
}

void Logic::inventoryRefresh() {
      uint16 x = 182;
      for (int i = 0; i < 4; ++i) {
            uint16 itemNum = _inventoryItem[i];
            if (itemNum != 0) {
                  // 1st object in inventory uses frame 9,
                  // whereas 2nd, 3rd and 4th uses frame 8
                  uint16 dstFrame = (itemNum != 0) ? 8 : 9;
                  // unpack frame for object and draw it
                  _vm->bankMan()->unpack(_itemData[itemNum].frame, dstFrame, 14);
                  _vm->graphics()->drawInventoryItem(dstFrame, x, 14);
            } else {
                  // no object, clear the panel
                  _vm->graphics()->drawInventoryItem(0, x, 14);
            }
            x += 35;
      }
}

int16 Logic::previousInventoryItem(int16 first) const {
      int i;
      for (i = first - 1; i >= 1; i--)
            if (_itemData[i].name > 0)
                  return i;
      for (i = _numItems; i > first; i--)
            if (_itemData[i].name > 0)
                  return i;

      return 0;   //nothing found
}

int16 Logic::nextInventoryItem(int16 first) const {
      int i;
      for (i = first + 1; i < _numItems; i++)
            if (_itemData[i].name > 0)
                  return i;
      for (i = 1; i < first; i++)
            if (_itemData[i].name > 0)
                  return i;

      return 0;   //nothing found
}

void Logic::removeDuplicateItems() {
      for (int i = 0; i < 4; i++)
            for (int j = i + 1; j < 4; j++)
                  if (_inventoryItem[i] == _inventoryItem[j])
                        _inventoryItem[j] = ITEM_NONE;
}

uint16 Logic::numItemsInventory() const {
      uint16 count = 0;
      for (int i = 1; i < _numItems; i++)
            if (_itemData[i].name > 0)
                  count++;

      return count;
}

void Logic::inventoryInsertItem(uint16 itemNum, bool refresh) {
      int16 item = _inventoryItem[0] = (int16)itemNum;
      _itemData[itemNum].name = ABS(_itemData[itemNum].name);     //set visible
      for (int i = 1; i < 4; i++) {
            item = nextInventoryItem(item);
            _inventoryItem[i] = item;
            removeDuplicateItems();
      }

      if (refresh)
            inventoryRefresh();
}

void Logic::inventoryDeleteItem(uint16 itemNum, bool refresh) {
      int16 item = (int16)itemNum;
      _itemData[itemNum].name = -ABS(_itemData[itemNum].name);    //set invisible
      for (int i = 0; i < 4; i++) {
            item = nextInventoryItem(item);
            _inventoryItem[i] = item;
            removeDuplicateItems();
      }

      if (refresh)
            inventoryRefresh();
}

void Logic::inventoryScroll(uint16 count, bool up) {
      if (!(numItemsInventory() > 4))
            return;
      while (count--) {
            if (up) {
                  for (int i = 3; i > 0; i--)
                        _inventoryItem[i] = _inventoryItem[i - 1];
                  _inventoryItem[0] = previousInventoryItem(_inventoryItem[0]);
            } else {
                  for (int i = 0; i < 3; i++)
                        _inventoryItem[i] = _inventoryItem[i + 1];
                  _inventoryItem[3] = nextInventoryItem(_inventoryItem[3]);
            }
      }

      inventoryRefresh();
}

void Logic::removeHotelItemsFromInventory() {
      if (currentRoom() == 1 && gameState(VAR_HOTEL_ITEMS_REMOVED) == 0) {
            inventoryDeleteItem(ITEM_CROWBAR, false);
            inventoryDeleteItem(ITEM_DRESS, false);
            inventoryDeleteItem(ITEM_CLOTHES, false);
            inventoryDeleteItem(ITEM_HAY, false);
            inventoryDeleteItem(ITEM_OIL, false);
            inventoryDeleteItem(ITEM_CHICKEN, false);
            gameState(VAR_HOTEL_ITEMS_REMOVED, 1);
            inventoryRefresh();
      }
}

void Logic::objectCopy(int dummyObjectIndex, int realObjectIndex) {
      // copy data from dummy object to real object, if COPY_FROM object
      // images are greater than COPY_TO Object images then swap the objects around.

      ObjectData *dummyObject = objectData(dummyObjectIndex);
      ObjectData *realObject  = objectData(realObjectIndex);

      int fromState = (dummyObject->name < 0) ? -1 : 0;

      int frameCountReal  = 1;
      int frameCountDummy = 1;

      int graphic = realObject->image;
      if (graphic > 0) {
            if (graphic > 5000)
                  graphic -= 5000;

            GraphicData *data = graphicData(graphic);

            if (data->lastFrame > 0)
                  frameCountReal = data->lastFrame - data->firstFrame + 1;

            graphic = dummyObject->image;
            if (graphic > 0) {
                  if (graphic > 5000)
                        graphic -= 5000;

                  data = graphicData(graphic);

                  if (data->lastFrame > 0)
                        frameCountDummy = data->lastFrame - data->firstFrame + 1;
            }
      }

      ObjectData temp = *realObject;
      *realObject = *dummyObject;

      if (frameCountDummy > frameCountReal)
            *dummyObject = temp;

      realObject->name = ABS(realObject->name);

      if (fromState == -1)
            dummyObject->name = -ABS(dummyObject->name);

      for (int i = 1; i <= _numWalkOffs; i++) {
            WalkOffData *walkOff = &_walkOffData[i];
            if (walkOff->entryObj == (int16)dummyObjectIndex) {
                  walkOff->entryObj = (int16)realObjectIndex;
                  break;
            }
      }
}

void Logic::handleSpecialArea(Direction facing, uint16 areaNum, uint16 walkDataNum) {
      // queen.c l.2838-2911
      debug(9, "handleSpecialArea(%d, %d, %d)\n", facing, areaNum, walkDataNum);

      // Stop animating Joe
      _vm->graphics()->bob(0)->animating = false;

      // Make Joe face the right direction
      joeFacing(facing);
      joeFace();

      _newRoom = 0;
      _entryObj = 0;

      char nextCut[20];
      memset(nextCut, 0, sizeof(nextCut));

      switch (_currentRoom) {
      case ROOM_JUNGLE_BRIDGE:
            makeJoeSpeak(16);
            break;
      case ROOM_JUNGLE_GORILLA_1:
            playCutaway("c6c.CUT", nextCut);
            break;
      case ROOM_JUNGLE_GORILLA_2:
            playCutaway("c14b.CUT", nextCut);
            break;
      case ROOM_AMAZON_ENTRANCE:
            if (areaNum == 3) {
                  playCutaway("c16a.CUT", nextCut);
            }
            break;
      case ROOM_AMAZON_HIDEOUT:
            if (walkDataNum == 4) {
                  playCutaway("c17a.CUT", nextCut);
            } else if (walkDataNum == 2) {
                  playCutaway("c17b.CUT", nextCut);
            }
            break;
      case ROOM_FLODA_OUTSIDE:
            playCutaway("c22a.CUT", nextCut);
            break;
      case ROOM_FLODA_KITCHEN:
            playCutaway("c26b.CUT", nextCut);
            break;
      case ROOM_FLODA_KLUNK:
            playCutaway("c30a.CUT", nextCut);
            break;
      case ROOM_FLODA_HENRY:
            playCutaway("c32c.CUT", nextCut);
            break;
      case ROOM_TEMPLE_ZOMBIES:
            if (areaNum == 6) {
                  switch (gameState(VAR_BYPASS_ZOMBIES)) {
                  case 0:
                        playCutaway("c50d.CUT", nextCut);
                        while (nextCut[0] != '\0') {
                              playCutaway(nextCut, nextCut);
                        }
                        gameState(VAR_BYPASS_ZOMBIES, 1);
                        break;
                  case 1:
                        playCutaway("c50h.CUT", nextCut);
                        break;
                  }
            }
            break;
      case ROOM_TEMPLE_SNAKE:
            playCutaway("c53b.CUT", nextCut);
            break;
      case ROOM_TEMPLE_LIZARD_LASER:
            makeJoeSpeak(19);
            break;
      case ROOM_HOTEL_DOWNSTAIRS:
            makeJoeSpeak(21);
            break;
      case ROOM_HOTEL_LOBBY:
            switch (gameState(VAR_HOTEL_ESCAPE_STATE)) {
            case 0:
                  playCutaway("c73a.CUT");
                  joeUseUnderwear();
                  joeFace();
                  gameState(VAR_HOTEL_ESCAPE_STATE, 1);
                  break;
            case 1:
                  playCutaway("c73b.CUT");
                  gameState(VAR_HOTEL_ESCAPE_STATE, 2);
                  break;
            case 2:
                  playCutaway("c73c.CUT");
                  break;
            }
            break;
      case ROOM_TEMPLE_MAZE_5:
            if (areaNum == 7) {
                  makeJoeSpeak(17);
            }
            break;
      case ROOM_TEMPLE_MAZE_6:
            if (areaNum == 5 && gameState(187) == 0) {
                  playCutaway("c101b.CUT", nextCut);
            }
            break;
      case ROOM_FLODA_FRONTDESK:
            if (areaNum == 3) {
                  switch (gameState(VAR_BYPASS_FLODA_RECEPTIONIST)) {
                  case 0:
                        playCutaway("c103b.CUT", nextCut);
                        gameState(VAR_BYPASS_FLODA_RECEPTIONIST, 1);
                        break;
                  case 1:
                        playCutaway("c103e.CUT", nextCut);
                        break;
                  }
            }
            break;
      }

      while (strlen(nextCut) > 4 &&
            scumm_stricmp(nextCut + strlen(nextCut) - 4, ".cut") == 0) {
            playCutaway(nextCut, nextCut);
      }
}

void Logic::handlePinnacleRoom() {
      // camera does not follow Joe anymore
      _vm->graphics()->putCameraOnBob(-1);
      displayRoom(ROOM_JUNGLE_PINNACLE, RDM_NOFADE_JOE, 100, 2, true);

      BobSlot *joe   = _vm->graphics()->bob(6);
      BobSlot *piton = _vm->graphics()->bob(7);

      // set scrolling value to mouse position to avoid glitch
      _vm->display()->horizontalScroll(_vm->input()->mousePosX());

      joe->x = piton->x = 3 * _vm->input()->mousePosX() / 4 + 200;
      joe->frameNum = _vm->input()->mousePosX() / 36 + 45;

      // bobs have been unpacked from animating objects, we don't need them
      // to animate anymore ; so turn animation off
      joe->animating = piton->animating = false;

      _vm->update();
      _vm->display()->palFadeIn(ROOM_JUNGLE_PINNACLE, joe->active, joe->x, joe->y);

      _entryObj = 0;
      uint16 prevObj = 0;
      CmdText cmdText((_vm->resource()->getLanguage() == HEBREW), 5, _vm);
      cmdText.setVerb(VERB_WALK_TO);
      while (_vm->input()->mouseButton() == 0 || _entryObj == 0) {

            _vm->update();
            int mx = _vm->input()->mousePosX();
            int my = _vm->input()->mousePosY();

            // update screen scrolling
            _vm->display()->horizontalScroll(mx);

            // update bobs position / frame
            joe->x = piton->x = 3 * mx / 4 + 200;
            joe->frameNum = mx / 36 + 45;

            _vm->display()->clearTexts(5, 5);

            uint16 curObj = _vm->grid()->findObjectUnderCursor(mx, my);
            if (curObj != 0 && curObj != prevObj) {
                  _entryObj = 0;
                  curObj += currentRoomData(); // global object number
                  ObjectData *objData = objectData(curObj);
                  if (objData->name > 0) {
                        _entryObj = objData->entryObj;
                        cmdText.displayTemp(INK_PINNACLE_ROOM, objectName(objData->name), true);
                  }
                  prevObj = curObj;
            }
      }
      _vm->input()->clearMouseButton();

      _newRoom = objectData(_entryObj)->room;

      // Only a few commands can be triggered from this room :
      // piton -> crash  : 0x216 (obj1=0x2a, song=3)
      // piton -> floda  : 0x217 (obj1=0x29, song=16)
      // piton -> bob    : 0x219 (obj1=0x2f, song=6)
      // piton -> embark : 0x218 (obj1=0x2c, song=7)
      // piton -> jungle : 0x20B (obj1=0x2b, song=3)
      // piton -> amazon : 0x21A (obj1=0x30, song=3)
      //
      // Because none of these update objects/areas/gamestate, the EXECUTE_ACTION()
      // call, as the original does, is useless. All we have to do is the playsong
      // call (all songs have the PLAY_BEFORE type). This way we could get rid of
      // the hack described in execute.c l.334-339.
      struct {
            uint16 obj;
            int16 song;
      } cmds[] = {
            { 0x2A,  3 },
            { 0x29, 16 },
            { 0x2F,  6 },
            { 0x2C,  7 },
            { 0x2B,  3 },
            { 0x30,  3 }
      };
      for (int i = 0; i < ARRAYSIZE(cmds); ++i) {
            if (cmds[i].obj == prevObj) {
                  _vm->sound()->playSong(cmds[i].song);
                  break;
            }
      }

      joe->active = piton->active = false;
      _vm->display()->clearTexts(5, 5);

      // camera follows Joe again
      _vm->graphics()->putCameraOnBob(0);

      _vm->display()->palFadeOut(ROOM_JUNGLE_PINNACLE);
}

void Logic::update() {
      if (_credits)
            _credits->update();

      if (_vm->debugger()->flags() & Debugger::DF_DRAW_AREAS) {
            _vm->grid()->drawZones();
      }
}

void Logic::saveState(byte *&ptr) {
      uint16 i;
      for (i = 0; i < 4; i++) {
            WRITE_BE_UINT16(ptr, _inventoryItem[i]); ptr += 2;
      }

      WRITE_BE_UINT16(ptr, _vm->graphics()->bob(0)->x); ptr += 2;
      WRITE_BE_UINT16(ptr, _vm->graphics()->bob(0)->y); ptr += 2;

      WRITE_BE_UINT16(ptr, _currentRoom); ptr += 2;

      for (i = 1; i <= _numObjects; i++)
            _objectData[i].writeToBE(ptr);

      for (i = 1; i <= _numItems; i++)
            _itemData[i].writeToBE(ptr);

      for (i = 0; i < GAME_STATE_COUNT; i++) {
            WRITE_BE_UINT16(ptr, _gameState[i]); ptr += 2;
      }

      for (i = 0; i < TALK_SELECTED_COUNT; i++)
            _talkSelected[i].writeToBE(ptr);

      for (i = 1; i <= _numWalkOffs; i++)
            _walkOffData[i].writeToBE(ptr);

      WRITE_BE_UINT16(ptr, _joe.facing); ptr += 2;

      // V1
      WRITE_BE_UINT16(ptr, _puzzleAttemptCount); ptr += 2;
      for (i = 1; i <= _numObjDesc; i++)
            _objectDescription[i].writeToBE(ptr);
}

void Logic::loadState(uint32 ver, byte *&ptr) {
      uint16 i;
      for (i = 0; i < 4; i++) {
            _inventoryItem[i] = (int16)READ_BE_INT16(ptr); ptr += 2;
      }

      _joe.x = (int16)READ_BE_INT16(ptr); ptr += 2;
      _joe.y = (int16)READ_BE_INT16(ptr); ptr += 2;

      _currentRoom = READ_BE_UINT16(ptr); ptr += 2;

      for (i = 1; i <= _numObjects; i++)
            _objectData[i].readFromBE(ptr);

      for (i = 1; i <= _numItems; i++)
            _itemData[i].readFromBE(ptr);

      for (i = 0; i < GAME_STATE_COUNT; i++) {
            _gameState[i] = (int16)READ_BE_INT16(ptr); ptr += 2;
      }

      for (i = 0; i < TALK_SELECTED_COUNT; i++)
            _talkSelected[i].readFromBE(ptr);

      for (i = 1; i <= _numWalkOffs; i++)
            _walkOffData[i].readFromBE(ptr);

      _joe.facing = READ_BE_UINT16(ptr); ptr += 2;

      if (ver >= 1) {
            _puzzleAttemptCount = READ_BE_UINT16(ptr); ptr += 2;

            for (i = 1; i <= _numObjDesc; i++)
                  _objectDescription[i].readFromBE(ptr);
      }
}

void Logic::setupRestoredGame() {
      _vm->sound()->playLastSong();

      switch (gameState(VAR_JOE_DRESSING_MODE)) {
      case 0:
            _vm->display()->palSetJoeNormal();
            loadJoeBanks("Joe_A.BBK", "Joe_B.BBK");
            break;
      case 1:
            _vm->display()->palSetJoeNormal();
            loadJoeBanks("JoeU_A.BBK", "JoeU_B.BBK");
            break;
      case 2:
            _vm->display()->palSetJoeDress();
            loadJoeBanks("JoeD_A.BBK", "JoeD_B.BBK");
            break;
      }

      BobSlot *pbs = _vm->graphics()->bob(0);
      pbs->xflip = (joeFacing() == DIR_LEFT);
      joePrevFacing(joeFacing());
      joeCutFacing(joeFacing());
      switch (joeFacing()) {
      case DIR_FRONT:
            pbs->frameNum = 36;
            _vm->bankMan()->unpack(3, 31, 7);
            break;
      case DIR_BACK:
            pbs->frameNum = 37;
            _vm->bankMan()->unpack(5, 31, 7);
            break;
      default:
            pbs->frameNum = 35;
            _vm->bankMan()->unpack(1, 31, 7);
            break;
      }

      _oldRoom = 0;
      _newRoom = _currentRoom;
      _entryObj = 0;

      if (_vm->bam()->_flag != BamScene::F_STOP) {
            _vm->bam()->prepareAnimation();
      }

      inventoryRefresh();
}

void Logic::sceneStart() {
      debug(6, "[Logic::sceneStart] _scene = %i", _scene);
      _scene++;

      _vm->display()->showMouseCursor(false);

      if (1 == _scene) {
            _vm->display()->palGreyPanel();
      }

      _vm->update();
}

void Logic::sceneStop() {
      debug(6, "[Logic::sceneStop] _scene = %i", _scene);
      _scene--;

      if (_scene > 0)
            return;

      _vm->display()->palSetAllDirty();
      _vm->display()->showMouseCursor(true);
      _vm->grid()->setupPanel();
}

void Logic::changeRoom() {
      if (!preChangeRoom())
            displayRoom(currentRoom(), RDM_FADE_JOE, 100, 1, false);
      _vm->display()->showMouseCursor(true);
}

void Logic::executeSpecialMove(uint16 sm) {
      debug(6, "Special move: %d", sm);
      if (!handleSpecialMove(sm))
            warning("unhandled / invalid special move : %d", sm);
}

void Logic::asmMakeJoeUseDress() {
      joeUseDress(false);
}

void Logic::asmMakeJoeUseNormalClothes() {
      joeUseClothes(false);
}

void Logic::asmMakeJoeUseUnderwear() {
      joeUseUnderwear();
}

void Logic::asmSwitchToDressPalette() {
      _vm->display()->palSetJoeDress();
}

void Logic::asmSwitchToNormalPalette() {
      _vm->display()->palSetJoeNormal();
}

void Logic::asmStartCarAnimation() {
      _vm->bam()->_flag = BamScene::F_PLAY;
      _vm->bam()->prepareAnimation();
}

void Logic::asmStopCarAnimation() {
      _vm->bam()->_flag = BamScene::F_STOP;
      _vm->graphics()->bob(findBob(594))->active = false; // oil object
      _vm->graphics()->bob(7)->active = false; // gun shots
}

void Logic::asmStartFightAnimation() {
      _vm->bam()->_flag = BamScene::F_PLAY;
      _vm->bam()->prepareAnimation();
      gameState(148, 1);
}

void Logic::asmWaitForFrankPosition() {
      _vm->bam()->_flag = BamScene::F_REQ_STOP;
      while (_vm->bam()->_flag != BamScene::F_STOP) {
            _vm->update();
      }
}

void Logic::asmMakeFrankGrowing() {
      _vm->bankMan()->unpack(1, 38, 15);
      BobSlot *bobFrank = _vm->graphics()->bob(5);
      bobFrank->frameNum = 38;
      bobFrank->curPos(160, 200);

      int i;
      for (i = 10; i <= 100; i += 4) {
            bobFrank->scale = i;
            _vm->update();
      }
      for (i = 0; i <= 20; ++i) {
            _vm->update();
      }

      objectData(521)->name =  ABS(objectData(521)->name); // Dinoray
      objectData(526)->name =  ABS(objectData(526)->name); // Frank obj
      objectData(522)->name = -ABS(objectData(522)->name); // TMPD object off
      objectData(525)->name = -ABS(objectData(525)->name); // Floda guards off
      objectData(523)->name = -ABS(objectData(523)->name); // Sparky object off
      gameState(157, 1); // No more Ironstein
}

void Logic::asmMakeRobotGrowing() {
      _vm->bankMan()->unpack(1, 38, 15);
      BobSlot *bobRobot = _vm->graphics()->bob(5);
      bobRobot->frameNum = 38;
      bobRobot->curPos(160, 200);

      int i;
      for (i = 10; i <= 100; i += 4) {
            bobRobot->scale = i;
            _vm->update();
      }
      for (i = 0; i <= 20; ++i) {
            _vm->update();
      }

      objectData(524)->name = -ABS(objectData(524)->name); // Azura object off
      objectData(526)->name = -ABS(objectData(526)->name); // Frank object off
}

void Logic::asmShrinkRobot() {
      int i;
      BobSlot *robot = _vm->graphics()->bob(6);
      for (i = 100; i >= 35; i -= 5) {
            robot->scale = i;
            _vm->update();
      }
}

void Logic::asmEndGame() {
      int n = 40;
      while (n--) {
            _vm->update();
      }
      debug(0, "Game completed.");
      _vm->quitGame();
}

void Logic::asmPutCameraOnDino() {
      _vm->graphics()->putCameraOnBob(-1);
      int16 scrollx = _vm->display()->horizontalScroll();
      while (scrollx < 320) {
            scrollx += 16;
            if (scrollx > 320) {
                  scrollx = 320;
            }
            _vm->display()->horizontalScroll(scrollx);
            _vm->update();
      }
      _vm->graphics()->putCameraOnBob(1);
}

void Logic::asmPutCameraOnJoe() {
      _vm->graphics()->putCameraOnBob(0);
}

void Logic::asmAltIntroPanRight() {
      _vm->graphics()->putCameraOnBob(-1);
      _vm->input()->fastMode(true);
      _vm->update();
      int16 scrollx = _vm->display()->horizontalScroll();
      while (scrollx < 285 && !_vm->input()->cutawayQuit()) {
            ++scrollx;
            if (scrollx > 285) {
                  scrollx = 285;
            }
            _vm->display()->horizontalScroll(scrollx);
            _vm->update();
      }
      _vm->input()->fastMode(false);
}

void Logic::asmAltIntroPanLeft() {
      _vm->graphics()->putCameraOnBob(-1);
      _vm->input()->fastMode(true);
      int16 scrollx = _vm->display()->horizontalScroll();
      while (scrollx > 0 && !_vm->input()->cutawayQuit()) {
            scrollx -= 4;
            if (scrollx < 0) {
                  scrollx = 0;
            }
            _vm->display()->horizontalScroll(scrollx);
            _vm->update();
      }
      _vm->input()->fastMode(false);
}

void Logic::asmSetAzuraInLove() {
      gameState(VAR_AZURA_IN_LOVE, 1);
}

void Logic::asmPanRightFromJoe() {
      _vm->graphics()->putCameraOnBob(-1);
      int16 scrollx = _vm->display()->horizontalScroll();
      while (scrollx < 320) {
            scrollx += 16;
            if (scrollx > 320) {
                  scrollx = 320;
            }
            _vm->display()->horizontalScroll(scrollx);
            _vm->update();
      }
}

void Logic::asmSetLightsOff() {
      _vm->display()->palCustomLightsOff(currentRoom());
}

void Logic::asmSetLightsOn() {
      _vm->display()->palCustomLightsOn(currentRoom());
}

void Logic::asmSetManequinAreaOn() {
      Area *a = _vm->grid()->area(ROOM_FLODA_FRONTDESK, 7);
      a->mapNeighbours = ABS(a->mapNeighbours);
}

void Logic::asmPanToJoe() {
      int i = _vm->graphics()->bob(0)->x - 160;
      if (i < 0) {
            i = 0;
      } else if (i > 320) {
            i = 320;
      }
      _vm->graphics()->putCameraOnBob(-1);
      int16 scrollx = _vm->display()->horizontalScroll();
      if (i < scrollx) {
            while (scrollx > i) {
                  scrollx -= 16;
                  if (scrollx < i) {
                        scrollx = i;
                  }
                  _vm->display()->horizontalScroll(scrollx);
                  _vm->update();
            }
      } else {
            while (scrollx < i) {
                  scrollx += 16;
                  if (scrollx > i) {
                        scrollx = i;
                  }
                  _vm->display()->horizontalScroll(scrollx);
                  _vm->update();
            }
            _vm->update();
      }
      _vm->graphics()->putCameraOnBob(0);
}

void Logic::asmTurnGuardOn() {
      gameState(VAR_GUARDS_TURNED_ON, 1);
}

void Logic::asmPanLeft320To144() {
      _vm->graphics()->putCameraOnBob(-1);
      int16 scrollx = _vm->display()->horizontalScroll();
      while (scrollx > 144) {
            scrollx -= 8;
            if (scrollx < 144) {
                  scrollx = 144;
            }
            _vm->display()->horizontalScroll(scrollx);
            _vm->update();
      }
}

void Logic::asmSmooch() {
      _vm->graphics()->putCameraOnBob(-1);
      BobSlot *bobAzura = _vm->graphics()->bob(5);
      BobSlot *bobJoe = _vm->graphics()->bob(6);
      int16 scrollx = _vm->display()->horizontalScroll();
      while (scrollx < 320) {
            scrollx += 8;
            _vm->display()->horizontalScroll(scrollx);
            if (bobJoe->x - bobAzura->x > 128) {
                  bobAzura->x += 10;
                  bobJoe->x += 6;
            } else {
                  bobAzura->x += 8;
                  bobJoe->x += 8;
            }
            _vm->update();
      }
}

void Logic::asmMakeLightningHitPlane() {
      _vm->graphics()->putCameraOnBob(-1);
      short iy = 0, x, ydir = -1, j, k;

      BobSlot *planeBob     = _vm->graphics()->bob(5);
      BobSlot *lightningBob = _vm->graphics()->bob(20);

      planeBob->y = 135;

      planeBob->scale = 20;

      for (x = 660; x > 163; x -= 6) {
            planeBob->x = x;
            planeBob->y = 135 + iy;

            iy -= ydir;
            if (iy < -9 || iy > 9)
                  ydir = -ydir;

            planeBob->scale++;
            if (planeBob->scale > 100)
                  planeBob->scale = 100;

            int scrollX = x - 163;
            if (scrollX > 320)
                  scrollX = 320;
            _vm->display()->horizontalScroll(scrollX);
            _vm->update();
      }

      planeBob->scale = 100;
      _vm->display()->horizontalScroll(0);

      planeBob->x += 8;
      planeBob->y += 6;

      lightningBob->x = 160;
      lightningBob->y = 0;

      _vm->sound()->playSfx(currentRoomSfx(), false);

      _vm->bankMan()->unpack(18, lightningBob->frameNum, 15);
      _vm->bankMan()->unpack(4,  planeBob    ->frameNum, 15);

      // Plane plunges into the jungle!
      BobSlot *fireBob = _vm->graphics()->bob(6);

      fireBob->animating = true;
      fireBob->x = planeBob->x;
      fireBob->y = planeBob->y + 10;

      _vm->bankMan()->unpack(19, fireBob->frameNum, 15);
      _vm->update();

      k = 20;
      j = 1;

      for (x = 163; x > -30; x -= 10) {
            planeBob->y += 4;
            fireBob->y += 4;
            planeBob->x = fireBob->x = x;

            if (k < 40) {
                  _vm->bankMan()->unpack(j, planeBob->frameNum, 15);
                  _vm->bankMan()->unpack(k, fireBob ->frameNum, 15);
                  k++;
                  j++;

                  if (j == 4)
                        j = 1;
            }

            _vm->update();
      }

      _vm->graphics()->putCameraOnBob(0);
}

void Logic::asmScaleBlimp() {
      int16 z = 256;
      BobSlot *bob = _vm->graphics()->bob(7);
      int16 x = bob->x;
      int16 y = bob->y;
      while (bob->x > 150) {
            bob->x = x * 256 / z + 150;
            bob->y = y * 256 / z + 112;
            bob->scale = 100 * 256 / z;

            ++z;
            if (z % 6 == 0) {
                  --x;
            }

            _vm->update();
      }
}

void Logic::asmScaleEnding() {
      _vm->graphics()->bob(7)->active = false; // Turn off blimp
      BobSlot *b = _vm->graphics()->bob(20);
      b->x = 160;
      b->y = 100;
      int i;
      for (i = 5; i <= 100; i += 5) {
            b->scale = i;
            _vm->update();
      }
      for (i = 0; i < 50; ++i) {
            _vm->update();
      }
      _vm->display()->palFadeOut(_currentRoom);
}

void Logic::asmWaitForCarPosition() {
      // Wait for car to reach correct position before pouring oil
      while (_vm->bam()->_index != 60) {
            _vm->update();
      }
}

void Logic::asmShakeScreen() {
      _vm->display()->shake(false);
      _vm->update();
      _vm->display()->shake(true);
      _vm->update();
}

void Logic::asmAttemptPuzzle() {
      ++_puzzleAttemptCount;
      if (_puzzleAttemptCount == 4) {
            makeJoeSpeak(226, true);
            _puzzleAttemptCount = 0;
      }
}

void Logic::asmScaleTitle() {
      BobSlot *bob = _vm->graphics()->bob(5);
      bob->animating = false;
      bob->x = 161;
      bob->y = 200;
      bob->scale = 100;

      int i;
      for (i = 5; i <= 100; i +=5) {
            bob->scale = i;
            bob->y -= 4;
            _vm->update();
      }
}

void Logic::asmPanRightToHugh() {
      BobSlot *bob_thugA1 = _vm->graphics()->bob(20);
      BobSlot *bob_thugA2 = _vm->graphics()->bob(21);
      BobSlot *bob_thugA3 = _vm->graphics()->bob(22);
      BobSlot *bob_hugh1  = _vm->graphics()->bob(1);
      BobSlot *bob_hugh2  = _vm->graphics()->bob(23);
      BobSlot *bob_hugh3  = _vm->graphics()->bob(24);
      BobSlot *bob_thugB1 = _vm->graphics()->bob(25);
      BobSlot *bob_thugB2 = _vm->graphics()->bob(26);

      _vm->graphics()->putCameraOnBob(-1);
      _vm->input()->fastMode(true);
      _vm->update();

      // Adjust thug1 gun so it matches rest of body
      bob_thugA1->x += 160 - 45;
      bob_thugA2->x += 160;
      bob_thugA3->x += 160;

      bob_hugh1->x += 160 * 2;
      bob_hugh2->x += 160 * 2;
      bob_hugh3->x += 160 * 2;

      bob_thugB1->x += 160 * 3;
      bob_thugB2->x += 160 * 3;

      int horizontalScroll = 0;
      while (horizontalScroll < 160 && !_vm->input()->cutawayQuit()) {

            horizontalScroll += 8;
            if (horizontalScroll > 160)
                  horizontalScroll = 160;

            _vm->display()->horizontalScroll(horizontalScroll);

            bob_thugA1->x -= 16;
            bob_thugA2->x -= 16;
            bob_thugA3->x -= 16;

            bob_hugh1->x -= 24;
            bob_hugh2->x -= 24;
            bob_hugh3->x -= 24;

            bob_thugB1->x -= 32;
            bob_thugB2->x -= 32;

            _vm->update();
      }

      _vm->input()->fastMode(false);
}

void Logic::asmMakeWhiteFlash() {
      _vm->display()->palCustomFlash();
}

void Logic::asmPanRightToJoeAndRita() { // cdint.cut
      BobSlot *bob_box   = _vm->graphics()->bob(20);
      BobSlot *bob_beam  = _vm->graphics()->bob(21);
      BobSlot *bob_crate = _vm->graphics()->bob(22);
      BobSlot *bob_clock = _vm->graphics()->bob(23);
      BobSlot *bob_hands = _vm->graphics()->bob(24);

      _vm->graphics()->putCameraOnBob(-1);
      _vm->input()->fastMode(true);

      _vm->update();

      bob_box  ->x += 280 * 2;
      bob_beam ->x += 30;
      bob_crate->x += 180 * 3;

      int horizontalScroll = _vm->display()->horizontalScroll();

      while (horizontalScroll < 290 && !_vm->input()->cutawayQuit()) {

            ++horizontalScroll;
            if (horizontalScroll > 290)
                  horizontalScroll = 290;

            _vm->display()->horizontalScroll(horizontalScroll);

            bob_box  ->x -= 2;
            bob_beam ->x -= 1;
            bob_crate->x -= 3;
            bob_clock->x -= 2;
            bob_hands->x -= 2;

            _vm->update();
      }
      _vm->input()->fastMode(false);
}

void Logic::asmPanLeftToBomb() {
      BobSlot *bob21 = _vm->graphics()->bob(21);
      BobSlot *bob22 = _vm->graphics()->bob(22);

      _vm->graphics()->putCameraOnBob(-1);
      _vm->input()->fastMode(true);

      int horizontalScroll = _vm->display()->horizontalScroll();

      while ((horizontalScroll > 0 || bob21->x < 136) && !_vm->input()->cutawayQuit()) {

            horizontalScroll -= 5;
            if (horizontalScroll < 0)
                  horizontalScroll = 0;

            _vm->display()->horizontalScroll(horizontalScroll);

            if (horizontalScroll < 272 && bob21->x < 136)
                  bob21->x += 2;

            bob22->x += 5;

            _vm->update();
      }

      _vm->input()->fastMode(false);
}

void Logic::asmEndDemo() {
      debug(0, "Flight of the Amazon Queen, released January 95.");
      _vm->quitGame();
}

void Logic::asmInterviewIntro() {
      // put camera on airship
      _vm->graphics()->putCameraOnBob(5);
      BobSlot *bas = _vm->graphics()->bob(5);

      bas->curPos(-30, 40);

      bas->move(700, 10, 3);
      int scale = 450;
      while (bas->moving && !_vm->input()->cutawayQuit()) {
            bas->scale = 256 * 100 / scale;
            --scale;
            if (scale < 256) {
                  scale = 256;
            }
            _vm->update();
      }

      bas->scale = 90;
      bas->xflip = true;

      bas->move(560, 25, 4);
      while (bas->moving && !_vm->input()->cutawayQuit()) {
            _vm->update();
      }

      bas->move(545, 65, 2);
      while (bas->moving && !_vm->input()->cutawayQuit()) {
            _vm->update();
      }

      bas->move(540, 75, 2);
      while (bas->moving && !_vm->input()->cutawayQuit()) {
            _vm->update();
      }

      // put camera on Joe
      _vm->graphics()->putCameraOnBob(0);
}

void Logic::asmEndInterview() {
      debug(0, "Interactive Interview copyright (c) 1995, IBI.");
      _vm->quitGame();
}

void Logic::startCredits(const char *filename) {
      stopCredits();
      _credits = new Credits(_vm, filename);
}

void Logic::stopCredits() {
      if (_credits) {
            _vm->display()->clearTexts(0, 199);
            delete _credits;
            _credits = NULL;
      }
}

void LogicDemo::useJournal() {
      makePersonSpeak("This is a demo, so I can't load or save games*14", NULL, "");
}

bool LogicDemo::preChangeRoom() {
      if (currentRoom() == FOTAQ_LOGO && gameState(VAR_INTRO_PLAYED) == 0) {
            currentRoom(79);
            displayRoom(currentRoom(), RDM_FADE_NOJOE, 100, 2, true);
            playCutaway("clogo.cut");
            sceneReset();
            currentRoom(ROOM_HOTEL_LOBBY);
            entryObj(584);
            displayRoom(currentRoom(), RDM_FADE_JOE, 100, 2, true);
            playCutaway("c70d.cut");
            gameState(VAR_INTRO_PLAYED, 1);
            inventoryRefresh();
            return true;
      }
      return false;
}

bool LogicDemo::handleSpecialMove(uint16 sm) {
      switch (sm) {
      case 4:
            asmMakeJoeUseUnderwear();
            break;
      case 5:
            asmSwitchToDressPalette();
            break;
      case 14:
            asmEndDemo();
            break;
      default:
            return false;
      }
      return true;
}

void LogicInterview::useJournal() {
      // no-op
}

bool LogicInterview::preChangeRoom() {
      if (currentRoom() == 2 && gameState(2) == 0) {
            currentRoom(6);
            displayRoom(currentRoom(), RDM_FADE_NOJOE, 100, 2, true);
            playCutaway("start.cut");
            gameState(2, 1);
            inventoryRefresh();
            return true;
      }
      return false;
}

bool LogicInterview::handleSpecialMove(uint16 sm) {
      switch (sm) {
      case 1:
            asmInterviewIntro();
            break;
      case 2:
            asmEndInterview();
            break;
      default:
            return false;
      }
      return true;
}

void LogicGame::useJournal() {
      _vm->command()->clear(false);
      _journal->use();
      _vm->walk()->stopJoe();
}

bool LogicGame::preChangeRoom() {
      if (currentRoom() == ROOM_JUNGLE_PINNACLE) {
            handlePinnacleRoom();
            return true;
      } else if (currentRoom() == FOTAQ_LOGO && gameState(VAR_INTRO_PLAYED) == 0) {
            displayRoom(currentRoom(), RDM_FADE_NOJOE, 100, 2, true);
            playCutaway("copy.cut");
            playCutaway("clogo.cut");

            if (ConfMan.getBool("alt_intro") && _vm->resource()->isCD()) {
                  playCutaway("cintr.cut");
            } else {
                  playCutaway("cdint.cut");
            }

            playCutaway("cred.cut");
            _vm->display()->palSetPanel();
            sceneReset();
            currentRoom(ROOM_HOTEL_LOBBY);
            entryObj(584);
            displayRoom(currentRoom(), RDM_FADE_JOE, 100, 2, true);
            playCutaway("c70d.cut");
            gameState(VAR_INTRO_PLAYED, 1);
            inventoryRefresh();
            return true;
      }
      return false;
}

bool LogicGame::handleSpecialMove(uint16 sm) {
      typedef void (LogicGame::*SpecialMoveProc)();
      static const SpecialMoveProc asmTable[] = {
            /* 00 */
            0,
            0,
            &LogicGame::asmMakeJoeUseDress,
            &LogicGame::asmMakeJoeUseNormalClothes,
            /* 04 */
            &LogicGame::asmMakeJoeUseUnderwear,
            &LogicGame::asmSwitchToDressPalette,
            &LogicGame::asmSwitchToNormalPalette,
            &LogicGame::asmStartCarAnimation,       // room 74
            /* 08 */
            &LogicGame::asmStopCarAnimation,        // room 74
            &LogicGame::asmStartFightAnimation,     // room 69
            &LogicGame::asmWaitForFrankPosition,    // c69e.cut
            &LogicGame::asmMakeFrankGrowing,        // c69z.cut
            /* 12 */
            &LogicGame::asmMakeRobotGrowing,        // c69z.cut
            &LogicGame::asmShrinkRobot,
            &LogicGame::asmEndGame,
            &LogicGame::asmPutCameraOnDino,
            /* 16 */
            &LogicGame::asmPutCameraOnJoe,
            &LogicGame::asmAltIntroPanRight,        // cintr.cut
            &LogicGame::asmAltIntroPanLeft,         // cintr.cut
            &LogicGame::asmSetAzuraInLove,
            /* 20 */
            &LogicGame::asmPanRightFromJoe,
            &LogicGame::asmSetLightsOff,
            &LogicGame::asmSetLightsOn,
            &LogicGame::asmSetManequinAreaOn,
            /* 24 */
            &LogicGame::asmPanToJoe,
            &LogicGame::asmTurnGuardOn,
            &LogicGame::asmPanLeft320To144,
            &LogicGame::asmSmooch,
            /* 28 */
            &LogicGame::asmMakeLightningHitPlane,
            &LogicGame::asmScaleBlimp,
            &LogicGame::asmScaleEnding,
            &LogicGame::asmWaitForCarPosition,
            /* 32 */
            &LogicGame::asmShakeScreen,
            &LogicGame::asmAttemptPuzzle,
            &LogicGame::asmScaleTitle,
            0,
            /* 36 */
            &LogicGame::asmPanRightToHugh,
            &LogicGame::asmMakeWhiteFlash,
            &LogicGame::asmPanRightToJoeAndRita,
            &LogicGame::asmPanLeftToBomb            // cdint.cut
      };
      if (sm >= ARRAYSIZE(asmTable) || asmTable[sm] == 0)
            return false;
      (this->*asmTable[sm])();
      return true;
}

} // End of namespace Queen

Generated by  Doxygen 1.6.0   Back to index