diff options
author | Max Horn | 2006-02-11 22:45:04 +0000 |
---|---|---|
committer | Max Horn | 2006-02-11 22:45:04 +0000 |
commit | 26ee630756ebdd7c96bccede0881a8c8b98e8f2b (patch) | |
tree | 26e378d5cf990a2b81c2c96e9e683a7f333b62e8 /engines/queen | |
parent | 2a9a0d4211b1ea5723f1409d91cb95de8984429e (diff) | |
download | scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.tar.gz scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.tar.bz2 scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.zip |
Moved engines to the new engines/ directory
svn-id: r20582
Diffstat (limited to 'engines/queen')
42 files changed, 20853 insertions, 0 deletions
diff --git a/engines/queen/bankman.cpp b/engines/queen/bankman.cpp new file mode 100644 index 0000000000..18ad276d0f --- /dev/null +++ b/engines/queen/bankman.cpp @@ -0,0 +1,145 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/bankman.h" + +#include "queen/resource.h" + +namespace Queen { + +BankManager::BankManager(Resource *res) + : _res(res) { + memset(_frames, 0, sizeof(_frames)); + memset(_banks, 0, sizeof(_banks)); + memset(_loadedBanks, 0, sizeof(_loadedBanks)); +} + +BankManager::~BankManager() { + for (uint32 i = 0; i < MAX_BANKS_NUMBER; ++i) { + close(i); + } + eraseFrames(true); +} + +void BankManager::load(const char *bankname, uint32 bankslot) { + debug(9, "BankManager::load(%s, %d)", bankname, bankslot); + assert(bankslot < MAX_BANKS_NUMBER); + + if (!scumm_stricmp(bankname, _loadedBanks[bankslot])) { + debug(9, "BankManager::load() bank '%s' already loaded", bankname); + return; + } + + close(bankslot); + _banks[bankslot].data = _res->loadFile(bankname); + + uint16 entries = READ_LE_UINT16(_banks[bankslot].data); + assert(entries < MAX_BANK_SIZE); + debug(9, "BankManager::load() entries = %d", entries); + + uint32 offset = 2; + const uint8 *p = _banks[bankslot].data; + for (uint16 i = 1; i <= entries; ++i) { + _banks[bankslot].indexes[i] = offset; + uint16 w = READ_LE_UINT16(p + offset + 0); + uint16 h = READ_LE_UINT16(p + offset + 2); + // jump to next entry, skipping data & header + offset += w * h + 8; + } + + // mark this bank as loaded + strcpy(_loadedBanks[bankslot], bankname); +} + +void BankManager::unpack(uint32 srcframe, uint32 dstframe, uint32 bankslot) { + debug(9, "BankManager::unpack(%d, %d, %d)", srcframe, dstframe, bankslot); + assert(bankslot < MAX_BANKS_NUMBER); + assert(_banks[bankslot].data != NULL); + + BobFrame *pbf = &_frames[dstframe]; + const uint8 *p = _banks[bankslot].data + _banks[bankslot].indexes[srcframe]; + pbf->width = READ_LE_UINT16(p + 0); + pbf->height = READ_LE_UINT16(p + 2); + pbf->xhotspot = READ_LE_UINT16(p + 4); + pbf->yhotspot = READ_LE_UINT16(p + 6); + + uint32 size = pbf->width * pbf->height; + delete[] pbf->data; + pbf->data = new uint8[ size ]; + memcpy(pbf->data, p + 8, size); +} + +void BankManager::overpack(uint32 srcframe, uint32 dstframe, uint32 bankslot) { + debug(9, "BankManager::overpack(%d, %d, %d)", srcframe, dstframe, bankslot); + assert(bankslot < MAX_BANKS_NUMBER); + assert(_banks[bankslot].data != NULL); + + const uint8 *p = _banks[bankslot].data + _banks[bankslot].indexes[srcframe]; + uint16 src_w = READ_LE_UINT16(p + 0); + uint16 src_h = READ_LE_UINT16(p + 2); + + // unpack if destination frame is smaller than source one + if (_frames[dstframe].width < src_w || _frames[dstframe].height < src_h) { + unpack(srcframe, dstframe, bankslot); + } else { + // copy data 'over' destination frame (without changing frame header) + memcpy(_frames[dstframe].data, p + 8, src_w * src_h); + } +} + +void BankManager::close(uint32 bankslot) { + debug(9, "BankManager::close(%d)", bankslot); + assert(bankslot < MAX_BANKS_NUMBER); + delete[] _banks[bankslot].data; + memset(&_banks[bankslot], 0, sizeof(PackedBank)); + _loadedBanks[bankslot][0] = '\0'; +} + +BobFrame *BankManager::fetchFrame(uint32 index) { + debug(9, "BankManager::fetchFrame(%d)", index); + assert(index < MAX_FRAMES_NUMBER); + BobFrame *pbf = &_frames[index]; + assert(pbf->data != 0); + return pbf; +} + +void BankManager::eraseFrame(uint32 index) { + debug(9, "BankManager::eraseFrame(%d)", index); + assert(index < MAX_FRAMES_NUMBER); + BobFrame *pbf = &_frames[index]; + delete[] pbf->data; + memset(pbf, 0, sizeof(BobFrame)); +} + +void BankManager::eraseFrames(bool joe) { + uint32 i = 0; + if (!joe) { + i = FRAMES_JOE; + } + while (i < MAX_FRAMES_NUMBER) { + eraseFrame(i); + ++i; + } +} + +} // End of namespace Queen diff --git a/engines/queen/bankman.h b/engines/queen/bankman.h new file mode 100644 index 0000000000..63eb9224ff --- /dev/null +++ b/engines/queen/bankman.h @@ -0,0 +1,87 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENBANKMAN_H +#define QUEENBANKMAN_H + +#include "common/util.h" +#include "queen/structs.h" + +namespace Queen { + +class Resource; + +class BankManager { +public: + + BankManager(Resource *res); + ~BankManager(); + + //! load a bank into the specified slot + void load(const char *bankname, uint32 bankslot); + + //! unpack a frame from a loaded bank + void unpack(uint32 srcframe, uint32 dstframe, uint32 bankslot); + + //! unpack a frame over an existing one from a loaded bank + void overpack(uint32 srcframe, uint32 dstframe, uint32 bankslot); + + //! close a bank + void close(uint32 bankslot); + + //! get a reference to unpacked frame + BobFrame *fetchFrame(uint32 index); + + //! erase a frame + void eraseFrame(uint32 index); + + //! erase all unpacked frames + void eraseFrames(bool joe); + + enum { + MAX_BANK_SIZE = 110, + MAX_FRAMES_NUMBER = 256, + MAX_BANKS_NUMBER = 18 + }; + +private: + + struct PackedBank { + uint32 indexes[MAX_BANK_SIZE]; + uint8 *data; + }; + + //! unpacked bob frames + BobFrame _frames[MAX_FRAMES_NUMBER]; + + //! banked bob frames + PackedBank _banks[MAX_BANKS_NUMBER]; + + //! loaded banks names + char _loadedBanks[MAX_BANKS_NUMBER][20]; + + Resource *_res; +}; + +} // End of namespace Queen + +#endif diff --git a/engines/queen/command.cpp b/engines/queen/command.cpp new file mode 100644 index 0000000000..a84d859fe6 --- /dev/null +++ b/engines/queen/command.cpp @@ -0,0 +1,1255 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/command.h" + +#include "queen/display.h" +#include "queen/input.h" +#include "queen/graphics.h" +#include "queen/grid.h" +#include "queen/logic.h" +#include "queen/queen.h" +#include "queen/resource.h" +#include "queen/sound.h" +#include "queen/state.h" +#include "queen/walk.h" + +namespace Queen { + +CmdText::CmdText(bool reversed, uint8 y, QueenEngine *vm) + : _isReversed(reversed), _y(y), _vm(vm) { + clear(); +} + +void CmdText::clear() { + memset(_command, 0, sizeof(_command)); +} + +void CmdText::display(uint8 color) { + _vm->display()->textCurrentColor(color); + _vm->display()->setTextCentered(_y, _command, false); +} + +void CmdText::displayTemp(uint8 color, Verb v, const char *name, bool outlined) { + char temp[MAX_COMMAND_LEN] = ""; + if (_isReversed) { + if (name != NULL) + sprintf(temp, "%s ", name); + strcat(temp, _vm->logic()->verbName(v)); + } else { + strcpy(temp, _vm->logic()->verbName(v)); + if (name != NULL) { + strcat(temp, " "); + strcat(temp, name); + } + } + _vm->display()->textCurrentColor(color); + _vm->display()->setTextCentered(_y, temp, outlined); +} + +void CmdText::displayTemp(uint8 color, const char *name, bool outlined) { + char temp[MAX_COMMAND_LEN]; + if (_isReversed) + sprintf(temp, "%s %s", name, _command); + else + sprintf(temp, "%s %s", _command, name); + _vm->display()->textCurrentColor(color); + _vm->display()->setTextCentered(_y, temp, outlined); +} + +void CmdText::setVerb(Verb v) { + strcpy(_command, _vm->logic()->verbName(v)); +} + +void CmdText::addLinkWord(Verb v) { + if (_isReversed) { + char temp[MAX_COMMAND_LEN]; + + strcpy(temp, _command); + strcpy(_command, _vm->logic()->verbName(v)); + strcat(_command, " "); + strcat(_command, temp); + } else { + strcat(_command, " "); + strcat(_command, _vm->logic()->verbName(v)); + } +} + +void CmdText::addObject(const char *objName) { + if (_isReversed) { + char temp[MAX_COMMAND_LEN]; + + strcpy(temp, _command); + strcpy(_command, objName); + strcat(_command, " "); + strcat(_command, temp); + } else { + strcat(_command, " "); + strcat(_command, objName); + } +} + +bool CmdText::isEmpty() const { + return _command[0] == 0; +} + +void CmdState::init() { + commandLevel = 1; + oldVerb = verb = action = VERB_NONE; + oldNoun = noun = subject[0] = subject[1] = 0; + + selAction = VERB_NONE; + selNoun = 0; +} + +Command::Command(QueenEngine *vm) + : _cmdList(NULL), _cmdArea(NULL), _cmdObject(NULL), _cmdInventory(NULL), _cmdGameState(NULL), + _cmdText((vm->resource()->getLanguage() == HEBREW), CmdText::COMMAND_Y_POS, vm), _vm(vm) { +} + +Command::~Command() { + delete[] _cmdList; + delete[] _cmdArea; + delete[] _cmdObject; + delete[] _cmdInventory; + delete[] _cmdGameState; +} + +void Command::clear(bool clearTexts) { + debug(6, "Command::clear(%d)", clearTexts); + _cmdText.clear(); + if (clearTexts) { + _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS); + } + _parse = false; + _state.init(); +} + +void Command::executeCurrentAction() { + _vm->logic()->entryObj(0); + + if (_mouseKey == Input::MOUSE_RBUTTON && _state.subject[0] > 0) { + + ObjectData *od = _vm->logic()->objectData(_state.subject[0]); + if (od == NULL || od->name <= 0) { + cleanupCurrentAction(); + return; + } + + _state.verb = State::findDefaultVerb(od->state); + _state.selAction = (_state.verb == VERB_NONE) ? VERB_WALK_TO : _state.verb; + _cmdText.setVerb(_state.selAction); + _cmdText.addObject(_vm->logic()->objectName(od->name)); + } + + // always highlight the current command when actioned + _cmdText.display(INK_CMD_SELECT); + + _state.selNoun = _state.noun; + _state.commandLevel = 1; + + if (handleWrongAction()) { + cleanupCurrentAction(); + return; + } + + // get the commands associated with this object/item + uint16 comMax = 0; + uint16 matchingCmds[MAX_MATCHING_CMDS]; + CmdListData *cmdList = &_cmdList[1]; + uint16 i; + for (i = 1; i <= _numCmdList; ++i, ++cmdList) { + if (cmdList->match(_state.selAction, _state.subject[0], _state.subject[1])) { + assert(comMax < MAX_MATCHING_CMDS); + matchingCmds[comMax] = i; + ++comMax; + } + } + + debug(6, "Command::executeCurrentAction() - comMax=%d subj1=%X subj2=%X", comMax, _state.subject[0], _state.subject[1]); + + if (comMax == 0) { + sayInvalidAction(_state.selAction, _state.subject[0], _state.subject[1]); + clear(true); + cleanupCurrentAction(); + return; + } + + // process each associated command for the Object, until all done + // or one of the Gamestate tests fails... + int16 cond = 0; + CmdListData *com = &_cmdList[0]; + uint16 comId = 0; + for (i = 1; i <= comMax; ++i) { + + comId = matchingCmds[i - 1]; + com = &_cmdList[comId]; + + // check the Gamestates and set them if necessary + cond = 0; + if (com->setConditions) { + cond = setConditions(comId, (i == comMax)); + } + + if (cond == -1 && i == comMax) { + // only exit on a condition fail if at last command + // Joe hasnt spoken, so do normal LOOK command + break; + } else if (cond == -2 && i == comMax) { + // only exit on a condition fail if at last command + // Joe has spoken, so skip LOOK command + cleanupCurrentAction(); + return; + } else if (cond >= 0) { + // we've had a successful Gamestate check, so we must now exit + cond = executeCommand(comId, cond); + break; + } + } + + if (_state.selAction == VERB_USE_JOURNAL) { + clear(true); + } else { + if (cond <= 0 && _state.selAction == VERB_LOOK_AT) { + lookAtSelectedObject(); + } else { + // only play song if it's a PLAY AFTER type + if (com->song < 0) { + _vm->sound()->playSong(-com->song); + } + clear(true); + } + cleanupCurrentAction(); + } +} + +void Command::updatePlayer() { + if (_vm->logic()->joeWalk() != JWM_MOVE) { + int16 cx = _vm->input()->mousePosX(); + int16 cy = _vm->input()->mousePosY(); + lookForCurrentObject(cx, cy); + lookForCurrentIcon(cx, cy); + } + + if (_vm->input()->keyVerb() != VERB_NONE) { + if (_vm->input()->keyVerb() == VERB_USE_JOURNAL) { + _vm->logic()->useJournal(); + } else if (_vm->input()->keyVerb() != VERB_SKIP_TEXT) { + _state.verb = _vm->input()->keyVerb(); + if (isVerbInv(_state.verb)) { + _state.noun = _state.selNoun = 0; + _state.oldNoun = 0; + _state.oldVerb = VERB_NONE; + grabSelectedItem(); + } else { + grabSelectedVerb(); + } + } + _vm->input()->clearKeyVerb(); + } + + _mouseKey = _vm->input()->mouseButton(); + _vm->input()->clearMouseButton(); + if (_mouseKey > 0) { + grabCurrentSelection(); + } +} + +void Command::readCommandsFrom(byte *&ptr) { + uint16 i; + + _numCmdList = READ_BE_UINT16(ptr); ptr += 2; + _cmdList = new CmdListData[_numCmdList + 1]; + if (_numCmdList == 0) { + _cmdList[0].readFromBE(ptr); + } else { + memset(&_cmdList[0], 0, sizeof(CmdListData)); + for (i = 1; i <= _numCmdList; i++) { + _cmdList[i].readFromBE(ptr); + } + } + + _numCmdArea = READ_BE_UINT16(ptr); ptr += 2; + _cmdArea = new CmdArea[_numCmdArea + 1]; + if (_numCmdArea == 0) { + _cmdArea[0].readFromBE(ptr); + } else { + memset(&_cmdArea[0], 0, sizeof(CmdArea)); + for (i = 1; i <= _numCmdArea; i++) { + _cmdArea[i].readFromBE(ptr); + } + } + + _numCmdObject = READ_BE_UINT16(ptr); ptr += 2; + _cmdObject = new CmdObject[_numCmdObject + 1]; + if (_numCmdObject == 0) { + _cmdObject[0].readFromBE(ptr); + } else { + memset(&_cmdObject[0], 0, sizeof(CmdObject)); + for (i = 1; i <= _numCmdObject; i++) { + _cmdObject[i].readFromBE(ptr); + } + } + + _numCmdInventory = READ_BE_UINT16(ptr); ptr += 2; + _cmdInventory = new CmdInventory[_numCmdInventory + 1]; + if (_numCmdInventory == 0) { + _cmdInventory[0].readFromBE(ptr); + } else { + memset(&_cmdInventory[0], 0, sizeof(CmdInventory)); + for (i = 1; i <= _numCmdInventory; i++) { + _cmdInventory[i].readFromBE(ptr); + } + } + + _numCmdGameState = READ_BE_UINT16(ptr); ptr += 2; + _cmdGameState = new CmdGameState[_numCmdGameState + 1]; + if (_numCmdGameState == 0) { + _cmdGameState[0].readFromBE(ptr); + } else { + memset(&_cmdGameState[0], 0, sizeof(CmdGameState)); + for (i = 1; i <= _numCmdGameState; i++) { + _cmdGameState[i].readFromBE(ptr); + } + } +} + +ObjectData *Command::findObjectData(uint16 objRoomNum) const { + ObjectData *od = NULL; + if (objRoomNum != 0) { + objRoomNum += _vm->logic()->currentRoomData(); + od = _vm->logic()->objectData(objRoomNum); + } + return od; +} + +ItemData *Command::findItemData(Verb invNum) const { + ItemData *id = NULL; + uint16 itNum = _vm->logic()->findInventoryItem(invNum - VERB_INV_FIRST); + if (itNum != 0) { + id = _vm->logic()->itemData(itNum); + } + return id; +} + +int16 Command::executeCommand(uint16 comId, int16 condResult) { + // execute.c l.313-452 + debug(6, "Command::executeCommand() - cond = %X, com = %X", condResult, comId); + + CmdListData *com = &_cmdList[comId]; + + if (com->setAreas) { + setAreas(comId); + } + + // don't try to grab if action is TALK or WALK + if (_state.selAction != VERB_TALK_TO && _state.selAction != VERB_WALK_TO) { + int i; + for (i = 0; i < 2; ++i) { + int16 obj = _state.subject[i]; + if (obj > 0) { + _vm->logic()->joeGrab(State::findGrab(_vm->logic()->objectData(obj)->state)); + } + } + } + + bool cutDone = false; + if (condResult > 0) { + // check for cutaway/dialogs before updating Objects + const char *desc = _vm->logic()->objectTextualDescription(condResult); + if (executeIfCutaway(desc)) { + condResult = 0; + cutDone = true; + } else if (executeIfDialog(desc)) { + condResult = 0; + } + } + + int16 oldImage = 0; + if (_state.subject[0] > 0) { + // an object (not an item) + oldImage = _vm->logic()->objectData(_state.subject[0])->image; + } + + if (com->setObjects) { + setObjects(comId); + } + + if (com->setItems) { + setItems(comId); + } + + if (com->imageOrder != 0 && _state.subject[0] > 0) { + ObjectData *od = _vm->logic()->objectData(_state.subject[0]); + // we must update the graphic image of the object + if (com->imageOrder < 0) { + // instead of setting to -1 or -2, flag as negative + if (od->image > 0) { + // make sure that object is not already updated + od->image = -(od->image + 10); + } + } else { + od->image = com->imageOrder; + } + _vm->graphics()->refreshObject(_state.subject[0]); + } else { + // this object is not being updated by command list, see if + // it has another image copied to it + if (_state.subject[0] > 0) { + // an object (not an item) + if (_vm->logic()->objectData(_state.subject[0])->image != oldImage) { + _vm->graphics()->refreshObject(_state.subject[0]); + } + } + } + + // don't play music on an OPEN/CLOSE command - in case the command fails + if (_state.selAction != VERB_NONE && + _state.selAction != VERB_OPEN && + _state.selAction != VERB_CLOSE) { + // only play song if it's a PLAY BEFORE type + if (com->song > 0) { + _vm->sound()->playSong(com->song); + } + } + + // do a special hardcoded section + // l.419-452 execute.c + switch (com->specialSection) { + case 1: + _vm->logic()->useJournal(); + _state.selAction = VERB_USE_JOURNAL; + return condResult; + case 2: + _vm->logic()->joeUseDress(true); + break; + case 3: + _vm->logic()->joeUseClothes(true); + break; + case 4: + _vm->logic()->joeUseUnderwear(); + break; + } + + if (_state.subject[0] > 0) + changeObjectState(_state.selAction, _state.subject[0], com->song, cutDone); + + if (condResult > 0) { + _vm->logic()->makeJoeSpeak(condResult, true); + } + return condResult; +} + +int16 Command::makeJoeWalkTo(int16 x, int16 y, int16 objNum, Verb v, bool mustWalk) { + // Check to see if object is actually an exit to another + // room. If so, then set up new room + ObjectData *objData = _vm->logic()->objectData(objNum); + if (objData->x != 0 || objData->y != 0) { + x = objData->x; + y = objData->y; + } + if (v == VERB_WALK_TO) { + _vm->logic()->entryObj(objData->entryObj); + if (objData->entryObj > 0) { + _vm->logic()->newRoom(_vm->logic()->objectData(objData->entryObj)->room); + // because this is an exit object, see if there is + // a walk off point and set (x,y) accordingly + WalkOffData *wod = _vm->logic()->walkOffPointForObject(objNum); + if (wod != NULL) { + x = wod->x; + y = wod->y; + } + } + } else { + _vm->logic()->entryObj(0); + _vm->logic()->newRoom(0); + } + + debug(6, "Command::makeJoeWalkTo() - x=%d y=%d newRoom=%d", x, y, _vm->logic()->newRoom()); + + int16 p = 0; + if (mustWalk) { + // determine which way for Joe to face Object + uint16 facing = State::findDirection(objData->state); + BobSlot *bobJoe = _vm->graphics()->bob(0); + if (x == bobJoe->x && y == bobJoe->y) { + _vm->logic()->joeFacing(facing); + _vm->logic()->joeFace(); + } else { + p = _vm->walk()->moveJoe(facing, x, y, false); + if (p != 0) { + _vm->logic()->newRoom(0); // cancel makeJoeWalkTo, that should be equivalent to cr10 fix + } + } + } + return p; +} + +void Command::grabCurrentSelection() { + _selPosX = _vm->input()->mousePosX(); + _selPosY = _vm->input()->mousePosY(); + + uint16 zone = _vm->grid()->findObjectUnderCursor(_selPosX, _selPosY); + _state.noun = _vm->grid()->findObjectNumber(zone); + _state.verb = _vm->grid()->findVerbUnderCursor(_selPosX, _selPosY); + + _selPosX += _vm->display()->horizontalScroll(); + + if (isVerbAction(_state.verb) || isVerbInvScroll(_state.verb)) { + grabSelectedVerb(); + } else if (isVerbInv(_state.verb)) { + grabSelectedItem(); + } else if (_state.noun != 0) { + grabSelectedNoun(); + } else if (_selPosY < ROOM_ZONE_HEIGHT && _state.verb == VERB_NONE) { + // select without a command, do a WALK + clear(true); + _vm->logic()->joeWalk(JWM_EXECUTE); + } +} + +void Command::grabSelectedObject(int16 objNum, uint16 objState, uint16 objName) { + if (_state.action != VERB_NONE) { + _cmdText.addObject(_vm->logic()->objectName(objName)); + } + + _state.subject[_state.commandLevel - 1] = objNum; + + // if first noun and it's a 2 level command then set up action word + if (_state.action == VERB_USE && _state.commandLevel == 1) { + if (State::findUse(objState) == STATE_USE_ON) { + // object supports 2 levels, command not fully constructed + _state.commandLevel = 2; + _cmdText.addLinkWord(VERB_PREP_WITH); + _cmdText.display(INK_CMD_NORMAL); + _parse = false; + } else { + _parse = true; + } + } else if (_state.action == VERB_GIVE && _state.commandLevel == 1) { + // command not fully constructed + _state.commandLevel = 2; + _cmdText.addLinkWord(VERB_PREP_TO); + _cmdText.display(INK_CMD_NORMAL); + _parse = false; + } else { + _parse = true; + } + + if (_parse) { + _state.verb = VERB_NONE; + _vm->logic()->joeWalk(JWM_EXECUTE); + _state.selAction = _state.action; + _state.action = VERB_NONE; + } +} + +void Command::grabSelectedItem() { + ItemData *id = findItemData(_state.verb); + if (id == NULL || id->name <= 0) { + return; + } + + int16 item = _vm->logic()->findInventoryItem(_state.verb - VERB_INV_FIRST); + + // If we've selected via keyboard, and there is no VERB then do + // the ITEMs default, otherwise keep constructing! + + if (_mouseKey == Input::MOUSE_LBUTTON || + (_vm->input()->keyVerb() != VERB_NONE && _state.verb != VERB_NONE)) { + if (_state.action == VERB_NONE) { + if (_vm->input()->keyVerb() != VERB_NONE) { + // We've selected via the keyboard, no command is being + // constructed, so we shall find the item's default + _state.verb = State::findDefaultVerb(id->state); + if (_state.verb == VERB_NONE) { + // set to Look At + _state.verb = VERB_LOOK_AT; + _cmdText.setVerb(VERB_LOOK_AT); + } + _state.action = _state.verb; + } else { + // Action>0 ONLY if command has been constructed + // Left Mouse Button pressed just do Look At + _state.action = VERB_LOOK_AT; + _cmdText.setVerb(VERB_LOOK_AT); + } + } + _state.verb = VERB_NONE; + } else { + if (_cmdText.isEmpty()) { + _state.verb = VERB_LOOK_AT; + _state.action = VERB_LOOK_AT; + _cmdText.setVerb(VERB_LOOK_AT); + } else { + if (_state.commandLevel == 2 && _parse) + _state.verb = _state.action; + else + _state.verb = State::findDefaultVerb(id->state); + if (_state.verb == VERB_NONE) { + // No match made, so command not yet completed. Redefine as LOOK AT + _state.action = VERB_LOOK_AT; + _cmdText.setVerb(VERB_LOOK_AT); + } else { + _state.action = _state.verb; + } + _state.verb = VERB_NONE; + } + } + + grabSelectedObject(-item, id->state, id->name); +} + +void Command::grabSelectedNoun() { + ObjectData *od = findObjectData(_state.noun); + if (od == NULL || od->name <= 0) { + // selected a turned off object, so just walk + clear(true); + _state.noun = 0; + _vm->logic()->joeWalk(JWM_EXECUTE); + return; + } + + if (_state.verb == VERB_NONE) { + if (_mouseKey == Input::MOUSE_LBUTTON) { + if ((_state.commandLevel != 2 && _state.action == VERB_NONE) || + (_state.commandLevel == 2 && _parse)) { + _state.verb = VERB_WALK_TO; + _state.action = VERB_WALK_TO; + _cmdText.setVerb(VERB_WALK_TO); + } + } else if (_mouseKey == Input::MOUSE_RBUTTON) { + if (_cmdText.isEmpty()) { + _state.verb = State::findDefaultVerb(od->state); + _state.selAction = (_state.verb == VERB_NONE) ? VERB_WALK_TO : _state.verb; + _cmdText.setVerb(_state.selAction); + _cmdText.addObject(_vm->logic()->objectName(od->name)); + } else { + if ((_state.commandLevel == 2 && !_parse) || _state.action != VERB_NONE) { + _state.verb = _state.action; + } else { + _state.verb = State::findDefaultVerb(od->state); + } + _state.action = (_state.verb == VERB_NONE) ? VERB_WALK_TO : _state.verb; + _state.verb = VERB_NONE; + } + } + } + + _state.selNoun = 0; + int16 objNum = _vm->logic()->currentRoomData() + _state.noun; + grabSelectedObject(objNum, od->state, od->name); +} + +void Command::grabSelectedVerb() { + if (isVerbInvScroll(_state.verb)) { + // move through inventory (by four if right mouse button) + uint16 scroll = (_mouseKey == Input::MOUSE_RBUTTON) ? 4 : 1; + _vm->logic()->inventoryScroll(scroll, _state.verb == VERB_SCROLL_UP); + } else { + _state.action = _state.verb; + _state.subject[0] = 0; + _state.subject[1] = 0; + + if (_vm->logic()->joeWalk() == JWM_MOVE && _state.verb != VERB_NONE) { + _vm->logic()->joeWalk(JWM_NORMAL); + } + _state.commandLevel = 1; + _state.oldVerb = VERB_NONE; + _state.oldNoun = 0; + _cmdText.setVerb(_state.verb); + _cmdText.display(INK_CMD_NORMAL); + } +} + +bool Command::executeIfCutaway(const char *description) { + if (strlen(description) > 4 && + scumm_stricmp(description + strlen(description) - 4, ".cut") == 0) { + + _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS); + + char nextCutaway[20]; + memset(nextCutaway, 0, sizeof(nextCutaway)); + _vm->logic()->playCutaway(description, nextCutaway); + while (nextCutaway[0] != '\0') { + _vm->logic()->playCutaway(nextCutaway, nextCutaway); + } + return true; + } + return false; +} + +bool Command::executeIfDialog(const char *description) { + if (strlen(description) > 4 && + scumm_stricmp(description + strlen(description) - 4, ".dog") == 0) { + + _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS); + + char cutaway[20]; + memset(cutaway, 0, sizeof(cutaway)); + _vm->logic()->startDialogue(description, _state.selNoun, cutaway); + + while (cutaway[0] != '\0') { + char currentCutaway[20]; + strcpy(currentCutaway, cutaway); + _vm->logic()->playCutaway(currentCutaway, cutaway); + } + return true; + } + return false; +} + +bool Command::handleWrongAction() { + // l.96-141 execute.c + uint16 objMax = _vm->grid()->objMax(_vm->logic()->currentRoom()); + uint16 roomData = _vm->logic()->currentRoomData(); + + // select without a command or WALK TO ; do a WALK + if ((_state.selAction == VERB_WALK_TO || _state.selAction == VERB_NONE) && + (_state.selNoun > objMax || _state.selNoun == 0)) { + if (_state.selAction == VERB_NONE) { + _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS); + } + _vm->walk()->moveJoe(0, _selPosX, _selPosY, false); + return true; + } + + // check to see if one of the objects is hidden + int i; + for (i = 0; i < 2; ++i) { + int16 obj = _state.subject[i]; + if (obj > 0 && _vm->logic()->objectData(obj)->name <= 0) { + return true; + } + } + + // check for USE command on exists + if (_state.selAction == VERB_USE && + _state.subject[0] > 0 && _vm->logic()->objectData(_state.subject[0])->entryObj > 0) { + _state.selAction = VERB_WALK_TO; + } + + if (_state.selNoun > 0 && _state.selNoun <= objMax) { + uint16 objNum = roomData + _state.selNoun; + if (makeJoeWalkTo(_selPosX, _selPosY, objNum, _state.selAction, true) != 0) { + return true; + } + if (_state.selAction == VERB_WALK_TO && _vm->logic()->objectData(objNum)->entryObj < 0) { + return true; + } + } + return false; +} + +void Command::sayInvalidAction(Verb action, int16 subj1, int16 subj2) { + // l.158-272 execute.c + switch (action) { + + case VERB_LOOK_AT: + lookAtSelectedObject(); + break; + + case VERB_OPEN: + // 'it doesn't seem to open' + _vm->logic()->makeJoeSpeak(1); + break; + + case VERB_USE: + if (subj1 < 0) { + uint16 k = _vm->logic()->itemData(-subj1)->sfxDescription; + if (k > 0) { + _vm->logic()->makeJoeSpeak(k, true); + } else { + _vm->logic()->makeJoeSpeak(2); + } + } else { + _vm->logic()->makeJoeSpeak(2); + } + break; + + case VERB_TALK_TO: + _vm->logic()->makeJoeSpeak(24 + _vm->randomizer.getRandomNumber(2)); + break; + + case VERB_CLOSE: + _vm->logic()->makeJoeSpeak(2); + break; + + case VERB_MOVE: + // 'I can't move it' + if (subj1 > 0) { + int16 img = _vm->logic()->objectData(subj1)->image; + if (img == -4 || img == -3) { + _vm->logic()->makeJoeSpeak(18); + } else { + _vm->logic()->makeJoeSpeak(3); + } + } else { + _vm->logic()->makeJoeSpeak(3); + } + break; + + case VERB_GIVE: + // 'I can't give the subj1 to subj2' + if (subj1 < 0) { + if (subj2 > 0) { + int16 img = _vm->logic()->objectData(subj2)->image; + if (img == -4 || img == -3) { + _vm->logic()->makeJoeSpeak(27 + _vm->randomizer.getRandomNumber(2)); + } + } else { + _vm->logic()->makeJoeSpeak(11); + } + } else { + _vm->logic()->makeJoeSpeak(12); + } + break; + + case VERB_PICK_UP: + if (subj1 < 0) { + _vm->logic()->makeJoeSpeak(14); + } else { + int16 img = _vm->logic()->objectData(subj1)->image; + if (img == -4 || img == -3) { + // Trying to get a person + _vm->logic()->makeJoeSpeak(20); + } else { + // 5 : 'I can't pick that up' + // 6 : 'I don't think I need that' + // 7 : 'I'd rather leave it here' + // 8 : 'I don't think I'd have any use for that' + _vm->logic()->makeJoeSpeak(5 + _vm->randomizer.getRandomNumber(3)); + } + } + break; + + default: + break; + } +} + +void Command::changeObjectState(Verb action, int16 obj, int16 song, bool cutDone) { + // l.456-533 execute.c + ObjectData *objData = _vm->logic()->objectData(obj); + + if (action == VERB_OPEN && !cutDone) { + if (State::findOn(objData->state) == STATE_ON_ON) { + State::alterOn(&objData->state, STATE_ON_OFF); + State::alterDefaultVerb(&objData->state, VERB_NONE); + + // play music if it exists... (or SFX for open/close door) + if (song != 0) { + _vm->sound()->playSong(ABS(song)); + } + + if (objData->entryObj != 0) { + // if it's a door, then update door that it links to + openOrCloseAssociatedObject(action, ABS(objData->entryObj)); + objData->entryObj = ABS(objData->entryObj); + } + } else { + // 'it's already open !' + _vm->logic()->makeJoeSpeak(9); + } + } else if (action == VERB_CLOSE && !cutDone) { + if (State::findOn(objData->state) == STATE_ON_OFF) { + State::alterOn(&objData->state, STATE_ON_ON); + State::alterDefaultVerb(&objData->state, VERB_OPEN); + + // play music if it exists... (or SFX for open/close door) + if (song != 0) { + _vm->sound()->playSong(ABS(song)); + } + + if (objData->entryObj != 0) { + // if it's a door, then update door that it links to + openOrCloseAssociatedObject(action, ABS(objData->entryObj)); + objData->entryObj = -ABS(objData->entryObj); + } + } else { + // 'it's already closed !' + _vm->logic()->makeJoeSpeak(10); + } + } else if (action == VERB_MOVE) { + State::alterOn(&objData->state, STATE_ON_OFF); + } +} + +void Command::cleanupCurrentAction() { + // l.595-597 execute.c + _vm->logic()->joeFace(); + _state.oldNoun = 0; + _state.oldVerb = VERB_NONE; +} + +void Command::openOrCloseAssociatedObject(Verb action, int16 otherObj) { + CmdListData *cmdList = &_cmdList[1]; + uint16 com = 0; + uint16 i; + for (i = 1; i <= _numCmdList && com == 0; ++i, ++cmdList) { + if (cmdList->match(action, otherObj, 0)) { + if (cmdList->setConditions) { + CmdGameState *cmdGs = _cmdGameState; + uint16 j; + for (j = 1; j <= _numCmdGameState; ++j) { + if (cmdGs[j].id == i && cmdGs[j].gameStateSlot > 0) { + if (_vm->logic()->gameState(cmdGs[j].gameStateSlot) == cmdGs[j].gameStateValue) { + com = i; + break; + } + } + } + } else { + com = i; + break; + } + } + } + + if (com != 0) { + + debug(6, "Command::openOrCloseAssociatedObject() com=%X", com); + + cmdList = &_cmdList[com]; + ObjectData *objData = _vm->logic()->objectData(otherObj); + + if (cmdList->imageOrder != 0) { + objData->image = cmdList->imageOrder; + } + + if (action == VERB_OPEN) { + if (State::findOn(objData->state) == STATE_ON_ON) { + State::alterOn(&objData->state, STATE_ON_OFF); + State::alterDefaultVerb(&objData->state, VERB_NONE); + objData->entryObj = ABS(objData->entryObj); + } + } else if (action == VERB_CLOSE) { + if (State::findOn(objData->state) == STATE_ON_OFF) { + State::alterOn(&objData->state, STATE_ON_ON); + State::alterDefaultVerb(&objData->state, VERB_OPEN); + objData->entryObj = -ABS(objData->entryObj); + } + } + } +} + +int16 Command::setConditions(uint16 command, bool lastCmd) { + debug(9, "Command::setConditions(%d, %d)", command, lastCmd); + + int16 ret = 0; + uint16 cmdState[21]; + memset(cmdState, 0, sizeof(cmdState)); + uint16 cmdStateCount = 0; + uint16 i; + CmdGameState *cmdGs = &_cmdGameState[1]; + for (i = 1; i <= _numCmdGameState; ++i, ++cmdGs) { + if (cmdGs->id == command) { + if (cmdGs->gameStateSlot > 0) { + if (_vm->logic()->gameState(cmdGs->gameStateSlot) != cmdGs->gameStateValue) { + debug(6, "Command::setConditions() - GS[%d] == %d (should be %d)", cmdGs->gameStateSlot, _vm->logic()->gameState(cmdGs->gameStateSlot), cmdGs->gameStateValue); + // failed test + ret = i; + break; + } + } else { + cmdState[cmdStateCount] = i; + ++cmdStateCount; + } + } + } + + if (ret > 0) { + // we've failed, so see if we need to make Joe speak + cmdGs = &_cmdGameState[ret]; + if (cmdGs->speakValue > 0 && lastCmd) { + // check to see if fail state is in fact a cutaway + const char *objDesc = _vm->logic()->objectTextualDescription(cmdGs->speakValue); + if (!executeIfCutaway(objDesc) && !executeIfDialog(objDesc)) { + _vm->logic()->makeJoeSpeak(cmdGs->speakValue, true); + } + ret = -2; + } else { + // return -1 so Joe will be able to speak a normal description + ret = -1; + } + } else { + ret = 0; + // all tests were okay, now set gamestates + for (i = 0; i < cmdStateCount; ++i) { + cmdGs = &_cmdGameState[cmdState[i]]; + _vm->logic()->gameState(ABS(cmdGs->gameStateSlot), cmdGs->gameStateValue); + // set return value for Joe to say something + ret = cmdGs->speakValue; + } + } + return ret; +} + +void Command::setAreas(uint16 command) { + debug(9, "Command::setAreas(%d)", command); + + CmdArea *cmdArea = &_cmdArea[1]; + for (uint16 i = 1; i <= _numCmdArea; ++i, ++cmdArea) { + if (cmdArea->id == command) { + uint16 areaNum = ABS(cmdArea->area); + Area *area = _vm->grid()->area(cmdArea->room, areaNum); + if (cmdArea->area > 0) { + // turn on area + area->mapNeighbours = ABS(area->mapNeighbours); + } else { + // turn off area + area->mapNeighbours = -ABS(area->mapNeighbours); + } + } + } +} + +void Command::setObjects(uint16 command) { + debug(9, "Command::setObjects(%d)", command); + + CmdObject *cmdObj = &_cmdObject[1]; + for (uint16 i = 1; i <= _numCmdObject; ++i, ++cmdObj) { + if (cmdObj->id == command) { + + // found an object + uint16 dstObj = ABS(cmdObj->dstObj); + ObjectData *objData = _vm->logic()->objectData(dstObj); + + debug(6, "Command::setObjects() - dstObj=%X srcObj=%X _state.subject[0]=%X", cmdObj->dstObj, cmdObj->srcObj, _state.subject[0]); + + if (cmdObj->dstObj > 0) { + // show the object + objData->name = ABS(objData->name); + // test that the object has not already been deleted + // by checking if it is not equal to zero + if (cmdObj->srcObj == -1 && objData->name != 0) { + // delete object by setting its name to 0 and + // turning off graphic image + objData->name = 0; + if (objData->room == _vm->logic()->currentRoom()) { + if (dstObj != _state.subject[0]) { + // if the new object we have updated is on screen and is not the + // current object, then we can update. This is because we turn + // current object off ourselves by COM_LIST(com, 8) + if (objData->image != -3 && objData->image != -4) { + // it is a normal object (not a person) + // turn the graphic image off for the object + objData->image = -(objData->image + 10); + } + } + // invalidate object area + uint16 objZone = dstObj - _vm->logic()->currentRoomData(); + _vm->grid()->setZone(GS_ROOM, objZone, 0, 0, 1, 1); + } + } + + if (cmdObj->srcObj > 0) { + // copy data from dummy object to object + int16 image1 = objData->image; + int16 image2 = _vm->logic()->objectData(cmdObj->srcObj)->image; + _vm->logic()->objectCopy(cmdObj->srcObj, dstObj); + if (image1 != 0 && image2 == 0 && objData->room == _vm->logic()->currentRoom()) { + uint16 bobNum = _vm->logic()->findBob(dstObj); + if (bobNum != 0) { + _vm->graphics()->bob(bobNum)->clear(); + } + } + } + + if (dstObj != _state.subject[0]) { + // if the new object we have updated is on screen and + // is not current object then update it + _vm->graphics()->refreshObject(dstObj); + } + } else { + // hide the object + if (objData->name > 0) { + objData->name = -objData->name; + // may need to turn BOBs off for objects to be hidden on current + // screen ! if the new object we have updated is on screen and + // is not current object then update it + _vm->graphics()->refreshObject(dstObj); + } + } + } + } +} + +void Command::setItems(uint16 command) { + debug(9, "Command::setItems(%d)", command); + + ItemData *items = _vm->logic()->itemData(0); + CmdInventory *cmdInv = &_cmdInventory[1]; + for (uint16 i = 1; i <= _numCmdInventory; ++i, ++cmdInv) { + if (cmdInv->id == command) { + uint16 dstItem = ABS(cmdInv->dstItem); + // found an item + if (cmdInv->dstItem > 0) { + // add item to inventory + if (cmdInv->srcItem > 0) { + // copy data from source item to item, then enable it + items[dstItem] = items[cmdInv->srcItem]; + items[dstItem].name = ABS(items[dstItem].name); + } + _vm->logic()->inventoryInsertItem(cmdInv->dstItem); + } else { + // delete item + if (items[dstItem].name > 0) { + _vm->logic()->inventoryDeleteItem(dstItem); + } + if (cmdInv->srcItem > 0) { + // copy data from source item to item, then disable it + items[dstItem] = items[cmdInv->srcItem]; + items[dstItem].name = -ABS(items[dstItem].name); + } + } + } + } +} + +uint16 Command::nextObjectDescription(ObjectDescription* objDesc, uint16 firstDesc) { + // l.69-103 select.c + uint16 i; + uint16 diff = objDesc->lastDescription - firstDesc; + debug(6, "Command::nextObjectDescription() - diff = %d, type = %d", diff, objDesc->type); + switch (objDesc->type) { + case 0: + // random type, start with first description + if (objDesc->lastSeenNumber == 0) { + // first time look at called + objDesc->lastSeenNumber = firstDesc; + break; + } + // already displayed first, do a random + case 1: + i = objDesc->lastSeenNumber; + while (i == objDesc->lastSeenNumber) { + i = firstDesc + _vm->randomizer.getRandomNumber(diff); + } + objDesc->lastSeenNumber = i; + break; + case 2: + // sequential, but loop + ++objDesc->lastSeenNumber; + if (objDesc->lastSeenNumber > objDesc->lastDescription) { + objDesc->lastSeenNumber = firstDesc; + } + break; + case 3: + // sequential without looping + if (objDesc->lastSeenNumber != objDesc->lastDescription) { + ++objDesc->lastSeenNumber; + } + break; + } + return objDesc->lastSeenNumber; +} + +void Command::lookAtSelectedObject() { + uint16 desc; + if (_state.subject[0] < 0) { + desc = _vm->logic()->itemData(-_state.subject[0])->description; + } else { + ObjectData *objData = _vm->logic()->objectData(_state.subject[0]); + if (objData->name <= 0) { + return; + } + desc = objData->description; + } + + debug(6, "Command::lookAtSelectedObject() - desc = %X, _state.subject[0] = %X", desc, _state.subject[0]); + + // check to see if the object/item has a series of description + ObjectDescription *objDesc = _vm->logic()->objectDescription(1); + uint16 i; + for (i = 1; i <= _vm->logic()->objectDescriptionCount(); ++i, ++objDesc) { + if (objDesc->object == _state.subject[0]) { + desc = nextObjectDescription(objDesc, desc); + break; + } + } + + _vm->logic()->makeJoeSpeak(desc, true); + _vm->logic()->joeFace(); +} + +void Command::lookForCurrentObject(int16 cx, int16 cy) { + uint16 obj = _vm->grid()->findObjectUnderCursor(cx, cy); + _state.noun = _vm->grid()->findObjectNumber(obj); + + if (_state.oldNoun == _state.noun) { + return; + } + + ObjectData *od = findObjectData(_state.noun); + if (od == NULL || od->name <= 0) { + _state.oldNoun = _state.noun; + _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS); + if (_state.action != VERB_NONE) { + _cmdText.display(INK_CMD_NORMAL); + } + return; + } + + // if no command yet selected, then use DEFAULT command, if any + if (_state.action == VERB_NONE) { + Verb v = State::findDefaultVerb(od->state); + _cmdText.setVerb((v == VERB_NONE) ? VERB_WALK_TO : v); + if (_state.noun == 0) { + _cmdText.clear(); + } + } + const char *name = _vm->logic()->objectName(od->name); + _cmdText.displayTemp(INK_CMD_NORMAL, name); + _state.oldNoun = _state.noun; +} + +void Command::lookForCurrentIcon(int16 cx, int16 cy) { + _state.verb = _vm->grid()->findVerbUnderCursor(cx, cy); + if (_state.oldVerb != _state.verb) { + + if (_state.action == VERB_NONE) { + _cmdText.clear(); + } + _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS); + + if (isVerbInv(_state.verb)) { + ItemData *id = findItemData(_state.verb); + if (id != NULL && id->name > 0) { + if (_state.action == VERB_NONE) { + Verb v = State::findDefaultVerb(id->state); + _cmdText.setVerb((v == VERB_NONE) ? VERB_LOOK_AT : v); + } + const char *name = _vm->logic()->objectName(id->name); + _cmdText.displayTemp(INK_CMD_NORMAL, name); + } + } else if (isVerbAction(_state.verb)) { + _cmdText.displayTemp(INK_CMD_NORMAL, _state.verb); + } else if (_state.verb == VERB_NONE) { + _cmdText.display(INK_CMD_NORMAL); + } + _state.oldVerb = _state.verb; + } +} + +} // End of namespace Queen diff --git a/engines/queen/command.h b/engines/queen/command.h new file mode 100644 index 0000000000..3ef848b24d --- /dev/null +++ b/engines/queen/command.h @@ -0,0 +1,234 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENCOMMAND_H +#define QUEENCOMMAND_H + +#include "common/util.h" +#include "queen/structs.h" + +namespace Queen { + +class QueenEngine; + +struct CmdText { + + CmdText(bool reversed, uint8 y, QueenEngine *vm); + + //! reset the command sentence + void clear(); + + //! display the command sentence using the specified color + void display(uint8 color); + + //! display a temporary command sentence using the specified parameters + void displayTemp(uint8 color, Verb v, const char *name = NULL, bool outlined = false); + + //! display a temporary command sentence using the specified parameters + void displayTemp(uint8 color, const char *name, bool outlined = false); + + //! set the verb for the command sentence + void setVerb(Verb v); + + //! set the link word (between verb and object) for the command sentence + void addLinkWord(Verb v); + + //! add an object name to the command sentence + void addObject(const char *objName); + + //! returns true if the command sentence is empty + bool isEmpty() const; + + enum { + MAX_COMMAND_LEN = 256, + COMMAND_Y_POS = 151 + }; + + uint8 _y; + + //! flag indicating if the words in the sentence are reversed (hebrew version) + bool _isReversed; + + //! buffer containing the current command sentence + char _command[MAX_COMMAND_LEN]; + + QueenEngine *_vm; +}; + +struct CmdState { + + void init(); + + Verb oldVerb, verb; + Verb action; + int16 oldNoun, noun; + int commandLevel; + int16 subject[2]; + + Verb selAction; + int16 selNoun; +}; + +class Command { +public: + + Command(QueenEngine *vm); + ~Command(); + + //! initialise command construction + void clear(bool clearTexts); + + //! execute last constructed command + void executeCurrentAction(); + + //! get player input and construct command from it + void updatePlayer(); + + //! read all command arrays from stream + void readCommandsFrom(byte *&ptr); + + enum { + MAX_MATCHING_CMDS = 50 + }; + +private: + + //! get a reference to the ObjectData for the specified room object + ObjectData *findObjectData(uint16 objRoomNum) const; + + //! get a reference to the ItemData for the specified inventory object + ItemData *findItemData(Verb invNum) const; + + //! execute the current command + int16 executeCommand(uint16 comId, int16 condResult); + + //! move Joe to the specified position, handling new room switching + int16 makeJoeWalkTo(int16 x, int16 y, int16 objNum, Verb v, bool mustWalk); + + //! update command state with current selected action + void grabCurrentSelection(); + + //! update command state with current selected object + void grabSelectedObject(int16 objNum, uint16 objState, uint16 objName); + + //! update command state with current selected inventory object + void grabSelectedItem(); + + //! update command state with current selected room object + void grabSelectedNoun(); + + //! update command state with current selected verb + void grabSelectedVerb(); + + //! if the description is a cutaway file, execute it + bool executeIfCutaway(const char *description); + + //! if the description is a dialog file, execute it + bool executeIfDialog(const char *description); + + //! handle a wrong/invalid user action + bool handleWrongAction(); + + //! make Joe speak something for a wrong/invalid action + void sayInvalidAction(Verb action, int16 subj1, int16 subj2); + + //! update an object state + void changeObjectState(Verb action, int16 obj, int16 song, bool cutDone); + + //! reset current action + void cleanupCurrentAction(); + + //! OPEN_CLOSE_OTHER(OBJECT_DATA[S][4]) + void openOrCloseAssociatedObject(Verb action, int16 obj); + + //! update gamestates - P1_SET_CONDITIONS + int16 setConditions(uint16 command, bool lastCmd); + + //! turn on/off areas - P2_SET_AREAS + void setAreas(uint16 command); + + //! hide/show objects, redisplay if in the same room as Joe - P3_SET_OBJECTS + void setObjects(uint16 command); + + //! inserts/deletes items (inventory) - P4_SET_ITEMS + void setItems(uint16 command); + + //! update description for object and returns description number to use + uint16 nextObjectDescription(ObjectDescription *objDesc, uint16 firstDesc); + + //! speak description of selected object + void lookAtSelectedObject(); + + //! get the current object under the cursor + void lookForCurrentObject(int16 cx, int16 cy); + + //! get the current icon panel under the cursor (inventory item or verb) + void lookForCurrentIcon(int16 cx, int16 cy); + + //! returns true if the verb is an action verb + bool isVerbAction(Verb v) const { return (v >= VERB_PANEL_COMMAND_FIRST && v <= VERB_PANEL_COMMAND_LAST) || (v == VERB_WALK_TO); }; + + //! return true if the verb is an inventory item + bool isVerbInv(Verb v) const { return v >= VERB_INV_FIRST && v <= VERB_INV_LAST; } + + //! returns true if the specified verb is an inventory scroll + bool isVerbInvScroll(Verb v) const { return v == VERB_SCROLL_UP || v == VERB_SCROLL_DOWN; } + + //! commands list for each possible action + CmdListData *_cmdList; + uint16 _numCmdList; + + //! commands list for areas + CmdArea *_cmdArea; + uint16 _numCmdArea; + + //! commands list for objects + CmdObject *_cmdObject; + uint16 _numCmdObject; + + //! commands list for inventory + CmdInventory *_cmdInventory; + uint16 _numCmdInventory; + + //! commands list for gamestate + CmdGameState *_cmdGameState; + uint16 _numCmdGameState; + + //! textual form of the command (displayed between room and panel areas) + CmdText _cmdText; + + //! flag indicating that the current command is fully constructed + bool _parse; + + //! state of current constructed command + CmdState _state; + + //! last user selection + int _mouseKey, _selPosX, _selPosY; + + QueenEngine *_vm; +}; + +} // End of namespace Queen + +#endif + diff --git a/engines/queen/credits.cpp b/engines/queen/credits.cpp new file mode 100644 index 0000000000..a88c67e587 --- /dev/null +++ b/engines/queen/credits.cpp @@ -0,0 +1,145 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/credits.h" + +#include "queen/display.h" +#include "queen/queen.h" +#include "queen/resource.h" + +namespace Queen { + +Credits::Credits(QueenEngine *vm, const char* filename) : + _vm(vm), _running(true), _count(0), _pause(0), _justify(0), _fontSize(0), _color(0), _zone(0) { + uint32 size; + char *buf = (char *)_vm->resource()->loadFile(filename, 0, &size); + _credits = new LineReader(buf, size); +} + +Credits::~Credits() { + delete _credits; +} + +void Credits::nextRoom() { + if (-1 == _pause) { + _pause = 0; + _vm->display()->clearTexts(0, 199); + } +} + +void Credits::update() { + if (!_running) + return; + + if (_pause > 0) { + _pause--; + if (!_pause) + _vm->display()->clearTexts(0, 199); + return; + } + + /* wait until next room */ + if (-1 == _pause) + return; + + for (;;) { + const char *line = _credits->nextLine(); + + if (0 == memcmp(line, "EN", 2)) { + _running = false; + return; + } + + if ('.' == line[0]) { + int i; + + switch (tolower(line[1])) { + case 'l' : + _justify = 0; + break; + case 'c' : + _justify = 1; + break; + case 'r' : + _justify = 2; + break; + case 's' : + _fontSize = 0; + break; + case 'b' : + _fontSize = 1; + break; + case 'p' : + _pause = atoi(&line[3]); + _pause *= 10; + /* wait until next room */ + if (0 == _pause) + _pause = -1; + for (i = 0; i < _count; i++) { + _vm->display()->textCurrentColor(_list[i].color); + _vm->display()->setText(_list[i].x, _list[i].y, _list[i].text); + } + _count = 0; + return; + case 'i' : + _color = atoi(&line[3]); + break; + case '1' : + case '2' : + case '3' : + case '4' : + case '5' : + case '6' : + case '7' : + case '8' : + case '9' : + _zone = line[1] - '1'; + break; + } + } else { + assert(_count < ARRAYSIZE(_list)); + _list[_count].text = line; + _list[_count].color = _color; + _list[_count].fontSize = _fontSize; + switch (_justify) { + case 0: + _list[_count].x = (_zone % 3) * (320 / 3) + 8; + break; + case 1: + _list[_count].x = (_zone % 3) * (320 / 3) + 54 - _vm->display()->textWidth(line) / 2; + if (_list[_count].x < 8) + _list[_count].x = 8; + break; + case 2: + _list[_count].x = (_zone % 3) * (320 / 3) + 100 - _vm->display()->textWidth(line); + break; + } + _list[_count].y = (_zone / 3) * (200 / 3) + (_count * 10); + _count++; + } + } +} + + +} // End of namespace Queen + diff --git a/engines/queen/credits.h b/engines/queen/credits.h new file mode 100644 index 0000000000..4f34772c28 --- /dev/null +++ b/engines/queen/credits.h @@ -0,0 +1,88 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef CREDITS_H +#define CREDITS_H + +#include "common/util.h" +#include "queen/defs.h" + +namespace Queen { + +class QueenEngine; +class LineReader; + +class Credits { +public: + + Credits(QueenEngine *vm, const char* filename); + ~Credits(); + + //! update/display credits for current room + void update(); + + //! handles room switching + void nextRoom(); + + //! returns true if the credits are running + bool running() const { return _running; } + +private: + + struct Line { + short x, y, color, fontSize; + const char *text; + }; + + //! contains the formatted lines of texts to display + Line _list[19]; + + //! true if end of credits description hasn't been reached + bool _running; + + //! number of elements in _list array + int _count; + + //! pause counts for next room + int _pause; + + //! current text justification mode + int _justify; + + //! current font size (unused ?) + int _fontSize; + + //! current text color + int _color; + + //! current text position + int _zone; + + //! contains the credits description + LineReader *_credits; + + QueenEngine *_vm; +}; + +} // End of namespace Queen + +#endif diff --git a/engines/queen/cutaway.cpp b/engines/queen/cutaway.cpp new file mode 100644 index 0000000000..dc24d5b627 --- /dev/null +++ b/engines/queen/cutaway.cpp @@ -0,0 +1,1324 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/cutaway.h" + +#include "queen/bankman.h" +#include "queen/display.h" +#include "queen/graphics.h" +#include "queen/grid.h" +#include "queen/input.h" +#include "queen/logic.h" +#include "queen/queen.h" +#include "queen/resource.h" +#include "queen/sound.h" +#include "queen/talk.h" +#include "queen/walk.h" + +namespace Queen { + +void Cutaway::run( + const char *filename, + char *nextFilename, + QueenEngine *vm) { + Cutaway *cutaway = new Cutaway(filename, vm); + cutaway->run(nextFilename); + delete cutaway; +} + +Cutaway::Cutaway( + const char *filename, + QueenEngine *vm) + : _vm(vm), _personDataCount(0), _personFaceCount(0), _lastSong(0), _songBeforeComic(0) { + memset(&_bankNames, 0, sizeof(_bankNames)); + _vm->input()->cutawayQuitReset(); + load(filename); +} + +Cutaway::~Cutaway() { + delete[] _fileData; +} + +void Cutaway::load(const char *filename) { + byte *ptr; + + debug(6, "----- Cutaway::load(\"%s\") -----", filename); + + ptr = _fileData = _vm->resource()->loadFile(filename, 20); + + if (0 == scumm_stricmp(filename, "comic.cut")) + _songBeforeComic = _vm->sound()->lastOverride(); + + strcpy(_basename, filename); + _basename[strlen(_basename)-4] = '\0'; + + _comPanel = READ_BE_UINT16(ptr); + ptr += 2; + debug(6, "_comPanel = %i", _comPanel); + _cutawayObjectCount = (int16)READ_BE_INT16(ptr); + ptr += 2; + + debug(6, "_cutawayObjectCount = %i", _cutawayObjectCount); + + if (_cutawayObjectCount < 0) { + _cutawayObjectCount = -_cutawayObjectCount; + _vm->input()->canQuit(false); + } else + _vm->input()->canQuit(true); + + int16 flags1 = (int16)READ_BE_INT16(ptr); + ptr += 2; + debug(6, "flags1 = %i", flags1); + + if (flags1 < 0) { + _vm->logic()->entryObj(0); + _finalRoom = -flags1; + } else + _finalRoom = PREVIOUS_ROOM; + + _anotherCutaway = (flags1 == 1); + + debug(6, "[Cutaway::load] _finalRoom = %i", _finalRoom); + debug(6, "[Cutaway::load] _anotherCutaway = %i", _anotherCutaway); + + /* + Pointers to other places in the cutaway data + */ + + _gameStatePtr = _fileData + READ_BE_UINT16(ptr); + ptr += 2; + + _nextSentenceOff = READ_BE_UINT16(ptr); + ptr += 2; + + uint16 bankNamesOff = READ_BE_UINT16(ptr); + ptr += 2; + + _objectData = ptr; + + loadStrings(bankNamesOff); + + if (_bankNames[0][0]) { + debug(6, "Loading bank '%s'", _bankNames[0]); + _vm->bankMan()->load(_bankNames[0], CUTAWAY_BANK); + } + + char entryString[MAX_STRING_SIZE]; + Talk::getString(_fileData, _nextSentenceOff, entryString, MAX_STRING_LENGTH); + debug(6, "Entry string = '%s'", entryString); + + _vm->logic()->joeCutFacing(_vm->logic()->joeFacing()); + _vm->logic()->joeFace(); + + if (entryString[0] == '*' && + entryString[1] == 'F' && + entryString[3] == '\0') { + switch (entryString[2]) { + case 'L': + _vm->logic()->joeCutFacing(DIR_LEFT); + break; + case 'R': + _vm->logic()->joeCutFacing(DIR_RIGHT); + break; + case 'F': + _vm->logic()->joeCutFacing(DIR_FRONT); + break; + case 'B': + _vm->logic()->joeCutFacing(DIR_BACK); + break; + } + } + +} + +void Cutaway::loadStrings(uint16 offset) { + int bankNameCount = READ_BE_UINT16(_fileData + offset); + offset += 2; + + debug(6, "Bank name count = %i", bankNameCount); + + /* + The _bankNames zero-based array is the one-based BANK_NAMEstr array in + the original source code. + */ + + for (int i = 0, j = 0; i < bankNameCount; i++) { + Talk::getString(_fileData, offset, _bankNames[j], MAX_FILENAME_LENGTH); + if (_bankNames[j][0]) { + debug(6, "Bank name %i = '%s'", j, _bankNames[j]); + j++; + } + } + + debug(6, "Getting talk file"); + Talk::getString(_fileData, offset, _talkFile, MAX_FILENAME_LENGTH); + debug(6, "Talk file = '%s'", _talkFile); + + _talkTo = (int16)READ_BE_INT16(_fileData + offset); + debug(6, "_talkTo = %i", _talkTo); +} + +const byte *Cutaway::getCutawayObject(const byte *ptr, CutawayObject &object) +{ + const byte *oldPtr = ptr; + + object.objectNumber = (int16)READ_BE_INT16(ptr); ptr += 2; + object.moveToX = (int16)READ_BE_INT16(ptr); ptr += 2; + object.moveToY = (int16)READ_BE_INT16(ptr); ptr += 2; + object.bank = (int16)READ_BE_INT16(ptr); ptr += 2; + object.animList = (int16)READ_BE_INT16(ptr); ptr += 2; + object.execute = (int16)READ_BE_INT16(ptr); ptr += 2; + object.limitBobX1 = (int16)READ_BE_INT16(ptr); ptr += 2; + object.limitBobY1 = (int16)READ_BE_INT16(ptr); ptr += 2; + object.limitBobX2 = (int16)READ_BE_INT16(ptr); ptr += 2; + object.limitBobY2 = (int16)READ_BE_INT16(ptr); ptr += 2; + object.specialMove = (int16)READ_BE_INT16(ptr); ptr += 2; + object.animType = (int16)READ_BE_INT16(ptr); ptr += 2; + object.fromObject = (int16)READ_BE_INT16(ptr); ptr += 2; + object.bobStartX = (int16)READ_BE_INT16(ptr); ptr += 2; + object.bobStartY = (int16)READ_BE_INT16(ptr); ptr += 2; + object.room = (int16)READ_BE_INT16(ptr); ptr += 2; + object.scale = (int16)READ_BE_INT16(ptr); ptr += 2; + + if ((ptr - oldPtr) != 17*sizeof(int16)) + error("Wrong number of values read"); + + // Make ugly reuse of data less ugly + if (object.limitBobX1 < 0) { + object.song = -object.limitBobX1; + object.limitBobX1 = 0; + } else + object.song = 0; + + return ptr; +} + +void Cutaway::dumpCutawayObject(int index, CutawayObject &object) +{ + debug(6, "----- CutawayObject[%i] -----", index); + + const char *objectNumberStr; + + switch (object.objectNumber) { + case -1: + objectNumberStr = "MESSAGE"; + break; + case 0: + objectNumberStr = "Joe"; + break; + default: + if (object.objectNumber > 0) + objectNumberStr = _vm->logic()->objectName(ABS(_vm->logic()->objectData(object.objectNumber)->name)); + else + objectNumberStr = "Unknown!"; + break; + } + + debug(6, "objectNumber = %i (%s)", object.objectNumber, objectNumberStr); + + if (object.moveToX) debug(6, "moveToX = %i", object.moveToX); + if (object.moveToY) debug(6, "moveToY = %i", object.moveToY); + if (object.bank) debug(6, "bank = %i", object.bank); + if (object.animList) debug(6, "animList = %i", object.animList); + if (object.execute) debug(6, "execute = %i", object.execute); + if (object.limitBobX1) debug(6, "limitBobX1 = %i", object.limitBobX1); + if (object.limitBobY1) debug(6, "limitBobY1 = %i", object.limitBobY1); + if (object.limitBobX2) debug(6, "limitBobX2 = %i", object.limitBobX2); + if (object.limitBobY2) debug(6, "limitBobY2 = %i", object.limitBobY2); + if (object.specialMove) debug(6, "specialMove = %i", object.specialMove); + if (object.animType) debug(6, "animType = %i", object.animType); + if (object.fromObject) debug(6, "fromObject = %i", object.fromObject); + if (object.bobStartX) debug(6, "bobStartX = %i", object.bobStartX); + if (object.bobStartY) debug(6, "bobStartY = %i", object.bobStartY); + if (object.room) debug(6, "room = %i", object.room); + if (object.scale) debug(6, "scale = %i", object.scale); + +} + + +const byte *Cutaway::turnOnPeople(const byte *ptr, CutawayObject &object) { + // Lines 1248-1259 in cutaway.c + object.personCount = (int16)READ_BE_INT16(ptr); + ptr += 2; + + if (object.personCount > MAX_PERSON_COUNT) + error("[Cutaway::turnOnPeople] object.personCount > MAX_PERSON_COUNT"); + + for (int i = 0; i < object.personCount; i++) { + object.person[i] = (int16)READ_BE_INT16(ptr); + ptr += 2; + debug(7, "[%i] Turn on person %i", i, object.person[i]); + } + + return ptr; +} + +void Cutaway::limitBob(CutawayObject &object) { + if (object.limitBobX1) { + + if (object.objectNumber < 0) { + warning("QueenCutaway::limitBob called with objectNumber = %i", object.objectNumber); + return; + } + + BobSlot *bob = + _vm->graphics()->bob( _vm->logic()->findBob(object.objectNumber) ); + + if (!bob) { + warning("Failed to find bob"); + return; + } + + bob->box.x1 = object.limitBobX1; + bob->box.y1 = object.limitBobY1; + bob->box.x2 = object.limitBobX2; + bob->box.y2 = object.limitBobY2; + } +} + +void Cutaway::restorePersonData() { + for (int i = 0; i < _personDataCount; i++) { + int index = _personData[i].index; + ObjectData *objectData = _vm->logic()->objectData(index); + objectData->name = _personData[i].name; + objectData->image = _personData[i].image; + } +} + +void Cutaway::changeRooms(CutawayObject &object) { + // Lines 1291-1385 in cutaway.c + + debug(6, "Changing from room %i to room %i", + _temporaryRoom, + object.room); + + restorePersonData(); + _personDataCount = 0; + + if (_finalRoom != object.room) { + int firstObjectInRoom = _vm->logic()->roomData(object.room) + 1; + int lastObjectInRoom = _vm->logic()->roomData(object.room) + _vm->grid()->objMax(object.room); + + for (int i = firstObjectInRoom; i <= lastObjectInRoom; i++) { + ObjectData *objectData = _vm->logic()->objectData(i); + + if (objectData->image == -3 || objectData->image == -4) { + + assert(_personDataCount < MAX_PERSON_COUNT); + // The object is a person! So record the details... + _personData[_personDataCount].index = i; + _personData[_personDataCount].name = objectData->name; + _personData[_personDataCount].image = objectData->image; + _personDataCount++; + + // Now, check to see if we need to keep the person on + bool on = false; + for (int j = 0; j < object.personCount; j++) { + if (object.person[j] == i) { + on = true; + break; + } + } + + if (on) { + // It is needed, so ensure it's ON + objectData->name = ABS(objectData->name); + } else { + // Not needed, so switch off! + objectData->name = -ABS(objectData->name); + } + + } + } // for () + } + + // set coordinates for Joe if he is on screen + + _vm->logic()->joePos(0, 0); + + for (int i = 0; i < object.personCount; i++) { + if (PERSON_JOE == object.person[i]) { + _vm->logic()->joePos(object.bobStartX, object.bobStartY); + } + } + + _vm->logic()->oldRoom(_initialRoom); + + // FIXME - the first cutaway is played at the end of the command 0x178. This + // command setups some persons and associates bob slots to them. They should be + // hidden as their y coordinate is > 150, but they aren't ! A (temporary) + // workaround is to display the room with the panel area enabled. Same problem + // for cutaway c62c. + int16 comPanel = _comPanel; + if ((strcmp(_basename, "c41f") == 0 && _temporaryRoom == 106 && object.room == 41) || + (strcmp(_basename, "c62c") == 0 && _temporaryRoom == 105 && object.room == 41)) { + comPanel = 1; + } + + // FIXME - in the original engine, panel is hidden once the 'head room' is displayed, we + // do it before (ie before palette fading) + if (object.room == FAYE_HEAD || object.room == AZURA_HEAD || object.room == FRANK_HEAD) { + comPanel = 2; + } + + RoomDisplayMode mode; + + if (!_vm->logic()->joeX() && !_vm->logic()->joeY()) { + mode = RDM_FADE_NOJOE; + } else { + // We need to display Joe on screen + if (_roomFade) + mode = RDM_NOFADE_JOE; + else + mode = RDM_FADE_JOE_XY; + } + + _vm->logic()->displayRoom(_vm->logic()->currentRoom(), mode, object.scale, comPanel, true); + + _currentImage = _vm->graphics()->numFrames(); + + _temporaryRoom = _vm->logic()->currentRoom(); + + restorePersonData(); +} + +Cutaway::ObjectType Cutaway::getObjectType(CutawayObject &object) { + // Lines 1387-1449 in cutaway.c + + ObjectType objectType = OBJECT_TYPE_ANIMATION; + + if (object.objectNumber > 0) { + if (!object.animList) { + // No anim frames, so treat as a PERSON, ie. allow to speak/walk + ObjectData *objectData = _vm->logic()->objectData(object.objectNumber); + if (objectData->image == -3 || objectData->image == -4) + objectType = OBJECT_TYPE_PERSON; + } + } else if (object.objectNumber == OBJECT_JOE) { + // It's Joe. See if he's to be treated as a person. + if (!object.animList) { + // There's no animation list, so Joe must be talking. + objectType = OBJECT_TYPE_PERSON; + } + } + + if (object.fromObject > 0) { + /* Copy FROM_OBJECT into OBJECT */ + + if (object.objectNumber != object.fromObject) { + _vm->logic()->objectCopy(object.fromObject, object.objectNumber); + } else { + // Same object, so just turn it on! + ObjectData *objectData = _vm->logic()->objectData(object.objectNumber); + objectData->name = ABS(objectData->name); + } + + _vm->graphics()->refreshObject(object.objectNumber); + + // Skip doing any anim stuff + objectType = OBJECT_TYPE_NO_ANIMATION; + } + + switch (object.objectNumber) { + case -2: + // Text to be spoken + objectType = OBJECT_TYPE_TEXT_SPEAK; + break; + case -3: + // Text to be displayed AND spoken + objectType = OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK; + break; + case -4: + // Text to be displayed only (not spoken) + objectType = OBJECT_TYPE_TEXT_DISPLAY; + break; + } + + if (OBJECT_TYPE_ANIMATION == objectType && !object.execute) { + // Execute is not on, and it's an object, so ignore any Anims + objectType = OBJECT_TYPE_NO_ANIMATION; + } + + return objectType; +} + +const byte *Cutaway::getCutawayAnim(const byte *ptr, int header, CutawayAnim &anim) { + // lines 1531-1607 in cutaway.c + + //debug(6, "[Cutaway::getCutawayAnim] header=%i", header); + + anim.currentFrame = 0; + anim.originalFrame = 0; + + if (-1 == header) + header = 0; + + if (0 == header) { + anim.object = 0; + anim.originalFrame = 31; + } else { + anim.object = _vm->logic()->findBob(header); + anim.originalFrame = _vm->logic()->findFrame(header); + } + + anim.unpackFrame = (int16)READ_BE_INT16(ptr); + ptr += 2; + + anim.speed = ((int16)READ_BE_INT16(ptr)) / 3 + 1; + ptr += 2; + + anim.bank = (int16)READ_BE_INT16(ptr); + ptr += 2; + + if (anim.bank == 0) { + anim.bank = 15; + } else { + if (anim.bank != 13) { + _vm->bankMan()->load(_bankNames[anim.bank-1], CUTAWAY_BANK); + anim.bank = 8; + } else { + // Make sure we ref correct JOE bank (7) + anim.bank = 7; + } + } + + anim.mx = (int16)READ_BE_INT16(ptr); + ptr += 2; + + anim.my = (int16)READ_BE_INT16(ptr); + ptr += 2; + + anim.cx = (int16)READ_BE_INT16(ptr); + ptr += 2; + + anim.cy = (int16)READ_BE_INT16(ptr); + ptr += 2; + + anim.scale = (int16)READ_BE_INT16(ptr); + ptr += 2; + + if (_vm->resource()->isDemo()) { + anim.song = 0; + } else { + anim.song = (int16)READ_BE_INT16(ptr); + ptr += 2; + } + + // Extract information that depend on the signedness of values + if (anim.unpackFrame < 0) { + anim.flip = true; + anim.unpackFrame = -anim.unpackFrame; + } else + anim.flip = false; + + return ptr; +} + +void Cutaway::dumpCutawayAnim(CutawayAnim &anim) { + debug(6, "----- CutawayAnim -----"); + if (anim.object) debug(6, "object = %i", anim.object); + if (anim.unpackFrame) debug(6, "unpackFrame = %i", anim.unpackFrame); + if (anim.speed) debug(6, "speed = %i", anim.speed); + if (anim.bank) debug(6, "bank = %i", anim.bank); + if (anim.mx) debug(6, "mx = %i", anim.mx); + if (anim.my) debug(6, "my = %i", anim.my); + if (anim.cx) debug(6, "cx = %i", anim.cx); + if (anim.cy) debug(6, "cy = %i", anim.cy); + if (anim.scale) debug(6, "scale = %i", anim.scale); + if (anim.currentFrame) debug(6, "currentFrame = %i", anim.currentFrame); + if (anim.originalFrame) debug(6, "originalFrame = %i", anim.originalFrame); + if (anim.song) debug(6, "song = %i", anim.song); +} + +const byte *Cutaway::handleAnimation(const byte *ptr, CutawayObject &object) { + // lines 1517-1770 in cutaway.c + int frameCount = 0; + int header = 0; + int i; + + CutawayAnim objAnim[56]; + + // Read animation frames + for (;;) { + + header = (int16)READ_BE_INT16(ptr); + ptr += 2; + + if (-2 == header) + break; + + //debug(6, "Animation frame %i, header = %i", frameCount, header); + + if (header > 1000) + error("Header too large"); + + ptr = getCutawayAnim(ptr, header, objAnim[frameCount]); + //dumpCutawayAnim(objAnim[frameCount]); + + frameCount++; + + if (_vm->input()->cutawayQuit()) + return NULL; + } + + if (object.animType == 1) { + // lines 1615-1636 in cutaway.c + + debug(6, "----- Complex cutaway animation (animType = %i) -----", object.animType); + + if ((_vm->logic()->currentRoom() == 47 || _vm->logic()->currentRoom() == 63) && + objAnim[0].object == 1) { + //CR 2 - 3/3/95, Special harcoded section to make Oracle work... + makeComplexAnimation(_vm->graphics()->personFrames(1) - 1, objAnim, frameCount); + } else { + _currentImage = makeComplexAnimation(_currentImage, objAnim, frameCount); + } + + if (object.bobStartX || object.bobStartY) { + BobSlot *bob = _vm->graphics()->bob(objAnim[0].object); + bob->x = object.bobStartX; + bob->y = object.bobStartY; + } + } + + // Setup the SYNCHRO bob channels + + for (i = 0; i < frameCount; i++) { + if (objAnim[i].mx || objAnim[i].my) { + BobSlot *bob = _vm->graphics()->bob(objAnim[i].object); + bob->frameNum = objAnim[i].originalFrame; + bob->move(objAnim[i].mx, objAnim[i].my, (object.specialMove > 0) ? object.specialMove : 4); + // Boat room hard coded + if (_vm->logic()->currentRoom() == ROOM_TEMPLE_OUTSIDE) { + BobSlot *bobJoe = _vm->graphics()->bob(0); + if (bobJoe->x < 320) { + bobJoe->move(bobJoe->x + 346, bobJoe->y, 4); + } + } + } + } + + // Normal cutaway + + if (object.animType != 1) { + // lines 1657-1761 in cutaway.c + + debug(6, "----- Normal cutaway animation (animType = %i) -----", object.animType); + + for (i = 0; i < frameCount; i++) { + //debug(6, "===== Animating frame %i =====", i); + //dumpCutawayAnim(objAnim[i]); + + BobSlot *bob = _vm->graphics()->bob(objAnim[i].object); + bob->active = true; + if (bob->animating) { + bob->animating = false; + bob->frameNum = objAnim[i].originalFrame; + } + + if (objAnim[i].object < 4) + bob->frameNum = 31 + objAnim[i].object; + + if (objAnim[i].unpackFrame == 0) { + // Turn off the bob + bob->active = false; + } else { + if (object.animType == 2 || object.animType == 0) { + // Unpack animation, but do not unpack moving people + + if (!((objAnim[i].mx > 0 || objAnim[i].my > 0) && inRange(objAnim[i].object, 1, 3))) { + _vm->bankMan()->unpack( + objAnim[i].unpackFrame, + objAnim[i].originalFrame, + objAnim[i].bank); + } + + if (0 == objAnim[i].object) { + // Scale Joe + bob->scale = scale(object); + } + } + + if (objAnim[i].cx || objAnim[i].cy) { + bob->x = objAnim[i].cx; + bob->y = objAnim[i].cy; + } + + // Only flip if we are not moving or it is not a person object + if (!(objAnim[i].object > 0 && objAnim[i].object < 4) || + !(objAnim[i].mx || objAnim[i].my) ) + bob->xflip = objAnim[i].flip; + + // Add frame alteration + if (!(objAnim[i].object > 0 && objAnim[i].object < 4)) { + bob->frameNum = objAnim[i].originalFrame; + } + + int j; + for (j = 0; j < objAnim[i].speed; j++) + _vm->update(); + } + + if (_vm->input()->cutawayQuit()) + return NULL; + + if (objAnim[i].song > 0) + _vm->sound()->playSong(objAnim[i].song); + + } // for () + } + + bool moving = true; + + while (moving) { + moving = false; + _vm->update(); + + for (i = 0; i < frameCount; i++) { + BobSlot *bob = _vm->graphics()->bob(objAnim[i].object); + if (bob->moving) { + moving = true; + break; + } + } + + if (_vm->input()->cutawayQuit()) + return NULL; + } + + return ptr; +} + +static void findCdCut(const char *basename, int index, char *result) { + strcpy(result, basename); + for (int i = strlen(basename); i < 5; i++) + result[i] = '_'; + snprintf(result + 5, 3, "%02i", index); +} + +void Cutaway::handlePersonRecord( + int index, + CutawayObject &object, + const char *sentence) { + // Lines 1455-1516 in cutaway.c + + Person p; + + if (object.objectNumber == OBJECT_JOE) { + if (object.moveToX || object.moveToY) { + _vm->walk()->moveJoe(0, object.moveToX, object.moveToY, true); + } + } else { + _vm->logic()->initPerson( + object.objectNumber - _vm->logic()->currentRoomData(), + "", true, &p); + + if (object.bobStartX || object.bobStartY) { + BobSlot *bob = _vm->graphics()->bob(p.actor->bobNum); + bob->scale = scale(object); + bob->x = object.bobStartX; + bob->y = object.bobStartY; + } + + if (object.moveToX || object.moveToY) + _vm->walk()->movePerson( + &p, + object.moveToX, object.moveToY, + _currentImage + 1, + _vm->logic()->objectData(object.objectNumber)->image + ); + } + + if (_vm->input()->cutawayQuit()) + return; + + if (0 != strcmp(sentence, "*")) { + if (sentence[0] == '#') { + debug(4, "Starting credits '%s'", sentence + 1); + _vm->logic()->startCredits(sentence + 1); + } else { + if (object.objectNumber > 0) { + bool foundPerson = false; + + for (int i = 1; i <= _personFaceCount; i++) { + if (_personFace[i].index == object.objectNumber) { + foundPerson = true; + break; + } + } + + if (!foundPerson) { + _personFaceCount++; + assert(_personFaceCount < MAX_PERSON_FACE_COUNT); + _personFace[_personFaceCount].index = object.objectNumber; + _personFace[_personFaceCount].image = _vm->logic()->objectData(object.objectNumber)->image; + } + } + + char voiceFilePrefix[MAX_STRING_SIZE]; + findCdCut(_basename, index, voiceFilePrefix); + _vm->logic()->makePersonSpeak(sentence, (object.objectNumber == OBJECT_JOE) ? NULL : &p, voiceFilePrefix); + } + + } + + if (_vm->input()->cutawayQuit()) + return; +} + +void Cutaway::run(char *nextFilename) { + int i; + nextFilename[0] = '\0'; + + _currentImage = _vm->graphics()->numFrames(); + + BobSlot *joeBob = _vm->graphics()->bob(0); + int initialJoeX = joeBob->x; + int initialJoeY = joeBob->y; + debug(6, "[Cutaway::run] Joe started at (%i, %i)", initialJoeX, initialJoeY); + + _vm->input()->cutawayRunning(true); + + _initialRoom = _temporaryRoom = _vm->logic()->currentRoom(); + + _vm->display()->screenMode(_comPanel, true); + + if (_comPanel == 0 || _comPanel == 2) { + _vm->logic()->sceneStart(); + } + + memset(_personFace, 0, sizeof(_personFace)); + _personFaceCount = 0; + + const byte *ptr = _objectData; + + for (i = 0; i < _cutawayObjectCount; i++) { + CutawayObject object; + ptr = getCutawayObject(ptr, object); + //dumpCutawayObject(i, object); + + if (!object.moveToX && + !object.moveToY && + object.specialMove > 0 && + object.objectNumber >= 0) { + _vm->logic()->executeSpecialMove(object.specialMove); + object.specialMove = 0; + } + + if (CURRENT_ROOM == object.room) { + // Get current room + object.room = _vm->logic()->currentRoom(); + } else { + // Change current room + _vm->logic()->currentRoom(object.room); + } + + ptr = turnOnPeople(ptr, object); + + limitBob(object); + + char sentence[MAX_STRING_SIZE]; + Talk::getString(_fileData, _nextSentenceOff, sentence, MAX_STRING_LENGTH); + + if (OBJECT_ROOMFADE == object.objectNumber) { + _roomFade = true; + object.objectNumber = OBJECT_JOE; + } else { + _roomFade = false; + } + + if (object.room != _temporaryRoom) + changeRooms(object); + + ObjectType objectType = getObjectType(object); + + if (object.song) + _vm->sound()->playSong(object.song); + + switch (objectType) { + case OBJECT_TYPE_ANIMATION: + ptr = handleAnimation(ptr, object); + break; + case OBJECT_TYPE_PERSON: + handlePersonRecord(i + 1, object, sentence); + break; + case OBJECT_TYPE_NO_ANIMATION: + // Do nothing? + break; + case OBJECT_TYPE_TEXT_SPEAK: + case OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK: + case OBJECT_TYPE_TEXT_DISPLAY: + handleText(i + 1, objectType, object, sentence); + break; + default: + warning("Unhandled object type: %i", objectType); + break; + } + + if (_vm->input()->cutawayQuit()) + break; + + if (_roomFade) { + _vm->update(); + BobSlot *j = _vm->graphics()->bob(0); + _vm->display()->palFadeIn(_vm->logic()->currentRoom(), j->active, j->x, j->y); + _roomFade = false; + } + + } // for () + + _vm->display()->clearTexts(0, 198); + // XXX lines 1887-1895 in cutaway.c + + stop(); + + updateGameState(); + + _vm->bankMan()->close(CUTAWAY_BANK); + + talk(nextFilename); + + if (_comPanel == 0 || (_comPanel == 2 && !_anotherCutaway)) { + _vm->logic()->sceneStop(); + _comPanel = 0; + } + + if (nextFilename[0] == '\0' && !_anotherCutaway && _vm->logic()->currentRoom() != ROOM_ENDING_CREDITS) { + _vm->display()->fullscreen(false); + + // Lines 2138-2182 in cutaway.c + if (_finalRoom) { + _vm->logic()->newRoom(0); + _vm->logic()->entryObj(0); + } else { + /// No need to stay in current room, so return to previous room + // if one exists. Reset Joe's X,Y coords to those when first entered + + restorePersonData(); + + debug(6, "_vm->logic()->entryObj() = %i", _vm->logic()->entryObj()); + if (_vm->logic()->entryObj() > 0) { + _initialRoom = _vm->logic()->objectData(_vm->logic()->entryObj())->room; + } else { + // We're not returning to new room, so return to old Joe X,Y coords + debug(6, "[Cutaway::run] Moving joe to (%i, %i)", initialJoeX, initialJoeY); + _vm->logic()->joePos(initialJoeX, initialJoeY); + } + + if (_vm->logic()->currentRoom() != _initialRoom) { + _vm->logic()->currentRoom(_initialRoom); + _vm->logic()->changeRoom(); + if (_vm->logic()->currentRoom() == _vm->logic()->newRoom()) { + _vm->logic()->newRoom(0); + } + } + _vm->logic()->joePos(0, 0); + } + + _vm->logic()->joeCutFacing(0); + _comPanel = 0; + + int k = 0; + for (i = _vm->logic()->roomData(_vm->logic()->currentRoom()); + i <= _vm->logic()->roomData(_vm->logic()->currentRoom() + 1); i++) { + + ObjectData *object = _vm->logic()->objectData(i); + if (object->image == -3 || object->image == -4) { + k++; + if (object->name > 0) { + _vm->graphics()->resetPersonAnim(k); + } + } + } + + _vm->logic()->removeHotelItemsFromInventory(); + } + + joeBob->animating = 0; + joeBob->moving = 0; + + // if the cutaway has been cancelled, we must stop the speech and the sfx as well + if (_vm->input()->cutawayQuit()) { + if (_vm->sound()->isSpeechActive()) + _vm->sound()->stopSpeech(); + _vm->sound()->stopSfx(); + } + + _vm->input()->cutawayRunning(false); + _vm->input()->cutawayQuitReset(); + _vm->input()->quickSaveReset(); + _vm->input()->quickLoadReset(); + + if (_songBeforeComic > 0) + _vm->sound()->playSong(_songBeforeComic); + else if (_lastSong > 0) + _vm->sound()->playSong(_lastSong); +} + +void Cutaway::stop() { + // Lines 1901-2032 in cutaway.c + byte *ptr = _gameStatePtr; + + // Skipping GAMESTATE data + int gameStateCount = (int16)READ_BE_INT16(ptr); ptr += 2; + if (gameStateCount > 0) + ptr += (gameStateCount * 12); + + // Get the final room and Joe's final position + + int16 joeRoom = READ_BE_UINT16(ptr); ptr += 2; + int16 joeX = READ_BE_UINT16(ptr); ptr += 2; + int16 joeY = READ_BE_UINT16(ptr); ptr += 2; + + debug(6, "[Cutaway::stop] Final position is room %i and coordinates (%i, %i)", + joeRoom, joeX, joeY); + + if ((!_vm->input()->cutawayQuit() || (!_anotherCutaway && joeRoom == _finalRoom)) && + joeRoom != _temporaryRoom && + joeRoom != 0) { + + debug(6, "[Cutaway::stop] Changing rooms and moving Joe"); + + _vm->logic()->joePos(joeX, joeY); + _vm->logic()->currentRoom(joeRoom); + _vm->logic()->oldRoom(_initialRoom); + _vm->logic()->displayRoom(_vm->logic()->currentRoom(), RDM_FADE_JOE_XY, 0, _comPanel, true); + } + + if (_vm->input()->cutawayQuit()) { + // Lines 1927-2032 in cutaway.c + int i; + + // Stop the credits from running + _vm->logic()->stopCredits(); + + _vm->graphics()->stopBobs(); + + for (i = 1; i <= _personFaceCount; i++) { + int index = _personFace[i].index; + if (index > 0) { + _vm->logic()->objectData(_personFace[i].index)->image = _personFace[i].image; + + _vm->graphics()->bob(_vm->logic()->findBob(index))->xflip = + (_personFace[i].image != -4); + } + } + + int quitObjectCount = (int16)READ_BE_INT16(ptr); ptr += 2; + + for (i = 0; i < quitObjectCount; i++) { + int16 objectIndex = (int16)READ_BE_INT16(ptr); ptr += 2; + int16 fromIndex = (int16)READ_BE_INT16(ptr); ptr += 2; + int16 x = (int16)READ_BE_INT16(ptr); ptr += 2; + int16 y = (int16)READ_BE_INT16(ptr); ptr += 2; + int16 room = (int16)READ_BE_INT16(ptr); ptr += 2; + int16 frame = (int16)READ_BE_INT16(ptr); ptr += 2; + int16 bank = (int16)READ_BE_INT16(ptr); ptr += 2; + + int bobIndex = _vm->logic()->findBob(objectIndex); + ObjectData *object = _vm->logic()->objectData(objectIndex); + + if (fromIndex > 0) { + if (fromIndex == objectIndex) { + // Enable object + object->name = ABS(object->name); + } else { + _vm->logic()->objectCopy(fromIndex, objectIndex); + + ObjectData *from = _vm->logic()->objectData(fromIndex); + if (object->image && !from->image && bobIndex && _vm->logic()->currentRoom() == object->room) + _vm->graphics()->bob(bobIndex)->clear(); + } + + if (_vm->logic()->currentRoom() == room) + _vm->graphics()->refreshObject(objectIndex); + } + + if (_vm->logic()->currentRoom() == object->room) { + BobSlot *pbs = _vm->graphics()->bob(bobIndex); + + if (x || y) { + pbs->x = x; + pbs->y = y; + if (inRange(object->image, -4, -3)) + pbs->scale = _vm->grid()->findScale(x, y); + } + + if (frame) { + if (0 == bank) + bank = 15; + else if (bank != 13) { + _vm->bankMan()->load(_bankNames[bank-1], CUTAWAY_BANK); + bank = 8; + } + + int objectFrame = _vm->logic()->findFrame(objectIndex); + + if (objectFrame == 1000) { + _vm->graphics()->bob(bobIndex)->clear(); + } else if (objectFrame) { + _vm->bankMan()->unpack(ABS(frame), objectFrame, bank); + pbs->frameNum = objectFrame; + if (frame < 0) + pbs->xflip = true; + + } + } + } + } // for () + + int16 specialMove = (int16)READ_BE_INT16(ptr); ptr += 2; + if (specialMove > 0) + _vm->logic()->executeSpecialMove(specialMove); + + _lastSong = (int16)READ_BE_INT16(ptr); ptr += 2; + } + + if (joeRoom == _temporaryRoom && + joeRoom != 37 && joeRoom != 105 && joeRoom != 106 && + (joeX || joeY)) { + BobSlot *joeBob = _vm->graphics()->bob(0); + + debug(6, "[Cutaway::stop] Moving Joe"); + + joeBob->x = joeX; + joeBob->y = joeY; + _vm->logic()->joeScale(_vm->grid()->findScale(joeX, joeY)); + _vm->logic()->joeFace(); + } +} + +void Cutaway::updateGameState() { + // Lines 2047-2115 in cutaway.c + byte *ptr = _gameStatePtr; + + int gameStateCount = (int16)READ_BE_INT16(ptr); ptr += 2; + + for (int i = 0; i < gameStateCount; i++) { + int16 stateIndex = (int16)READ_BE_INT16(ptr); ptr += 2; + int16 stateValue = (int16)READ_BE_INT16(ptr); ptr += 2; + int16 objectIndex = (int16)READ_BE_INT16(ptr); ptr += 2; + int16 areaIndex = (int16)READ_BE_INT16(ptr); ptr += 2; + int16 areaSubIndex = (int16)READ_BE_INT16(ptr); ptr += 2; + int16 fromObject = (int16)READ_BE_INT16(ptr); ptr += 2; + + bool update = false; + + if (stateIndex > 0) { + if (_vm->logic()->gameState(stateIndex) == stateValue) + update = true; + } else { + _vm->logic()->gameState(ABS(stateIndex), stateValue); + update = true; + } + + if (update) { + + if (objectIndex > 0) { // Show the object + ObjectData *objectData = _vm->logic()->objectData(objectIndex); + objectData->name = ABS(objectData->name); + if (fromObject > 0) + _vm->logic()->objectCopy(fromObject, objectIndex); + _vm->graphics()->refreshObject(objectIndex); + } else if (objectIndex < 0) { // Hide the object + objectIndex = -objectIndex; + ObjectData *objectData = _vm->logic()->objectData(objectIndex); + objectData->name = -ABS(objectData->name); + _vm->graphics()->refreshObject(objectIndex); + } + + if (areaIndex > 0) { + + // Turn area on or off + + if (areaSubIndex > 0) { + Area *area = _vm->grid()->area(areaIndex, areaSubIndex); + area->mapNeighbours = ABS(area->mapNeighbours); + } else { + Area *area = _vm->grid()->area(areaIndex, ABS(areaSubIndex)); + area->mapNeighbours = -ABS(area->mapNeighbours); + } + } + + } + } // for () +} + +void Cutaway::talk(char *nextFilename) { + const char *p = strrchr(_talkFile, '.'); + if (p && 0 == scumm_stricmp(p, ".dog")) { + nextFilename[0] = '\0'; + assert(_talkTo > 0); + int personInRoom = _talkTo - _vm->logic()->roomData(_vm->logic()->currentRoom()); + _vm->logic()->startDialogue(_talkFile, personInRoom, nextFilename); + } +} + +int Cutaway::makeComplexAnimation(int16 currentImage, Cutaway::CutawayAnim *objAnim, int frameCount) { + int frameIndex[256]; + int i; + assert(frameCount < 30); + AnimFrame cutAnim[30]; + + memset(frameIndex, 0, sizeof(frameIndex)); + debug(6, "[Cutaway::makeComplexAnimation] currentImage = %i", currentImage); + + for (i = 0; i < frameCount; i++) { + cutAnim[i].frame = objAnim[i].unpackFrame; + cutAnim[i].speed = objAnim[i].speed; + frameIndex[objAnim[i].unpackFrame] = 1; + } + + cutAnim[frameCount].frame = 0; + cutAnim[frameCount].speed = 0; + + int nextFrameIndex = 1; + + for (i = 1; i < 256; i++) + if (frameIndex[i]) + frameIndex[i] = nextFrameIndex++; + + for (i = 0; i < frameCount; i++) { + cutAnim[i].frame = currentImage + frameIndex[objAnim[i].unpackFrame]; + } + + for (i = 1; i < 256; i++) { + if (frameIndex[i]) { + currentImage++; + _vm->bankMan()->unpack(i, currentImage, objAnim[0].bank); + } + } + + _vm->graphics()->setBobCutawayAnim(objAnim[0].object, objAnim[0].flip, cutAnim, frameCount + 1); + return currentImage; +} + +void Cutaway::handleText( + int index, + ObjectType type, + CutawayObject &object, + const char *sentence) { + // lines 1776-1863 in cutaway.c + + int spaces = countSpaces(type, sentence); + + int x; + int flags; + + if (OBJECT_TYPE_TEXT_DISPLAY == type) { + x = _vm->display()->textCenterX(sentence); + flags = 2; + } else { + x = object.bobStartX; + flags = 1; + } + + BobSlot *bob = + _vm->graphics()->bob( _vm->logic()->findBob(ABS(object.objectNumber)) ); + + _vm->graphics()->setBobText(bob, sentence, x, object.bobStartY, object.specialMove, flags); + + if (OBJECT_TYPE_TEXT_SPEAK == type || OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK == type) { + if (_vm->sound()->speechOn()) { + char voiceFileName[MAX_STRING_SIZE]; + findCdCut(_basename, index, voiceFileName); + strcat(voiceFileName, "1"); + _vm->sound()->playSfx(voiceFileName, true); + } + + if (OBJECT_TYPE_TEXT_SPEAK == type && _vm->sound()->speechOn() && !_vm->subtitles()) + _vm->display()->clearTexts(0, 150); + } + + while (1) { + _vm->update(); + + if (_vm->input()->cutawayQuit()) + return; + + if (_vm->input()->keyVerb() == VERB_SKIP_TEXT) { + _vm->input()->clearKeyVerb(); + break; + } + + if ((OBJECT_TYPE_TEXT_SPEAK == type || OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK == type) && _vm->sound()->speechOn() && _vm->sound()->speechSfxExists()) { + if (!_vm->sound()->isSpeechActive()) { + break; + } + } else { + --spaces; + if (spaces <= 0) { + break; + } + } + } + + _vm->display()->clearTexts(0, 198); + _vm->update(); +} + +int Cutaway::countSpaces(ObjectType type, const char *segment) { + int tmp = 0; + + while (*segment++) + tmp++; + + if (tmp < 50) + tmp = 50; + + if (OBJECT_TYPE_TEXT_DISPLAY == type) + tmp *= 3; + + return (tmp * 2) / (_vm->talkSpeed() / 3); + +} + +int Cutaway::scale(CutawayObject &object) { + int scaling = 100; + + if (object.scale > 0) + scaling = object.scale; + else if (!object.objectNumber) { + // Only scale Joe + int x, y; + + if (object.bobStartX > 0 || object.bobStartY > 0) { + x = object.bobStartX; + y = object.bobStartY; + } else { + BobSlot *bob = _vm->graphics()->bob(0); + x = bob->x; + y = bob->y; + } + + int zone = _vm->grid()->findAreaForPos(GS_ROOM, x, y); + if (zone > 0) { + Area *area = _vm->grid()->area(_vm->logic()->currentRoom(), zone); + scaling = area->calcScale(y); + } + } + + return scaling; +} + +} // End of namespace Queen diff --git a/engines/queen/cutaway.h b/engines/queen/cutaway.h new file mode 100644 index 0000000000..bbf5ec0652 --- /dev/null +++ b/engines/queen/cutaway.h @@ -0,0 +1,265 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENCUTAWAY_H +#define QUEENCUTAWAY_H + +#include "common/util.h" +#include "queen/structs.h" + +namespace Queen { + +class QueenEngine; + +class Cutaway { +public: + + //! Public interface to run a cutaway from a file + static void run(const char *filename, char *nextFilename, QueenEngine *vm); + + //! Collection of constants used by QueenCutaway + enum { + PREVIOUS_ROOM = 0, + CURRENT_ROOM = 0, + OBJECT_ROOMFADE = -1, + PERSON_JOE = -1, + OBJECT_JOE = 0, + MAX_PERSON_COUNT = 6, + CUTAWAY_BANK = 8, + MAX_BANK_NAME_COUNT = 5, + MAX_FILENAME_LENGTH = 12, + MAX_FILENAME_SIZE = (MAX_FILENAME_LENGTH + 1), + MAX_PERSON_FACE_COUNT = 13, + MAX_STRING_LENGTH = 255, + MAX_STRING_SIZE = (MAX_STRING_LENGTH + 1), + LEFT = 1, + RIGHT = 2, + FRONT = 3, + BACK = 4 + }; + + //! Different kinds of cutaway objects + enum ObjectType { + OBJECT_TYPE_ANIMATION = 0, + OBJECT_TYPE_PERSON = 1, + OBJECT_TYPE_NO_ANIMATION = 2, + OBJECT_TYPE_TEXT_SPEAK = 3, + OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK = 4, + OBJECT_TYPE_TEXT_DISPLAY = 5 + }; + +private: + //! Data for a cutaway object + struct CutawayObject { + int16 objectNumber; // 0 = JOE, -1 = MESSAGE + int16 moveToX; + int16 moveToY; + int16 bank; // 0 = PBOB, 13 = Joe Bank, else BANK NAMEstr() + int16 animList; + int16 execute; // 1 Yes, 0 No + int16 limitBobX1; + int16 limitBobY1; + int16 limitBobX2; + int16 limitBobY2; + int16 specialMove; + int16 animType; // 0 - Packet, 1 - Amal, 2 - Unpack + int16 fromObject; + int16 bobStartX; + int16 bobStartY; + int16 room; + int16 scale; + // Variables derived from the variables above + int song; + + //! People to turn on + int person[MAX_PERSON_COUNT]; + + //! Number of elements used in _person array + int personCount; + }; + + struct CutawayAnim { + int16 object; + int16 unpackFrame; // Frame to unpack + int16 speed; + int16 bank; + int16 mx; + int16 my; + int16 cx; + int16 cy; + int16 scale; + int16 currentFrame; // Index to Current Frame + int16 originalFrame; // Index to Original Object Frame + int16 song; + bool flip; // set this if unpackFrame is negative + }; + + struct ObjectDataBackup { + int index; + int16 name; + int16 image; + }; + + struct PersonFace { + int16 index; + int16 image; + }; + + QueenEngine *_vm; + + //! Raw .cut file data (without 20 byte header) + byte *_fileData; + + //! COMPANEL + int16 _comPanel; + + //! Game state data inside of _fileDat + byte *_gameStatePtr; + + //! Actual cutaway data inside of _fileData + byte *_objectData; + + //! Pointer to next sentence string in _fileData + uint16 _nextSentenceOff; + + //! ??? + bool _roomFade; + + //! Number of cutaway objects at _cutawayData + int16 _cutawayObjectCount; + + //! This cutaway is followed by another + bool _anotherCutaway; + + //! Room before cutaway + int _initialRoom; + + //! Temporary room for cutaway + int _temporaryRoom; + + //! Room to stay in + int _finalRoom; + + //! Bank names + char _bankNames[MAX_BANK_NAME_COUNT][MAX_FILENAME_SIZE]; + + //! Filename without ".cut" + char _basename[MAX_FILENAME_SIZE]; + + //! Name of .dog file + char _talkFile[MAX_FILENAME_SIZE]; + + //! Person to talk to + int16 _talkTo; + + //! Used by changeRooms + ObjectDataBackup _personData[MAX_PERSON_COUNT]; + + //! Number of elements used in _personData array + int _personDataCount; + + //! Used by handlePersonRecord() + PersonFace _personFace[MAX_PERSON_FACE_COUNT]; + + //! Number of entries in _personFace array + int _personFaceCount; + + //! Play this song when leaving cutaway + int16 _lastSong; + + //! Song played before running comic.cut + int16 _songBeforeComic; + + int16 _currentImage; + + Cutaway(const char *filename, QueenEngine *vm); + ~Cutaway(); + + //! Run this cutaway object + void run(char *nextFilename); + + //! Load cutaway data from file + void load(const char *filename); + + //! Used by load to read string data + void loadStrings(uint16 offset); + + //! Get persons + const byte *turnOnPeople(const byte *ptr, CutawayObject &object); + + //! Limit the BOB + void limitBob(CutawayObject &object); + + //! This cutaway object occurs in another room + void changeRooms(CutawayObject &object); + + //! Get the object type for this CutawayObject + ObjectType getObjectType(CutawayObject &object); + + //! Perform actions for an animation + const byte *handleAnimation(const byte *ptr, CutawayObject &object); + + //! Perform actions for a person record + void handlePersonRecord(int index, CutawayObject &object, const char *sentence); + + //! Perform text actions + void handleText(int index, ObjectType type, CutawayObject &object, const char *sentence); + + //! Restore Logic::_objectData from _personData + void restorePersonData(); + + //! Stop the cutaway + void stop(); + + //! Update game state after cutaway + void updateGameState(); + + //! Prepare for talk after cutaway + void talk(char *nextFilename); + + //! Get CutawayAnim data from ptr and return new ptr + const byte *getCutawayAnim(const byte *ptr, int header, CutawayAnim &anim); + + //! Special animation + int makeComplexAnimation(int16 currentImage, CutawayAnim *objAnim, int frameCount); + + //! Read a CutawayObject from ptr and return new ptr + static const byte *getCutawayObject(const byte *ptr, CutawayObject &object); + + //! Dump a CutawayObject with debug() + void dumpCutawayObject(int index, CutawayObject &object); + + //! Used by handleText() + int countSpaces(ObjectType type, const char *segment); + + //! Scale Joe + int scale(CutawayObject &object); + + //! Dump CutawayAnum data with debug() + static void dumpCutawayAnim(CutawayAnim &anim); + + bool inRange(int16 x, int16 l, int16 h) const { return (x <= h && x >= l); } +}; + +} // End of namespace Queen + +#endif diff --git a/engines/queen/debug.cpp b/engines/queen/debug.cpp new file mode 100644 index 0000000000..63fc40f444 --- /dev/null +++ b/engines/queen/debug.cpp @@ -0,0 +1,219 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/debug.h" + +#include "queen/graphics.h" +#include "queen/logic.h" +#include "queen/queen.h" +#include "queen/resource.h" +#include "queen/sound.h" + +#include "common/debugger.cpp" + +namespace Queen { + +Debugger::Debugger(QueenEngine *vm) + : _vm(vm), _flags(0) { + + DCmd_Register("exit", &Debugger::Cmd_Exit); + DCmd_Register("help", &Debugger::Cmd_Help); + DCmd_Register("areas", &Debugger::Cmd_Areas); + DCmd_Register("asm", &Debugger::Cmd_Asm); + DCmd_Register("bob", &Debugger::Cmd_Bob); + DCmd_Register("bobs", &Debugger::Cmd_PrintBobs); + DCmd_Register("gs", &Debugger::Cmd_GameState); + DCmd_Register("info", &Debugger::Cmd_Info); + DCmd_Register("items", &Debugger::Cmd_Items); + DCmd_Register("room", &Debugger::Cmd_Room); + DCmd_Register("song", &Debugger::Cmd_Song); +} + +Debugger::~Debugger() {} // we need this here for __SYMBIAN32__ +void Debugger::preEnter() { +} + +void Debugger::postEnter() { + _vm->graphics()->setupMouseCursor(); +} + +bool Debugger::Cmd_Exit(int argc, const char **argv) { + _detach_now = true; + return false; +} + +bool Debugger::Cmd_Help(int argc, const char **argv) { + // console normally has 39 line width + // wrap around nicely + int width = 0, size, i; + + DebugPrintf("Commands are:\n"); + for (i = 0 ; i < _dcmd_count ; i++) { + size = strlen(_dcmds[i].name) + 1; + + if ((width + size) >= 39) { + DebugPrintf("\n"); + width = size; + } else + width += size; + + DebugPrintf("%s ", _dcmds[i].name); + } + DebugPrintf("\n"); + return true; +} + +bool Debugger::Cmd_Asm(int argc, const char **argv) { + if (argc == 2) { + uint16 sm = atoi(argv[1]); + _vm->logic()->executeSpecialMove(sm); + return false; + } else { + DebugPrintf("Usage: %s smnum\n", argv[0]); + } + return true; +} + +bool Debugger::Cmd_Areas(int argc, const char **argv) { + _flags ^= DF_DRAW_AREAS; + DebugPrintf("Room areas display %s\n", (_flags & DF_DRAW_AREAS) != 0 ? "on" : "off"); + return true; +} + +bool Debugger::Cmd_Bob(int argc, const char **argv) { + if (argc >= 3) { + int bobNum = atoi(argv[1]); + if (bobNum >= Graphics::MAX_BOBS_NUMBER) { + DebugPrintf("Bob %d is out of range (range: 0 - %d)\n", bobNum, Graphics::MAX_BOBS_NUMBER); + } else { + int param = (argc > 3) ? atoi(argv[3]) : 0; + BobSlot *bob = _vm->graphics()->bob(bobNum); + if (!strcmp(argv[2], "toggle")) { + bob->active = !bob->active; + DebugPrintf("bob[%d].active = %d\n", bobNum, bob->active); + } else if (!strcmp(argv[2], "x")) { + bob->x = param; + DebugPrintf("bob[%d].x = %d\n", bobNum, bob->x); + } else if (!strcmp(argv[2], "y")) { + bob->y = param; + DebugPrintf("bob[%d].y = %d\n", bobNum, bob->y); + } else if (!strcmp(argv[2], "frame")) { + bob->frameNum = param; + DebugPrintf("bob[%d].frameNum = %d\n", bobNum, bob->frameNum); + } else if (!strcmp(argv[2], "speed")) { + bob->speed = param; + DebugPrintf("bob[%d].speed = %d\n", bobNum, bob->speed); + } else { + DebugPrintf("Unknown bob command '%s'\n", argv[2]); + } + } + } else { + DebugPrintf("Usage: %s bobnum command parameter\n", argv[0]); + } + return true; +} + +bool Debugger::Cmd_GameState(int argc, const char **argv) { + uint16 slot; + switch (argc) { + case 2: + slot = atoi(argv[1]); + DebugPrintf("GAMESTATE[%d] ", slot); + DebugPrintf("is %d\n", _vm->logic()->gameState(slot)); + break; + case 3: + slot = atoi(argv[1]); + DebugPrintf("GAMESTATE[%d] ", slot); + DebugPrintf("was %d ", _vm->logic()->gameState(slot)); + _vm->logic()->gameState(slot, atoi(argv[2])); + DebugPrintf("now %d\n", _vm->logic()->gameState(slot)); + break; + default: + DebugPrintf("Usage: %s slotnum value\n", argv[0]); + break; + } + return true; +} + +bool Debugger::Cmd_Info(int argc, const char **argv) { + DebugPrintf("Version: %s\n", _vm->resource()->JASVersion()); + DebugPrintf("Audio compression: %d\n", _vm->resource()->compression()); + return true; +} + +bool Debugger::Cmd_Items(int argc, const char **argv) { + int n = _vm->logic()->itemDataCount(); + ItemData *item = _vm->logic()->itemData(1); + while (n--) { + item->name = ABS(item->name); + ++item; + } + DebugPrintf("Enabled all inventory items\n"); + return true; +} + +bool Debugger::Cmd_PrintBobs(int argc, const char**argv) { + int i; + BobSlot *bob = _vm->graphics()->bob(0); + DebugPrintf("+--------------------------------+\n"); + DebugPrintf("|# | x| y|f|scl|frm|a|m| ex| ey|\n"); + DebugPrintf("+--+---+---+-+---+---+-+-+---+---+\n"); + for (i = 0; i < Graphics::MAX_BOBS_NUMBER; ++i, ++bob) { + if (bob->active) { + DebugPrintf("|%2d|%3d|%3d|%1d|%3d|%3d|%1d|%1d|%3d|%3d|\n", + i, bob->x, bob->y, bob->xflip, bob->scale, bob->frameNum, + bob->animating, bob->moving, bob->speed, bob->endx, bob->endy); + } + } + DebugPrintf("+--------------------------------+\n"); + return true; +} + +bool Debugger::Cmd_Room(int argc, const char **argv) { + if (argc == 2) { + uint16 roomNum = atoi(argv[1]); + _vm->logic()->joePos(0, 0); + _vm->logic()->newRoom(roomNum); + _vm->logic()->entryObj(_vm->logic()->roomData(roomNum) + 1); + return false; + } else { + DebugPrintf("Current room: %d (%s), use '%s <roomnum>' to switch\n", + _vm->logic()->currentRoom(), + _vm->logic()->roomName(_vm->logic()->currentRoom()), + argv[0]); + } + return true; +} + +bool Debugger::Cmd_Song(int argc, const char **argv) { + if (argc == 2) { + int16 songNum = atoi(argv[1]); + _vm->sound()->playSong(songNum); + DebugPrintf("Playing song %d\n", songNum); + } else { + DebugPrintf("Usage: %s songnum\n", argv[0]); + } + return true; +} + +} // End of namespace Queen diff --git a/engines/queen/debug.h b/engines/queen/debug.h new file mode 100644 index 0000000000..40cb8bb6ff --- /dev/null +++ b/engines/queen/debug.h @@ -0,0 +1,69 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENDEBUG_H +#define QUEENDEBUG_H + +#include "common/debugger.h" + +namespace Queen { + +class QueenEngine; + +class Debugger : public Common::Debugger<Debugger> { +public: + + Debugger(QueenEngine *vm); + virtual ~Debugger(); // we need this here for __SYMBIAN32__ archaic gcc/UIQ + + int flags() const { return _flags; } + + enum { + DF_DRAW_AREAS = 1 << 0 + }; + +protected: + + virtual void preEnter(); + virtual void postEnter(); + + bool Cmd_Exit(int argc, const char **argv); + bool Cmd_Help(int argc, const char **argv); + bool Cmd_Areas(int argc, const char **argv); + bool Cmd_Asm(int argc, const char **argv); + bool Cmd_Bob(int argc, const char **argv); + bool Cmd_GameState(int argc, const char **argv); + bool Cmd_Info(int argc, const char **argv); + bool Cmd_Items(int argc, const char **argv); + bool Cmd_PrintBobs(int argc, const char **argv); + bool Cmd_Room(int argc, const char **argv); + bool Cmd_Song(int argc, const char **argv); + +private: + + QueenEngine *_vm; + int _flags; +}; + +} // End of namespace Queen + +#endif diff --git a/engines/queen/defs.h b/engines/queen/defs.h new file mode 100644 index 0000000000..223e539894 --- /dev/null +++ b/engines/queen/defs.h @@ -0,0 +1,322 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENDEFS_H +#define QUEENDEFS_H + +namespace Queen { + +#define SAVEGAME_SIZE 24622 + +enum { + COMPRESSION_NONE = 0, + COMPRESSION_MP3 = 1, + COMPRESSION_OGG = 2, + COMPRESSION_FLAC = 3 +}; + +enum { + GAME_SCREEN_WIDTH = 320, + GAME_SCREEN_HEIGHT = 200, + ROOM_ZONE_HEIGHT = 150, + PANEL_ZONE_HEIGHT = 50 +}; + + +enum { + FRAMES_JOE = 38, + FRAMES_JOURNAL = 40 +}; + + +enum Direction { + DIR_LEFT = 1, + DIR_RIGHT = 2, + DIR_FRONT = 3, + DIR_BACK = 4 +}; + + +enum { + INK_BG_PANEL = 226, + INK_JOURNAL = 248, + INK_PINNACLE_ROOM = 243, + INK_CMD_SELECT = 255, + INK_CMD_NORMAL = 225, + INK_CMD_LOCK = 234, + INK_TALK_NORMAL = 7, + INK_JOE = 14, + INK_OUTLINED_TEXT = 16 +}; + + +enum { + ITEM_NONE = 0, + ITEM_BAT, + ITEM_JOURNAL, + ITEM_KNIFE, + ITEM_COCONUT_HALVES, + ITEM_BEEF_JERKY, + ITEM_PROPELLER, + ITEM_BANANA, + ITEM_VINE, + ITEM_SLOTH_HAIR, + ITEM_COMIC_BOOK, + ITEM_FLOWER, + ITEM_BEETLE, + ITEM_ORCHID, + ITEM_DICTIONARY, + ITEM_DEATH_MASH, + ITEM_PERFUME, + ITEM_TYRANNO_HORN, + ITEM_LOTION, + ITEM_RECORD, + ITEM_VACUUM_CLEANER, + ITEM_NET, + ITEM_ALCOHOL, + ITEM_ROCKET_PACK, + ITEM_SOME_MONEY, + ITEM_CHEESE_BITZ, + ITEM_DOG_FOOD, + ITEM_CAN_OPENER, + ITEM_LETTER, + ITEM_SQUEAKY_TOY, + ITEM_KEY, + ITEM_BOOK, + ITEM_PIECE_OF_PAPER, + ITEM_ROCKET_PLAN, + ITEM_PADLOCK_KEY, + ITEM_RIB_CAGE, + ITEM_SKULL, + ITEM_LEG_BONE, + ITEM_BAT2, + ITEM_MAKESHIFT_TOCH, + ITEM_LIGHTER, + ITEM_GREEN_JEWEL, + ITEM_PICK, + ITEM_STONE_KEY, + ITEM_BLUE_JEWEL, + ITEM_CRYSTAL_SKULL, + ITEM_TREE_SAP, + ITEM_DINO_RAY_GUN, + ITEM_BRANCHES, + ITEM_WIG, + ITEM_TOWEL, + ITEM_OTHER_SHEET, + ITEM_SHEET, + ITEM_SHEET_ROPE, + ITEM_CROWBAR, + ITEM_COMEDY_BREASTS, + ITEM_DRESS, + ITEM_KEY2, + ITEM_CLOTHES, + ITEM_HAY, + ITEM_OIL, + ITEM_CHICKEN, + ITEM_LIT_TORCH, + ITEM_OPENED_DOG_FOOD, + ITEM_SOME_MONEY2, + ITEM_SOME_MORE_MONEY, + ITEM_PEELED_BANANA, + ITEM_STONE_DISC, + ITEM_GNARLED_VINE, + ITEM_FLINT, + ITEM_LIGHTER2, + ITEM_REST_OF_BEEF_JERKY, + ITEM_LOTS_OF_MONEY, + ITEM_HEAPS_OF_MONEY, + ITEM_OPEN_BOOK, + ITEM_REST_OF_THE_CHEESE_BITZ, + ITEM_SCISSORS, + ITEM_PENCIL, + ITEM_SUPER_WEENIE_SERUM, + ITEM_MUMMY_WRAPPINGS, + ITEM_COCONUT, + ITEM_ID_CARD, + ITEM_BIT_OF_STONE, + ITEM_CHUNK_OF_ROCK, + ITEM_BIG_STICK, + ITEM_STICKY_BIT_OF_STONE, + ITEM_STICKY_CHUNK_OF_ROCK, + ITEM_DEATH_MASK2, + ITEM_CHEFS_SURPRISE, + ITEM_STICKY_BAT, + ITEM_REST_OF_WRAPPINGS, + ITEM_BANANA2, + ITEM_MUG, + ITEM_FILE, + ITEM_POCKET_ROCKET_BLUEPRINTS, + ITEM_HAND_PUPPET, + ITEM_ARM_BONE, + ITEM_CROWN, + ITEM_COMIC_COUPON, + ITEM_TORN_PAGE +}; + +enum { + ROOM_JUNGLE_INSIDE_PLANE = 1, + ROOM_JUNGLE_OUTSIDE_PLANE = 2, + ROOM_JUNGLE_BRIDGE = 4, + ROOM_JUNGLE_GORILLA_1 = 6, + ROOM_JUNGLE_PINNACLE = 7, + ROOM_JUNGLE_SLOTH = 8, + ROOM_JUNGLE_BUD_SKIP = 9, + ROOM_JUNGLE_BEETLE = 11, + ROOM_JUNGLE_MISSIONARY = 13, + ROOM_JUNGLE_GORILLA_2 = 14, + + ROOM_AMAZON_ENTRANCE = 16, + ROOM_AMAZON_HIDEOUT = 17, + ROOM_AMAZON_THRONE = 18, + ROOM_AMAZON_JAIL = 19, + + ROOM_VILLAGE = 20, + ROOM_TRADER_BOBS = 21, + + ROOM_FLODA_OUTSIDE = 22, + ROOM_FLODA_KITCHEN = 26, + ROOM_FLODA_LOCKERROOM = 27, + ROOM_FLODA_KLUNK = 30, + ROOM_FLODA_HENRY = 32, + ROOM_FLODA_OFFICE = 35, + ROOM_FLODA_JAIL = 41, + ROOM_FLODA_FRONTDESK = 103, + + ROOM_TEMPLE_OUTSIDE = 43, + ROOM_TEMPLE_MUMMIES = 46, + ROOM_TEMPLE_ZOMBIES = 50, + ROOM_TEMPLE_TREE = 51, + ROOM_TEMPLE_SNAKE = 53, + ROOM_TEMPLE_LIZARD_LASER = 55, + ROOM_TEMPLE_MAZE = 58, + ROOM_TEMPLE_MAZE_2 = 59, + ROOM_TEMPLE_MAZE_3 = 60, + ROOM_TEMPLE_MAZE_4 = 61, + ROOM_TEMPLE_MAZE_5 = 100, + ROOM_TEMPLE_MAZE_6 = 101, + + ROOM_VALLEY_CARCASS = 67, + + ROOM_HOTEL_UPSTAIRS = 70, + ROOM_HOTEL_DOWNSTAIRS = 71, + ROOM_HOTEL_LOLA = 72, + ROOM_HOTEL_LOBBY = 73, + + ROOM_CAR_CHASE = 74, + + ROOM_FINAL_FIGHT = 69, + + ROOM_INTRO_RITA_JOE_HEADS = 116, + ROOM_INTRO_EXPLOSION = 123, + + //special + SPARKY_OUTSIDE_HOTEL = 77, + DEATH_MASK = 79, + IBI_LOGO = 82, + COMIC_1 = 87, + COMIC_2 = 88, + COMIC_3 = 89, + ROOM_UNUSED_INTRO_1 = 90, + ROOM_UNUSED_INTRO_2 = 91, + ROOM_UNUSED_INTRO_3 = 92, + ROOM_UNUSED_INTRO_4 = 93, + ROOM_UNUSED_INTRO_5 = 94, + FOTAQ_LOGO = 95, + WARNER_LOGO = 126, + + FAYE_HEAD = 37, + AZURA_HEAD = 106, + FRANK_HEAD = 107, + + ROOM_ENDING_CREDITS = 110, + + ROOM_JOURNAL = 200 // dummy value to keep Display methods happy +}; + + +//! GameState vars +enum { + VAR_HOTEL_ITEMS_REMOVED = 3, + VAR_JOE_DRESSING_MODE = 19, + VAR_BYPASS_ZOMBIES = 21, + VAR_BYPASS_FLODA_RECEPTIONIST = 35, + VAR_GUARDS_TURNED_ON = 85, + VAR_HOTEL_ESCAPE_STATE = 93, + VAR_INTRO_PLAYED = 117, + VAR_AZURA_IN_LOVE = 167 +}; + + +enum Language { + ENGLISH = 'E', + FRENCH = 'F', + GERMAN = 'G', + HEBREW = 'H', + ITALIAN = 'I', + SPANISH = 'S' +}; + + +enum Verb { + VERB_NONE = 0, + + VERB_PANEL_COMMAND_FIRST = 1, + VERB_OPEN = 1, + VERB_CLOSE = 2, + VERB_MOVE = 3, + // no verb 4 + VERB_GIVE = 5, + VERB_USE = 6, + VERB_PICK_UP = 7, + VERB_LOOK_AT = 9, + VERB_TALK_TO = 8, + VERB_PANEL_COMMAND_LAST = 9, + + VERB_WALK_TO = 10, + VERB_SCROLL_UP = 11, + VERB_SCROLL_DOWN = 12, + + VERB_DIGIT_FIRST = 13, + VERB_DIGIT_1 = 13, + VERB_DIGIT_2 = 14, + VERB_DIGIT_3 = 15, + VERB_DIGIT_4 = 16, + VERB_DIGIT_LAST = 16, + + VERB_INV_FIRST = VERB_DIGIT_FIRST, + VERB_INV_1 = VERB_DIGIT_1, + VERB_INV_2 = VERB_DIGIT_2, + VERB_INV_3 = VERB_DIGIT_3, + VERB_INV_4 = VERB_DIGIT_4, + VERB_INV_LAST = VERB_DIGIT_LAST, + + VERB_USE_JOURNAL = 20, + VERB_SKIP_TEXT = 101, + + VERB_PREP_WITH = 11, + VERB_PREP_TO = 12 +}; + + +} // End of namespace Queen + +#endif diff --git a/engines/queen/display.cpp b/engines/queen/display.cpp new file mode 100644 index 0000000000..d56e472e30 --- /dev/null +++ b/engines/queen/display.cpp @@ -0,0 +1,1307 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "common/system.h" +#include "queen/display.h" + +#include "queen/input.h" +#include "queen/logic.h" +#include "queen/queen.h" +#include "queen/resource.h" + +namespace Queen { + +#ifdef PALMOS_68K +static const uint8 *_fontRegular; +static const uint8 *_fontHebrew; +static const uint8 *_palJoeClothes; +static const uint8 *_palJoeDress; +#endif + +Display::Display(QueenEngine *vm, OSystem *system) + : _fullscreen(true), _horizontalScroll(0), _bdWidth(0), _bdHeight(0), + _system(system), _vm(vm) { + + if (vm->resource()->getLanguage() == HEBREW) + _font = _fontHebrew; + else + _font = _fontRegular; + + initFont(); + + _screenBuf = new uint8[SCREEN_W * SCREEN_H]; + _panelBuf = new uint8[PANEL_W * PANEL_H]; + _backdropBuf = new uint8[BACKDROP_W * BACKDROP_H]; + memset(_screenBuf, 0, SCREEN_W * SCREEN_H); + memset(_panelBuf, 0, PANEL_W * PANEL_H); + memset(_backdropBuf, 0, BACKDROP_W * BACKDROP_H); + + _fullRefresh = 1; + _dirtyBlocksWidth = SCREEN_W / D_BLOCK_W; + _dirtyBlocksHeight = SCREEN_H / D_BLOCK_H; + _dirtyBlocks = new uint8[_dirtyBlocksWidth * _dirtyBlocksHeight]; + memset(_dirtyBlocks, 0, _dirtyBlocksWidth * _dirtyBlocksHeight); + + _pal.room = new uint8[ 256 * 3 ]; + _pal.screen = new uint8[ 256 * 3 ]; + _pal.panel = new uint8[ 112 * 3 ]; + memset(_pal.room, 0, 256 * 3); + memset(_pal.screen, 0, 256 * 3); + memset(_pal.panel, 0, 112 * 3); + _pal.dirtyMin = 0; + _pal.dirtyMax = 255; + _pal.scrollable = true; + + memset(&_dynalum, 0, sizeof(_dynalum)); +} + +Display::~Display() { + delete[] _backdropBuf; + delete[] _panelBuf; + delete[] _screenBuf; + + delete[] _dirtyBlocks; + + delete[] _pal.room; + delete[] _pal.screen; + delete[] _pal.panel; + + delete[] _dynalum.mskBuf; + delete[] _dynalum.lumBuf; +} + +void Display::dynalumInit(const char *roomName, uint16 roomNum) { + debug(9, "Display::dynalumInit(%s, %d)", roomName, roomNum); + + _dynalum.valid = false; + delete[] _dynalum.mskBuf; + _dynalum.mskBuf = NULL; + delete[] _dynalum.lumBuf; + _dynalum.lumBuf = NULL; + + if (!isPalFadingDisabled(roomNum)) { + char filename[20]; + sprintf(filename, "%s.msk", roomName); + if (_vm->resource()->fileExists(filename)) { + _dynalum.mskBuf = (uint8 *)_vm->resource()->loadFile(filename, 0, &_dynalum.mskSize); + sprintf(filename, "%s.lum", roomName); + if (_vm->resource()->fileExists(filename)) { + _dynalum.lumBuf = (int8 *)_vm->resource()->loadFile(filename, 0, &_dynalum.lumSize); + _dynalum.valid = true; + _dynalum.prevColMask = 0xFF; + } + } + } +} + +void Display::dynalumUpdate(int16 x, int16 y) { + if (!_dynalum.valid) + return; + + if (x < 0) { + x = 0; + } else if (x > _bdWidth) { + x = _bdWidth; + } + if (y < 0) { + y = 0; + } else if (y > ROOM_ZONE_HEIGHT - 1) { + y = ROOM_ZONE_HEIGHT - 1; + } + + uint32 offset = (y / 4) * 160 + (x / 4); + assert(offset < _dynalum.mskSize); + + uint8 colMask = _dynalum.mskBuf[offset]; + debug(9, "Display::dynalumUpdate(%d, %d) - colMask = %d", x, y, colMask); + if (colMask != _dynalum.prevColMask) { + for (int i = 144; i < 160; ++i) { + for (int j = 0; j < 3; ++j) { + int16 c = (int16)(_pal.room[i * 3 + j] + _dynalum.lumBuf[colMask * 3 + j] * 4); + if (c < 0) { + c = 0; + } else if (c > 255) { + c = 255; + } + _pal.screen[i * 3 + j] = (uint8)c; + } + } + _pal.dirtyMin = MIN(_pal.dirtyMin, 144); + _pal.dirtyMax = MAX(_pal.dirtyMax, 159); + _dynalum.prevColMask = colMask; + } +} + +void Display::palSet(const uint8 *pal, int start, int end, bool updateScreen) { + debug(9, "Display::palSet(%d, %d)", start, end); + const int numColors = end - start + 1; + uint8 tempPal[256 * 4]; + for (int i = 0; i < numColors; i++) { + tempPal[4 * i + 0] = *pal++; + tempPal[4 * i + 1] = *pal++; + tempPal[4 * i + 2] = *pal++; + tempPal[4 * i + 3] = 0; + } + _system->setPalette(tempPal, start, numColors); + if (updateScreen) { + _system->updateScreen(); + _vm->input()->delay(20); + } +} + +void Display::palSetJoeDress() { + memcpy(_pal.room + 144 * 3, _palJoeDress, 16 * 3); + memcpy(_pal.screen + 144 * 3, _palJoeDress, 16 * 3); + palSet(_pal.screen, 144, 159, true); +} + +void Display::palSetJoeNormal() { + memcpy(_pal.room + 144 * 3, _palJoeClothes, 16 * 3); + memcpy(_pal.screen + 144 * 3, _palJoeClothes, 16 * 3); + palSet(_pal.screen, 144, 159, true); +} + +void Display::palSetPanel() { + memcpy(_pal.room + 144 * 3, _pal.panel, (256 - 144) * 3); + memcpy(_pal.screen + 144 * 3, _pal.panel, (256 - 144) * 3); +} + +void Display::palFadeIn(uint16 roomNum, bool dynalum, int16 dynaX, int16 dynaY) { + debug(9, "Display::palFadeIn(%d)", roomNum); + int n = getNumColorsForRoom(roomNum); + memcpy(_pal.screen, _pal.room, n * 3); + if (!isPalFadingDisabled(roomNum)) { + if (dynalum) { + dynalumUpdate(dynaX, dynaY); + } + uint8 tempPal[256 * 3]; + for (int i = 0; i <= FADE_SPEED; ++i) { + for (int j = 0; j < n * 3; ++j) { + tempPal[j] = _pal.screen[j] * i / FADE_SPEED; + } + palSet(tempPal, 0, n - 1, true); + } + } + _pal.dirtyMin = 0; + _pal.dirtyMax = n - 1; + _pal.scrollable = true; +} + +void Display::palFadeOut(uint16 roomNum) { + debug(9, "Display::palFadeOut(%d)", roomNum); + _pal.scrollable = false; + int n = getNumColorsForRoom(roomNum); + if (isPalFadingDisabled(roomNum)) { + memset(_pal.screen, 0, n * 3); + palSet(_pal.screen, 0, n - 1, true); + } else { + uint8 tempPal[256 * 3]; + memcpy(tempPal, _pal.screen, n * 3); + for (int i = FADE_SPEED; i >= 0; --i) { + for (int j = 0; j < n * 3; ++j) { + _pal.screen[j] = tempPal[j] * i / FADE_SPEED; + } + palSet(_pal.screen, 0, n - 1, true); + } + } +} + +void Display::palGreyPanel() { + debug(9, "Display::palGreyPanel()"); + uint8 tempPal[256 * 3]; + for (int i = 224 * 3; i < 256 * 3; i += 3) { + tempPal[i] = tempPal[i + 1] = tempPal[i + 2] = _pal.screen[i + 1] * 2 / 3; + } + palSet(tempPal, 224, 255, true); +} + +void Display::palScroll(int start, int end) { + debug(9, "Display::palScroll(%d, %d)", start, end); + + uint8 *palEnd = _pal.screen + end * 3; + uint8 *palStart = _pal.screen + start * 3; + + uint8 r = *palEnd++; + uint8 g = *palEnd++; + uint8 b = *palEnd; + + int n = (end - start) * 3; + while (n--) { + *palEnd = *(palEnd - 3); + --palEnd; + } + + *palStart++ = r; + *palStart++ = g; + *palStart = b; +} + +void Display::palCustomColors(uint16 roomNum) { + debug(9, "Display::palCustomColors(%d)", roomNum); + int i; + switch (roomNum) { + case 31: + for (i = 72; i < 84; i++) { + _pal.room[i * 3 + 1] = _pal.room[i * 3 + 1] * 90 / 100; + _pal.room[i * 3 + 2] = _pal.room[i * 3 + 2] * 70 / 100; + } + break; + case 29: + for (i = 72; i < 84; i++) { + _pal.room[i * 3 + 1] = _pal.room[i * 3 + 1] * 60 / 100; + _pal.room[i * 3 + 2] = _pal.room[i * 3 + 2] * 60 / 100; + } + break; + case 30: + for (i = 72; i < 84; i++) { + _pal.room[i * 3 + 0] = _pal.room[i * 3 + 0] * 60 / 100; + _pal.room[i * 3 + 1] = _pal.room[i * 3 + 1] * 80 / 100; + } + break; + case 28: + for (i = 72; i < 84; i++) { + _pal.room[i * 3 + 0] = _pal.room[i * 3 + 0] * 80 / 100; + _pal.room[i * 3 + 2] = _pal.room[i * 3 + 1] * 60 / 100; + } + break; + } +} + +void Display::palCustomScroll(uint16 roomNum) { + debug(9, "Display::palCustomScroll(%d)", roomNum); + static int16 scrollx = 0; + + if (!_pal.scrollable) { + return; + } + + int hiPal = 0; + int loPal = 255; + int i; + + ++scrollx; + switch (roomNum) { + case 123: { + static int16 j = 0, jdir = 2; + for (i = 96; i < 111; ++i) { + _pal.screen[i * 3 + 0] = MIN(255, _pal.room[i * 3 + 0] + j * 8); + _pal.screen[i * 3 + 1] = MIN(255, _pal.room[i * 3 + 1] + j * 4); + } + j += jdir; + if (j <= 0 || j >= 18) { + jdir = -jdir; + } + loPal = 96; + hiPal = 111; + } + break; + case 124: { + static int16 j = 0,jdir = 2; + for (i = 80; i < 144; ++i) { + _pal.screen[i * 3 + 0] = MIN(255, _pal.room[i * 3 + 0] + j * 8); + _pal.screen[i * 3 + 1] = MIN(255, _pal.room[i * 3 + 1] + j * 4); + } + j += jdir; + if (j <= 0 || j >= 14) { + jdir = -jdir; + if (_rnd.getRandomNumber(1)) { + if (ABS(jdir) == 1) { + jdir *= 2; + } else { + jdir /= 2; + } + } + } + loPal = 80; + hiPal = 143; + } + break; + case 125: + palScroll(32, 63); + palScroll(64, 95); + loPal = 32; + hiPal = 95; + break; + case 100: + if (scrollx & 1) { + palScroll(128, 132); + palScroll(133, 137); + palScroll(138, 143); + loPal = 128; + hiPal = 143; + } + break; + case 102: + if (scrollx & 1) { + palScroll(112, 127); + loPal = 112; + hiPal = 127; + } + break; + case 62: + if (scrollx & 1) { + palScroll(108, 119); + loPal = 108; + hiPal = 119; + } + break; + case 25: + palScroll(116, 123); + loPal = 116; + hiPal = 123; + break; + case 59: + if (scrollx & 1) { + palScroll(56, 63); + loPal = 56; + hiPal = 63; + } + break; + case 39: + palScroll(112, 143); + loPal = 112; + hiPal = 143; + break; + case 74: + palScroll(28, 31); + palScroll(88, 91); + palScroll(92, 95); + palScroll(128, 135); + if (scrollx & 1) { + palScroll(136, 143); + } + loPal = 28; + hiPal = 143; + break; + case 40: + if (scrollx & 1) { + palScroll(96, 103); + } + if (scrollx & 3) { + palScroll(104, 107); + } + loPal = 96; + hiPal = 107; + break; + case 97: + if (scrollx & 1) { + palScroll(96, 107); + palScroll(108, 122); + loPal = 96; + hiPal = 122; + } + break; + case 55: + palScroll(128, 143); + loPal = 128; + hiPal = 143; + break; + case 57: + palScroll(128, 143); + if (scrollx & 1) { + palScroll(96, 103); + } + loPal = 96; + hiPal = 143; + break; + case 76: + palScroll(88, 95); + loPal = 88; + hiPal = 95; + break; + case 2: + if (scrollx & 1) { + palScroll(120, 127); + loPal = 120; + hiPal = 127; + } + break; + case 3: + case 5: + if (scrollx & 1) { + palScroll(128, 135); + palScroll(136, 143); + loPal = 128; + hiPal = 143; + } + break; + case 7: + if (scrollx & 1) { + palScroll(119, 127); + loPal = 119; + hiPal = 127; + } + break; + case 42: + if (scrollx & 1) { + palScroll(118, 127); + palScroll(136, 143); + loPal = 118; + hiPal = 143; + } + break; + case 4: + if (scrollx & 1) { + palScroll(32,47); + } + palScroll(64, 70); + palScroll(71, 79); + loPal = 32; + hiPal = 79; + break; + case 8: + if (scrollx & 1) { + palScroll(120, 127); + } + loPal = 120; + hiPal = 127; + break; + case 12: + case 64: + if (scrollx & 1) { + palScroll(112, 119); + palScroll(120, 127); + loPal = 112; + hiPal = 127; + } + break; + case 49: + palScroll(101, 127); + loPal = 101; + hiPal = 127; + break; + } + _pal.dirtyMin = MIN(_pal.dirtyMin, loPal); + _pal.dirtyMax = MAX(_pal.dirtyMax, hiPal); +} + +void Display::palCustomFlash() { + uint8 tempPal[256 * 3]; + memset(tempPal, 255, 17 * 3); + memset(tempPal + 17 * 3, 0, 67 * 3); + memset(tempPal + 67 * 3, 255, 172 * 3); + // set flash palette + palSet(tempPal, 0, 255, true); + // restore original palette + palSet(_pal.screen, 0, 255, true); +} + +void Display::palCustomLightsOff(uint16 roomNum) { + int end = 223; + int start = (roomNum == ROOM_FLODA_FRONTDESK) ? 32 : 16; + int n = end - start + 1; + + memset(_pal.screen + start * 3, 0, n * 3); + palSet(_pal.screen, start, end, true); + + _pal.scrollable = false; +} + +void Display::palCustomLightsOn(uint16 roomNum) { + int end = 223; + int start = (roomNum == ROOM_FLODA_FRONTDESK) ? 32 : 0; + int n = end - start + 1; + + memcpy(_pal.screen + start * 3, _pal.room + start * 3, n * 3); + palSet(_pal.screen, start, end, true); + + _pal.dirtyMin = 0; + _pal.dirtyMax = 223; + _pal.scrollable = true; +} + +int Display::getNumColorsForRoom(uint16 room) const { + int n = 224; + if (room >= 114 && room <= 125) { + n = 256; + } + return n; +} + +bool Display::isPalFadingDisabled(uint16 room) const { + // introduction rooms don't fade palette + return (room >= 90 && room <= 94) || (room >= 115 && room <= 125); +} + +void Display::screenMode(int comPanel, bool inCutaway) { + debug(6, "Display::screenMode(%d, %d)", comPanel, inCutaway); + + if (comPanel == 2 && inCutaway) { + fullscreen((_bdHeight == GAME_SCREEN_HEIGHT)); + } else if (comPanel == 1) { + fullscreen(false); + } +} + +void Display::prepareUpdate() { + int h = GAME_SCREEN_HEIGHT; + if (!_fullscreen) { + h = ROOM_ZONE_HEIGHT; + memcpy(_screenBuf + SCREEN_W * ROOM_ZONE_HEIGHT, _panelBuf, PANEL_W * PANEL_H); + } + uint8 *dst = _screenBuf; + const uint8 *src = _backdropBuf + _horizontalScroll; + + while (h--) { + memcpy(dst, src, SCREEN_W); + dst += SCREEN_W; + src += BACKDROP_W; + } +} + +void Display::update(bool dynalum, int16 dynaX, int16 dynaY) { + drawTexts(); + if (_pal.scrollable && dynalum) { + dynalumUpdate(dynaX, dynaY); + } + if (_pal.dirtyMin != 144 || _pal.dirtyMax != 144) { + palSet(_pal.screen, _pal.dirtyMin, _pal.dirtyMax); + _pal.dirtyMin = 144; + _pal.dirtyMax = 144; + } + // uncomment this line to disable the dirty blocks rendering +// _fullRefresh = 1; + if (_fullRefresh) { + _system->copyRectToScreen(_screenBuf, SCREEN_W, 0, 0, SCREEN_W, SCREEN_H); + _system->updateScreen(); + --_fullRefresh; + if (_fullRefresh) { + memset(_dirtyBlocks, 0, _dirtyBlocksWidth * _dirtyBlocksHeight); + } + debug(7, "Display::update() - Full blit (%d)", _fullRefresh); + } else { + uint16 count = 0; + uint8 *scrBuf = _screenBuf; + uint8 *dbBuf = _dirtyBlocks; + for (int j = 0; j < _dirtyBlocksHeight; ++j) { + uint16 accW = 0; + for (int i = 0; i < _dirtyBlocksWidth; ++i) { + if (dbBuf[i] != 0) { + --dbBuf[i]; + ++accW; + } else if (accW != 0) { + int x = (i - accW) * D_BLOCK_W; + _system->copyRectToScreen(scrBuf + x, SCREEN_W, x, j * D_BLOCK_H, accW * D_BLOCK_W, D_BLOCK_H); + accW = 0; + ++count; + } + } + if (accW != 0) { + int x = (_dirtyBlocksWidth - accW) * D_BLOCK_W; + _system->copyRectToScreen(scrBuf + x, SCREEN_W, x, j * D_BLOCK_H, accW * D_BLOCK_W, D_BLOCK_H); + ++count; + } + dbBuf += _dirtyBlocksWidth; + scrBuf += SCREEN_W * D_BLOCK_H; + } + if (count != 0) { + _system->updateScreen(); + } + debug(7, "Display::update() - Dirtyblocks blit (%d)", count); + } +} + +void Display::setupPanel() { + uint32 size; + uint8 *pcxBuf = _vm->resource()->loadFile("panel.pcx", 0, &size); + uint8 *dst = _panelBuf + PANEL_W * 10; + readPCX(dst, PANEL_W, pcxBuf + 128, PANEL_W, PANEL_H - 10); + const uint8 *pal = pcxBuf + size - 768 + 144 * 3; + memcpy(_pal.panel, pal, (256 - 144) * 3); + delete[] pcxBuf; + + palSetPanel(); +} + +void Display::setupNewRoom(const char *name, uint16 room) { + dynalumInit(name, room); + uint32 size; + char filename[20]; + sprintf(filename, "%s.PCX", name); + uint8 *pcxBuf = _vm->resource()->loadFile(filename, 0, &size); + _bdWidth = READ_LE_UINT16(pcxBuf + 12); + _bdHeight = READ_LE_UINT16(pcxBuf + 14); + readPCX(_backdropBuf, BACKDROP_W, pcxBuf + 128, _bdWidth, _bdHeight); + int n = getNumColorsForRoom(room); + if (n != 256) { + n = 144; + } + memcpy(_pal.room, pcxBuf + size - 768, n * 3); + delete[] pcxBuf; + + palCustomColors(room); + + forceFullRefresh(); +} + +void Display::drawBobSprite(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h, uint16 pitch, bool xflip) { + blit(_screenBuf, SCREEN_W, x, y, data, pitch, w, h, xflip, true); + setDirtyBlock(xflip ? (x - w + 1) : x, y, w, h); +} + +void Display::drawBobPasteDown(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h) { + blit(_backdropBuf, BACKDROP_W, x, y, data, w, w, h, false, true); +} + +void Display::drawInventoryItem(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h) { + if (data != NULL) { + blit(_panelBuf, PANEL_W, x, y, data, w, w, h, false, false); + } else { + fill(_panelBuf, PANEL_W, x, y, w, h, INK_BG_PANEL); + } + setDirtyBlock(x, 150 + y, w, h); +} + +void Display::blit(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, const uint8 *srcBuf, uint16 srcPitch, uint16 w, uint16 h, bool xflip, bool masked) { + assert(w <= dstPitch); + dstBuf += dstPitch * y + x; + + if (!masked) { // Unmasked always unflipped + while (h--) { + memcpy(dstBuf, srcBuf, w); + srcBuf += srcPitch; + dstBuf += dstPitch; + } + } else if (!xflip) { // Masked bitmap unflipped + while (h--) { + for (int i = 0; i < w; ++i) { + uint8 b = *(srcBuf + i); + if (b != 0) { + *(dstBuf + i) = b; + } + } + srcBuf += srcPitch; + dstBuf += dstPitch; + } + } else { // Masked bitmap flipped + while (h--) { + for (int i = 0; i < w; ++i) { + uint8 b = *(srcBuf + i); + if (b != 0) { + *(dstBuf - i) = b; + } + } + srcBuf += srcPitch; + dstBuf += dstPitch; + } + } +} + +void Display::fill(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, uint16 w, uint16 h, uint8 color) { + assert(w <= dstPitch); + dstBuf += dstPitch * y + x; + while (h--) { + memset(dstBuf, color, w); + dstBuf += dstPitch; + } +} + +void Display::readPCX(uint8 *dst, uint16 dstPitch, const uint8 *src, uint16 w, uint16 h) { + while (h--) { + uint8 *p = dst; + while (p < dst + w) { + uint8 col = *src++; + if ((col & 0xC0) == 0xC0) { + uint8 len = col & 0x3F; + memset(p, *src++, len); + p += len; + } else { + *p++ = col; + } + } + dst += dstPitch; + } +} + +void Display::horizontalScrollUpdate(int16 xCamera) { + debug(9, "Display::horizontalScrollUpdate(%d)", xCamera); + if (_bdWidth <= 320) { + horizontalScroll(0); + } else { + if (xCamera > 160 && xCamera < 480) { + horizontalScroll(xCamera - 160); + } else if (xCamera >= 480) { + horizontalScroll(320); + } else { + horizontalScroll(0); + } + } +} + +void Display::horizontalScroll(int16 scroll) { + if (_horizontalScroll != scroll) { + _fullRefresh = 2; + _horizontalScroll = scroll; + } +} + +void Display::setDirtyBlock(uint16 x, uint16 y, uint16 w, uint16 h) { + if (_fullRefresh < 2) { + uint16 ex = (x + w - 1) / D_BLOCK_W; + uint16 ey = (y + h - 1) / D_BLOCK_H; + x /= D_BLOCK_W; + y /= D_BLOCK_H; + uint16 cy = ey - y + 1; + uint16 cx = ex - x + 1; + if (cy >= _dirtyBlocksHeight) cy = _dirtyBlocksHeight - 1; + if (cx >= _dirtyBlocksWidth) cx = _dirtyBlocksWidth - 1; + uint8 *p = _dirtyBlocks + _dirtyBlocksWidth * y + x; + while (cy--) { + for (uint16 i = 0; i < cx; ++i) { + p[i] = 2; + } + p += _dirtyBlocksWidth; + } + } +} + +void Display::setMouseCursor(uint8 *buf, uint16 w, uint16 h) { + _system->setMouseCursor(buf, w, h, 1, 1, 0); +} + +void Display::showMouseCursor(bool show) { + _system->showMouse(show); +} + +void Display::initFont() { + // calculate font justification sizes + for (int i = 0; i < 256; ++i) { + _charWidth[i] = 0; + for (int y = 0; y < 8; ++y) { + uint8 c = _font[i * 8 + y]; + for (int x = 0; x < 8; ++x) { + if ((c & (0x80 >> x)) && (x > _charWidth[i])) { + _charWidth[i] = x; + } + } + } + _charWidth[i] += 2; + } + _charWidth[0x20] = 4; + --_charWidth[0x5E]; +} + +void Display::setText(uint16 x, uint16 y, const char *text, bool outlined) { + if (y < GAME_SCREEN_HEIGHT) { + if (x == 0) x = 1; + if (y == 0) y = 1; + TextSlot *pts = &_texts[y]; + pts->x = x; + pts->color = _curTextColor; + pts->outlined = outlined; + pts->text = text; + } +} + +void Display::setTextCentered(uint16 y, const char *text, bool outlined) { + int len = strlen(text); + int16 x; + while ((x = (GAME_SCREEN_WIDTH - textWidth(text, len)) / 2) <= 0) { + ++text; + len -= 2; + } + assert(y < GAME_SCREEN_HEIGHT); + TextSlot *pts = &_texts[y]; + pts->x = x; + pts->color = _curTextColor; + pts->outlined = outlined; + pts->text = Common::String(text, len); +} + +void Display::drawTexts() { + for (int y = GAME_SCREEN_HEIGHT - 1; y > 0; --y) { + const TextSlot *pts = &_texts[y]; + if (!pts->text.isEmpty()) { + drawText(pts->x, y, pts->color, pts->text.c_str(), pts->outlined); + } + } +} + +void Display::clearTexts(uint16 y1, uint16 y2) { + assert(y1 <= y2 && y2 < GAME_SCREEN_HEIGHT); + while (y1 <= y2) { + _texts[y1].text.clear(); + ++y1; + } +} + +int Display::textCenterX(const char *text) const { + return (GAME_SCREEN_WIDTH - textWidth(text)) / 2; +} + +uint16 Display::textWidth(const char *text) const { + return textWidth(text, strlen(text)); +} + +uint16 Display::textWidth(const char *text, uint16 len) const { + assert(len <= strlen(text)); + uint16 width = 0; + for (uint16 i = 0; i < len; ++i) { + width += _charWidth[ (uint8)text[i] ]; + } + return width; +} + +void Display::drawChar(uint16 x, uint16 y, uint8 color, const uint8 *chr) { + uint8 *dstBuf = _screenBuf + SCREEN_W * y + x; + for (int j = 0; j < 8; ++j) { + uint8 *p = dstBuf; + uint8 c = *chr++; + if (c != 0) { + for (int i = 0; i < 8; ++i) { + if (c & 0x80) { + *p = color; + } + ++p; + c <<= 1; + } + } + dstBuf += SCREEN_W; + } +} + +void Display::drawText(uint16 x, uint16 y, uint8 color, const char *text, bool outlined) { + static const int dx[] = { -1, 0, 1, 1, 1, 0, -1, -1 }; + static const int dy[] = { -1, -1, -1, 0, 1, 1, 1, 0 }; + const uint8 *str = (const uint8 *)text; + uint16 xs = x; + while (*str && x < SCREEN_W) { + const uint8 ch = *str++; + const uint8 *ftch = _font + ch * 8; + if (outlined) { + for (int i = 0; i < 8; ++i) { + drawChar(x + dx[i], y + dy[i], INK_OUTLINED_TEXT, ftch); + } + } + drawChar(x, y, color, ftch); + x += _charWidth[ch]; + } + setDirtyBlock(xs - 1, y - 1, x - xs + 2, 8 + 2); +} + +void Display::drawBox(int16 x1, int16 y1, int16 x2, int16 y2, uint8 col) { + int i; + for (i = y1; i <= y2; ++i) { + _screenBuf[i * SCREEN_W + x1] = _screenBuf[i * SCREEN_W + x2] = col; + } + setDirtyBlock(x1, y1, 1, y2 - y1); + setDirtyBlock(x2, y1, 1, y2 - y1); + for (i = x1; i <= x2; ++i) { + _screenBuf[y1 * SCREEN_W + i] = _screenBuf[y2 * SCREEN_W + i] = col; + } + setDirtyBlock(x1, y1, x2 - x1, 1); + setDirtyBlock(x1, y2, x2 - x1, 1); +} + +void Display::shake(bool reset) { + _system->setShakePos(reset ? 0 : 3); +} + +void Display::blankScreen() { + static int current = 0; + typedef void (Display::*BlankerEffect)(); + static const BlankerEffect effects[] = { + &Display::blankScreenEffect1, + &Display::blankScreenEffect2, + &Display::blankScreenEffect3 + }; + (this->*effects[current])(); + current = (current + 1) % ARRAYSIZE(effects); + forceFullRefresh(); +} + +void Display::blankScreenEffect1() { + uint8 buf[32 * 32]; + while (_vm->input()->idleTime() >= Input::DELAY_SCREEN_BLANKER) { + for (int i = 0; i < 2; ++i) { + int x = _rnd.getRandomNumber(SCREEN_W - 32 - 2) + 1; + int y = _rnd.getRandomNumber(SCREEN_H - 32 - 2) + 1; + const uint8 *p = _screenBuf + SCREEN_W * y + x; + for (int j = 0; j < 32; ++j) { + memcpy(buf + j * 32, p, 32); + p += SCREEN_W; + } + if (_rnd.getRandomNumber(1)) { + ++x; + } else { + --x; + } + if (_rnd.getRandomNumber(1)) { + ++y; + } else { + --y; + } + _system->copyRectToScreen(buf, 32, x, y, 32, 32); + _system->updateScreen(); + _vm->input()->delay(10); + } + } +} + +void Display::blankScreenEffect2() { + while (_vm->input()->idleTime() >= Input::DELAY_SCREEN_BLANKER) { + int x = _rnd.getRandomNumber(SCREEN_W - 2); + int y = _rnd.getRandomNumber(SCREEN_H - 2); + uint8 *p = _screenBuf + y * SCREEN_W + x; + uint8 c = 0; + switch (_rnd.getRandomNumber(3)) { + case 0: + c = *p; + break; + case 1: + c = *(p + 1); + break; + case 2: + c = *(p + SCREEN_W); + break; + case 3: + c = *(p + SCREEN_W + 1); + break; + } + memset(p, c, 2); + memset(p + SCREEN_W, c, 2); + _system->copyRectToScreen(p, SCREEN_W, x, y, 2, 2); + _system->updateScreen(); + _vm->input()->delay(10); + } +} + +void Display::blankScreenEffect3() { + uint32 i = 0; + while (_vm->input()->idleTime() >= Input::DELAY_SCREEN_BLANKER) { + if (i > 4000000) { + memset(_screenBuf, 0, SCREEN_W * SCREEN_H); + _system->copyRectToScreen(_screenBuf, SCREEN_W, 0, 0, SCREEN_W, SCREEN_H); + } else { + int x = _rnd.getRandomNumber(SCREEN_W - 2); + int y = _rnd.getRandomNumber(SCREEN_H - 2); + uint8 *p = _screenBuf + SCREEN_W * y + x; + int sum = *p + *(p + 1) + *(p + SCREEN_W) + *(p + SCREEN_W + 1); + uint8 c = (uint8)(sum / 4); + memset(p, c, 2); + memset(p + SCREEN_W, c, 2); + ++i; + _system->copyRectToScreen(p, SCREEN_W, x, y, 2, 2); + } + _system->updateScreen(); + _vm->input()->delay(10); + } +} + +#ifndef PALMOS_68K +const uint8 Display::_fontRegular[] = { + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0x00, + 0xD8, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, + 0x30, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x30, 0x00, 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00, + 0x38, 0x6C, 0x68, 0x36, 0xDC, 0xCC, 0x76, 0x00, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x60, 0xC0, 0xC0, 0xC0, 0x60, 0x30, 0x00, 0xC0, 0x60, 0x30, 0x30, 0x30, 0x60, 0xC0, 0x00, + 0x00, 0x6C, 0x38, 0xFE, 0x38, 0x6C, 0x00, 0x00, 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00, + 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x30, 0x70, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xCC, 0x0C, 0x78, 0xC0, 0xC0, 0xFC, 0x00, 0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x78, 0x00, + 0x1C, 0x3C, 0x6C, 0xCC, 0xFC, 0x0C, 0x0C, 0x00, 0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00, + 0x78, 0xCC, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00, 0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, 0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00, + 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0xC0, + 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0x00, 0x00, + 0xC0, 0x60, 0x30, 0x18, 0x30, 0x60, 0xC0, 0x00, 0x78, 0xCC, 0x0C, 0x18, 0x30, 0x00, 0x30, 0x00, + 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, 0x38, 0x7C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, + 0xF8, 0xCC, 0xCC, 0xF8, 0xCC, 0xCC, 0xF8, 0x00, 0x78, 0xCC, 0xC0, 0xC0, 0xC0, 0xCC, 0x78, 0x00, + 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xF8, 0x00, 0xFC, 0xC0, 0xC0, 0xF0, 0xC0, 0xC0, 0xFC, 0x00, + 0xFC, 0xC0, 0xC0, 0xF0, 0xC0, 0xC0, 0xC0, 0x00, 0x78, 0xCC, 0xC0, 0xDC, 0xCC, 0xCC, 0x7C, 0x00, + 0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, + 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, 0xC6, 0xCC, 0xD8, 0xF8, 0xD8, 0xCC, 0xC6, 0x00, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFC, 0x00, 0x82, 0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00, + 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, + 0xF8, 0xCC, 0xCC, 0xF8, 0xC0, 0xC0, 0xC0, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xDC, 0x78, 0x0C, + 0xF8, 0xCC, 0xCC, 0xF8, 0xD8, 0xCC, 0xCC, 0x00, 0x78, 0xCC, 0xC0, 0x78, 0x0C, 0xCC, 0x78, 0x00, + 0xFC, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x00, + 0xC6, 0xC6, 0x6C, 0x6C, 0x38, 0x38, 0x10, 0x00, 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00, + 0xC6, 0x6C, 0x38, 0x10, 0x38, 0x6C, 0xC6, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x30, 0x00, + 0xFC, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xFC, 0x00, 0xF0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xF0, 0x00, + 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x00, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x30, 0xF0, 0x00, + 0xE8, 0x4D, 0x4A, 0x48, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x7C, 0x00, + 0xC0, 0xC0, 0xF8, 0xCC, 0xCC, 0xCC, 0xF8, 0x00, 0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00, + 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x7C, 0x00, 0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0x38, 0x6C, 0x60, 0xF8, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x7C, 0xCC, 0xCC, 0x7C, 0x0C, 0x78, + 0xC0, 0xC0, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0xC0, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, + 0x0C, 0x00, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0x78, 0xC0, 0xC0, 0xCC, 0xD8, 0xF0, 0xD8, 0xCC, 0x00, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0xCC, 0xEE, 0xD6, 0xC6, 0xC6, 0x00, + 0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xF8, 0xC0, 0xC0, 0x00, 0x00, 0x7C, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, + 0x00, 0x00, 0xF8, 0xCC, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x7C, 0xC0, 0x78, 0x0C, 0x78, 0x00, + 0x30, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, 0x00, 0x00, 0xC6, 0xD6, 0xD6, 0x6C, 0x6C, 0x00, + 0x00, 0x00, 0xCC, 0x78, 0x30, 0x78, 0xCC, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0xE0, + 0x00, 0x00, 0xFC, 0x18, 0x30, 0x60, 0xFC, 0x00, 0x38, 0x60, 0x60, 0xC0, 0x60, 0x60, 0x38, 0x00, + 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0xC0, 0x00, 0xE0, 0x30, 0x30, 0x18, 0x30, 0x30, 0xE0, 0x00, + 0x38, 0x44, 0xBA, 0xAA, 0xBA, 0x44, 0x38, 0x00, 0x00, 0x98, 0x30, 0x60, 0xC8, 0x98, 0x30, 0x00, + 0x1E, 0x30, 0x60, 0x60, 0x30, 0x1E, 0x0C, 0x18, 0x00, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, + 0x0C, 0x18, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x18, 0x66, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x30, 0x60, 0x60, 0x30, 0x1E, 0x0C, 0x18, + 0x18, 0x66, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x66, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, + 0x30, 0x18, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x00, 0x66, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x66, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x30, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, + 0x00, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x30, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, + 0x18, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x78, 0x0C, 0x7C, 0xCC, 0x7C, 0x00, 0x0C, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x30, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, 0x18, 0x30, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x00, + 0x71, 0x8E, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x00, 0x71, 0xCE, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0x00, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, 0x3C, 0x60, 0x3C, 0x66, 0x3C, 0x06, 0x3C, 0x00, + 0x18, 0x00, 0x18, 0x30, 0x60, 0x66, 0x3C, 0x00, 0x3F, 0x40, 0x4E, 0x58, 0x4E, 0x40, 0x3F, 0x00, + 0x1C, 0xA4, 0xC4, 0xBC, 0x80, 0xFE, 0x00, 0x00, 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00, + 0x3E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, + 0x81, 0xB9, 0xA5, 0xB9, 0xA5, 0x81, 0x7E, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0xFC, 0x00, + 0xF0, 0x18, 0x30, 0x60, 0xF8, 0x00, 0x00, 0x00, 0xF0, 0x18, 0x30, 0x18, 0xF0, 0x00, 0x00, 0x00, + 0x30, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xFE, 0xC0, + 0x3E, 0x7A, 0x7A, 0x3A, 0x0A, 0x0A, 0x0A, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x60, 0xE0, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, + 0x38, 0x44, 0x44, 0x38, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00, + 0x40, 0xC6, 0x4C, 0x58, 0x32, 0x66, 0xCF, 0x02, 0x40, 0xC6, 0x4C, 0x58, 0x3E, 0x62, 0xC4, 0x0E, + 0xC0, 0x23, 0x66, 0x2C, 0xD9, 0x33, 0x67, 0x01, 0x18, 0x00, 0x18, 0x30, 0x60, 0x66, 0x3C, 0x00, + 0x60, 0x30, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, 0x0C, 0x18, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, + 0x38, 0xC6, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, 0x71, 0x8E, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, + 0x6C, 0x00, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, 0x38, 0x44, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, + 0x1F, 0x3C, 0x3C, 0x6F, 0x7C, 0xCC, 0xCF, 0x00, 0x1E, 0x30, 0x60, 0x60, 0x30, 0x1E, 0x0C, 0x18, + 0x60, 0x30, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, 0x18, 0x30, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, + 0x30, 0xCC, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, 0xCC, 0x00, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, + 0x60, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, 0x18, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x30, 0xCC, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, 0xCC, 0x00, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x78, 0x6C, 0x66, 0xF6, 0x66, 0x6C, 0x78, 0x00, 0x71, 0xCE, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0x00, + 0x30, 0x18, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x0C, 0x18, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, + 0x18, 0x66, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x71, 0x8E, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, + 0xC3, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, 0x00, + 0x3F, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0xFC, 0x00, 0x30, 0x18, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, + 0x0C, 0x18, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x18, 0x24, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, + 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x06, 0x08, 0xC3, 0x66, 0x3C, 0x18, 0x18, 0x00, + 0x60, 0x60, 0x7E, 0x63, 0x7E, 0x60, 0x60, 0x00, 0x3C, 0x66, 0x66, 0x6C, 0x66, 0x66, 0x6C, 0x60, + 0x30, 0x18, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, 0x0C, 0x18, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, + 0x18, 0x66, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, 0x71, 0x8E, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, + 0x66, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, 0x18, 0x24, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, + 0x00, 0x00, 0x7E, 0x1B, 0x7F, 0xD8, 0x77, 0x00, 0x00, 0x00, 0x3C, 0x60, 0x60, 0x60, 0x3C, 0x18, + 0x30, 0x18, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x0C, 0x18, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, + 0x18, 0x66, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x66, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, + 0x30, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x0C, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x66, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x66, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x60, 0xFC, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x71, 0x8E, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x00, + 0x30, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, + 0x18, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x71, 0x8E, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, + 0x00, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x02, 0x7C, 0xCE, 0xD6, 0xE6, 0x7C, 0x80, 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, + 0x0C, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x18, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, + 0x00, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x0C, 0x18, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x30, + 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00, 0x66, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x30 +}; + +const uint8 Display::_fontHebrew[] = { + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0x00, + 0xD8, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, + 0x30, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x30, 0x00, 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00, + 0x38, 0x6C, 0x68, 0x36, 0xDC, 0xCC, 0x76, 0x00, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x60, 0xC0, 0xC0, 0xC0, 0x60, 0x30, 0x00, 0xC0, 0x60, 0x30, 0x30, 0x30, 0x60, 0xC0, 0x00, + 0x00, 0x6C, 0x38, 0xFE, 0x38, 0x6C, 0x00, 0x00, 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00, + 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x30, 0x70, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xCC, 0x0C, 0x78, 0xC0, 0xC0, 0xFC, 0x00, 0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x78, 0x00, + 0x1C, 0x3C, 0x6C, 0xCC, 0xFC, 0x0C, 0x0C, 0x00, 0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00, + 0x78, 0xCC, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00, 0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, 0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00, + 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0xC0, + 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0x00, 0x00, + 0xC0, 0x60, 0x30, 0x18, 0x30, 0x60, 0xC0, 0x00, 0x78, 0xCC, 0x0C, 0x18, 0x30, 0x00, 0x30, 0x00, + 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, 0x38, 0x7C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, + 0xF8, 0xCC, 0xCC, 0xF8, 0xCC, 0xCC, 0xF8, 0x00, 0x78, 0xCC, 0xC0, 0xC0, 0xC0, 0xCC, 0x78, 0x00, + 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xF8, 0x00, 0xFC, 0xC0, 0xC0, 0xF0, 0xC0, 0xC0, 0xFC, 0x00, + 0xFC, 0xC0, 0xC0, 0xF0, 0xC0, 0xC0, 0xC0, 0x00, 0x78, 0xCC, 0xC0, 0xDC, 0xCC, 0xCC, 0x7C, 0x00, + 0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, + 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, 0xC6, 0xCC, 0xD8, 0xF8, 0xD8, 0xCC, 0xC6, 0x00, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFC, 0x00, 0x82, 0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00, + 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, + 0xF8, 0xCC, 0xCC, 0xF8, 0xC0, 0xC0, 0xC0, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xDC, 0x78, 0x0C, + 0xF8, 0xCC, 0xCC, 0xF8, 0xD8, 0xCC, 0xCC, 0x00, 0x78, 0xCC, 0xC0, 0x78, 0x0C, 0xCC, 0x78, 0x00, + 0xFC, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x00, + 0xC6, 0xC6, 0x6C, 0x6C, 0x38, 0x38, 0x10, 0x00, 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00, + 0xC6, 0x6C, 0x38, 0x10, 0x38, 0x6C, 0xC6, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x30, 0x00, + 0xFC, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xFC, 0x00, 0xF0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xF0, 0x00, + 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x00, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x30, 0xF0, 0x00, + 0xE8, 0x4D, 0x4A, 0x48, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x7C, 0x00, + 0xC0, 0xC0, 0xF8, 0xCC, 0xCC, 0xCC, 0xF8, 0x00, 0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00, + 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x7C, 0x00, 0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0x38, 0x6C, 0x60, 0xF8, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x7C, 0xCC, 0xCC, 0x7C, 0x0C, 0x78, + 0xC0, 0xC0, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0xC0, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, + 0x0C, 0x00, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0x78, 0xC0, 0xC0, 0xCC, 0xD8, 0xF0, 0xD8, 0xCC, 0x00, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0xCC, 0xEE, 0xD6, 0xC6, 0xC6, 0x00, + 0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xF8, 0xC0, 0xC0, 0x00, 0x00, 0x7C, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, + 0x00, 0x00, 0xF8, 0xCC, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x7C, 0xC0, 0x78, 0x0C, 0x78, 0x00, + 0x30, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, 0x00, 0x00, 0xC6, 0xD6, 0xD6, 0x6C, 0x6C, 0x00, + 0x00, 0x00, 0xCC, 0x78, 0x30, 0x78, 0xCC, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0xE0, + 0x00, 0x00, 0xFC, 0x18, 0x30, 0x60, 0xFC, 0x00, 0x38, 0x60, 0x60, 0xC0, 0x60, 0x60, 0x38, 0x00, + 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0xC0, 0x00, 0xE0, 0x30, 0x30, 0x18, 0x30, 0x30, 0xE0, 0x00, + 0x38, 0x44, 0xBA, 0xAA, 0xBA, 0x44, 0x38, 0x00, 0x00, 0x98, 0x30, 0x60, 0xC8, 0x98, 0x30, 0x00, + 0xCC, 0x66, 0x76, 0xBC, 0x98, 0x8C, 0xE6, 0x00, 0xFC, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xFE, 0x00, + 0x78, 0x18, 0x18, 0x18, 0x38, 0x78, 0xD8, 0x00, 0xFE, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0xFE, 0x06, 0x06, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x7C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0xFE, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, + 0xDC, 0x66, 0xE6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0xF0, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0xF8, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xF8, 0x00, + 0xC0, 0xFE, 0x06, 0x06, 0x0C, 0x18, 0x18, 0x00, 0xFE, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7E, 0x00, + 0xFC, 0x76, 0x66, 0x66, 0x66, 0x66, 0x6E, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, 0xFE, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, + 0xEE, 0x66, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, 0xFE, 0xC6, 0xC6, 0xF6, 0x06, 0x06, 0x06, 0x06, + 0xFE, 0xC6, 0xC6, 0xFE, 0x06, 0x06, 0xFE, 0x00, 0xFE, 0x66, 0x6C, 0x78, 0x60, 0x60, 0x60, 0x60, + 0xEE, 0x66, 0x3C, 0x18, 0x0C, 0x06, 0xFE, 0x00, 0xFE, 0x06, 0x0E, 0xD8, 0xF0, 0xF0, 0xC0, 0xC0, + 0xFC, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0xEE, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0x7C, 0x00, + 0xFF, 0x67, 0x67, 0x67, 0x67, 0x67, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x0C, 0x3E, 0x6C, 0x3E, 0x0C, 0x00, 0x00, 0x38, 0x6C, 0x60, 0xF0, 0x60, 0x60, 0xFC, 0x00, + 0x42, 0x3C, 0x66, 0x3C, 0x42, 0x00, 0x00, 0x00, 0xC3, 0x66, 0x3C, 0x18, 0x3C, 0x18, 0x18, 0x00, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, 0x3C, 0x60, 0x3C, 0x66, 0x3C, 0x06, 0x3C, 0x00, + 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x4E, 0x58, 0x4E, 0x40, 0x3F, 0x00, + 0x1C, 0xA4, 0xC4, 0xBC, 0x80, 0xFE, 0x00, 0x00, 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00, + 0x3E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x81, 0xB9, 0xA5, 0xB9, 0xA5, 0x81, 0x7E, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0xFC, 0x00, + 0xF0, 0x18, 0x30, 0x60, 0xF8, 0x00, 0x00, 0x00, 0xF0, 0x18, 0x30, 0x18, 0xF0, 0x00, 0x00, 0x00, + 0x30, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xFE, 0xC0, + 0x3E, 0x7A, 0x7A, 0x3A, 0x0A, 0x0A, 0x0A, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x60, 0xE0, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, + 0x38, 0x44, 0x44, 0x38, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00, + 0x40, 0xC6, 0x4C, 0x58, 0x32, 0x66, 0xCF, 0x02, 0x40, 0xC6, 0x4C, 0x58, 0x3E, 0x62, 0xC4, 0x0E, + 0xC0, 0x23, 0x66, 0x2C, 0xD9, 0x33, 0x67, 0x01, 0x18, 0x00, 0x18, 0x30, 0x60, 0x66, 0x3C, 0x00, + 0x60, 0x30, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, 0x0C, 0x18, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, + 0x38, 0xC6, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, 0x71, 0x8E, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, + 0x6C, 0x00, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, 0x38, 0x44, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, + 0x1F, 0x3C, 0x3C, 0x6F, 0x7C, 0xCC, 0xCF, 0x00, 0x1E, 0x30, 0x60, 0x60, 0x30, 0x1E, 0x0C, 0x18, + 0x60, 0x30, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, 0x18, 0x30, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, + 0x30, 0xCC, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, 0xCC, 0x00, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, + 0x60, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, 0x18, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x30, 0xCC, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, 0xCC, 0x00, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x78, 0x6C, 0x66, 0xF6, 0x66, 0x6C, 0x78, 0x00, 0x71, 0xCE, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0x00, + 0x30, 0x18, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x0C, 0x18, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, + 0x18, 0x66, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x71, 0x8E, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, + 0xC3, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, 0x00, + 0x3F, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0xFC, 0x00, 0x30, 0x18, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, + 0x0C, 0x18, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x18, 0x24, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, + 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x06, 0x08, 0xC3, 0x66, 0x3C, 0x18, 0x18, 0x00, + 0x60, 0x60, 0x7E, 0x63, 0x7E, 0x60, 0x60, 0x00, 0x3C, 0x66, 0x66, 0x6C, 0x66, 0x66, 0x6C, 0x60, + 0x30, 0x18, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, 0x0C, 0x18, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, + 0x18, 0x66, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, 0x71, 0x8E, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, + 0x66, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, 0x18, 0x24, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, + 0x00, 0x00, 0x7E, 0x1B, 0x7F, 0xD8, 0x77, 0x00, 0x00, 0x00, 0x3C, 0x60, 0x60, 0x60, 0x3C, 0x18, + 0x30, 0x18, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x0C, 0x18, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, + 0x18, 0x66, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x66, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, + 0x30, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x0C, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x66, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x66, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x60, 0xFC, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x71, 0x8E, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x00, + 0x30, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, + 0x18, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x71, 0x8E, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, + 0x00, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x02, 0x7C, 0xCE, 0xD6, 0xE6, 0x7C, 0x80, 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, + 0x0C, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x18, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, + 0x00, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x0C, 0x18, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x30, + 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00, 0x66, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x30 +}; + +const uint8 Display::_palJoeClothes[] = { + 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0x87, 0x87, 0x87, 0xB0, 0xB0, 0xB0, 0xDA, 0xDA, 0xDA, 0x43, + 0x34, 0x20, 0x77, 0x33, 0x1F, 0xA3, 0x43, 0x27, 0x80, 0x45, 0x45, 0x9E, 0x5D, 0x5B, 0xB9, 0x78, + 0x75, 0xDF, 0x97, 0x91, 0x17, 0x27, 0x63, 0x1F, 0x3F, 0x83, 0x27, 0x5B, 0xA7, 0x98, 0xD4, 0xFF +}; + +const uint8 Display::_palJoeDress[] = { + 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0x70, 0x70, 0x70, 0x90, 0x90, 0x90, 0xC6, 0xC6, 0xC6, 0xFF, + 0xFF, 0xFF, 0x30, 0x30, 0x90, 0x47, 0x49, 0xD0, 0x40, 0x24, 0x00, 0x79, 0x34, 0x0B, 0xB2, 0x3D, + 0x22, 0xED, 0x42, 0x42, 0x80, 0x45, 0x45, 0xA3, 0x5F, 0x5F, 0xC8, 0x7C, 0x7C, 0xEC, 0x9C, 0x9C +}; + +#endif + +} // End of namespace Queen + +#ifdef PALMOS_68K +#include "scumm_globals.h" + +_GINIT(Queen_Display) +_GSETPTR(Queen::_fontRegular, GBVARS_DISPLAYFONTREGULAR_INDEX, uint8, GBVARS_QUEEN) +_GSETPTR(Queen::_fontHebrew, GBVARS_DISPLAYFONTHEBREW_INDEX, uint8, GBVARS_QUEEN) +_GSETPTR(Queen::_palJoeClothes, GBVARS_DISPLAYPALJOECLOTHES_INDEX, uint8, GBVARS_QUEEN) +_GSETPTR(Queen::_palJoeDress, GBVARS_DISPLAYPALJOEDRESS_INDEX, uint8, GBVARS_QUEEN) +_GEND + +_GRELEASE(Queen_Display) +_GRELEASEPTR(GBVARS_DISPLAYFONTREGULAR_INDEX, GBVARS_QUEEN) +_GRELEASEPTR(GBVARS_DISPLAYFONTHEBREW_INDEX, GBVARS_QUEEN) +_GRELEASEPTR(GBVARS_DISPLAYPALJOECLOTHES_INDEX, GBVARS_QUEEN) +_GRELEASEPTR(GBVARS_DISPLAYPALJOEDRESS_INDEX, GBVARS_QUEEN) +_GEND + +#endif diff --git a/engines/queen/display.h b/engines/queen/display.h new file mode 100644 index 0000000000..87608a57e3 --- /dev/null +++ b/engines/queen/display.h @@ -0,0 +1,246 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENDISPLAY_H +#define QUEENDISPLAY_H + +#include "common/str.h" +#include "common/util.h" +#include "queen/defs.h" + +class OSystem; + +namespace Queen { + +class QueenEngine; + +class Display { +public: + + Display(QueenEngine *vm, OSystem *system); + ~Display(); + + //! initialise dynalum for the specified room + void dynalumInit(const char *roomName, uint16 roomNum); + + //! update dynalum for the current room + void dynalumUpdate(int16 x, int16 y); + + //! update the palette + void palSet(const uint8 *pal, int start, int end, bool updateScreen = false); + + //! setup palette for Joe's dress + void palSetJoeDress(); + + //! setup palette for Joe's normal clothes + void palSetJoeNormal(); + + //! setup palette for panel and inventory objects + void palSetPanel(); + + //! fade the current palette in + void palFadeIn(uint16 roomNum, bool dynalum = false, int16 dynaX = 0, int16 dynaY = 0); + + //! fade the current palette out + void palFadeOut(uint16 roomNum); + + //! grey the panel area (used when panel is disabled) + void palGreyPanel(); + + //! scroll some palette colors + void palScroll(int start, int end); + + //! custom palette effect for the specified room + void palCustomColors(uint16 roomNum); + + //! custom palette scroll for the specified room + void palCustomScroll(uint16 roomNum); + + //! process a 'palette flash' effect + void palCustomFlash(); + + void palCustomLightsOff(uint16 roomNum); + void palCustomLightsOn(uint16 roomNum); + + //! mark all palette entries as dirty + void palSetAllDirty() { _pal.dirtyMin = 0; _pal.dirtyMax = 255; } + + //! returns the number of colors used by the room + int getNumColorsForRoom(uint16 room) const; + + //! returns true if we shouldn't fade the palette in the specified room + bool isPalFadingDisabled(uint16 room) const; + + //! change fullscreen/panel mode + void screenMode(int comPanel, bool inCutaway); + + void prepareUpdate(); + void update(bool dynalum = false, int16 dynaX = 0, int16 dynaY = 0); + + void setupPanel(); + void setupNewRoom(const char *name, uint16 room); + + void drawBobSprite(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h, uint16 pitch, bool xflip); + void drawBobPasteDown(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h); + void drawInventoryItem(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h); + + void blit(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, const uint8 *srcBuf, uint16 srcPitch, uint16 w, uint16 h, bool xflip, bool masked); + void fill(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, uint16 w, uint16 h, uint8 color); + + //! decode a PCX stream + void readPCX(uint8 *dst, uint16 dstPitch, const uint8 *src, uint16 w, uint16 h); + + void horizontalScrollUpdate(int16 xCamera); + void horizontalScroll(int16 scroll); + int16 horizontalScroll() const { return _horizontalScroll; } + + void fullscreen(bool fs) { _fullRefresh = 2; _fullscreen = fs; } + bool fullscreen() const { return _fullscreen; } + + //! mark the specified block as dirty + void setDirtyBlock(uint16 x, uint16 y, uint16 w, uint16 h); + + //! force a full refresh (bypassing the dirtyblocks rendering), on next screen update + void forceFullRefresh() { _fullRefresh = 2; } + + //! change mouse cursor bitmap + void setMouseCursor(uint8 *buf, uint16 w, uint16 h); + + //! show/hide mouse cursor + void showMouseCursor(bool show); + + //! initialise font, compute justification sizes + void initFont(); + + //! add the specified text to the texts list + void setText(uint16 x, uint16 y, const char *text, bool outlined = true); + + //! add the specified text to the texts list + void setTextCentered(uint16 y, const char *text, bool outlined = true); + + //! draw the text lists + void drawTexts(); + + //! remove entries from the texts list + void clearTexts(uint16 y1, uint16 y2); + + //! change the current text color + void textCurrentColor(uint8 color) { _curTextColor = color; } + + //! change the text color for the specified texts list entry + void textColor(uint16 y, uint8 color) { _texts[y].color = color; } + + int textCenterX(const char *text) const; + uint16 textWidth(const char *text) const; + uint16 textWidth(const char *text, uint16 len) const; + void drawChar(uint16 x, uint16 y, uint8 color, const uint8 *chr); + void drawText(uint16 x, uint16 y, uint8 color, const char *text, bool outlined = true); + void drawBox(int16 x1, int16 y1, int16 x2, int16 y2, uint8 col); + + void shake(bool reset); + + void blankScreen(); + void blankScreenEffect1(); + void blankScreenEffect2(); + void blankScreenEffect3(); + +private: + + enum { + FADE_SPEED = 16, + D_BLOCK_W = 8, + D_BLOCK_H = 8 + }; + + enum BufferDimension { + BACKDROP_W = 640, + BACKDROP_H = 200, + SCREEN_W = 320, + SCREEN_H = 200, + PANEL_W = 320, + PANEL_H = 50 + }; + + struct { + uint8 *room; + uint8 *screen; + uint8 *panel; + int dirtyMin, dirtyMax; + bool scrollable; + } _pal; + + struct Dynalum { + bool valid; + uint8 *mskBuf; + uint32 mskSize; + int8 *lumBuf; + uint32 lumSize; + uint8 prevColMask; + }; + + struct TextSlot { + uint16 x; + uint8 color; + Common::String text; + bool outlined; + }; + + uint8 *_screenBuf; + uint8 *_panelBuf; + uint8 *_backdropBuf; + + uint8 _fullRefresh; + uint8 *_dirtyBlocks; + uint16 _dirtyBlocksWidth, _dirtyBlocksHeight; + + bool _fullscreen; + + uint16 _horizontalScroll; + uint16 _bdWidth, _bdHeight; + + //! texts list + TextSlot _texts[GAME_SCREEN_HEIGHT]; + + //! current text color to use for display + uint8 _curTextColor; + + //! font justification sizes + uint8 _charWidth[256]; + + Common::RandomSource _rnd; + Dynalum _dynalum; + OSystem *_system; + QueenEngine *_vm; + + const uint8 *_font; +#ifndef PALMOS_68K + static const uint8 _fontRegular[]; + static const uint8 _fontHebrew[]; + static const uint8 _palJoeClothes[]; + static const uint8 _palJoeDress[]; +#endif +}; + + +} // End of namespace Queen + +#endif diff --git a/engines/queen/graphics.cpp b/engines/queen/graphics.cpp new file mode 100644 index 0000000000..be2f8d6017 --- /dev/null +++ b/engines/queen/graphics.cpp @@ -0,0 +1,1537 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/graphics.h" + +#include "queen/bankman.h" +#include "queen/display.h" +#include "queen/grid.h" +#include "queen/logic.h" +#include "queen/queen.h" +#include "queen/resource.h" +#include "queen/sound.h" + +namespace Queen { + +#ifdef PALMOS_68K +static const BamScene::BamDataBlock *_carData; +static const BamScene::BamDataBlock *_fight1Data; +static const BamScene::BamDataBlock *_fight2Data; +static const BamScene::BamDataBlock *_fight3Data; +#endif + +const Box BobSlot::_defaultBox(-1, -1, -1, -1); + +void BobSlot::curPos(int16 xx, int16 yy) { + active = true; + x = xx; + y = yy; +} + +void BobSlot::move(int16 dstx, int16 dsty, int16 spd) { + active = true; + moving = true; + + endx = dstx; + endy = dsty; + + speed = (spd < 1) ? 1 : spd; + + int16 deltax = endx - x; + if (deltax < 0) { + dx = -deltax; + xdir = -1; + } else { + dx = deltax; + xdir = 1; + } + int16 deltay = endy - y; + if (deltay < 0) { + dy = -deltay; + ydir = -1; + } else { + dy = deltay; + ydir = 1; + } + + if (dx > dy) { + total = dy / 2; + xmajor = true; + } else { + total = dx / 2; + xmajor = false; + } + + // move one step along line to avoid glitching + moveOneStep(); +} + +void BobSlot::moveOneStep() { + if (xmajor) { + if (x == endx) { + y = endy; + moving = false; + } else { + x += xdir; + total += dy; + if (total > dx) { + y += ydir; + total -= dx; + } + } + } else { + if (y == endy) { + x = endx; + moving = false; + } else { + y += ydir; + total += dx; + if (total > dy) { + x += xdir; + total -= dy; + } + } + } +} + +void BobSlot::animOneStep() { + if (anim.string.buffer != NULL) { + --anim.speed; + if (anim.speed <= 0) { + // jump to next entry + ++anim.string.curPos; + uint16 nextFrame = anim.string.curPos->frame; + if (nextFrame == 0) { + anim.string.curPos = anim.string.buffer; + frameNum = anim.string.curPos->frame; + } else { + frameNum = nextFrame; + } + anim.speed = anim.string.curPos->speed / 4; + } + } else { + // normal looping animation + --anim.speed; + if (anim.speed == 0) { + anim.speed = anim.speedBak; + + int16 nextFrame = frameNum + frameDir; + if (nextFrame > anim.normal.lastFrame || nextFrame < anim.normal.firstFrame) { + if (anim.normal.rebound) { + frameDir *= -1; + } else { + frameNum = anim.normal.firstFrame - 1; + } + } + frameNum += frameDir; + } + } +} + +void BobSlot::animString(const AnimFrame *animBuf) { + active = true; + animating = true; + anim.string.buffer = animBuf; + anim.string.curPos = animBuf; + frameNum = animBuf->frame; + anim.speed = animBuf->speed / 4; +} + +void BobSlot::animNormal(uint16 firstFrame, uint16 lastFrame, uint16 spd, bool rebound, bool flip) { + active = true; + animating = true; + frameNum = firstFrame; + anim.speed = spd; + anim.speedBak = spd; + anim.string.buffer = NULL; + anim.normal.firstFrame = firstFrame; + anim.normal.lastFrame = lastFrame; + anim.normal.rebound = rebound; + frameDir = 1; + xflip = flip; +} + +void BobSlot::scaleWalkSpeed(uint16 ms) { + if (!xmajor) { + ms /= 2; + } + speed = scale * ms / 100; + if (speed == 0) { + speed = 1; + } +} + +void BobSlot::clear() { + active = false; + xflip = false; + animating = false; + anim.string.buffer = NULL; + moving = false; + scale = 100; + +#ifdef PALMOS_ARM + Box *tmp = (Box *)&_defaultBox; + tmp->x1 = -1; + tmp->y1 = -1; + tmp->x2 = -1; + tmp->y2 = -1; +#endif + box = _defaultBox; +} + +static int compareBobDrawOrder(const void *a, const void *b) { + const BobSlot *bob1 = *(const BobSlot * const *)a; + const BobSlot *bob2 = *(const BobSlot * const *)b; + int d = bob1->y - bob2->y; + // As the qsort() function may reorder "equal" elements, + // we use the bob slot number when needed. This is required + // during the introduction, to hide a crate behind the clock. + if (d == 0) { + d = bob1 - bob2; + } + return d; +} + +const Box Graphics::_gameScreenBox(0, 0, GAME_SCREEN_WIDTH - 1, ROOM_ZONE_HEIGHT - 1); +const Box Graphics::_fullScreenBox(0, 0, GAME_SCREEN_WIDTH - 1, GAME_SCREEN_HEIGHT - 1); + +Graphics::Graphics(QueenEngine *vm) + : _cameraBob(0), _vm(vm) { + memset(_bobs, 0, sizeof(_bobs)); + memset(_sortedBobs, 0, sizeof(_sortedBobs)); + _sortedBobsCount = 0; + _shrinkBuffer.data = new uint8[ BOB_SHRINK_BUF_SIZE ]; + +#ifdef PALMOS_ARM + Box *tmp1 = (Box *)&BobSlot::_defaultBox; + tmp1->x1 = -1; + tmp1->y1 = -1; + tmp1->x2 = -1; + tmp1->y2 = -1; + Box *tmp2 = (Box *)&_gameScreenBox; + tmp2->x1 = 0; + tmp2->y1 = 0; + tmp2->x2 = GAME_SCREEN_WIDTH - 1; + tmp2->y2 = ROOM_ZONE_HEIGHT - 1; + Box *tmp3 = (Box *)&_fullScreenBox; + tmp3->x1 = 0; + tmp3->y1 = 0; + tmp3->x2 = GAME_SCREEN_WIDTH - 1; + tmp3->y2 = GAME_SCREEN_HEIGHT - 1; +#endif +} + +Graphics::~Graphics() { + delete[] _shrinkBuffer.data; +} + +void Graphics::unpackControlBank() { + _vm->bankMan()->load("control.BBK",17); + _vm->bankMan()->unpack(1, 1, 17); // Mouse pointer + // unpack arrows frames and change hotspot to be always on top + for (int i = 3; i <= 4; ++i) { + _vm->bankMan()->unpack(i, i, 17); + BobFrame *bf = _vm->bankMan()->fetchFrame(i); + bf->yhotspot += 200; + } + _vm->bankMan()->close(17); +} + +void Graphics::setupArrows() { + int scrollX = _vm->display()->horizontalScroll(); + BobSlot *arrow; + arrow = bob(ARROW_BOB_UP); + arrow->curPos(303 + 8 + scrollX, 150 + 1 + 200); + arrow->frameNum = 3; + arrow = bob(ARROW_BOB_DOWN); + arrow->curPos(303 + scrollX, 175 + 200); + arrow->frameNum = 4; +} + +void Graphics::setupMouseCursor() { + BobFrame *bf = _vm->bankMan()->fetchFrame(1); + _vm->display()->setMouseCursor(bf->data, bf->width, bf->height); +} + +void Graphics::drawBob(const BobSlot *bs, const BobFrame *bf, const Box *bbox, int16 x, int16 y) { + debug(9, "Graphics::drawBob(%d, %d, %d)", bs->frameNum, x, y); + + uint16 w, h; + if (bs->scale < 100) { + shrinkFrame(bf, bs->scale); + bf = &_shrinkBuffer; + } + w = bf->width; + h = bf->height; + + const Box *box = (bs->box == BobSlot::_defaultBox) ? bbox : &bs->box; + + if (w != 0 && h != 0 && box->intersects(x, y, w, h)) { + uint8 *src = bf->data; + uint16 x_skip = 0; + uint16 y_skip = 0; + uint16 w_new = w; + uint16 h_new = h; + + // compute bounding box intersection with frame + if (x < box->x1) { + x_skip = box->x1 - x; + w_new -= x_skip; + x = box->x1; + } + + if (y < box->y1) { + y_skip = box->y1 - y; + h_new -= y_skip; + y = box->y1; + } + + if (x + w_new > box->x2 + 1) { + w_new = box->x2 - x + 1; + } + + if (y + h_new > box->y2 + 1) { + h_new = box->y2 - y + 1; + } + + src += w * y_skip; + if (!bs->xflip) { + src += x_skip; + } else { + src += w - w_new - x_skip; + x += w_new - 1; + } + _vm->display()->drawBobSprite(src, x, y, w_new, h_new, w, bs->xflip); + } +} + +void Graphics::drawInventoryItem(uint32 frameNum, uint16 x, uint16 y) { + if (frameNum != 0) { + BobFrame *bf = _vm->bankMan()->fetchFrame(frameNum); + _vm->display()->drawInventoryItem(bf->data, x, y, bf->width, bf->height); + } else { + _vm->display()->drawInventoryItem(NULL, x, y, 32, 32); + } +} + +void Graphics::pasteBob(uint16 objNum, uint16 image) { + GraphicData *pgd = _vm->logic()->graphicData(objNum); + _vm->bankMan()->unpack(pgd->firstFrame, image, 15); + BobFrame *bf = _vm->bankMan()->fetchFrame(image); + _vm->display()->drawBobPasteDown(bf->data, pgd->x, pgd->y, bf->width, bf->height); + _vm->bankMan()->eraseFrame(image); +} + +void Graphics::shrinkFrame(const BobFrame *bf, uint16 percentage) { + // computing new size, rounding to upper value + uint16 new_w = (bf->width * percentage + 50) / 100; + uint16 new_h = (bf->height * percentage + 50) / 100; + assert(new_w * new_h < BOB_SHRINK_BUF_SIZE); + + if (new_w != 0 && new_h != 0) { + _shrinkBuffer.width = new_w; + _shrinkBuffer.height = new_h; + + uint16 x, y; + uint16 sh[GAME_SCREEN_WIDTH]; + for (x = 0; x < MAX(new_h, new_w); ++x) { + sh[x] = x * 100 / percentage; + } + uint8* dst = _shrinkBuffer.data; + for (y = 0; y < new_h; ++y) { + uint8 *p = bf->data + sh[y] * bf->width; + for (x = 0; x < new_w; ++x) { + *dst++ = *(p + sh[x]); + } + } + } +} + +void Graphics::sortBobs() { + _sortedBobsCount = 0; + + // animate/move the bobs + for (int32 i = 0; i < ARRAYSIZE(_bobs); ++i) { + BobSlot *pbs = &_bobs[i]; + if (pbs->active) { + _sortedBobs[_sortedBobsCount] = pbs; + ++_sortedBobsCount; + + if (pbs->animating) { + pbs->animOneStep(); + if (pbs->frameNum > 500) { // SFX frame + _vm->sound()->playSfx(_vm->logic()->currentRoomSfx(), false); + pbs->frameNum -= 500; + } + } + if (pbs->moving) { + int16 j; + for (j = 0; pbs->moving && j < pbs->speed; ++j) { + pbs->moveOneStep(); + } + } + } + } + qsort(_sortedBobs, _sortedBobsCount, sizeof(BobSlot *), compareBobDrawOrder); +} + +void Graphics::drawBobs() { + const Box *bobBox = _vm->display()->fullscreen() ? &_fullScreenBox : &_gameScreenBox; + for (int i = 0; i < _sortedBobsCount; ++i) { + BobSlot *pbs = _sortedBobs[i]; + if (pbs->active) { + + BobFrame *pbf = _vm->bankMan()->fetchFrame(pbs->frameNum); + uint16 xh, yh, x, y; + + xh = pbf->xhotspot; + yh = pbf->yhotspot; + + if (pbs->xflip) { + xh = pbf->width - xh; + } + + // adjusts hot spots when object is scaled + if (pbs->scale != 100) { + xh = (xh * pbs->scale) / 100; + yh = (yh * pbs->scale) / 100; + } + + // adjusts position to hot-spot and screen scroll + x = pbs->x - xh - _vm->display()->horizontalScroll(); + y = pbs->y - yh; + + drawBob(pbs, pbf, bobBox, x, y); + } + } +} + +void Graphics::clearBobs() { + for (int32 i = 0; i < ARRAYSIZE(_bobs); ++i) { + _bobs[i].clear(); + } +} + +void Graphics::stopBobs() { + for (int32 i = 0; i < ARRAYSIZE(_bobs); ++i) { + _bobs[i].moving = false; + } +} + +BobSlot *Graphics::bob(int index) { + assert(index < MAX_BOBS_NUMBER); + return &_bobs[index]; +} + +void Graphics::setBobText(const BobSlot *pbs, const char *text, int textX, int textY, int color, int flags) { + + if (text[0] == '\0') + return; + + // Duplicate string and append zero if needed + + char textCopy[MAX_STRING_SIZE]; + + int length = strlen(text); + memcpy(textCopy, text, length); + + if (textCopy[length - 1] >= 'A') + textCopy[length++] = '.'; + + textCopy[length] = '\0'; + + // Split text into lines + + char lines[8][MAX_STRING_SIZE]; + int lineCount = 0; + int lineLength = 0; + int i; + + // Hebrew strings are written from right to left and should be cut + // to lines in reverse + if (_vm->resource()->getLanguage() == HEBREW) { + for (i = length - 1; i >= 0; i--) { + lineLength++; + + if ((lineLength > 20 && textCopy[i] == ' ') || i == 0) { + memcpy(lines[lineCount], textCopy + i, lineLength); + lines[lineCount][lineLength] = '\0'; + lineCount++; + lineLength = 0; + } + } + } else { + for (i = 0; i < length; i++) { + lineLength++; + + if ((lineLength > 20 && textCopy[i] == ' ') || i == (length-1)) { + memcpy(lines[lineCount], textCopy + i + 1 - lineLength, lineLength); + lines[lineCount][lineLength] = '\0'; + lineCount++; + lineLength = 0; + } + } + } + + // Find width of widest line + + int maxLineWidth = 0; + + for (i = 0; i < lineCount; i++) { + int width = _vm->display()->textWidth(lines[i]); + if (maxLineWidth < width) + maxLineWidth = width; + } + + // Calc text position + + short x, y, width, height; + + if (flags) { + if (flags == 2) + x = 160 - maxLineWidth / 2; + else + x = textX; + + y = textY; + + width = 0; + } else { + x = pbs->x; + y = pbs->y; + + BobFrame *pbf = _vm->bankMan()->fetchFrame(pbs->frameNum); + + width = (pbf->width * pbs->scale) / 100; + height = (pbf->height * pbs->scale) / 100; + + y = y - height - 16 - lineCount * 9; + } + + x -= _vm->display()->horizontalScroll(); + + if (y < 0) { + y = 0; + + if (x < 160) + x += width / 2; + else + x -= width / 2 + maxLineWidth; + } else if (!flags) + x -= maxLineWidth / 2; + + if (x < 0) + x = 4; + else if ((x + maxLineWidth) > 320) + x = 320 - maxLineWidth - 4; + + _vm->display()->textCurrentColor(color); + + for (i = 0; i < lineCount; i++) { + int lineX = x + (maxLineWidth - _vm->display()->textWidth(lines[i])) / 2; + + debug(7, "Setting text '%s' at (%i, %i)", lines[i], lineX, y + 9 * i); + _vm->display()->setText(lineX, y + 9 * i, lines[i]); + } +} + +void Graphics::handleParallax(uint16 roomNum) { + uint16 screenScroll = _vm->display()->horizontalScroll(); + switch (roomNum) { + case ROOM_AMAZON_HIDEOUT: + _bobs[8].x = 250 - screenScroll / 2; + break; + case ROOM_TEMPLE_MAZE_5: + _bobs[5].x = 410 - screenScroll / 2; + _bobs[6].x = 790 - screenScroll / 2; + break; + case ROOM_TEMPLE_OUTSIDE: + _bobs[5].x = 320 - screenScroll / 2; + break; + case ROOM_TEMPLE_TREE: + _bobs[5].x = 280 - screenScroll / 2; + break; + case ROOM_VALLEY_CARCASS: + _bobs[5].x = 600 - screenScroll / 2; + break; + case ROOM_UNUSED_INTRO_1: + _bobs[5].x = 340 - screenScroll / 2; + _bobs[6].x = 50 - screenScroll / 2; + _bobs[7].x = 79 - screenScroll / 2; + break; + case ROOM_CAR_CHASE: + _vm->bam()->updateCarAnimation(); + break; + case ROOM_FINAL_FIGHT: + _vm->bam()->updateFightAnimation(); + break; + case ROOM_INTRO_RITA_JOE_HEADS: + _cameraBob = -1; + if (screenScroll < 80) { + _vm->display()->horizontalScroll(screenScroll + 4); + // Joe's body and head + _bobs[ 1].x += 4; + _bobs[20].x += 4; + // Rita's body and head + _bobs[ 2].x -= 2; + _bobs[21].x -= 2; + } + break; + case ROOM_INTRO_EXPLOSION: + _bobs[21].x += 2; + _bobs[21].y += 2; + break; + } +} + +void Graphics::setupNewRoom(const char *room, uint16 roomNum, int16 *furniture, uint16 furnitureCount) { + // reset sprites table + clearBobs(); + + // load/setup objects associated to this room + char filename[20]; + sprintf(filename, "%s.BBK", room); + _vm->bankMan()->load(filename, 15); + + _numFrames = FRAMES_JOE + 1; + setupRoomFurniture(furniture, furnitureCount); + setupRoomObjects(); + + if (roomNum >= 90) { + putCameraOnBob(0); + } +} + +void Graphics::setBobCutawayAnim(uint16 bobNum, bool xflip, const AnimFrame *af, uint8 frameCount) { + assert(bobNum < 21 && frameCount < 30); + memcpy(_cutAnim[bobNum], af, sizeof(AnimFrame) * frameCount); + _bobs[bobNum].xflip = xflip; + _bobs[bobNum].animString(_cutAnim[bobNum]); +} + +void Graphics::fillAnimBuffer(const char *anim, AnimFrame *af) { + for (;;) { + // anim frame format is "%3hu,%3hu," (frame number, frame speed) + af->frame = atoi(anim); + anim += 4; + af->speed = atoi(anim); + anim += 4; + if (af->frame == 0) + break; + ++af; + } +} + +uint16 Graphics::countAnimFrames(const char *anim) { + AnimFrame afbuf[30]; + fillAnimBuffer(anim, afbuf); + + bool frames[256]; + memset(frames, 0, sizeof(frames)); + uint16 count = 0; + AnimFrame *af = afbuf; + for ( ; af->frame != 0; ++af) { + uint16 frameNum = af->frame; + if (frameNum > 500) { + frameNum -= 500; + } + if (!frames[frameNum]) { + frames[frameNum] = true; + ++count; + } + } + return count; +} + +void Graphics::setupObjectAnim(const GraphicData *gd, uint16 firstImage, uint16 bobNum, bool visible) { + int16 tempFrames[20]; + memset(tempFrames, 0, sizeof(tempFrames)); + uint16 numTempFrames = 0; + uint16 i, j; + for (i = 1; i <= _vm->logic()->graphicAnimCount(); ++i) { + const GraphicAnim *pga = _vm->logic()->graphicAnim(i); + if (pga->keyFrame == gd->firstFrame) { + int16 frame = pga->frame; + if (frame > 500) { // SFX + frame -= 500; + } + bool foundMatchingFrame = false; + for (j = 0; j < numTempFrames; ++j) { + if (tempFrames[j] == frame) { + foundMatchingFrame = true; + break; + } + } + if (!foundMatchingFrame) { + assert(numTempFrames < 20); + tempFrames[numTempFrames] = frame; + ++numTempFrames; + } + } + } + + // sort found frames ascending + bool swap = true; + while (swap) { + swap = false; + for (i = 0; i < numTempFrames - 1; ++i) { + if (tempFrames[i] > tempFrames[i + 1]) { + SWAP(tempFrames[i], tempFrames[i + 1]); + swap = true; + } + } + } + + // queen.c l.962-980 / l.1269-1294 + for (i = 0; i < gd->lastFrame; ++i) { + _vm->bankMan()->unpack(ABS(tempFrames[i]), firstImage + i, 15); + } + BobSlot *pbs = bob(bobNum); + pbs->animating = false; + if (visible) { + pbs->curPos(gd->x, gd->y); + if (tempFrames[0] < 0) { + pbs->xflip = true; + } + AnimFrame *paf = _newAnim[bobNum]; + for (i = 1; i <= _vm->logic()->graphicAnimCount(); ++i) { + const GraphicAnim *pga = _vm->logic()->graphicAnim(i); + if (pga->keyFrame == gd->firstFrame) { + uint16 frameNr = 0; + for (j = 1; j <= gd->lastFrame; ++j) { + if (pga->frame > 500) { + if (pga->frame - 500 == tempFrames[j - 1]) { + frameNr = j + firstImage - 1 + 500; + } + } else if (pga->frame == tempFrames[j - 1]) { + frameNr = j + firstImage - 1; + } + } + paf->frame = frameNr; + paf->speed = pga->speed; + ++paf; + } + } + paf->frame = 0; + paf->speed = 0; + pbs->animString(_newAnim[bobNum]); + } +} + +uint16 Graphics::setupPersonAnim(const ActorData *ad, const char *anim, uint16 curImage) { + debug(9, "Graphics::setupPersonAnim(%s, %d)", anim, curImage); + _personFrames[ad->bobNum] = curImage + 1; + + AnimFrame *animFrames = _newAnim[ad->bobNum]; + fillAnimBuffer(anim, animFrames); + uint16 frameCount[256]; + memset(frameCount, 0, sizeof(frameCount)); + AnimFrame *af = animFrames; + for ( ; af->frame != 0; ++af) { + uint16 frameNum = af->frame; + if (frameNum > 500) { + frameNum -= 500; + } + if (!frameCount[frameNum]) { + frameCount[frameNum] = 1; + } + } + uint16 i, n = 1; + for (i = 1; i < 256; ++i) { + if (frameCount[i]) { + frameCount[i] = n; + ++n; + } + } + af = animFrames; + for ( ; af->frame != 0; ++af) { + if (af->frame > 500) { + af->frame = curImage + frameCount[af->frame - 500] + 500; + } else { + af->frame = curImage + frameCount[af->frame]; + } + } + + // unpack necessary frames + for (i = 1; i < 256; ++i) { + if (frameCount[i]) { + ++curImage; + _vm->bankMan()->unpack(i, curImage, ad->bankNum); + } + } + + // start animation + bob(ad->bobNum)->animString(animFrames); + return curImage; +} + +void Graphics::resetPersonAnim(uint16 bobNum) { + if (_newAnim[bobNum][0].frame != 0) { + bob(bobNum)->animString(_newAnim[bobNum]); + } +} + +void Graphics::erasePersonAnim(uint16 bobNum) { + _newAnim[bobNum][0].frame = 0; + BobSlot *pbs = bob(bobNum); + pbs->animating = false; + pbs->anim.string.buffer = NULL; +} + +void Graphics::eraseAllAnims() { + for (int i = 1; i <= 16; ++i) { + _newAnim[i][0].frame = 0; + } +} + +uint16 Graphics::refreshObject(uint16 obj) { + debug(6, "Graphics::refreshObject(%X)", obj); + uint16 curImage = _numFrames; + + ObjectData *pod = _vm->logic()->objectData(obj); + if (pod->image == 0) { + return curImage; + } + + // check the object is in the current room + if (pod->room != _vm->logic()->currentRoom()) { + return curImage; + } + + // find bob for the object + uint16 curBob = _vm->logic()->findBob(obj); + BobSlot *pbs = bob(curBob); + + if (pod->image == -3 || pod->image == -4) { + // a person object + if (pod->name <= 0) { + pbs->clear(); + } else { + // find person number + uint16 pNum = _vm->logic()->findPersonNumber(obj, _vm->logic()->currentRoom()); + curImage = _personFrames[pNum] - 1; + if (_personFrames[pNum] == 0) { + _personFrames[pNum] = curImage = _numFrames; + } + curImage = setupPerson(obj - _vm->logic()->currentRoomData(), curImage); + } + return curImage; + } + + // find frame used for object + curImage = _vm->logic()->findFrame(obj); + + if (pod->name < 0 || pod->image < 0) { + // object is hidden or disabled + pbs->clear(); + return curImage; + } + + int image = pod->image; + if (image > 5000) { + image -= 5000; + } + + GraphicData *pgd = _vm->logic()->graphicData(image); + bool rebound = false; + int16 lastFrame = pgd->lastFrame; + if (lastFrame < 0) { + lastFrame = -lastFrame; + rebound = true; + } + if (pgd->firstFrame < 0) { + setupObjectAnim(pgd, curImage, curBob, pod->name != 0); + curImage += pgd->lastFrame - 1; + } else if (lastFrame != 0) { + // turn on an animated bob + pbs->animating = false; + uint16 firstImage = curImage; + --curImage; + uint16 j; + for (j = pgd->firstFrame; j <= lastFrame; ++j) { + ++curImage; + _vm->bankMan()->unpack(j, curImage, 15); + } + pbs->curPos(pgd->x, pgd->y); + pbs->frameNum = firstImage; + if (pgd->speed > 0) { + pbs->animNormal(firstImage, curImage, pgd->speed / 4, rebound, false); + } + } else { + _vm->bankMan()->unpack(pgd->firstFrame, curImage, 15); + pbs->curPos(pgd->x, pgd->y); + pbs->frameNum = curImage; + } + + return curImage; +} + +void Graphics::setupRoomFurniture(int16 *furniture, uint16 furnitureCount) { + uint16 i; + uint16 curImage = FRAMES_JOE; + + // unpack the static bobs + _numFurnitureStatic = 0; + for (i = 1; i <= furnitureCount; ++i) { + int16 obj = furniture[i]; + if (obj > 0 && obj <= 5000) { + GraphicData *pgd = _vm->logic()->graphicData(obj); + if (pgd->lastFrame == 0) { + ++_numFurnitureStatic; + ++curImage; + _vm->bankMan()->unpack(pgd->firstFrame, curImage, 15); + ++_numFrames; + BobSlot *pbs = bob(19 + _numFurnitureStatic); + pbs->curPos(pgd->x, pgd->y); + pbs->frameNum = curImage; + } + } + } + + // unpack the animated bobs + _numFurnitureAnimated = 0; + _numFurnitureAnimatedLen = 0; + uint16 curBob = 0; + for (i = 1; i <= furnitureCount; ++i) { + int16 obj = furniture[i]; + if (obj > 0 && obj <= 5000) { + GraphicData *pgd = _vm->logic()->graphicData(obj); + + bool rebound = false; + int16 lastFrame = pgd->lastFrame; + if (lastFrame < 0) { + rebound = true; + lastFrame = -lastFrame; + } + + if (lastFrame > 0) { + _numFurnitureAnimatedLen += lastFrame - pgd->firstFrame + 1; + ++_numFurnitureAnimated; + uint16 image = curImage + 1; + int k; + for (k = pgd->firstFrame; k <= lastFrame; ++k) { + ++curImage; + _vm->bankMan()->unpack(k, curImage, 15); + ++_numFrames; + } + BobSlot *pbs = bob(5 + curBob); + pbs->animNormal(image, curImage, pgd->speed / 4, rebound, false); + pbs->curPos(pgd->x, pgd->y); + ++curBob; + } + } + } + + // unpack the paste downs + for (i = 1; i <= furnitureCount; ++i) { + if (furniture[i] > 5000) { + pasteBob(furniture[i] - 5000, curImage + 1); + } + } +} + +void Graphics::setupRoomObjects() { + uint16 i; + // furniture frames are reserved in ::setupRoomFurniture(), we append objects + // frames after the furniture ones. + uint16 curImage = FRAMES_JOE + _numFurnitureStatic + _numFurnitureAnimatedLen; + uint16 firstRoomObj = _vm->logic()->currentRoomData() + 1; + uint16 lastRoomObj = _vm->logic()->roomData(_vm->logic()->currentRoom() + 1); + uint16 numObjectStatic = 0; + uint16 numObjectAnimated = 0; + uint16 curBob; + + // invalidates all Bobs for persons (except Joe's one) + for (i = 1; i <= 3; ++i) { + _bobs[i].active = false; + } + + // static/animated Bobs + for (i = firstRoomObj; i <= lastRoomObj; ++i) { + ObjectData *pod = _vm->logic()->objectData(i); + // setup blanks bobs for turned off objects (in case + // you turn them on again) + if (pod->image == -1) { + // static OFF Bob + curBob = 20 + _numFurnitureStatic + numObjectStatic; + ++numObjectStatic; + // create a blank frame for the OFF object + ++_numFrames; + ++curImage; + } else if (pod->image == -2) { + // animated OFF Bob + curBob = 5 + _numFurnitureAnimated + numObjectAnimated; + ++numObjectAnimated; + } else if (pod->image > 0 && pod->image < 5000) { + GraphicData *pgd = _vm->logic()->graphicData(pod->image); + int16 lastFrame = pgd->lastFrame; + bool rebound = false; + if (lastFrame < 0) { + lastFrame = -lastFrame; + rebound = true; + } + if (pgd->firstFrame < 0) { + curBob = 5 + _numFurnitureAnimated; + setupObjectAnim(pgd, curImage + 1, curBob + numObjectAnimated, pod->name > 0); + curImage += pgd->lastFrame; + ++numObjectAnimated; + } else if (lastFrame != 0) { + // animated objects + uint16 j; + uint16 firstFrame = curImage + 1; + for (j = pgd->firstFrame; j <= lastFrame; ++j) { + ++curImage; + _vm->bankMan()->unpack(j, curImage, 15); + ++_numFrames; + } + curBob = 5 + _numFurnitureAnimated + numObjectAnimated; + if (pod->name > 0) { + BobSlot *pbs = bob(curBob); + pbs->curPos(pgd->x, pgd->y); + pbs->frameNum = firstFrame; + if (pgd->speed > 0) { + pbs->animNormal(firstFrame, curImage, pgd->speed / 4, rebound, false); + } + } + ++numObjectAnimated; + } else { + // static objects + curBob = 20 + _numFurnitureStatic + numObjectStatic; + ++curImage; + bob(curBob)->clear(); + + _vm->bankMan()->unpack(pgd->firstFrame, curImage, 15); + ++_numFrames; + if (pod->name > 0) { + BobSlot *pbs = bob(curBob); + pbs->curPos(pgd->x, pgd->y); + pbs->frameNum = curImage; + } + ++numObjectStatic; + } + } + } + + // persons Bobs + for (i = firstRoomObj; i <= lastRoomObj; ++i) { + ObjectData *pod = _vm->logic()->objectData(i); + if (pod->image == -3 || pod->image == -4) { + debug(6, "Graphics::setupRoomObjects() - Setting up person %X, name=%X", i, pod->name); + uint16 noun = i - _vm->logic()->currentRoomData(); + if (pod->name > 0) { + curImage = setupPerson(noun, curImage); + } else { + curImage = allocPerson(noun, curImage); + } + } + } + + // paste downs list + ++curImage; + _numFrames = curImage; + for (i = firstRoomObj; i <= lastRoomObj; ++i) { + ObjectData *pod = _vm->logic()->objectData(i); + if (pod->name > 0 && pod->image > 5000) { + pasteBob(pod->image - 5000, curImage); + } + } +} + +uint16 Graphics::setupPerson(uint16 noun, uint16 curImage) { + if (noun == 0) { + warning("Trying to setup person 0"); + return curImage; + } + + Person p; + if (!_vm->logic()->initPerson(noun, "", true, &p)) { + return curImage; + } + + const ActorData *pad = p.actor; + uint16 scale = 100; + uint16 a = _vm->grid()->findAreaForPos(GS_ROOM, pad->x, pad->y); + if (a != 0) { + // person is not standing in the area box, scale it accordingly + scale = _vm->grid()->area(_vm->logic()->currentRoom(), a)->calcScale(pad->y); + } + + _vm->bankMan()->unpack(pad->bobFrameStanding, p.bobFrame, p.actor->bankNum); + uint16 obj = _vm->logic()->currentRoomData() + noun; + BobSlot *pbs = bob(pad->bobNum); + pbs->curPos(pad->x, pad->y); + pbs->scale = scale; + pbs->frameNum = p.bobFrame; + pbs->xflip = (_vm->logic()->objectData(obj)->image == -3); // person is facing left + + debug(6, "Graphics::setupPerson(%d, %d) - bob = %d name = %s", noun, curImage, pad->bobNum, p.name); + + if (p.anim != NULL) { + curImage = setupPersonAnim(pad, p.anim, curImage); + } else { + erasePersonAnim(pad->bobNum); + } + return curImage; +} + +uint16 Graphics::allocPerson(uint16 noun, uint16 curImage) { + Person p; + if (_vm->logic()->initPerson(noun, "", false, &p) && p.anim != NULL) { + curImage += countAnimFrames(p.anim); + _personFrames[p.actor->bobNum] = curImage + 1; + } + return curImage; +} + +void Graphics::update(uint16 room) { + sortBobs(); + if (_cameraBob >= 0) { + _vm->display()->horizontalScrollUpdate(_bobs[_cameraBob].x); + } + handleParallax(room); + _vm->display()->prepareUpdate(); + drawBobs(); +} + + +BamScene::BamScene(QueenEngine *vm) + : _flag(F_STOP), _screenShaked(false), _fightData(_fight1Data), _vm(vm) { +} + +void BamScene::playSfx() { + // FIXME - we don't play all sfx here. This is only necessary for + // the fight bam, where the number of 'sfx bam frames' is too much + // important / too much closer. The original game does not have + // this problem since their playSfx() function returns immediately + // if a sound is already being played. + if (_lastSoundIndex == 0 || _index - _lastSoundIndex >= SFX_SKIP) { + _vm->sound()->playSfx(_vm->logic()->currentRoomSfx(), false); + _lastSoundIndex = _index; + } +} + +void BamScene::prepareAnimation() { + _obj1 = _vm->graphics()->bob(BOB_OBJ1); + _obj1->clear(); + _obj1->active = true; + + _obj2 = _vm->graphics()->bob(BOB_OBJ2); + _obj2->clear(); + _obj2->active = true; + + _objfx = _vm->graphics()->bob(BOB_FX); + _objfx->clear(); + _objfx->active = true; + + _index = 0; + _lastSoundIndex = 0; +} + +void BamScene::updateCarAnimation() { + if (_flag != F_STOP) { + const BamDataBlock *bdb = &_carData[_index]; + + // Truck + _obj1->curPos(bdb->obj1.x, bdb->obj1.y); + _obj1->frameNum = 40 + bdb->obj1.frame; + + // Rico + _obj2->curPos(bdb->obj2.x, bdb->obj2.y); + _obj2->frameNum = 30 + bdb->obj2.frame; + + // FX + _objfx->curPos(bdb->fx.x, bdb->fx.y); + _objfx->frameNum = 41 + bdb->fx.frame; + + if (bdb->sfx < 0) { + _vm->sound()->playSong(-bdb->sfx); + } + + if (bdb->sfx == 99) { + _lastSoundIndex = _index = 0; + } else { + ++_index; + } + + if (bdb->sfx == 2) { + playSfx(); + } + } +} + +void BamScene::updateFightAnimation() { + static const BamDataBlock *fightDataBlocks[] = { + _fight1Data, + _fight2Data, + _fight3Data + }; + if (_flag != F_STOP) { + const BamDataBlock *bdb = &_fightData[_index]; + + // Frank + _obj1->curPos(bdb->obj1.x, bdb->obj1.y); + _obj1->frameNum = 40 + ABS(bdb->obj1.frame); + _obj1->xflip = (bdb->obj1.frame < 0); + + // Robot + _obj2->curPos(bdb->obj2.x, bdb->obj2.y); + _obj2->frameNum = 40 + ABS(bdb->obj2.frame); + _obj2->xflip = (bdb->obj2.frame < 0); + + // FX + _objfx->curPos(bdb->fx.x, bdb->fx.y); + _objfx->frameNum = 40 + ABS(bdb->fx.frame); + _objfx->xflip = (bdb->fx.frame < 0); + + if (bdb->sfx < 0) { + _vm->sound()->playSong(-bdb->sfx); + } + + ++_index; + switch (bdb->sfx) { + case 0: // nothing, so reset shaked screen if necessary + if (_screenShaked) { + _vm->display()->shake(true); + _screenShaked = false; + } + break; + case 1: // shake screen + _vm->display()->shake(false); + _screenShaked = true; + break; + case 2: // play background sfx + playSfx(); + break; + case 3: // play background sfx and shake screen + playSfx(); + _vm->display()->shake(false); + _screenShaked = true; + break; + case 99: // end of BAM data + _lastSoundIndex = _index = 0; + _fightData = fightDataBlocks[_vm->randomizer.getRandomNumber(2)]; + if (_flag == F_REQ_STOP) { + _flag = F_STOP; + } + break; + } + } +} + +void BamScene::saveState(byte *&ptr) { + WRITE_BE_UINT16(ptr, _flag); ptr += 2; +} + +void BamScene::loadState(uint32 ver, byte *&ptr) { + _flag = READ_BE_UINT16(ptr); ptr += 2; +} + +#ifndef PALMOS_68K +const BamScene::BamDataBlock BamScene::_carData[] = { + { { 310, 105, 1 }, { 314, 106, 17 }, { 366, 101, 1 }, 0 }, + { { 303, 105, 1 }, { 307, 106, 17 }, { 214, 0, 10 }, 0 }, + { { 297, 104, 1 }, { 301, 105, 17 }, { 214, 0, 10 }, 0 }, + { { 294, 103, 1 }, { 298, 104, 17 }, { 214, 0, 10 }, 0 }, + { { 291, 102, 1 }, { 295, 103, 18 }, { 214, 0, 10 }, 0 }, + { { 287, 101, 1 }, { 291, 102, 18 }, { 266, 51, 10 }, 2 }, + { { 283, 100, 1 }, { 287, 101, 19 }, { 279, 47, 11 }, 0 }, + { { 279, 99, 1 }, { 283, 100, 20 }, { 294, 46, 12 }, 0 }, + { { 274, 98, 1 }, { 278, 99, 20 }, { 305, 44, 13 }, 0 }, + { { 269, 98, 1 }, { 273, 99, 20 }, { 320, 42, 14 }, 0 }, + { { 264, 98, 1 }, { 268, 99, 17 }, { 214, 0, 10 }, 0 }, + { { 261, 98, 1 }, { 265, 99, 17 }, { 214, 0, 10 }, 0 }, + { { 259, 98, 1 }, { 263, 99, 17 }, { 214, 0, 10 }, 0 }, + { { 258, 98, 1 }, { 262, 99, 17 }, { 214, 0, 10 }, 0 }, + { { 257, 98, 2 }, { 260, 99, 17 }, { 214, 0, 10 }, 0 }, + { { 255, 99, 3 }, { 258, 100, 17 }, { 214, 0, 10 }, 0 }, + { { 258, 99, 4 }, { 257, 100, 17 }, { 214, 0, 10 }, 0 }, + { { 264, 102, 4 }, { 263, 103, 17 }, { 214, 0, 10 }, 0 }, + { { 272, 105, 5 }, { 274, 106, 17 }, { 214, 0, 10 }, 0 }, + { { 276, 107, 5 }, { 277, 108, 17 }, { 214, 0, 10 }, 0 }, + { { 283, 108, 5 }, { 284, 109, 17 }, { 214, 0, 10 }, 0 }, + { { 288, 109, 5 }, { 288, 110, 17 }, { 214, 0, 10 }, 0 }, + { { 293, 110, 5 }, { 293, 111, 18 }, { 266, 59, 10 }, 2 }, + { { 298, 110, 5 }, { 299, 111, 18 }, { 277, 56, 11 }, 0 }, + { { 303, 110, 5 }, { 304, 111, 19 }, { 285, 55, 12 }, 0 }, + { { 308, 110, 4 }, { 307, 111, 20 }, { 296, 54, 13 }, 0 }, + { { 309, 110, 3 }, { 312, 111, 20 }, { 304, 53, 14 }, 0 }, + { { 310, 110, 3 }, { 313, 111, 20 }, { 214, 0, 10 }, 0 }, + { { 311, 110, 3 }, { 314, 111, 17 }, { 214, 0, 10 }, 0 }, + { { 309, 110, 2 }, { 312, 111, 17 }, { 214, 0, 10 }, 0 }, + { { 304, 111, 2 }, { 307, 112, 17 }, { 214, 0, 10 }, 0 }, + { { 300, 110, 2 }, { 303, 111, 17 }, { 214, 0, 10 }, 0 }, + { { 296, 109, 2 }, { 299, 110, 17 }, { 214, 0, 10 }, 0 }, + { { 292, 108, 1 }, { 296, 109, 17 }, { 214, 0, 10 }, 0 }, + { { 285, 107, 2 }, { 289, 108, 17 }, { 214, 0, 10 }, 0 }, + { { 282, 107, 3 }, { 285, 108, 17 }, { 214, 0, 10 }, 0 }, + { { 278, 107, 4 }, { 277, 108, 18 }, { 214, 0, 10 }, 0 }, + { { 279, 108, 4 }, { 278, 109, 18 }, { 252, 57, 10 }, 2 }, + { { 281, 108, 5 }, { 283, 109, 18 }, { 265, 55, 11 }, 0 }, + { { 284, 109, 5 }, { 285, 110, 19 }, { 277, 55, 12 }, 0 }, + { { 287, 110, 5 }, { 288, 111, 20 }, { 288, 54, 13 }, 0 }, + { { 289, 111, 5 }, { 290, 112, 20 }, { 299, 54, 14 }, 0 }, + { { 291, 112, 4 }, { 290, 113, 20 }, { 214, 0, 10 }, 0 }, + { { 293, 113, 3 }, { 295, 114, 17 }, { 214, 0, 10 }, 0 }, + { { 296, 114, 2 }, { 299, 115, 17 }, { 214, 0, 10 }, 0 }, + { { 295, 115, 2 }, { 298, 116, 17 }, { 214, 0, 10 }, 0 }, + { { 293, 116, 1 }, { 297, 117, 17 }, { 214, 0, 10 }, 0 }, + { { 289, 116, 1 }, { 292, 117, 17 }, { 214, 0, 10 }, 0 }, + { { 285, 115, 1 }, { 289, 116, 17 }, { 214, 0, 10 }, 0 }, + { { 281, 114, 1 }, { 284, 115, 17 }, { 214, 0, 10 }, 0 }, + { { 277, 113, 1 }, { 280, 114, 17 }, { 214, 0, 10 }, 0 }, + { { 274, 112, 1 }, { 277, 113, 17 }, { 214, 0, 10 }, 0 }, + { { 271, 111, 1 }, { 274, 112, 17 }, { 214, 0, 10 }, 0 }, + { { 267, 110, 1 }, { 270, 111, 17 }, { 214, 0, 10 }, 0 }, + { { 263, 109, 1 }, { 266, 110, 17 }, { 214, 0, 10 }, 0 }, + { { 260, 108, 1 }, { 263, 109, 17 }, { 214, 0, 10 }, 0 }, + { { 254, 108, 2 }, { 256, 109, 17 }, { 214, 0, 10 }, 0 }, + { { 252, 107, 3 }, { 254, 108, 17 }, { 214, 0, 10 }, 0 }, + { { 253, 108, 3 }, { 255, 109, 17 }, { 214, 0, 10 }, 0 }, + { { 255, 109, 3 }, { 257, 110, 18 }, { 231, 59, 10 }, 2 }, + { { 258, 111, 3 }, { 260, 112, 18 }, { 242, 57, 11 }, 0 }, + { { 263, 112, 4 }, { 262, 113, 19 }, { 256, 57, 12 }, 0 }, + { { 270, 111, 4 }, { 269, 112, 20 }, { 267, 57, 13 }, 0 }, + { { 274, 112, 5 }, { 276, 113, 20 }, { 281, 56, 14 }, 0 }, + { { 280, 111, 6 }, { 282, 112, 19 }, { 214, 0, 10 }, 0 }, + { { 284, 109, 6 }, { 285, 110, 17 }, { 214, 0, 10 }, 0 }, + { { 289, 108, 6 }, { 291, 109, 17 }, { 214, 0, 10 }, 0 }, + { { 294, 107, 6 }, { 296, 108, 17 }, { 214, 0, 10 }, 0 }, + { { 294, 107, 5 }, { 296, 108, 18 }, { 272, 57, 10 }, 2 }, + { { 295, 107, 5 }, { 297, 108, 18 }, { 282, 57, 11 }, 0 }, + { { 296, 108, 5 }, { 298, 109, 19 }, { 295, 57, 12 }, 0 }, + { { 300, 108, 4 }, { 299, 109, 20 }, { 303, 57, 13 }, 0 }, + { { 303, 108, 3 }, { 306, 109, 20 }, { 313, 57, 14 }, 0 }, + { { 307, 109, 2 }, { 311, 110, 17 }, { 214, 0, 10 }, 0 }, + { { 310, 110, 1 }, { 314, 111, 17 }, { 214, 0, 10 }, 99 } +}; + +const BamScene::BamDataBlock BamScene::_fight1Data[] = { + { { 75, 96, 1 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 }, + { { 75, 96, 2 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 }, + { { 75, 96, 3 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 }, + { { 75, 96, 4 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 }, + { { 75, 96, 1 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 }, + { { 75, 96, 2 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 }, + { { 75, 96, 3 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 }, + { { 75, 96, 4 }, { 187, 96, -24 }, { 58, 37, 46 }, 0 }, + { { 79, 96, 1 }, { 187, 96, -24 }, { 58, 37, 46 }, 0 }, + { { 85, 96, 2 }, { 187, 96, -24 }, { 58, 37, 46 }, 0 }, + { { 94, 96, 3 }, { 187, 96, -24 }, { 58, 37, 46 }, 0 }, + { { 100, 96, 4 }, { 187, 96, -24 }, { 58, 37, 46 }, 0 }, + { { 113, 96, 1 }, { 187, 96, -25 }, { 58, 37, 46 }, 0 }, + { { 121, 96, 1 }, { 187, 96, -25 }, { 58, 37, 46 }, 0 }, + { { 136, 96, 16 }, { 187, 96, -26 }, { 58, 37, 46 }, 0 }, + { { 151, 93, 6 }, { 187, 96, -27 }, { 58, 37, 46 }, 0 }, + { { 159, 83, 16 }, { 187, 96, -28 }, { 58, 37, 46 }, 0 }, + { { 170, 73, 16 }, { 187, 96, -29 }, { 182, 96, 48 }, 3 }, + { { 176, 69, 13 }, { 187, 96, -31 }, { 182, 94, 49 }, 1 }, + { { 168, 66, 13 }, { 187, 98, -32 }, { 182, 92, 50 }, 0 }, + { { 155, 75, 13 }, { 187, 96, -32 }, { 182, 88, 51 }, 3 }, + { { 145, 86, 13 }, { 187, 98, -32 }, { 182, 85, 52 }, 0 }, + { { 127, 104, 13 }, { 187, 98, -32 }, { 182, 25, 52 }, 1 }, + { { 122, 108, 13 }, { 187, 98, -32 }, { 182, 25, 52 }, 1 }, + { { 120, 104, 14 }, { 187, 96, -34 }, { 107, 145, 42 }, 2 }, + { { 111, 103, 13 }, { 187, 96, -23 }, { 107, 144, 43 }, 0 }, + { { 102, 105, 13 }, { 187, 96, -23 }, { 107, 142, 43 }, 0 }, + { { 97, 107, 13 }, { 187, 96, -23 }, { 107, 139, 44 }, 0 }, + { { 92, 101, 14 }, { 187, 96, -23 }, { 107, 34, 47 }, 3 }, + { { 90, 105, 14 }, { 187, 96, -23 }, { 107, 34, 47 }, 0 }, + { { 88, 104, 14 }, { 187, 96, -23 }, { 107, 34, 47 }, 0 }, + { { 87, 105, 14 }, { 187, 96, -23 }, { 107, 34, 47 }, 0 }, + { { 86, 105, 14 }, { 187, 96, -23 }, { 107, 34, 47 }, 0 }, + { { 86, 105, 14 }, { 187, 96, -23 }, { 107, 34, 47 }, 0 }, + { { 86, 105, 15 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 }, + { { 85, 98, 16 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 }, + { { 92, 96, 1 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 }, + { { 92, 96, 1 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 }, + { { 89, 96, 4 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 }, + { { 86, 96, 3 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 }, + { { 83, 96, 2 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 }, + { { 81, 96, 1 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 }, + { { 78, 96, 4 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 }, + { { 75, 96, 3 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 }, + { { 75, 96, 1 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 }, + { { 75, 96, 1 }, { 187, 96, -23 }, { 0, 0, 0 }, 99 } +}; + +const BamScene::BamDataBlock BamScene::_fight2Data[] = { + { { 75, 96, 1 }, { 187, 96, -23 }, { 150, 45, 35 }, 0 }, + { { 78, 96, 2 }, { 187, 96, -23 }, { 150, 45, 35 }, 0 }, + { { 81, 96, 3 }, { 189, 96, -18 }, { 150, 45, 35 }, 0 }, + { { 84, 96, 4 }, { 183, 96, -19 }, { 150, 45, 35 }, 0 }, + { { 87, 96, 1 }, { 181, 96, -20 }, { 150, 45, 35 }, 0 }, + { { 90, 96, 2 }, { 177, 96, -21 }, { 150, 45, 35 }, 0 }, + { { 93, 96, 3 }, { 171, 96, -22 }, { 150, 45, 35 }, 0 }, + { { 96, 96, 4 }, { 169, 96, -17 }, { 150, 45, 35 }, 0 }, + { { 99, 96, 1 }, { 165, 96, -18 }, { 150, 45, 35 }, 0 }, + { { 102, 96, 2 }, { 159, 96, -19 }, { 150, 45, 35 }, 0 }, + { { 105, 96, 3 }, { 157, 96, -20 }, { 150, 45, 35 }, 0 }, + { { 108, 96, 4 }, { 153, 96, -21 }, { 150, 45, 35 }, 0 }, + { { 111, 96, 1 }, { 147, 96, -22 }, { 150, 45, 35 }, 0 }, + { { 114, 96, 2 }, { 147, 96, -23 }, { 150, 45, 35 }, 0 }, + { { 117, 96, 3 }, { 147, 96, -23 }, { 150, 45, 35 }, 0 }, + { { 120, 96, 4 }, { 147, 96, -24 }, { 150, 45, 35 }, 0 }, + { { 123, 96, 1 }, { 147, 96, -25 }, { 150, 45, 35 }, 0 }, + { { 125, 96, 2 }, { 147, 96, -25 }, { 150, 45, 35 }, 0 }, + { { 127, 96, 12 }, { 147, 96, -69 }, { 122, 94, 36 }, 3 }, + { { 127, 95, 11 }, { 147, 96, -70 }, { 122, 94, 41 }, 0 }, + { { 127, 96, 12 }, { 147, 96, -71 }, { 122, 100, 36 }, 3 }, + { { 127, 97, 11 }, { 147, 96, -69 }, { 122, 100, 41 }, 0 }, + { { 127, 96, 12 }, { 147, 96, -70 }, { 127, 103, 36 }, 3 }, + { { 127, 95, 11 }, { 147, 96, -71 }, { 127, 103, 41 }, 0 }, + { { 127, 94, 12 }, { 147, 96, -69 }, { 123, 94, 36 }, 3 }, + { { 127, 95, 11 }, { 147, 96, -70 }, { 123, 94, 41 }, 0 }, + { { 127, 96, 12 }, { 147, 96, -71 }, { 120, 99, 36 }, 3 }, + { { 127, 96, 12 }, { 147, 96, -71 }, { 115, 98, 41 }, 0 }, + { { 117, 93, 11 }, { 147, 96, -25 }, { 115, 134, 42 }, 0 }, + { { 110, 92, 11 }, { 147, 96, -25 }, { 114, 133, 42 }, 0 }, + { { 102, 93, 11 }, { 147, 96, -25 }, { 114, 131, 43 }, 0 }, + { { 92, 93, 11 }, { 147, 96, -25 }, { 114, 130, 43 }, 0 }, + { { 82, 94, 11 }, { 147, 96, -25 }, { 114, 128, 44 }, 0 }, + { { 76, 95, 11 }, { 147, 96, -25 }, { 114, 127, 44 }, 0 }, + { { 70, 96, 11 }, { 147, 96, -25 }, { 114, 126, 45 }, 0 }, + { { 75, 96, 5 }, { 147, 96, -25 }, { 114, 125, 46 }, 1 }, + { { 75, 96, 6 }, { 147, 96, -25 }, { 114, 43, 46 }, 0 }, + { { 75, 96, 6 }, { 147, 96, -25 }, { 114, 43, 46 }, 0 }, + { { 75, 96, 5 }, { 147, 96, -25 }, { 114, 43, 46 }, 0 }, + { { 75, 96, 7 }, { 147, 96, -25 }, { 114, 43, 46 }, 0 }, + { { 75, 96, 68 }, { 147, 96, -25 }, { 114, 43, 46 }, 0 }, + { { 75, 96, 68 }, { 147, 96, -25 }, { 89, 104, 36 }, 2 }, + { { 75, 96, 68 }, { 147, 96, -25 }, { 94, 103, 62 }, 0 }, + { { 75, 96, 68 }, { 147, 96, -25 }, { 122, 103, 63 }, 0 }, + { { 75, 96, 68 }, { 147, 96, -25 }, { 141, 103, 64 }, 0 }, + { { 75, 96, 68 }, { 147, 96, -30 }, { 150, 103, 65 }, 3 }, + { { 75, 96, 68 }, { 156, 96, -30 }, { 160, 103, 66 }, 0 }, + { { 75, 96, 7 }, { 164, 96, -30 }, { 169, 103, 67 }, 0 }, + { { 75, 96, 5 }, { 169, 96, -30 }, { 177, 103, 48 }, 3 }, + { { 75, 96, 5 }, { 173, 96, -30 }, { 185, 103, 49 }, 0 }, + { { 75, 96, 6 }, { 178, 96, -30 }, { 198, 103, 50 }, 0 }, + { { 75, 96, 6 }, { 181, 96, -30 }, { 207, 103, 51 }, 1 }, + { { 75, 96, 5 }, { 184, 96, -30 }, { 221, 103, 52 }, 0 }, + { { 75, 96, 5 }, { 186, 96, -30 }, { 224, 53, 53 }, 0 }, + { { 75, 96, 5 }, { 187, 96, -23 }, { 224, 53, 53 }, 99 } +}; + +const BamScene::BamDataBlock BamScene::_fight3Data[] = { + { { 75, 96, 1 }, { 187, 96, -23 }, { 150, 45, 35 }, 0 }, + { { 77, 96, 2 }, { 187, 96, -22 }, { 150, 45, 35 }, 0 }, + { { 80, 96, 3 }, { 185, 96, -17 }, { 150, 45, 35 }, 0 }, + { { 83, 96, 4 }, { 181, 96, -18 }, { 150, 45, 35 }, 0 }, + { { 86, 96, 1 }, { 175, 96, -19 }, { 150, 45, 35 }, 0 }, + { { 88, 96, 2 }, { 173, 96, -20 }, { 150, 45, 35 }, 0 }, + { { 91, 96, 3 }, { 169, 96, -21 }, { 150, 45, 35 }, 0 }, + { { 94, 96, 4 }, { 163, 96, -22 }, { 150, 45, 35 }, 0 }, + { { 97, 96, 1 }, { 161, 96, -17 }, { 150, 45, 35 }, 0 }, + { { 99, 96, 2 }, { 157, 96, -18 }, { 150, 45, 35 }, 0 }, + { { 102, 96, 3 }, { 151, 96, -19 }, { 150, 45, 35 }, 0 }, + { { 105, 96, 4 }, { 149, 96, -20 }, { 150, 45, 35 }, 0 }, + { { 108, 96, 1 }, { 145, 96, -21 }, { 150, 45, 35 }, 0 }, + { { 110, 96, 2 }, { 145, 96, -25 }, { 150, 45, 35 }, 0 }, + { { 113, 96, 3 }, { 145, 96, -26 }, { 132, 96, 36 }, 2 }, + { { 117, 96, 7 }, { 145, 96, -27 }, { 122, 97, 36 }, 0 }, + { { 117, 96, 7 }, { 145, 96, -28 }, { 117, 97, 37 }, 0 }, + { { 116, 96, 12 }, { 145, 96, -24 }, { 110, 96, 38 }, 3 }, + { { 109, 96, 12 }, { 145, 96, -24 }, { 103, 95, 39 }, 0 }, + { { 105, 96, 12 }, { 145, 96, -24 }, { 95, 90, 40 }, 1 }, + { { 96, 96, 11 }, { 145, 96, -24 }, { 86, 80, 41 }, 0 }, + { { 92, 96, 11 }, { 145, 96, -24 }, { 86, 80, 41 }, 0 }, + { { 93, 96, 5 }, { 145, 96, -24 }, { 86, 38, 41 }, 0 }, + { { 91, 96, 5 }, { 145, 96, -24 }, { 86, 38, 41 }, 0 }, + { { 89, 96, 5 }, { 145, 96, -24 }, { 86, 38, 41 }, 0 }, + { { 88, 96, 5 }, { 145, 96, -24 }, { 86, 38, 41 }, 0 }, + { { 87, 96, 6 }, { 145, 96, -24 }, { 86, 38, 41 }, 0 }, + { { 87, 96, 6 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 }, + { { 87, 96, 6 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 }, + { { 87, 96, 5 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 }, + { { 87, 96, 5 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 }, + { { 87, 96, 6 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 }, + { { 87, 96, 6 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 }, + { { 87, 96, 5 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 }, + { { 87, 96, 5 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 }, + { { 87, 96, 6 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 }, + { { 87, 96, 6 }, { 145, 96, -26 }, { 86, 38, 41 }, 0 }, + { { 87, 96, 6 }, { 145, 96, -27 }, { 132, 97, 36 }, 2 }, + { { 87, 96, 5 }, { 145, 96, -28 }, { 118, 97, 36 }, 0 }, + { { 87, 96, 7 }, { 145, 96, -24 }, { 107, 97, 36 }, 0 }, + { { 87, 96, 8 }, { 145, 96, -24 }, { 101, 97, 36 }, 0 }, + { { 87, 96, 9 }, { 145, 96, -23 }, { 102, 97, 66 }, 3 }, + { { 87, 96, 10 }, { 145, 96, -23 }, { 120, 97, 67 }, 0 }, + { { 87, 96, 10 }, { 145, 96, -30 }, { 139, 97, 67 }, 1 }, + { { 87, 96, 7 }, { 146, 96, -30 }, { 144, 97, 62 }, 2 }, + { { 86, 96, 4 }, { 160, 96, -30 }, { 144, 97, 48 }, 1 }, + { { 83, 96, 3 }, { 170, 96, -31 }, { 154, 93, 49 }, 0 }, + { { 80, 96, 2 }, { 174, 96, -31 }, { 161, 89, 50 }, 0 }, + { { 78, 96, 1 }, { 178, 99, -31 }, { 169, 85, 51 }, 0 }, + { { 75, 96, 4 }, { 183, 104, -31 }, { 175, 79, 52 }, 0 }, + { { 75, 96, 1 }, { 185, 99, -32 }, { 180, 144, 42 }, 3 }, + { { 75, 96, 1 }, { 185, 106, -31 }, { 181, 141, 42 }, 0 }, + { { 75, 96, 5 }, { 185, 104, -31 }, { 181, 138, 43 }, 0 }, + { { 75, 96, 5 }, { 188, 106, -31 }, { 182, 135, 43 }, 0 }, + { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 131, 44 }, 3 }, + { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 127, 45 }, 0 }, + { { 75, 96, 5 }, { 191, 99, -32 }, { 184, 121, 46 }, 0 }, + { { 75, 96, 5 }, { 191, 99, -32 }, { 183, 115, 46 }, 0 }, + { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 5 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 5 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 5 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 5 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 5 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 5 }, { 195, 98, -33 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 6 }, { 191, 96, -34 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 6 }, { 193, 96, -25 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 5 }, { 193, 96, -24 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 5 }, { 193, 96, -24 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 5 }, { 193, 96, -24 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 6 }, { 191, 96, -18 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 6 }, { 190, 96, -19 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 6 }, { 187, 96, -20 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 1 }, { 187, 96, -23 }, { 183, 41, 47 }, 0 }, + { { 75, 96, 1 }, { 187, 96, -23 }, { 183, 41, 47 }, 99 } +}; +#endif + +} // End of namespace Queen + +#ifdef PALMOS_68K +#include "scumm_globals.h" + +_GINIT(Queen_Graphics) +_GSETPTR(Queen::_carData, GBVARS_GRAPHICSCARDATA_INDEX, Queen::BamScene::BamDataBlock, GBVARS_QUEEN) +_GSETPTR(Queen::_fight1Data, GBVARS_GRAPHICSFIGHT1DATA_INDEX, Queen::BamScene::BamDataBlock, GBVARS_QUEEN) +_GSETPTR(Queen::_fight2Data, GBVARS_GRAPHICSFIGHT2DATA_INDEX, Queen::BamScene::BamDataBlock, GBVARS_QUEEN) +_GSETPTR(Queen::_fight3Data, GBVARS_GRAPHICSFIGHT3DATA_INDEX, Queen::BamScene::BamDataBlock, GBVARS_QUEEN) +_GEND + +_GRELEASE(Queen_Graphics) +_GRELEASEPTR(GBVARS_GRAPHICSCARDATA_INDEX, GBVARS_QUEEN) +_GRELEASEPTR(GBVARS_GRAPHICSFIGHT1DATA_INDEX, GBVARS_QUEEN) +_GRELEASEPTR(GBVARS_GRAPHICSFIGHT2DATA_INDEX, GBVARS_QUEEN) +_GRELEASEPTR(GBVARS_GRAPHICSFIGHT3DATA_INDEX, GBVARS_QUEEN) +_GEND + +#endif diff --git a/engines/queen/graphics.h b/engines/queen/graphics.h new file mode 100644 index 0000000000..b2899a5036 --- /dev/null +++ b/engines/queen/graphics.h @@ -0,0 +1,293 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENGRAPHICS_H +#define QUEENGRAPHICS_H + +#include "common/util.h" +#include "queen/structs.h" + +namespace Queen { + +struct BobSlot { + bool active; + //! current position + int16 x, y; + //! bounding box + Box box; + bool xflip; + //! shrinking percentage + uint16 scale; + //! associated BobFrame + uint16 frameNum; + //! 'direction' for the next frame (-1, 1) + int frameDir; + + //! animation stuff + bool animating; + struct { + int16 speed, speedBak; + + //! string based animation + struct { + const AnimFrame *buffer; + const AnimFrame *curPos; + } string; + + //! normal moving animation + struct { + bool rebound; + uint16 firstFrame, lastFrame; + } normal; + + } anim; + + bool moving; + //! moving speed + int16 speed; + //! move along x axis instead of y + bool xmajor; + //! moving direction + int8 xdir, ydir; + //! destination point + int16 endx, endy; + uint16 dx, dy; + uint16 total; + + void curPos(int16 xx, int16 yy); + void move(int16 dstx, int16 dsty, int16 spd); + void moveOneStep(); + void animOneStep(); + + void animString(const AnimFrame *animBuf); + void animNormal(uint16 firstFrame, uint16 lastFrame, uint16 speed, bool rebound, bool xflip); + + void scaleWalkSpeed(uint16 ms); + + void clear(); + + static const Box _defaultBox; +}; + +class QueenEngine; + +class Graphics { +public: + + Graphics(QueenEngine *vm); + ~Graphics(); + + //! unpacks control frames (ie. arrows) + void unpackControlBank(); + + //! setup dialog arrows + void setupArrows(); + + //! setup mouse cursor + void setupMouseCursor(); + + //! draw a bob + void drawBob(const BobSlot *bs, const BobFrame *bf, const Box *box, int16 x, int16 y); + + //! draw an inventory item + void drawInventoryItem(uint32 frameNum, uint16 x, uint16 y); + + //! draw a bob directly on the backdrop bitmap + void pasteBob(uint16 objNum, uint16 image); + + //! resize a bobframe + void shrinkFrame(const BobFrame *bf, uint16 percentage); + + //! animate/move bobs and sort them + void sortBobs(); + + //! draw all the sorted bobs + void drawBobs(); + + //! clear all setup bobs + void clearBobs(); + + //! stop all animating/movings bobs + void stopBobs(); + + //! returns a reference to the specified bob + BobSlot *bob(int index); + + //! display a text 'near' the specified bob + void setBobText(const BobSlot *bob, const char *text, int textX, int textY, int color, int flags); + + //! handles parallax scrolling for the specified room + void handleParallax(uint16 roomNum); + + void setupNewRoom(const char *room, uint16 roomNum, int16 *furniture, uint16 furnitureCount); + + void setBobCutawayAnim(uint16 bobNum, bool xflip, const AnimFrame *af, uint8 frameCount); + void fillAnimBuffer(const char *anim, AnimFrame *af); + uint16 countAnimFrames(const char *anim); + void setupObjectAnim(const GraphicData *gd, uint16 firstImage, uint16 bobNum, bool visible); + uint16 setupPersonAnim(const ActorData *ad, const char *anim, uint16 curImage); + void resetPersonAnim(uint16 bobNum); + void erasePersonAnim(uint16 bobNum); + void eraseAllAnims(); + + uint16 refreshObject(uint16 obj); + + void setupRoomFurniture(int16 *furniture, uint16 furnitureCount); + void setupRoomObjects(); + + uint16 setupPerson(uint16 noun, uint16 curImage); + uint16 allocPerson(uint16 noun, uint16 curImage); + + uint16 personFrames(uint16 bobNum) const { return _personFrames[bobNum]; } + void clearPersonFrames() { memset(_personFrames, 0, sizeof(_personFrames)); } + uint16 numFrames() const { return _numFrames; } + uint16 numStaticFurniture() const { return _numFurnitureStatic; } + uint16 numAnimatedFurniture() const { return _numFurnitureAnimated; } + uint16 numFurnitureFrames() const { return _numFurnitureStatic + _numFurnitureAnimatedLen; } + + void putCameraOnBob(int bobNum) { _cameraBob = bobNum; } + + void update(uint16 room); + + enum { + ARROW_BOB_UP = 62, + ARROW_BOB_DOWN = 63, + MAX_BOBS_NUMBER = 64, + MAX_STRING_LENGTH = 255, + MAX_STRING_SIZE = (MAX_STRING_LENGTH + 1), + BOB_SHRINK_BUF_SIZE = 60000 + }; + + +private: + + BobSlot _bobs[MAX_BOBS_NUMBER]; + + //! bobs to display + BobSlot *_sortedBobs[MAX_BOBS_NUMBER]; + + //! number of bobs to display + uint16 _sortedBobsCount; + + //! used to scale a BobFrame + BobFrame _shrinkBuffer; + + //! in-game objects/persons animations + AnimFrame _newAnim[17][30]; + + //! cutaway objects/persons animations + AnimFrame _cutAnim[21][30]; + + uint16 _personFrames[4]; + + //! number of animated furniture in current room + uint16 _numFurnitureAnimated; + + //! number of static furniture in current room + uint16 _numFurnitureStatic; + + //! total number of frames for the animated furniture + uint16 _numFurnitureAnimatedLen; + + //! current number of frames unpacked + uint16 _numFrames; + + //! bob number followed by camera + int _cameraBob; + + QueenEngine *_vm; + + static const Box _gameScreenBox; + static const Box _fullScreenBox; +}; + +class BamScene { +public: + + BamScene(QueenEngine *vm); + + void playSfx(); + void prepareAnimation(); + void updateCarAnimation(); + void updateFightAnimation(); + + void saveState(byte *&ptr); + void loadState(uint32 ver, byte *&ptr); + + enum { + BOB_OBJ1 = 5, + BOB_OBJ2 = 6, + BOB_FX = 7 + }; + + enum { + F_STOP = 0, + F_PLAY = 1, + F_REQ_STOP = 2 + }; + + enum { + SFX_SKIP = 8 + }; + + uint16 _flag, _index; + +private: + + struct BamDataObj { + int16 x, y; + int16 frame; + }; + +#ifdef PALMOS_68K +public: +#endif + struct BamDataBlock { + BamDataObj obj1; // truck / Frank + BamDataObj obj2; // Rico / robot + BamDataObj fx; + int16 sfx; + }; +#ifdef PALMOS_68K +private: +#endif + + BobSlot *_obj1; + BobSlot *_obj2; + BobSlot *_objfx; + bool _screenShaked; + const BamDataBlock *_fightData; + uint16 _lastSoundIndex; + + QueenEngine *_vm; + +#ifndef PALMOS_68K + static const BamDataBlock _carData[]; + static const BamDataBlock _fight1Data[]; + static const BamDataBlock _fight2Data[]; + static const BamDataBlock _fight3Data[]; +#endif +}; + +} // End of namespace Queen + +#endif diff --git a/engines/queen/grid.cpp b/engines/queen/grid.cpp new file mode 100644 index 0000000000..cb8ed4696b --- /dev/null +++ b/engines/queen/grid.cpp @@ -0,0 +1,255 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/grid.h" + +#include "queen/display.h" +#include "queen/logic.h" +#include "queen/queen.h" + +namespace Queen { + +Grid::Grid(QueenEngine *vm) + : _vm(vm) { + memset(_zones, 0, sizeof(_zones)); +} + +Grid::~Grid() { + delete[] _objMax; + delete[] _areaMax; + delete[] _area; + delete[] _objectBox; +} + +void Grid::readDataFrom(uint16 numObjects, uint16 numRooms, byte *&ptr) { + uint16 i, j; + + _numRoomAreas = numRooms; + + _objMax = new int16[_numRoomAreas + 1]; + _areaMax = new int16[_numRoomAreas + 1]; + _area = new Area[_numRoomAreas + 1][MAX_AREAS_NUMBER]; + + _objMax[0] = 0; + _areaMax[0] = 0; + memset(&_area[0], 0, sizeof(Area) * MAX_AREAS_NUMBER); + for (i = 1; i <= _numRoomAreas; i++) { + _objMax[i] = (int16)READ_BE_INT16(ptr); ptr += 2; + _areaMax[i] = (int16)READ_BE_INT16(ptr); ptr += 2; + memset(&_area[i][0], 0, sizeof(Area)); + for (j = 1; j <= _areaMax[i]; j++) { + assert(j < MAX_AREAS_NUMBER); + _area[i][j].readFromBE(ptr); + } + } + + _objectBox = new Box[numObjects + 1]; + memset(&_objectBox[0], 0, sizeof(Box)); + for (i = 1; i <= numObjects; i++) { + _objectBox[i].readFromBE(ptr); + } +} + +void Grid::setZone(GridScreen screen, uint16 zoneNum, uint16 x1, uint16 y1, uint16 x2, uint16 y2) { + debug(9, "Grid::setZone(%d, %d, (%d,%d), (%d,%d))", screen, zoneNum, x1, y1, x2, y2); + ZoneSlot *pzs = &_zones[screen][zoneNum]; + pzs->valid = true; + pzs->box.x1 = x1; + pzs->box.y1 = y1; + pzs->box.x2 = x2; + pzs->box.y2 = y2; +} + +void Grid::setZone(GridScreen screen, uint16 zoneNum, const Box &box) { + debug(9, "Grid::setZone(%d, %d, (%d,%d), (%d,%d))", screen, zoneNum, box.x1, box.y1, box.x2, box.y2); + ZoneSlot *pzs = &_zones[screen][zoneNum]; + pzs->valid = true; + pzs->box = box; +} + +uint16 Grid::findZoneForPos(GridScreen screen, uint16 x, uint16 y) const { + debug(9, "Logic::findZoneForPos(%d, (%d,%d))", screen, x, y); + int i; + if (screen == GS_PANEL) { + y -= ROOM_ZONE_HEIGHT; + } + for (i = 1; i < MAX_ZONES_NUMBER; ++i) { + const ZoneSlot *pzs = &_zones[screen][i]; + if (pzs->valid && pzs->box.contains(x, y)) { + return i; + } + } + return 0; +} + +uint16 Grid::findAreaForPos(GridScreen screen, uint16 x, uint16 y) const { + uint16 room = _vm->logic()->currentRoom(); + uint16 zoneNum = findZoneForPos(screen, x, y); + if (zoneNum <= _objMax[room]) { + zoneNum = 0; + } else { + zoneNum -= _objMax[room]; + } + return zoneNum; +} + +void Grid::clear(GridScreen screen) { + debug(9, "Grid::clear(%d)", screen); + for (int i = 1; i < MAX_ZONES_NUMBER; ++i) { + _zones[screen][i].valid = false; + } +} + +void Grid::setupNewRoom(uint16 room, uint16 firstRoomObjNum) { + debug(9, "Grid::setupNewRoom()"); + clear(GS_ROOM); + + uint16 i; + uint16 zoneNum; + + // setup objects zones + uint16 maxObjRoom = _objMax[room]; + zoneNum = 1; + for (i = firstRoomObjNum + 1; i <= firstRoomObjNum + maxObjRoom; ++i) { + if (_vm->logic()->objectData(i)->name != 0) { + setZone(GS_ROOM, zoneNum, _objectBox[i]); + } + ++zoneNum; + } + + // setup room zones (areas) + uint16 maxAreaRoom = _areaMax[room]; + for (zoneNum = 1; zoneNum <= maxAreaRoom; ++zoneNum) { + setZone(GS_ROOM, maxObjRoom + zoneNum, _area[room][zoneNum].box); + } +} + +void Grid::setupPanel() { + for (int i = 0; i <= 7; ++i) { + uint16 x = i * 20; + setZone(GS_PANEL, i + 1, x, 10, x + 19, 49); + } + + // inventory scrolls + setZone(GS_PANEL, 9, 160, 10, 179, 29); + setZone(GS_PANEL, 10, 160, 30, 179, 49); + + // inventory items + setZone(GS_PANEL, 11, 180, 10, 213, 49); + setZone(GS_PANEL, 12, 214, 10, 249, 49); + setZone(GS_PANEL, 13, 250, 10, 284, 49); + setZone(GS_PANEL, 14, 285, 10, 320, 49); +} + +void Grid::drawZones() { + for (int i = 1; i < MAX_ZONES_NUMBER; ++i) { + const ZoneSlot *pzs = &_zones[GS_ROOM][i]; + if (pzs->valid) { + const Box *b = &pzs->box; + _vm->display()->drawBox(b->x1, b->y1, b->x2, b->y2, 3); + } + } +} + +const Box *Grid::zone(GridScreen screen, uint16 index) const { + const ZoneSlot *zs = &_zones[screen][index]; + assert(zs->valid); + return &zs->box; +} + +Verb Grid::findVerbUnderCursor(int16 cursorx, int16 cursory) const { + static const Verb pv[] = { + VERB_NONE, + VERB_OPEN, + VERB_CLOSE, + VERB_MOVE, + VERB_GIVE, + VERB_LOOK_AT, + VERB_PICK_UP, + VERB_TALK_TO, + VERB_USE, + VERB_SCROLL_UP, + VERB_SCROLL_DOWN, + VERB_INV_1, + VERB_INV_2, + VERB_INV_3, + VERB_INV_4, + }; + return pv[findZoneForPos(GS_PANEL, cursorx, cursory)]; +} + +uint16 Grid::findObjectUnderCursor(int16 cursorx, int16 cursory) const { + uint16 roomObj = 0; + if (cursory < ROOM_ZONE_HEIGHT) { + int16 x = cursorx + _vm->display()->horizontalScroll(); + roomObj = findZoneForPos(GS_ROOM, x, cursory); + } + return roomObj; +} + +uint16 Grid::findObjectNumber(uint16 zoneNum) const { + // l.316-327 select.c + uint16 room = _vm->logic()->currentRoom(); + uint16 obj = zoneNum; + uint16 objectMax = _objMax[room]; + debug(9, "Grid::findObjectNumber(%X, %X)", zoneNum, objectMax); + if (zoneNum > objectMax) { + // this is an area box, check for associated object + obj = _area[room][zoneNum - objectMax].object; + if (obj != 0) { + // there is an object, get its number + obj -= _vm->logic()->currentRoomData(); + } + } + return obj; +} + +uint16 Grid::findScale(uint16 x, uint16 y) const { + uint16 room = _vm->logic()->currentRoom(); + uint16 scale = 100; + uint16 areaNum = findAreaForPos(GS_ROOM, x, y); + if (areaNum != 0) { + scale = _area[room][areaNum].calcScale(y); + } + return scale; +} + +void Grid::saveState(byte *&ptr) { + uint16 i, j; + for (i = 1; i <= _numRoomAreas; ++i) { + for (j = 1; j <= _areaMax[i]; ++j) { + _area[i][j].writeToBE(ptr); + } + } +} + +void Grid::loadState(uint32 ver, byte *&ptr) { + uint16 i, j; + for (i = 1; i <= _numRoomAreas; ++i) { + for (j = 1; j <= _areaMax[i]; ++j) { + _area[i][j].readFromBE(ptr); + } + } +} + +} // End of namespace Queen diff --git a/engines/queen/grid.h b/engines/queen/grid.h new file mode 100644 index 0000000000..d6e148eec2 --- /dev/null +++ b/engines/queen/grid.h @@ -0,0 +1,136 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENGRID_H +#define QUEENGRID_H + +#include "common/util.h" +#include "queen/structs.h" + +namespace Queen { + +enum GridScreen { + GS_ROOM = 0, + GS_PANEL = 1, + GS_COUNT = 2 +}; + +class QueenEngine; + +class Grid { +public: + + Grid(QueenEngine *vm); + ~Grid(); + + //! read areas data from specified stream + void readDataFrom(uint16 numObjects, uint16 numRooms, byte *&ptr); + + //! defines a new zone + void setZone(GridScreen screen, uint16 zoneNum, uint16 x1, uint16 y1, uint16 x2, uint16 y2); + + //! defines a new zone + void setZone(GridScreen screen, uint16 zoneNum, const Box &box); + + //! find the zone number containing the specified point + uint16 findZoneForPos(GridScreen screen, uint16 x, uint16 y) const; + + //! find the area number containing the specified point + uint16 findAreaForPos(GridScreen screen, uint16 x, uint16 y) const; + + //! clear the zones for current room + void clear(GridScreen screen); + + //! setup objects zones for the specified room + void setupNewRoom(uint16 room, uint16 firstRoomObjNum); + + //! setup panel zones + void setupPanel(); + + //! draw the zones for current room (debug only) + void drawZones(); + + //! retuns a reference to the specified zone + const Box *zone(GridScreen screen, uint16 index) const; + + //! get the verb for the specified cursor position + Verb findVerbUnderCursor(int16 cursorx, int16 cursory) const; + + //! get the object for the specified cursor position + uint16 findObjectUnderCursor(int16 cursorx, int16 cursory) const; + + //! get the object for the specified zone number + uint16 findObjectNumber(uint16 zoneNum) const; + + //! get scale for the specified position + uint16 findScale(uint16 x, uint16 y) const; + + //! returns a reference to the specfied room area + Area *area(int room, int num) const { return &_area[room][num]; } + + //! returns the number of areas in this room + uint16 areaMax(int room) const { return _areaMax[room]; } + + //! returns the number of objects in this room + uint16 objMax(int room) const { return _objMax[room]; } + + void saveState(byte *&ptr); + void loadState(uint32 ver, byte *&ptr); + + enum { + MAX_ZONES_NUMBER = 32, + MAX_AREAS_NUMBER = 11 + }; + + +private: + + struct ZoneSlot { + bool valid; + Box box; + }; + + //! current room zones + ZoneSlot _zones[GS_COUNT][MAX_ZONES_NUMBER]; + + //! number of objects for each room + int16 *_objMax; + + //! number of areas for each room + int16 *_areaMax; + + //! areas for each room + Area (*_area)[MAX_AREAS_NUMBER]; + + //! total number of room areas + uint16 _numRoomAreas; + + //! box/zone for each objects + Box *_objectBox; + + QueenEngine *_vm; +}; + + +} // End of namespace Queen + +#endif diff --git a/engines/queen/input.cpp b/engines/queen/input.cpp new file mode 100644 index 0000000000..7ae2e2abce --- /dev/null +++ b/engines/queen/input.cpp @@ -0,0 +1,213 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "common/system.h" + +#include "queen/input.h" + +namespace Queen { + +const char *Input::_commandKeys[LANGUAGE_COUNT] = { + "ocmglptu", // English + "osbgpnre", // German + "ofdnepau", // French + "acsdgpqu", // Italian + "ocmglptu", // Hebrew + "acodmthu" // Spanish +}; + +const Verb Input::_verbKeys[8] = { + VERB_OPEN, + VERB_CLOSE, + VERB_MOVE, + VERB_GIVE, + VERB_LOOK_AT, + VERB_PICK_UP, + VERB_TALK_TO, + VERB_USE +}; + +Input::Input(Language language, OSystem *system) : + _system(system), _fastMode(false), _keyVerb(VERB_NONE), + _cutawayRunning(false), _canQuit(false), _cutawayQuit(false), + _dialogueRunning(false), _talkQuit(false), _quickSave(false), + _quickLoad(false), _debugger(false), _inKey(0), _mouse_x(0), + _mouse_y(0), _mouseButton(0), _idleTime(0) { + + switch (language) { + case ENGLISH: + _currentCommandKeys = _commandKeys[0]; + break; + case GERMAN: + _currentCommandKeys = _commandKeys[1]; + break; + case FRENCH: + _currentCommandKeys = _commandKeys[2]; + break; + case ITALIAN: + _currentCommandKeys = _commandKeys[3]; + break; + case HEBREW: + _currentCommandKeys = _commandKeys[4]; + break; + case SPANISH: + _currentCommandKeys = _commandKeys[5]; + break; + default: + error("Unknown language"); + break; + } +} + +void Input::delay() { + delay(_fastMode ? DELAY_SHORT : DELAY_NORMAL); +} + +void Input::delay(uint amount) { + if (_idleTime < DELAY_SCREEN_BLANKER) { + _idleTime += amount; + } + uint32 end = _system->getMillis() + amount; + do { + OSystem::Event event; + while (_system->pollEvent(event)) { + _idleTime = 0; + switch (event.type) { + case OSystem::EVENT_KEYDOWN: + if (event.kbd.flags == OSystem::KBD_CTRL) { + if (event.kbd.keycode == 'd') { + _debugger = true; + } else if (event.kbd.keycode == 'f') { + _fastMode = !_fastMode; + } + } else { + _inKey = event.kbd.keycode; + } + break; + + case OSystem::EVENT_MOUSEMOVE: + _mouse_x = event.mouse.x; + _mouse_y = event.mouse.y; + break; + + case OSystem::EVENT_LBUTTONDOWN: + _mouseButton |= MOUSE_LBUTTON; + _mouse_x = event.mouse.x; + _mouse_y = event.mouse.y; + break; + + case OSystem::EVENT_RBUTTONDOWN: + _mouseButton |= MOUSE_RBUTTON; + _mouse_x = event.mouse.x; + _mouse_y = event.mouse.y; + break; + + case OSystem::EVENT_QUIT: + _system->quit(); + break; + + default: + break; + } + } + + if (amount == 0) + break; + + _system->delayMillis((amount > 20) ? 20 : amount); + } while (_system->getMillis() < end); +} + +int Input::checkKeys() { + + if (_inKey) + debug(6, "[Input::checkKeys] _inKey = %i", _inKey); + + switch (_inKey) { + case KEY_SPACE: + _keyVerb = VERB_SKIP_TEXT; + break; + case KEY_COMMA: + _keyVerb = VERB_SCROLL_UP; + break; + case KEY_DOT: + _keyVerb = VERB_SCROLL_DOWN; + break; + case KEY_DIGIT_1: + _keyVerb = VERB_DIGIT_1; + break; + case KEY_DIGIT_2: + _keyVerb = VERB_DIGIT_2; + break; + case KEY_DIGIT_3: + _keyVerb = VERB_DIGIT_3; + break; + case KEY_DIGIT_4: + _keyVerb = VERB_DIGIT_4; + break; + case KEY_ESCAPE: // slip cutaway / dialogue + if (_canQuit) { + if (_cutawayRunning) { + debug(6, "[Input::checkKeys] Setting _cutawayQuit to true!"); + _cutawayQuit = true; + } + if (_dialogueRunning) + _talkQuit = true; + } + break; + case KEY_F1: // use Journal + case KEY_F5: + if (_cutawayRunning) { + if (_canQuit) { + _keyVerb = VERB_USE_JOURNAL; + _cutawayQuit = _talkQuit = true; + } + } else { + _keyVerb = VERB_USE_JOURNAL; + if (_canQuit) + _talkQuit = true; + } + break; + case KEY_F11: // quicksave + _quickSave = true; + break; + case KEY_F12: // quickload + _quickLoad = true; + break; + default: + for (int i = 0; i < ARRAYSIZE(_verbKeys); ++i) { + if (_inKey == _currentCommandKeys[i]) { + _keyVerb = _verbKeys[i]; + break; + } + } + break; + } + + int inKey = _inKey; + _inKey = 0; // reset + return inKey; +} + + +} // End of namespace Queen diff --git a/engines/queen/input.h b/engines/queen/input.h new file mode 100644 index 0000000000..d1e86016c9 --- /dev/null +++ b/engines/queen/input.h @@ -0,0 +1,178 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef INPUT_H +#define INPUT_H + +#include "common/util.h" +#include "queen/defs.h" + +class OSystem; + +namespace Queen { + +class Input { +public: + + //! Adjust here to change delays! + enum { + DELAY_SHORT = 10, + DELAY_NORMAL = 100, + DELAY_SCREEN_BLANKER = 5 * 60 * 1000 + }; + enum { + MOUSE_LBUTTON = 1, + MOUSE_RBUTTON = 2 + }; + + Input(Language language, OSystem *system); + + //! calls the other delay() with a value adjusted depending on _fastMode + void delay(); + + //! moved QueenEngine::delay() here + void delay(uint amount); + + //! convert input to verb + int checkKeys(); + + //! use instead of KEYVERB=0 + void clearKeyVerb() { _keyVerb = VERB_NONE; } + + void canQuit(bool cq) { _canQuit = cq; } + + bool cutawayRunning() const { return _cutawayRunning; } + void cutawayRunning(bool running) { _cutawayRunning = running; } + + bool cutawayQuit() const { return _cutawayQuit; } + void cutawayQuitReset() { _cutawayQuit = false; } + + void dialogueRunning(bool running) { _dialogueRunning = running; } + + bool talkQuit() const { return _talkQuit; } + void talkQuitReset() { _talkQuit = false; } + + bool quickSave() const { return _quickSave; } + void quickSaveReset() { _quickSave = false; } + bool quickLoad() const { return _quickLoad; } + void quickLoadReset() { _quickLoad = false; } + bool debugger() const { return _debugger; } + void debuggerReset() { _debugger = false; } + + bool fastMode() const { return _fastMode; } + void fastMode(bool fm) { _fastMode = fm; } + + Verb keyVerb() const { return _keyVerb; } + + int mousePosX() const { return _mouse_x; } + int mousePosY() const { return _mouse_y; } + + int mouseButton() const { return _mouseButton; } + void clearMouseButton() { _mouseButton = 0; } + + //! returns user idle time (used by Display, to trigger the screensaver) + uint32 idleTime() const { return _idleTime; } + +private: + + enum KeyCode { + KEY_SPACE = ' ', + KEY_COMMA = ',', + KEY_DOT = '.', + + KEY_DIGIT_1 = '1', + KEY_DIGIT_2 = '2', + KEY_DIGIT_3 = '3', + KEY_DIGIT_4 = '4', + + KEY_ESCAPE = 27, + KEY_RETURN = 13, + KEY_BACKSPACE = 8, + + KEY_F1 = 282, + KEY_F11 = KEY_F1 + 10, + KEY_F5 = KEY_F1 + 4, + KEY_F12 + }; + + enum { + LANGUAGE_COUNT = 6 + }; + + //! used to get keyboard and mouse events + OSystem *_system; + + //! some cutaways require update() run faster + bool _fastMode; + + //! the current verb received from keyboard + Verb _keyVerb; + + //! set if a cutaway is running + bool _cutawayRunning; + + //! set this if we can quit + bool _canQuit; + + //! moved Cutaway::_quit here + bool _cutawayQuit; + + //! set if a dialogue is running + bool _dialogueRunning; + + //! moved Talk::_quit here + bool _talkQuit; + + //! set if quicksave requested + bool _quickSave; + + //! set if quickload requested + bool _quickLoad; + + //! set if debugger requested + bool _debugger; + + //! set by delay(); + int _inKey; + + //! set by delay(); + int _mouse_x, _mouse_y; + + //! set by delay(); + int _mouseButton; + + //! user idle time + uint32 _idleTime; + + //! command keys for current language + const char *_currentCommandKeys; + + //! command keys for all languages + static const char *_commandKeys[LANGUAGE_COUNT]; + + //! verbs matching the command keys + static const Verb _verbKeys[8]; +}; + +} // End of namespace Queen + +#endif diff --git a/engines/queen/journal.cpp b/engines/queen/journal.cpp new file mode 100644 index 0000000000..0aeb71e083 --- /dev/null +++ b/engines/queen/journal.cpp @@ -0,0 +1,587 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "common/system.h" +#include "queen/journal.h" + +#include "queen/bankman.h" +#include "queen/display.h" +#include "queen/graphics.h" +#include "queen/grid.h" +#include "queen/logic.h" +#include "queen/music.h" +#include "queen/queen.h" +#include "queen/resource.h" +#include "queen/sound.h" + +namespace Queen { + +Journal::Journal(QueenEngine *vm) + : _vm(vm) { + _currentSavePage = 0; + _currentSaveSlot = 0; +} + +void Journal::use() { + BobSlot *joe = _vm->graphics()->bob(0); + _prevJoeX = joe->x; + _prevJoeY = joe->y; + + _panelMode = PM_NORMAL; + _system = &OSystem::instance(); + + _panelTextCount = 0; + memset(_panelTextY, 0, sizeof(_panelTextY)); + memset(&_textField, 0, sizeof(_textField)); + + memset(_saveDescriptions, 0, sizeof(_saveDescriptions)); + _vm->findGameStateDescriptions(_saveDescriptions); + + setup(); + redraw(); + update(); + _vm->display()->palFadeIn(ROOM_JOURNAL); + + _quitMode = QM_LOOP; + while (_quitMode == QM_LOOP) { + OSystem::Event event; + while (_system->pollEvent(event)) { + switch (event.type) { + case OSystem::EVENT_KEYDOWN: + handleKeyDown(event.kbd.ascii, event.kbd.keycode); + break; + case OSystem::EVENT_LBUTTONDOWN: + handleMouseDown(event.mouse.x, event.mouse.y); + break; + case OSystem::EVENT_WHEELUP: + handleMouseWheel(-1); + break; + case OSystem::EVENT_WHEELDOWN: + handleMouseWheel(1); + break; + case OSystem::EVENT_QUIT: + _system->quit(); + break; + default: + break; + } + } + _system->delayMillis(20); + _system->updateScreen(); + } + + _vm->writeOptionSettings(); + + _vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1); + _vm->graphics()->putCameraOnBob(0); + if (_quitMode == QM_CONTINUE) { + continueGame(); + } +} + +void Journal::continueGame() { + _vm->display()->fullscreen(false); + _vm->display()->forceFullRefresh(); + + _vm->logic()->joePos(_prevJoeX, _prevJoeY); + _vm->logic()->joeCutFacing(_vm->logic()->joeFacing()); + + _vm->logic()->oldRoom(_vm->logic()->currentRoom()); + _vm->logic()->displayRoom(_vm->logic()->currentRoom(), RDM_FADE_JOE, 0, 0, false); +} + +void Journal::setup() { + _vm->display()->palFadeOut(_vm->logic()->currentRoom()); + _vm->display()->horizontalScroll(0); + _vm->display()->fullscreen(true); + _vm->graphics()->clearBobs(); + _vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1); + _vm->bankMan()->eraseFrames(false); + _vm->display()->textCurrentColor(INK_JOURNAL); + + _vm->grid()->clear(GS_ROOM); + for (int i = 0; i < MAX_ZONES; ++i) { + const Zone *zn = &_zones[i]; + _vm->grid()->setZone(GS_ROOM, zn->num, zn->x1, zn->y1, zn->x2, zn->y2); + } + + _vm->display()->setupNewRoom("journal", ROOM_JOURNAL); + _vm->bankMan()->load("journal.BBK", JOURNAL_BANK); + for (int f = 1; f <= 20; ++f) { + int frameNum = JOURNAL_FRAMES + f; + _vm->bankMan()->unpack(f, frameNum, JOURNAL_BANK); + BobFrame *bf = _vm->bankMan()->fetchFrame(frameNum); + bf->xhotspot = 0; + bf->yhotspot = 0; + if (f == FRAME_INFO_BOX) { // adjust info box hot spot to put it always on top + bf->yhotspot = 200; + } + } + _vm->bankMan()->close(JOURNAL_BANK); + + _textField.x = 136; + _textField.y = 9; + _textField.w = 146; + _textField.h = 13; +} + +void Journal::redraw() { + drawNormalPanel(); + drawConfigPanel(); + drawSaveDescriptions(); + drawSaveSlot(); +} + +void Journal::update() { + _vm->graphics()->sortBobs(); + _vm->display()->prepareUpdate(); + _vm->graphics()->drawBobs(); + if (_textField.enabled) { + int16 x = _textField.x + _textField.posCursor; + int16 y = _textField.y + _currentSaveSlot * _textField.h + 8; + _vm->display()->drawBox(x, y, x + 6, y, INK_JOURNAL); + } + _vm->display()->forceFullRefresh(); + _vm->display()->update(); + _system->updateScreen(); +} + +void Journal::showBob(int bobNum, int16 x, int16 y, int frameNum) { + BobSlot *bob = _vm->graphics()->bob(bobNum); + bob->curPos(x, y); + bob->frameNum = JOURNAL_FRAMES + frameNum; +} + +void Journal::hideBob(int bobNum) { + _vm->graphics()->bob(bobNum)->active = false; +} + +void Journal::drawSaveDescriptions() { + for (int i = 0; i < NUM_SAVES_PER_PAGE; ++i) { + int n = _currentSavePage * 10 + i; + char nb[4]; + sprintf(nb, "%d", n + 1); + int y = _textField.y + i * _textField.h; + _vm->display()->setText(_textField.x, y, _saveDescriptions[n], false); + _vm->display()->setText(_textField.x - 27, y + 1, nb, false); + } + // highlight current page + showBob(BOB_SAVE_PAGE, 300, 3 + _currentSavePage * 15, 6 + _currentSavePage); +} + +void Journal::drawSaveSlot() { + showBob(BOB_SAVE_DESC, 130, 6 + _currentSaveSlot * 13, 17); +} + +void Journal::enterYesNoPanelMode(int16 prevZoneNum, int titleNum) { + _panelMode = PM_YES_NO; + _prevZoneNum = prevZoneNum; + drawYesNoPanel(titleNum); +} + +void Journal::exitYesNoPanelMode() { + _panelMode = PM_NORMAL; + if (_prevZoneNum == ZN_MAKE_ENTRY) { + closeTextField(); + } + redraw(); +} + +void Journal::enterInfoPanelMode() { + _panelMode = PM_INFO_BOX; + _vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1); + drawInfoPanel(); +} + +void Journal::exitInfoPanelMode() { + _vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1); + hideBob(BOB_INFO_BOX); + redraw(); + _panelMode = PM_NORMAL; +} + +void Journal::handleKeyDown(uint16 ascii, int keycode) { + switch (_panelMode) { + case PM_INFO_BOX: + break; + case PM_YES_NO: + if (keycode == 27) { + exitYesNoPanelMode(); + } else if (_textField.enabled) { + updateTextField(ascii, keycode); + } + break; + case PM_NORMAL: + if (keycode == 27) { + _quitMode = QM_CONTINUE; + } + break; + } +} + +void Journal::handleMouseWheel(int inc) { + if (_panelMode == PM_NORMAL) { + int curSave = _currentSavePage * NUM_SAVES_PER_PAGE + _currentSaveSlot + inc; + if (curSave >= 0 && curSave < NUM_SAVES_PER_PAGE * 10) { + _currentSavePage = curSave / NUM_SAVES_PER_PAGE; + _currentSaveSlot = curSave % NUM_SAVES_PER_PAGE; + drawSaveDescriptions(); + drawSaveSlot(); + update(); + } + } +} + +void Journal::handleMouseDown(int x, int y) { + int val; + int16 zoneNum = _vm->grid()->findZoneForPos(GS_ROOM, x, y); + switch (_panelMode) { + case PM_INFO_BOX: + exitInfoPanelMode(); + break; + case PM_YES_NO: + if (zoneNum == ZN_YES) { + _panelMode = PM_NORMAL; + int currentSlot = _currentSavePage * 10 + _currentSaveSlot; + switch (_prevZoneNum) { + case ZN_REVIEW_ENTRY: + if (_saveDescriptions[currentSlot][0]) { + _vm->graphics()->clearBobs(); + _vm->display()->palFadeOut(ROOM_JOURNAL); + _vm->music()->stopSong(); + _vm->loadGameState(currentSlot); + _vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1); + _quitMode = QM_RESTORE; + } else { + exitYesNoPanelMode(); + } + break; + case ZN_MAKE_ENTRY: + if (_textField.text[0]) { + closeTextField(); + _vm->saveGameState(currentSlot, _textField.text); + _quitMode = QM_CONTINUE; + } else { + exitYesNoPanelMode(); + } + break; + case ZN_GIVEUP: + _quitMode = QM_CONTINUE; + _vm->quitGame(); + break; + } + } else if (zoneNum == ZN_NO) { + exitYesNoPanelMode(); + } + break; + case PM_NORMAL: + switch (zoneNum) { + case ZN_REVIEW_ENTRY: + enterYesNoPanelMode(zoneNum, TXT_REVIEW_ENTRY); + break; + case ZN_MAKE_ENTRY: + initTextField(_saveDescriptions[_currentSavePage * 10 + _currentSaveSlot]); + enterYesNoPanelMode(zoneNum, TXT_MAKE_ENTRY); + break; + case ZN_CLOSE: + _quitMode = QM_CONTINUE; + break; + case ZN_GIVEUP: + enterYesNoPanelMode(zoneNum, TXT_GIVE_UP); + break; + case ZN_TEXT_SPEED: + val = (x - 136) * QueenEngine::MAX_TEXT_SPEED / (266 - 136); + _vm->talkSpeed(val); + drawConfigPanel(); + break; + case ZN_SFX_TOGGLE: + _vm->sound()->toggleSfx(); + drawConfigPanel(); + break; + case ZN_MUSIC_VOLUME: + val = (x - 136) * QueenEngine::MAX_MUSIC_VOLUME / (266 - 136); + _vm->music()->setVolume(val); + drawConfigPanel(); + break; + case ZN_DESC_1: + case ZN_DESC_2: + case ZN_DESC_3: + case ZN_DESC_4: + case ZN_DESC_5: + case ZN_DESC_6: + case ZN_DESC_7: + case ZN_DESC_8: + case ZN_DESC_9: + case ZN_DESC_10: + _currentSaveSlot = zoneNum - ZN_DESC_1; + drawSaveSlot(); + break; + case ZN_PAGE_A: + case ZN_PAGE_B: + case ZN_PAGE_C: + case ZN_PAGE_D: + case ZN_PAGE_E: + case ZN_PAGE_F: + case ZN_PAGE_G: + case ZN_PAGE_H: + case ZN_PAGE_I: + case ZN_PAGE_J: + _currentSavePage = zoneNum - ZN_PAGE_A; + drawSaveDescriptions(); + break; + case ZN_INFO_BOX: + enterInfoPanelMode(); + break; + case ZN_MUSIC_TOGGLE: + _vm->sound()->toggleMusic(); + if (_vm->sound()->musicOn()) { + _vm->sound()->playLastSong(); + } else { + _vm->music()->stopSong(); + } + drawConfigPanel(); + break; + case ZN_VOICE_TOGGLE: + _vm->sound()->toggleSpeech(); + drawConfigPanel(); + break; + case ZN_TEXT_TOGGLE: + _vm->subtitles(!_vm->subtitles()); + drawConfigPanel(); + break; + } + break; + } + update(); +} + +void Journal::drawPanelText(int y, const char *text) { + debug(7, "Journal::drawPanelText(%d, '%s')", y, text); + char s[80]; + strcpy(s, text); + char *p = strchr(s, ' '); + if (p == NULL) { + int x = (128 - _vm->display()->textWidth(s)) / 2; + _vm->display()->setText(x, y, s, false); + assert(_panelTextCount < MAX_PANEL_TEXTS); + _panelTextY[_panelTextCount++] = y; + } else { + *p++ = '\0'; + if (_vm->resource()->getLanguage() == HEBREW) { + drawPanelText(y - 5, p); + drawPanelText(y + 5, s); + } else { + drawPanelText(y - 5, s); + drawPanelText(y + 5, p); + } + } +} + +void Journal::drawCheckBox(bool active, int bobNum, int16 x, int16 y, int frameNum) { + if (active) { + showBob(bobNum, x, y, frameNum); + } else { + hideBob(bobNum); + } +} + +void Journal::drawSlideBar(int value, int maxValue, int bobNum, int16 y, int frameNum) { + showBob(bobNum, 136 + value * (266 - 136) / maxValue, y, frameNum); +} + +void Journal::drawPanel(const int *frames, const int *titles, int n) { + for (int i = 0; i < _panelTextCount; ++i) { + _vm->display()->clearTexts(_panelTextY[i], _panelTextY[i]); + } + _panelTextCount = 0; + int bobNum = 1; + int y = 8; + while (n--) { + showBob(bobNum++, 32, y, *frames++); + drawPanelText(y + 12, _vm->logic()->joeResponse(*titles++)); + y += 48; + } +} + +void Journal::drawNormalPanel() { + static const int frames[] = { FRAME_BLUE_1, FRAME_BLUE_2, FRAME_BLUE_1, FRAME_ORANGE }; + static const int titles[] = { TXT_REVIEW_ENTRY, TXT_MAKE_ENTRY, TXT_CLOSE, TXT_GIVE_UP }; + drawPanel(frames, titles, 4); +} + +void Journal::drawYesNoPanel(int titleNum) { + static const int frames[] = { FRAME_GREY, FRAME_BLUE_1, FRAME_BLUE_2 }; + const int titles[] = { titleNum, TXT_YES, TXT_NO }; + drawPanel(frames, titles, 3); + + hideBob(BOB_LEFT_RECT_4); + hideBob(BOB_TALK_SPEED); + hideBob(BOB_SFX_TOGGLE); + hideBob(BOB_MUSIC_VOLUME); + hideBob(BOB_SPEECH_TOGGLE); + hideBob(BOB_TEXT_TOGGLE); + hideBob(BOB_MUSIC_TOGGLE); +} + +void Journal::drawConfigPanel() { + _vm->checkOptionSettings(); + + drawSlideBar(_vm->talkSpeed(), QueenEngine::MAX_TEXT_SPEED, BOB_TALK_SPEED, 164, FRAME_BLUE_PIN); + drawSlideBar(_vm->music()->volume(), QueenEngine::MAX_MUSIC_VOLUME, BOB_MUSIC_VOLUME, 177, FRAME_GREEN_PIN); + + drawCheckBox(_vm->sound()->sfxOn(), BOB_SFX_TOGGLE, 221, 155, FRAME_CHECK_BOX); + drawCheckBox(_vm->sound()->speechOn(), BOB_SPEECH_TOGGLE, 158, 155, FRAME_CHECK_BOX); + drawCheckBox(_vm->subtitles(), BOB_TEXT_TOGGLE, 125, 167, FRAME_CHECK_BOX); + drawCheckBox(_vm->sound()->musicOn(), BOB_MUSIC_TOGGLE, 125, 181, FRAME_CHECK_BOX); +} + +void Journal::drawInfoPanel() { + showBob(BOB_INFO_BOX, 72, 221, FRAME_INFO_BOX); + const char *ver = _vm->resource()->JASVersion(); + switch (ver[0]) { + case 'P': + _vm->display()->setTextCentered(132, "PC Hard Drive", false); + break; + case 'C': + _vm->display()->setTextCentered(132, "PC CD-ROM", false); + break; + case 'a': + _vm->display()->setTextCentered(132, "Amiga A500/600", false); + break; + case 'A': + _vm->display()->setTextCentered(132, "Amiga A1200", false); + break; + case 'c': + _vm->display()->setTextCentered(132, "Amiga CD-32", false); + break; + } + switch (ver[1]) { + case 'E': + _vm->display()->setTextCentered(144, "English", false); + break; + case 'F' : + _vm->display()->setTextCentered(144, "Fran\x87""ais", false); + break; + case 'G': + _vm->display()->setTextCentered(144, "Deutsch", false); + break; + case 'H': + _vm->display()->setTextCentered(144, "Hebrew", false); + break; + case 'I': + _vm->display()->setTextCentered(144, "Italiano", false); + break; + case 'S': + _vm->display()->setTextCentered(144, "Espa\xA4""ol", false); + break; + } + char versionId[13]; + sprintf(versionId, "Version %c.%c%c", ver[2], ver[3], ver[4]); + _vm->display()->setTextCentered(156, versionId, false); +} + +void Journal::initTextField(const char *desc) { + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true); + _textField.enabled = true; + _textField.posCursor = _vm->display()->textWidth(desc); + _textField.textCharsCount = strlen(desc); + memset(_textField.text, 0, sizeof(_textField.text)); + strcpy(_textField.text, desc); +} + +void Journal::updateTextField(uint16 ascii, int keycode) { + bool dirty = false; + switch (keycode) { + case 8: // backspace + if (_textField.textCharsCount > 0) { + --_textField.textCharsCount; + _textField.text[_textField.textCharsCount] = '\0'; + dirty = true; + } + break; + case '\n': + case '\r': + if (_textField.text[0]) { + closeTextField(); + int currentSlot = _currentSavePage * 10 + _currentSaveSlot; + _vm->saveGameState(currentSlot, _textField.text); + _quitMode = QM_CONTINUE; + } + break; + default: + if (isprint((char)ascii) && + _textField.textCharsCount < (sizeof(_textField.text) - 1) && + _vm->display()->textWidth(_textField.text) < _textField.w) { + _textField.text[_textField.textCharsCount] = (char)ascii; + ++_textField.textCharsCount; + dirty = true; + } + break; + } + if (dirty) { + _vm->display()->setText(_textField.x, _textField.y + _currentSaveSlot * _textField.h, _textField.text, false); + _textField.posCursor = _vm->display()->textWidth(_textField.text); + update(); + } +} + +void Journal::closeTextField() { + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false); + _textField.enabled = false; +} + +const Journal::Zone Journal::_zones[] = { + { ZN_REVIEW_ENTRY, 32, 8, 96, 40 }, + { ZN_MAKE_ENTRY, 32, 56, 96, 88 }, // == ZN_YES + { ZN_CLOSE, 32, 104, 96, 136 }, // == ZN_NO + { ZN_GIVEUP, 32, 152, 96, 184 }, + { ZN_TEXT_SPEED, 136, 169, 265, 176 }, + { ZN_SFX_TOGGLE, 197, 155, 231, 164 }, + { ZN_MUSIC_VOLUME, 136, 182, 265, 189 }, + { ZN_DESC_1, 131, 7, 290, 18 }, + { ZN_DESC_2, 131, 20, 290, 31 }, + { ZN_DESC_3, 131, 33, 290, 44 }, + { ZN_DESC_4, 131, 46, 290, 57 }, + { ZN_DESC_5, 131, 59, 290, 70 }, + { ZN_DESC_6, 131, 72, 290, 83 }, + { ZN_DESC_7, 131, 85, 290, 96 }, + { ZN_DESC_8, 131, 98, 290, 109 }, + { ZN_DESC_9, 131, 111, 290, 122 }, + { ZN_DESC_10, 131, 124, 290, 135 }, + { ZN_PAGE_A, 300, 4, 319, 17 }, + { ZN_PAGE_B, 300, 19, 319, 32 }, + { ZN_PAGE_C, 300, 34, 319, 47 }, + { ZN_PAGE_D, 300, 49, 319, 62 }, + { ZN_PAGE_E, 300, 64, 319, 77 }, + { ZN_PAGE_F, 300, 79, 319, 92 }, + { ZN_PAGE_G, 300, 94, 319, 107 }, + { ZN_PAGE_H, 300, 109, 319, 122 }, + { ZN_PAGE_I, 300, 124, 319, 137 }, + { ZN_PAGE_J, 300, 139, 319, 152 }, + { ZN_INFO_BOX, 273, 146, 295, 189 }, + { ZN_MUSIC_TOGGLE, 109, 181, 135, 190 }, + { ZN_VOICE_TOGGLE, 134, 155, 168, 164 }, + { ZN_TEXT_TOGGLE, 109, 168, 135, 177 } +}; + +} // End of namespace Queen diff --git a/engines/queen/journal.h b/engines/queen/journal.h new file mode 100644 index 0000000000..ab3e2a0ecd --- /dev/null +++ b/engines/queen/journal.h @@ -0,0 +1,208 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENJOURNAL_H +#define QUEENJOURNAL_H + +#include "common/util.h" + +class OSystem; + +namespace Queen { + +class QueenEngine; + +class Journal { +public: + + Journal(QueenEngine *vm); + void use(); + + enum { + JOURNAL_BANK = 8, + JOURNAL_FRAMES = 40 + }; + + enum { + ZN_REVIEW_ENTRY = 1, + ZN_MAKE_ENTRY = 2, + ZN_YES = ZN_MAKE_ENTRY, + ZN_CLOSE = 3, + ZN_NO = ZN_CLOSE, + ZN_GIVEUP = 4, + ZN_TEXT_SPEED = 5, + ZN_SFX_TOGGLE = 6, + ZN_MUSIC_VOLUME = 7, + ZN_DESC_1 = 8, + ZN_DESC_2 = 9, + ZN_DESC_3 = 10, + ZN_DESC_4 = 11, + ZN_DESC_5 = 12, + ZN_DESC_6 = 13, + ZN_DESC_7 = 14, + ZN_DESC_8 = 15, + ZN_DESC_9 = 16, + ZN_DESC_10 = 17, + ZN_PAGE_A = 18, + ZN_PAGE_B = 19, + ZN_PAGE_C = 20, + ZN_PAGE_D = 21, + ZN_PAGE_E = 22, + ZN_PAGE_F = 23, + ZN_PAGE_G = 24, + ZN_PAGE_H = 25, + ZN_PAGE_I = 26, + ZN_PAGE_J = 27, + ZN_INFO_BOX = 28, + ZN_MUSIC_TOGGLE = 29, + ZN_VOICE_TOGGLE = 30, + ZN_TEXT_TOGGLE = 31 + }; + + enum { + BOB_LEFT_RECT_1 = 1, + BOB_LEFT_RECT_2 = 2, + BOB_LEFT_RECT_3 = 3, + BOB_LEFT_RECT_4 = 4, + BOB_TALK_SPEED = 5, + BOB_SFX_TOGGLE = 6, + BOB_MUSIC_VOLUME = 7, + BOB_SAVE_DESC = 8, + BOB_SAVE_PAGE = 9, + BOB_SPEECH_TOGGLE = 10, + BOB_TEXT_TOGGLE = 11, + BOB_MUSIC_TOGGLE = 12, + BOB_INFO_BOX = 13 + }; + + enum { + FRAME_BLUE_1 = 1, + FRAME_BLUE_2 = 2, + FRAME_ORANGE = 3, + FRAME_GREY = 5, + FRAME_CHECK_BOX = 16, + FRAME_BLUE_PIN = 18, + FRAME_GREEN_PIN = 19, + FRAME_INFO_BOX = 20 + }; + + enum { + TXT_CLOSE = 30, + TXT_GIVE_UP = 31, + TXT_MAKE_ENTRY = 32, + TXT_REVIEW_ENTRY = 33, + TXT_YES = 34, + TXT_NO = 35 + }; + + enum { + NUM_SAVES_PER_PAGE = 10, + MAX_PANEL_TEXTS = 8, + MAX_ZONES = 31 + }; + + enum PanelMode { + PM_NORMAL, + PM_INFO_BOX, + PM_YES_NO + }; + + enum QuitMode { + QM_LOOP, + QM_RESTORE, + QM_CONTINUE + }; + + +private: + + void continueGame(); + + void setup(); + void redraw(); + void update(); + + void showBob(int bobNum, int16 x, int16 y, int frameNum); + void hideBob(int bobNum); + + void drawSaveDescriptions(); + void drawSaveSlot(); + + void enterYesNoPanelMode(int16 prevZoneNum, int titleNum); + void exitYesNoPanelMode(); + void enterInfoPanelMode(); + void exitInfoPanelMode(); + + void handleMouseWheel(int inc); + void handleMouseDown(int x, int y); + void handleKeyDown(uint16 ascii, int keycode); + + void drawPanelText(int y, const char *text); + void drawCheckBox(bool active, int bobNum, int16 x, int16 y, int frameNum); + void drawSlideBar(int value, int maxValue, int bobNum, int16 y, int frameNum); + void drawPanel(const int *frames, const int *titles, int n); + void drawNormalPanel(); + void drawYesNoPanel(int titleNum); + void drawConfigPanel(); + void drawInfoPanel(); + + void initTextField(const char *desc); + void updateTextField(uint16 ascii, int keycode); + void closeTextField(); + + struct TextField { + bool enabled; + int posCursor; + uint textCharsCount; + char text[32]; + int x, y; + int w, h; + }; + + struct Zone { + int num; + int16 x1, y1, x2, y2; + }; + + PanelMode _panelMode; + QuitMode _quitMode; + + int _currentSavePage; + int _currentSaveSlot; + + int _prevJoeX, _prevJoeY; + + int _panelTextCount; + int _panelTextY[MAX_PANEL_TEXTS]; + TextField _textField; + uint16 _prevZoneNum; + char _saveDescriptions[100][32]; + + OSystem *_system; + QueenEngine *_vm; + + static const Zone _zones[MAX_ZONES]; +}; + +} // End of namespace Queen + +#endif diff --git a/engines/queen/logic.cpp b/engines/queen/logic.cpp new file mode 100644 index 0000000000..a535907dd9 --- /dev/null +++ b/engines/queen/logic.cpp @@ -0,0 +1,2221 @@ +/* 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$ + * $Id$ + * + */ + +#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()); + } + + // FIXME - the spanish version adds some space characters (0x20) at the + // beginning and the end of the journal button captions. As we don't need + // that 'trick' to center horizontally the texts, we simply trim them. + 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; + + // FIXME - 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 diff --git a/engines/queen/logic.h b/engines/queen/logic.h new file mode 100644 index 0000000000..f1f93de17e --- /dev/null +++ b/engines/queen/logic.h @@ -0,0 +1,410 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENLOGIC_H +#define QUEENLOGIC_H + +#include "common/str.h" +#include "common/util.h" +#include "queen/structs.h" + +namespace Queen { + +enum RoomDisplayMode { + RDM_FADE_NOJOE = 0, // fade in, hide Joe + RDM_FADE_JOE = 1, // fade in, display Joe + RDM_NOFADE_JOE = 2, // screen does not dissolve into view + RDM_FADE_JOE_XY = 3 // display Joe at the current X, Y coords +}; + +enum JoeWalkMode { + JWM_NORMAL = 0, + JWM_MOVE = 1, + JWM_EXECUTE = 2, + JWM_SPEAK = 3 +}; + +class Credits; +class Journal; +class LineReader; +class QueenEngine; + +class Logic { + +public: + Logic(QueenEngine *vm); + virtual ~Logic(); + + uint16 currentRoom() const { return _currentRoom; } + void currentRoom(uint16 room) { + assert(room >= 1 && room <= _numRooms); + _currentRoom = room; + } + + uint16 oldRoom() const { return _oldRoom; } + void oldRoom(uint16 room) { + assert(room <= _numRooms); + _oldRoom = room; + } + + uint16 newRoom() const { return _newRoom; } + void newRoom(uint16 room) { + assert(room <= _numRooms); + _newRoom = room; + } + + ObjectData *objectData(int index) const; + uint16 roomData(int room) const { return _roomData[room]; } + GraphicData *graphicData(int index) const { return &_graphicData[index]; } + ItemData *itemData(int index) const { return &_itemData[index]; } + uint16 itemDataCount() const { return _numItems; } + + uint16 findBob(uint16 obj) const; + uint16 findFrame(uint16 obj) const; + uint16 objectForPerson(uint16 bobnum) const; + WalkOffData *walkOffPointForObject(int16 obj) const; + + uint16 walkOffCount() const { return _numWalkOffs; } + WalkOffData *walkOffData(int index) const { return &_walkOffData[index]; } + uint16 currentRoomData() const { return _roomData[_currentRoom]; } + GraphicAnim *graphicAnim(int index) const { return &_graphicAnim[index]; } + uint16 graphicAnimCount() const { return _numGraphicAnim; } + ObjectDescription *objectDescription(uint16 objNum) const { return &_objectDescription[objNum]; } + uint16 objectDescriptionCount() const { return _numObjDesc; } + uint16 currentRoomSfx() const { return _sfxName[_currentRoom]; } + + uint16 joeFacing() const { return _joe.facing; } + uint16 joeX() const { return _joe.x; } + uint16 joeY() const { return _joe.y; } + JoeWalkMode joeWalk() const { return _joe.walk; } + uint16 joeScale() const { return _joe.scale; } + uint16 joeCutFacing() const { return _joe.cutFacing; } + uint16 joePrevFacing() const { return _joe.prevFacing; } + + void joeFacing(uint16 dir) { _joe.facing = dir; } + void joePos(uint16 x, uint16 y) { _joe.x = x; _joe.y = y; } + void joeWalk(JoeWalkMode walking); + void joeScale(uint16 scale) { _joe.scale = scale; } + void joeCutFacing(uint16 dir) { _joe.cutFacing = dir; } + void joePrevFacing(uint16 dir) { _joe.prevFacing = dir; } + + int16 gameState(int index) const; + void gameState(int index, int16 newValue); + + TalkSelected *talkSelected(int index) { return &_talkSelected[index]; } + + const char *roomName(uint16 roomNum) const; + const char *objectName(uint16 objNum) const; + const char *objectTextualDescription(uint16 objNum) const; + const char *joeResponse(int i) const; + const char *verbName(Verb v) const; + + void eraseRoom(); + void setupRoom(const char *room, int comPanel, bool inCutaway); + void displayRoom(uint16 room, RoomDisplayMode mode, uint16 joeScale, int comPanel, bool inCutaway); + + int16 entryObj() const { return _entryObj; } + void entryObj(int16 obj) { _entryObj = obj; } + + ActorData *findActor(uint16 noun, const char *name = NULL) const; + bool initPerson(uint16 noun, const char *actorName, bool loadBank, Person *pp); + uint16 findPersonNumber(uint16 obj, uint16 room) const; + + //! load banks used for Joe animation + void loadJoeBanks(const char *animBank, const char *standBank); + + //! load the various bobs needed to animate Joe + void setupJoe(); + + //! setup Joe at the right place when entering a room + void setupJoeInRoom(bool autoPosition, uint16 scale); + + uint16 joeFace(); + void joeGrab(int16 grabState); + + //! change Joe clothes to dress + void joeUseDress(bool showCut); + + //! restore Joe clothes + void joeUseClothes(bool showCut); + + //! change Joe clothes to underwear + void joeUseUnderwear(); + + void makeJoeSpeak(uint16 descNum, bool objectType = false); + void makePersonSpeak(const char *sentence, Person *person, const char *voiceFilePrefix); + + //! start the specified dialogue + void startDialogue(const char *dlgFile, int personInRoom, char *cutaway); + + //! play the specified cutaway + void playCutaway(const char *cutFile, char *next = NULL); + + //! initialize the inventory + void inventorySetup(); + + //! get the inventory item for the specified inventory slot + uint16 findInventoryItem(int invSlot) const; + + //! refresh inventory contents + void inventoryRefresh(); + int16 previousInventoryItem(int16 first) const; + int16 nextInventoryItem(int16 first) const; + void removeDuplicateItems(); + uint16 numItemsInventory() const; + void inventoryInsertItem(uint16 itemNum, bool refresh = true); + void inventoryDeleteItem(uint16 itemNum, bool refresh = true); + void inventoryScroll(uint16 count, bool up); + void removeHotelItemsFromInventory(); + + //! copy data from dummy object to object + void objectCopy(int dummyObjectIndex, int objectIndex); + + //! handle a particular event when Joe walks on this area + void handleSpecialArea(Direction facing, uint16 areaNum, uint16 walkDataNum); + + //! handle the pinnacle room (== room chooser in the jungle) + void handlePinnacleRoom(); + + void update(); + + void saveState(byte *&ptr); + void loadState(uint32 ver, byte *&ptr); + + //! called after a save state has been loaded + void setupRestoredGame(); + + //! ugly hack from original code + void sceneReset() { _scene = 0; } + + //! make a scene + void sceneStart(); + + //! stop making a scene + void sceneStop(); + + void changeRoom(); + + //! enter the Journal (save/load, configuration) + virtual void useJournal() = 0; + + //! execute a special move + void executeSpecialMove(uint16 sm); + + void startCredits(const char *filename); + void stopCredits(); + + void start(); + + enum { + JOE_RESPONSE_MAX = 40, + DEFAULT_TALK_SPEED = 7 * 3, + GAME_STATE_COUNT = 211, + TALK_SELECTED_COUNT = 86 + }; + +protected: + + void initialise(); + + void asmMakeJoeUseDress(); + void asmMakeJoeUseNormalClothes(); + void asmMakeJoeUseUnderwear(); + void asmSwitchToDressPalette(); + void asmSwitchToNormalPalette(); + void asmStartCarAnimation(); + void asmStopCarAnimation(); + void asmStartFightAnimation(); + void asmWaitForFrankPosition(); + void asmMakeFrankGrowing(); + void asmMakeRobotGrowing(); + void asmShrinkRobot(); + void asmEndGame(); + void asmPutCameraOnDino(); + void asmPutCameraOnJoe(); + void asmAltIntroPanRight(); + void asmAltIntroPanLeft(); + void asmSetAzuraInLove(); + void asmPanRightFromJoe(); + void asmSetLightsOff(); + void asmSetLightsOn(); + void asmSetManequinAreaOn(); + void asmPanToJoe(); + void asmTurnGuardOn(); + void asmPanLeft320To144(); + void asmSmooch(); + void asmMakeLightningHitPlane(); + void asmScaleBlimp(); + void asmScaleEnding(); + void asmWaitForCarPosition(); + void asmShakeScreen(); + void asmAttemptPuzzle(); + void asmScaleTitle(); + void asmPanRightToHugh(); + void asmMakeWhiteFlash(); + void asmPanRightToJoeAndRita(); + void asmPanLeftToBomb(); + void asmEndDemo(); + void asmInterviewIntro(); + void asmEndInterview(); + + virtual bool preChangeRoom() = 0; + virtual bool handleSpecialMove(uint16 sm) = 0; + + + uint16 _currentRoom; + uint16 _oldRoom; + uint16 _newRoom; + + //! total number of room in game + uint16 _numRooms; + + //! first object number in room + uint16 *_roomData; + + //! background music to play in room + uint16 *_sfxName; + + //! bounding box of object + Box *_objectBox; + + //! inventory items + ItemData *_itemData; + uint16 _numItems; + + GraphicData *_graphicData; + uint16 _numGraphics; + + ObjectData *_objectData; + uint16 _numObjects; + + ObjectDescription *_objectDescription; + uint16 _numObjDesc; + + ActorData *_actorData; + uint16 _numActors; + + //! walk off point for an object + WalkOffData *_walkOffData; + uint16 _numWalkOffs; + + FurnitureData *_furnitureData; + uint16 _numFurniture; + + GraphicAnim *_graphicAnim; + uint16 _numGraphicAnim; + + //! actor initial position in room is _walkOffData[_entryObj] + int16 _entryObj; + + //! object description (Look At) + Common::StringList _objDescription; + uint16 _numDescriptions; + + Common::StringList _objName; + uint16 _numNames; + + //! room name, prefix for data files (PCX, LUM...) + Common::StringList _roomName; + + Common::StringList _verbName; + + Common::StringList _joeResponse; + + //! actor animation strings + Common::StringList _aAnim; + uint16 _numAAnim; + + //! actor names + Common::StringList _aName; + uint16 _numAName; + + //! actor filenames + Common::StringList _aFile; + uint16 _numAFile; + + struct { + uint16 x, y; + uint16 facing, cutFacing, prevFacing; + JoeWalkMode walk; + uint16 scale; + } _joe; + + int16 _gameState[GAME_STATE_COUNT]; + + TalkSelected _talkSelected[TALK_SELECTED_COUNT]; + + //! inventory items + int16 _inventoryItem[4]; + + //! puzzle counter for room T7 + uint8 _puzzleAttemptCount; + + //! cutscene counter + int _scene; + + Credits *_credits; + Journal *_journal; + + QueenEngine *_vm; +}; + +class LogicDemo : public Logic { +public: + + LogicDemo(QueenEngine *vm) : Logic(vm) {} + void useJournal(); + +protected: + + bool preChangeRoom(); + bool handleSpecialMove(uint16 sm); +}; + +class LogicInterview : public Logic { +public: + + LogicInterview(QueenEngine *vm) : Logic(vm) {} + void useJournal(); + +protected: + + bool preChangeRoom(); + bool handleSpecialMove(uint16 sm); +}; + +class LogicGame : public Logic { +public: + + LogicGame(QueenEngine *vm) : Logic(vm) {} + void useJournal(); + +protected: + + bool preChangeRoom(); + bool handleSpecialMove(uint16 sm); +}; + + +} // End of namespace Queen + +#endif diff --git a/engines/queen/module.mk b/engines/queen/module.mk new file mode 100644 index 0000000000..be3c5707e6 --- /dev/null +++ b/engines/queen/module.mk @@ -0,0 +1,34 @@ +MODULE := engines/queen + +MODULE_OBJS := \ + engines/queen/bankman.o \ + engines/queen/command.o \ + engines/queen/credits.o \ + engines/queen/cutaway.o \ + engines/queen/debug.o \ + engines/queen/display.o \ + engines/queen/graphics.o \ + engines/queen/grid.o \ + engines/queen/input.o \ + engines/queen/journal.o \ + engines/queen/logic.o \ + engines/queen/music.o \ + engines/queen/musicdata.o \ + engines/queen/queen.o \ + engines/queen/resource.o \ + engines/queen/restables.o \ + engines/queen/sound.o \ + engines/queen/state.o \ + engines/queen/talk.o \ + engines/queen/walk.o + +MODULE_DIRS += \ + engines/queen + +# This module can be built as a plugin +ifdef BUILD_PLUGINS +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/common.rules diff --git a/engines/queen/music.cpp b/engines/queen/music.cpp new file mode 100644 index 0000000000..c4a7a24503 --- /dev/null +++ b/engines/queen/music.cpp @@ -0,0 +1,344 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/music.h" +#include "queen/queen.h" +#include "queen/resource.h" +#include "queen/sound.h" + +#include "sound/midiparser.h" + +namespace Queen { + +MusicPlayer::MusicPlayer(MidiDriver *driver, byte *data, uint32 size) : _driver(driver), _isPlaying(false), _looping(false), _randomLoop(false), _masterVolume(192), _queuePos(0), _musicData(data), _musicDataSize(size), _passThrough(false), _buf(0) { + memset(_channel, 0, sizeof(_channel)); + queueClear(); + _lastSong = _currentSong = 0; + _parser = MidiParser::createParser_SMF(); + _parser->setMidiDriver(this); + _parser->setTimerRate(_driver->getBaseTempo()); + + _numSongs = READ_LE_UINT16(_musicData); + this->open(); +} + +MusicPlayer::~MusicPlayer() { + _parser->unloadMusic(); + delete _parser; + this->close(); + delete[] _buf; +} + +void MusicPlayer::setVolume(int volume) { + if (volume < 0) + volume = 0; + else if (volume > 255) + volume = 255; + + if (_masterVolume == volume) + return; + + _masterVolume = volume; + + for (int i = 0; i < 16; ++i) { + if (_channel[i]) + _channel[i]->volume(_channelVolume[i] * _masterVolume / 255); + } +} + +bool MusicPlayer::queueSong(uint16 songNum) { + if (songNum >= _numSongs && songNum < 1000) { + // this happens at the end of the car chase, where we try to play song 176, + // see Sound::_tune[], entry 39 + debug(3, "Trying to queue an invalid song number %d, max %d", songNum, _numSongs); + return false; + } + uint8 emptySlots = 0; + for (int i = 0; i < MUSIC_QUEUE_SIZE; i++) + if (!_songQueue[i]) + emptySlots++; + + if (!emptySlots) + return false; + + // Work around bug in Roland music, note that these numbers are 'one-off' + // from the original code + if (/*isRoland && */ songNum == 88 || songNum == 89) + songNum = 62; + + _songQueue[MUSIC_QUEUE_SIZE - emptySlots] = songNum; + return true; +} + +void MusicPlayer::queueClear() { + _lastSong = _songQueue[0]; + _queuePos = 0; + _looping = _randomLoop = false; + memset(_songQueue, 0, sizeof(_songQueue)); +} + +int MusicPlayer::open() { + // Don't ever call open without first setting the output driver! + if (!_driver) + return 255; + + int ret = _driver->open(); + if (ret) + return ret; + _driver->setTimerCallback(this, &onTimer); + return 0; +} + +void MusicPlayer::close() { + _driver->setTimerCallback(NULL, NULL); + if (_driver) + _driver->close(); + _driver = 0; +} + +void MusicPlayer::send(uint32 b) { + if (_passThrough) { + _driver->send(b); + return; + } + + byte channel = (byte)(b & 0x0F); + if ((b & 0xFFF0) == 0x07B0) { + // Adjust volume changes by master volume + byte volume = (byte)((b >> 16) & 0x7F); + _channelVolume[channel] = volume; + volume = volume * _masterVolume / 255; + b = (b & 0xFF00FFFF) | (volume << 16); + } else if ((b & 0xF0) == 0xC0 && !_nativeMT32) { + b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8; + } + else if ((b & 0xFFF0) == 0x007BB0) { + //Only respond to All Notes Off if this channel + //has currently been allocated + if (_channel[b & 0x0F]) + return; + } + + //Work around annoying loud notes in certain Roland Floda tunes + if (channel == 3 && _currentSong == 90) + return; + if (channel == 4 && _currentSong == 27) + return; + if (channel == 5 && _currentSong == 38) + return; + + if (!_channel[channel]) + _channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + + if (_channel[channel]) + _channel[channel]->send(b); +} + +void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) { + //Only thing we care about is End of Track. + if (type != 0x2F) + return; + + if (_looping || _songQueue[1]) + playMusic(); + else + stopMusic(); +} + +void MusicPlayer::onTimer(void *refCon) { + MusicPlayer *music = (MusicPlayer *)refCon; + if (music->_isPlaying) + music->_parser->onTimer(); +} + +void MusicPlayer::queueTuneList(int16 tuneList) { + queueClear(); + + //Jungle is the only part of the game that uses multiple tunelists. + //For the sake of code simplification we just hardcode the extended list ourselves + if ((tuneList + 1) == 3) { + _randomLoop = true; + int i = 0; + while (Sound::_jungleList[i]) + queueSong(Sound::_jungleList[i++] - 1); + return; + } + + int mode = (_numSongs == 40) ? Sound::_tuneDemo[tuneList].mode : Sound::_tune[tuneList].mode; + switch (mode) { + case 0: // random loop + _randomLoop = true; + setLoop(false); + break; + case 1: // sequential loop + setLoop(_songQueue[1] == 0); + break; + case 2: // play once + default: + setLoop(false); + break; + } + + int i = 0; + if (_numSongs == 40) { + while (Sound::_tuneDemo[tuneList].tuneNum[i]) + queueSong(Sound::_tuneDemo[tuneList].tuneNum[i++] - 1); + } else { + while (Sound::_tune[tuneList].tuneNum[i]) + queueSong(Sound::_tune[tuneList].tuneNum[i++] - 1); + } + + if (_randomLoop) + _queuePos = randomQueuePos(); +} + +void MusicPlayer::playMusic() { + if (!_songQueue[0]) { + debug(5, "MusicPlayer::playMusic - Music queue is empty!"); + return; + } + + uint16 songNum = _songQueue[_queuePos]; + + //Special type + // > 1000 && < 2000 -> queue different tunelist + // 2000 -> repeat music from previous queue + if (songNum > 999) { + if ((songNum + 1) == 2000) { + songNum = _lastSong; + queueClear(); + queueSong(songNum); + } else { + queueTuneList(songNum - 1000); + _queuePos = _randomLoop ? randomQueuePos() : 0; + songNum = _songQueue[_queuePos]; + } + } + + byte *prevSong = _musicData + songOffset(_currentSong); + if (*prevSong == 0x43 || *prevSong == 0x63) { + if (_buf) { + delete[] _buf; + _buf = 0; + } + } + + _currentSong = songNum; + if (!songNum) { + stopMusic(); + return; + } + + byte *musicPtr = _musicData + songOffset(songNum); + uint32 size = songLength(songNum); + if (*musicPtr == 0x43 || *musicPtr == 0x63) { + uint32 packedSize = songLength(songNum) - 0x200; + _buf = new uint16[packedSize]; + + uint16 *data = (uint16 *)(musicPtr + 1); + byte *idx = ((byte *)data) + 0x200; + + for (uint i = 0; i < packedSize; i++) +#if defined(SCUMM_NEED_ALIGNMENT) + memcpy(&_buf[i], (byte*)((byte*)data + *(idx + i) * sizeof(uint16)), sizeof(uint16)); +#else + _buf[i] = data[*(idx + i)]; +#endif + + musicPtr = ((byte *)_buf) + ((*musicPtr == 0x63) ? 1 : 0); + size = packedSize * 2; + } + + _parser->loadMusic(musicPtr, size); + _parser->setTrack(0); + debug(8, "Playing song %d [queue position: %d]", songNum, _queuePos); + _isPlaying = true; + queueUpdatePos(); +} + +void MusicPlayer::queueUpdatePos() { + if (_randomLoop) { + _queuePos = randomQueuePos(); + } else { + if (_queuePos < (MUSIC_QUEUE_SIZE - 1) && _songQueue[_queuePos + 1]) + _queuePos++; + else if (_looping) + _queuePos = 0; + } +} + +uint8 MusicPlayer::randomQueuePos() { + int queueSize = 0; + for (int i = 0; i < MUSIC_QUEUE_SIZE; i++) + if (_songQueue[i]) + queueSize++; + + if (!queueSize) + return 0; + + return (uint8) _rnd.getRandomNumber(queueSize - 1) & 0xFF; +} + +void MusicPlayer::stopMusic() { + _isPlaying = false; + _parser->unloadMusic(); +} + +uint32 MusicPlayer::songOffset(uint16 songNum) const { + uint16 offsLo = READ_LE_UINT16(_musicData + (songNum * 4) + 2); + uint16 offsHi = READ_LE_UINT16(_musicData + (songNum * 4) + 4); + return (offsHi << 4) | offsLo; +} + +uint32 MusicPlayer::songLength(uint16 songNum) const { + if (songNum < _numSongs) + return (songOffset(songNum + 1) - songOffset(songNum)); + return (_musicDataSize - songOffset(songNum)); +} + +Music::Music(MidiDriver *driver, QueenEngine *vm) : _vToggle(false) { + if (vm->resource()->isDemo()) { + _musicData = vm->resource()->loadFile("AQ8.RL", 0, &_musicDataSize); + } else { + _musicData = vm->resource()->loadFile("AQ.RL", 0, &_musicDataSize); + } + _player = new MusicPlayer(driver, _musicData, _musicDataSize); +} + +Music::~Music() { + delete _player; + delete[] _musicData; +} + +void Music::playSong(uint16 songNum) { + _player->queueClear(); + _player->queueSong(songNum); + _player->playMusic(); +} + +void Music::toggleVChange() { + setVolume(_vToggle ? (volume() * 2) : (volume() / 2)); + _vToggle ^= true; +} + +} // End of namespace Queen diff --git a/engines/queen/music.h b/engines/queen/music.h new file mode 100644 index 0000000000..1051b8b988 --- /dev/null +++ b/engines/queen/music.h @@ -0,0 +1,125 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENMUSIC_H +#define QUEENMUSIC_H + +#include "common/util.h" +#include "sound/mididrv.h" + +class MidiParser; + +namespace Queen { + +class QueenEngine; + +class MusicPlayer : public MidiDriver { +public: + MusicPlayer(MidiDriver *driver, byte *data, uint32 size); + ~MusicPlayer(); + void setVolume(int volume); + int getVolume() { return _masterVolume; } + + void hasNativeMT32(bool b) { _nativeMT32 = b; } + void playMusic(); + void stopMusic(); + void setLoop(bool loop) { _looping = loop; } + void queueTuneList(int16 tuneList); + bool queueSong(uint16 songNum); + void queueClear(); + void setPassThrough(bool b) { _passThrough = b; } + + //MidiDriver interface implementation + int open(); + void close(); + void send(uint32 b); + + void metaEvent(byte type, byte *data, uint16 length); + + void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { } + uint32 getBaseTempo(void) { return _driver ? _driver->getBaseTempo() : 0; } + + //Channel allocation functions + MidiChannel *allocateChannel() { return 0; } + MidiChannel *getPercussionChannel() { return 0; } + +protected: + + enum { + MUSIC_QUEUE_SIZE = 14 + }; + + void queueUpdatePos(); + uint8 randomQueuePos(); + static void onTimer(void *data); + uint32 songOffset(uint16 songNum) const; + uint32 songLength(uint16 songNum) const; + + MidiDriver *_driver; + MidiParser *_parser; + MidiChannel *_channel[16]; + byte _channelVolume[16]; + bool _nativeMT32; + bool _passThrough; + + Common::RandomSource _rnd; + + bool _isPlaying; + bool _looping; + bool _randomLoop; + byte _masterVolume; + uint8 _queuePos; + int16 _currentSong; + int16 _lastSong; //first song from previous queue + int16 _songQueue[MUSIC_QUEUE_SIZE]; + + uint16 _numSongs; + byte *_musicData; + uint16 *_buf; + uint32 _musicDataSize; +}; + +class Music { +public: + Music(MidiDriver *_driver, QueenEngine *vm); + ~Music(); + void hasNativeMT32(bool b) { _player->hasNativeMT32(b); } + void playSong(uint16 songNum); + void queueTuneList(int16 tuneList) { _player->queueTuneList(tuneList); } + void playMusic() { _player->playMusic(); } + void stopSong() { _player->stopMusic(); } + void setPassThrough(bool b) { _player->setPassThrough(b); } + + void toggleVChange(); + void setVolume(int vol) { _player->setVolume(vol); } + int volume() { return _player->getVolume(); } + +protected: + bool _vToggle; + byte *_musicData; + uint32 _musicDataSize; + MusicPlayer *_player; +}; + +} // End of namespace Queen + +#endif diff --git a/engines/queen/musicdata.cpp b/engines/queen/musicdata.cpp new file mode 100644 index 0000000000..39acdf8ab4 --- /dev/null +++ b/engines/queen/musicdata.cpp @@ -0,0 +1,1944 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/sound.h" + +namespace Queen { + +#ifdef PALMOS_68K + +const songData *Sound::_songDemo; +const songData *Sound::_song; +const tuneData *Sound::_tuneDemo; +const tuneData *Sound::_tune; +const char *Sound::_sfxName; +const int16 *Sound::_jungleList; + +#else +const songData Sound::_songDemo[] = { + /* 1 - Hotel Gangsters */ + { { 1, 0 }, 128, 128, 128, 1, 0 }, + + /* 2 - Hotel General */ + { { 2, 0 }, 128, 128, 128, 1, 0 }, + + /* 3 - Jungle */ + { { 3, 4, 5, 6, 0 }, 128, 128, 128, 1, 0 }, + + /* 4 - Waterfall On */ + { { 7, 0 }, 128, 128, 128, 0, 0 }, + + /* 5 - Vnormal */ + { { 8, 0 }, 128, 128, 128, 2, 0 }, + + /* 6 - Trader Bob */ + { { 9, 0 }, 120, 128, 128, 1, 0 }, + + /* 7 - Jetty Music */ + { { 10, 0 }, 128, 128, 128, 1, 0 }, + + /* 8 - Ferry Music */ + { { 11, 0 }, 128, 128, 128, 1, 0 }, + + /* 9 - Temple Upstairs */ + { { 12, 0 }, 128, 128, 128, 1, 0 }, + + /* 10 - Temple Downstairs */ + { { 13, 0 }, 128, 128, 128, 1, 0 }, + + /* 11 - Temple Maze */ + { { 14, 0 }, 128, 128, 128, 1, 0 }, + + /* 12 - Temple Skull */ + { { 15, 0 }, 128, 128, 128, 1, 0 }, + + /* 13 - Johns Theme (Love Story) */ + { { 16, 0 }, 128, 128, 128, 1, 0 }, + + /* 14 - Vmedium */ + { { 17, 0 }, 128, 128, 0, 2, 0 }, + + /* 15 - Vsoft */ + { { 18, 0 }, 128, 128, 0, 2, 0 }, + + /* 16 - Floda Upstairs */ + { { 19, 0 }, 128, 128, 0, 1, 0 }, + + /* 17 - Floda General */ + { { 20, 0 }, 128, 128, 128, 1, 0 }, + + /* 18 - Klunks Room */ + { { 21, 0 }, 110, 128, 128, 1, 0 }, + + /* 19 - Hotel Lola */ + { { 22, 0 }, 120, 18128, 128, 1, 0 }, + + /* 20 - Hotel Escape 1 */ + { { 23, 0 }, 128, 18128, 128, 1, 0 }, + + /* 21 - Amazon Fortress */ + { { 24, 0 }, 128, 128, 128, 1, 0 }, + + /* 22 - Waterfall Off */ + { { 25, 0 }, 128, 128, 128, 0, 0 }, + + /* 23 - Wave Torch */ + { { 26, 0 }, 128, 128, 128, 0, 1 }, + + /* 24 - Zombies Rez Out */ + { { 27, 0 }, 128, 128, 128, 0, 1 }, + + /* 25 - Open Door (standard) */ + { { 28, 0 }, 128, 128, 128, 0, 1 }, + + /* 26 - Close Door (standard) */ + { { 29, 0 }, 128, 128, 128, 0, 1 }, + + /* 27 - Cloth Unrolls */ + { { 30, 0 }, 128, 128, 128, 0, 1 }, + + /* 28 - Snake Slithers Off */ + { { 31, 0 }, 128, 128, 128, 0, 1 }, + + /* 29 - Hotel Fanfare 2 */ + { { 32, 0 }, 128, 128, 128, 1, 1 }, + + /* 30 - Floda Secret */ + { { 33, 0 }, 120, 128, 128, 1, 0 }, + + /* 31 - Temple Fanfare 1 */ + { { 34, 0 }, 128, 128, 128, 1, 1 }, +}; + +const songData Sound::_song[] = { + /* 1 - Hotel Gangsters */ + { { 1, 0 }, 128, 180, 0, 1, 0 }, + + /* 2 - Hotel General */ + { { 2, 0 }, 128, 180, 0, 1, 0 }, + + /* 3 - Jungle */ + { { 3, 4, 5, 6, 0 }, 128, 0, 0, 1, 0 }, + + /* 4 - Waterfall On */ + { { 7, 0 }, 128, 0, 0, 0, 0 }, + + /* 5 - Vnormal */ + { { 8, 0 }, 128, 0, 0, 2, 0 }, + + /* 6 - Trader Bob */ + { { 9, 0 }, 120, 0, 0, 1, 0 }, + + /* 7 - Jetty Music */ + { { 10, 0 }, 128, 0, 0, 1, 0 }, + + /* 8 - Ferry Music */ + { { 11, 0 }, 128, 0, 0, 1, 0 }, + + /* 9 - Temple Upstairs */ + { { 12, 0 }, 128, 0, 0, 1, 0 }, + + /* 10 - Temple Downstairs */ + { { 13, 0 }, 128, 0, 0, 1, 0 }, + + /* 11 - Temple Maze */ + { { 14, 0 }, 128, 0, 0, 1, 0 }, + + /* 12 - Temple Skull */ + { { 15, 0 }, 128, 0, 0, 1, 0 }, + + /* 13 - Johns Theme (Love Story) */ + { { 16, 0 }, 128, 0, 0, 1, 0 }, + + /* 14 - Vmedium */ + { { 17, 0 }, 120, 0, 0, 2, 0 }, + + /* 15 - Vsoft */ + { { 18, 0 }, 110, 0, 0, 2, 0 }, + + /* 16 - Floda Upstairs */ + { { 19, 0 }, 110, 0, 0, 1, 0 }, + + /* 17 - Floda General */ + { { 20, 0 }, 128, 0, 0, 1, 0 }, + + /* 18 - Klunks Room */ + { { 21, 0 }, 128, 0, 0, 1, 0 }, + + /* 19 - Hotel Lola */ + { { 22, 0 }, 120, 180, 0, 1, 0 }, + + /* 20 - Hotel Escape 1 */ + { { 23, 0 }, 128, 180, 0, 1, 0 }, + + /* 21 - Amazon Fortress */ + { { 24, 0 }, 128, 0, 0, 1, 0 }, + + /* 22 - Waterfall Off */ + { { 25, 0 }, 128, 0, 0, 0, 0 }, + + /* 23 - Wave Torch */ + { { 26, 0 }, 128, 0, 0, 0, 1 }, + + /* 24 - Zombies Rez Out */ + { { 27, 0 }, 128, 0, 0, 0, 1 }, + + /* 25 - Open Door (standard) */ + { { 28, 0 }, 128, 0, 0, 0, 1 }, + + /* 26 - Close Door (standard) */ + { { 29, 0 }, 128, 0, 0, 0, 1 }, + + /* 27 - Cloth Unrolls */ + { { 30, 0 }, 128, 0, 0, 0, 1 }, + + /* 28 - Snake Slithers Off */ + { { 31, 0 }, 128, 0, 0, 0, 1 }, + + /* 29 - Hotel Fanfare 2 */ + { { 32, 0 }, 128, 0, 0, 1, 1 }, + + /* 30 - Floda Secret */ + { { 33, 0 }, 128, 0, 0, 1, 0 }, + + /* 31 - Temple Fanfare 1 */ + { { 34, 0 }, 128, 0, 0, 1, 1 }, + + /* 32 - Commander Rocket 1 */ + { { 35, 0 }, 128, 0, 0, 1, 0 }, + + /* 33 - Hotel Escape 2 */ + { { 36, 0 }, 128, 180, 0, 1, 0 }, + + /* 34 - Back of Truck */ + { { 37, 0 }, 128, 180, 0, 1, 0 }, + + /* 35 - Hotel Fanfare 1 */ + { { 38, 0 }, 128, 0, 0, 1, 1 }, + + /* 36 - Truck Fanfare */ + { { 39, 0 }, 128, 0, 0, 1, 1 }, + + /* 37 - Airport */ + { { 40, 0 }, 128, 0, 0, 1, 0 }, + + /* 38 - Plane Leaves */ + { { 41, 0 }, 128, 0, 0, 1, 1 }, + + /* 39 - Arrive Hotel */ + { { 42, 0 }, 128, 0, 0, 1, 0 }, + + /* 40 - Jungle Fanfare */ + { { 43, 0 }, 128, 0, 0, 1, 1 }, + + /* 41 - General Fanfare */ + { { 44, 0 }, 128, 0, 0, 1, 1 }, + + /* 42 - Johns Room */ + { { 45, 0 }, 128, 0, 0, 1, 0 }, + + /* 43 - Floda Lab */ + { { 46, 0 }, 128, 0, 0, 1, 0 }, + + /* 44 - Azura's Theme */ + { { 47, 0 }, 128, 0, 0, 1, 0 }, + + /* 45 - Use Record */ + { { 48, 0 }, 128, 0, 0, 1, 1 }, + + /* 46 - Franks Theme */ + { { 49, 0 }, 128, 0, 0, 1, 0 }, + + /* 47 - Anderson Doubts */ + { { 50, 0 }, 128, 0, 0, 1, 0 }, + + /* 48 - Bud and Lou Theme */ + { { 51, 0 }, 128, 0, 0, 1, 1 }, + + /* 49 - Gorilla Theme */ + { { 52, 0 }, 128, 0, 0, 1, 1 }, + + /* 50 - Missionaries Theme */ + { { 53, 0 }, 128, 0, 0, 1, 1 }, + + /* 51 - Sloth Theme */ + { { 54, 0 }, 128, 0, 0, 1, 1 }, + + /* 52 - Amazon Dungeon */ + { { 55, 0 }, 128, 0, 0, 1, 0 }, + + /* 53 - Throne Room */ + { { 56, 0 }, 128, 0, 0, 1, 0 }, + + /* 54 - Temple Puzzle */ + { { 57, 0 }, 128, 0, 0, 1, 0 }, + + /* 55 - Temple Fountain Room */ + { { 58, 0 }, 128, 0, 0, 1, 0 }, + + /* 56 - Light Switch */ + { { 59, 0 }, 128, 0, 0, 0, 1 }, + + /* 57 - Hydraulic Open */ + { { 60, 0 }, 128, 0, 0, 0, 1 }, + + /* 58 - Hydraulic Close */ + { { 61, 0 }, 128, 0, 0, 0, 1 }, + + /* 59 - Close Door (metal) */ + { { 62, 0 }, 128, 0, 0, 0, 1 }, + + /* 60 - Small Hatch Close */ + { { 63, 0 }, 128, 0, 0, 0, 1 }, + + /* 61 - Scissors Snip */ + { { 64, 0 }, 128, 0, 0, 0, 1 }, + + /* 62 - Pick up Sticky */ + { { 65, 0 }, 128, 0, 0, 0, 1 }, + + /* 63 - Oracle Rezzes In */ + { { 66, 0 }, 128, 0, 0, 0, 1 }, + + /* 64 - Sparkle SFX */ + { { 67, 0 }, 128, 0, 0, 0, 1 }, + + /* 65 - Splorch! */ + { { 68, 0 }, 128, 0, 0, 0, 1 }, + + /* 66 - Pour Liquid */ + { { 69, 0 }, 128, 0, 0, 0, 1 }, + + /* 67 - End Credit Medley */ + { { 70, 0 }, 128, 0, 0, 1, 0 }, + + /* 68 - Dino Ray */ + { { 71, 0 }, 128, 0, 0, 0, 1 }, + + /* 69 - Squish! */ + { { 72, 0 }, 128, 0, 0, 0, 1 }, + + /* 70 - Robot Laser */ + { { 73, 0 }, 128, 0, 0, 0, 1 }, + + /* 71 - Thud wood light */ + { { 74, 0 }, 128, 0, 0, 0, 1 }, + + /* 72 - Thud wood deep */ + { { 75, 0 }, 128, 0, 0, 0, 1 }, + + /* 73 - Thud metallic */ + { { 76, 0 }, 128, 0, 0, 0, 1 }, + + /* 74 - Cut Coconut */ + { { 77, 0 }, 128, 0, 0, 0, 1 }, + + /* 75 - Thud Stone */ + { { 78, 0 }, 128, 0, 0, 0, 1 }, + + /* 76 - Cloth Slide 1 */ + { { 79, 0 }, 128, 0, 0, 0, 1 }, + + /* 77 - Open Chest */ + { { 80, 0 }, 128, 0, 0, 0, 1 }, + + /* 78 - Close Chest */ + { { 81, 0 }, 128, 0, 0, 0, 1 }, + + /* 79 - Open Drawer */ + { { 82, 0 }, 128, 0, 0, 0, 1 }, + + /* 80 - Truck door closes */ + { { 83, 0 }, 128, 0, 0, 0, 1 }, + + /* 81 - Truck Starts */ + { { 84, 0 }, 128, 0, 0, 0, 1 }, + + /* 82 - Truck Drives Off */ + { { 85, 0 }, 128, 0, 0, 0, 1 }, + + /* 83 - Fish Splash */ + { { 86, 0 }, 128, 0, 0, 0, 1 }, + + /* 84 - Close Drawer/Push Ladder */ + { { 87, 0 }, 128, 0, 0, 0, 1 }, + + /* 85 - Agression Enhancer */ + { { 88, 0 }, 128, 0, 0, 0, 1 }, + + /* 86 - Stone Door Grind 1 */ + { { 89, 0 }, 128, 0, 0, 0, 1 }, + + /* 87 - Prequel 1 */ + { { 90, 0 }, 128, 0, 0, 1, 0 }, + + /* 88 - Intro Credits */ + { { 91, 0 }, 128, 0, 0, 1, 0 }, + + /* 89 - Valley 1 */ + { { 92, 0 }, 128, 0, 0, 1, 0 }, + + /* 90 - Valley 3 */ + { { 93, 0 }, 128, 0, 0, 1, 0 }, + + /* 91 - Fight Music */ + { { 94, 0 }, 128, 0, 0, 1, 0 }, + + /* 92 - Confrontation 1 */ + { { 95, 0 }, 128, 0, 0, 1, 0 }, + + /* 93 - Confrontation 2 */ + { { 96, 0 }, 128, 0, 0, 1, 0 }, + + /* 94 - Plane Hatch Open */ + { { 97, 0 }, 128, 128, 128, 0, 1 }, + + /* 95 - Plane Hatch Close */ + { { 98, 0 }, 128, 128, 128, 0, 1 }, + + /* 96 - Tie Vines */ + { { 99, 0 }, 128, 128, 128, 0, 1 }, + + /* 97 - Pterodactyl */ + { { 100, 0 }, 128, 128, 128, 0, 1 }, + + /* 98 - Beef Jerky Splash */ + { { 101, 0 }, 128, 128, 128, 0, 1 }, + + /* 99 - Piranha Burp */ + { { 102, 0 }, 128, 128, 128, 0, 1 }, + + /* 100 - Falling Vine */ + { { 103, 0 }, 128, 128, 128, 0, 1 }, + + /* 101 - Stone Door Grind 2 */ + { { 104, 0 }, 128, 128, 128, 0, 1 }, + + /* 102 - Stone Grind (light) */ + { { 105, 0 }, 128, 128, 128, 0, 1 }, + + /* 103 - Ape Takes Off Mask */ + { { 106, 0 }, 128, 128, 128, 0, 1 }, + + /* 104 - Bark Breaks */ + { { 107, 0 }, 128, 128, 128, 0, 1 }, + + /* 105 - Stone Click */ + { { 108, 0 }, 128, 128, 128, 0, 1 }, + + /* 106 - Sproing! */ + { { 109, 0 }, 128, 128, 128, 0, 1 }, + + /* 107 - Cash Register */ + { { 110, 0 }, 128, 128, 128, 0, 1 }, + + /* 108 - Squeaky Toy */ + { { 111, 0 }, 128, 128, 128, 0, 1 }, + + /* 109 - Falling Chains */ + { { 112, 0 }, 128, 128, 128, 0, 1 }, + + /* 110 - Open Locker Door */ + { { 113, 0 }, 128, 128, 128, 0, 1 }, + + /* 111 - Close Locker Door */ + { { 114, 0 }, 128, 128, 128, 0, 1 }, + + /* 112 - Rub Pencil */ + { { 115, 0 }, 128, 128, 128, 0, 1 }, + + /* 113 - Open Safe */ + { { 116, 0 }, 128, 128, 128, 0, 1 }, + + /* 114 - Close Safe */ + { { 117, 0 }, 128, 128, 128, 0, 1 }, + + /* 115 - Push Chair */ + { { 118, 0 }, 128, 128, 128, 0, 1 }, + + /* 116 - Snake Hiss */ + { { 119, 0 }, 128, 128, 128, 0, 1 }, + + /* 117 - Oracle Rezzes Out */ + { { 120, 0 }, 128, 128, 128, 0, 1 }, + + /* 118 - Wall Crumbles */ + { { 121, 0 }, 128, 128, 128, 0, 1 }, + + /* 119 - Crypt Crumbles */ + { { 122, 0 }, 128, 128, 128, 0, 1 }, + + /* 120 - Joe Sucked Up */ + { { 123, 0 }, 128, 128, 128, 0, 1 }, + + /* 121 - Rocket Pack Zoom */ + { { 124, 0 }, 128, 128, 128, 0, 1 }, + + /* 122 - Piranha Splash */ + { { 125, 0 }, 128, 128, 128, 0, 1 }, + + /* 123 - Snap Branch */ + { { 126, 0 }, 128, 128, 128, 0, 1 }, + + /* 124 - Dino Horn */ + { { 127, 0 }, 128, 128, 128, 0, 1 }, + + /* 125 - Tyre Screech */ + { { 128, 0 }, 128, 128, 128, 0, 1 }, + + /* 126 - Oil Splat */ + { { 129, 0 }, 128, 128, 128, 0, 1 }, + + /* 127 - Punch */ + { { 130, 0 }, 128, 128, 128, 0, 1 }, + + /* 128 - Body Hits Ground */ + { { 131, 0 }, 128, 128, 128, 0, 1 }, + + /* 129 - Chicken */ + { { 132, 0 }, 128, 128, 128, 0, 1 }, + + /* 130 - Open Sarcophagus */ + { { 133, 0 }, 128, 128, 128, 0, 1 }, + + /* 131 - Close Sarcophagus */ + { { 134, 0 }, 128, 128, 128, 0, 1 }, + + /* 132 - Creaking Stick */ + { { 135, 0 }, 128, 128, 128, 0, 1 }, + + /* 133 - Pick Hits Stone */ + { { 136, 0 }, 128, 128, 128, 0, 1 }, + + /* 134 - Stalactite Crumbles */ + { { 137, 0 }, 128, 128, 128, 0, 1 }, + + /* 135 - Tic-Toc */ + { { 138, 0 }, 128, 128, 128, 0, 1 }, + + /* 136 - Stone Grind (heavy) */ + { { 139, 0 }, 128, 128, 128, 0, 1 }, + + /* 137 - Explosion */ + { { 140, 0 }, 128, 128, 128, 0, 1 }, + + /* 138 - Cloth Slide 2 */ + { { 141, 0 }, 128, 128, 128, 0, 1 }, + + /* 139 - Temple Laser */ + { { 142, 0 }, 128, 128, 128, 0, 1 }, + + /* 140 - Dino Transformation */ + { { 143, 0 }, 128, 128, 128, 0, 1 }, + + /* 141 - Experimental Laser */ + { { 144, 0 }, 128, 128, 128, 0, 1 }, + + /* 142 - Stone Grind (medium) */ + { { 145, 0 }, 128, 128, 128, 0, 1 }, + + /* 143 - Weeping God Grind */ + { { 146, 0 }, 128, 128, 128, 0, 1 }, + + /* 144 - Alien Hum */ + { { 147, 0 }, 128, 128, 128, 0, 1 }, + + /* 145 - Alien Puzzle */ + { { 148, 0 }, 128, 128, 128, 0, 1 }, + + /* 146 - Vacuum On */ + { { 149, 0 }, 128, 128, 128, 0, 1 }, + + /* 147 - Vacuum Off */ + { { 150, 0 }, 128, 128, 128, 0, 1 }, + + /* 148 - Elevator Starts */ + { { 151, 0 }, 128, 128, 128, 0, 1 }, + + /* 149 - Mummy Crumbles */ + { { 152, 0 }, 128, 128, 128, 0, 1 }, + + /* 150 - Temple Green Circle */ + { { 153, 0 }, 128, 128, 128, 0, 1 }, + + /* 151 - Rattle Bars */ + { { 154, 0 }, 128, 128, 128, 0, 1 }, + + /* 152 - Door Dissolves */ + { { 155, 0 }, 128, 128, 128, 0, 1 }, + + /* 153 - Altar Slides */ + { { 156, 0 }, 128, 128, 128, 0, 1 }, + + /* 154 - Light Torch */ + { { 157, 0 }, 128, 128, 128, 0, 1 }, + + /* 155 - Stamp Sound */ + { { 158, 0 }, 128, 128, 128, 0, 1 }, + + /* 156 - Plaster Loud */ + { { 159, 0 }, 128, 128, 128, 0, 1 }, + + /* 157 - Sparky Bathtub */ + { { 160, 0 }, 128, 128, 128, 0, 1 }, + + /* 158 - Ape Rezzes Out */ + { { 161, 0 }, 128, 128, 128, 0, 1 }, + + /* 159 - Song 159 */ + { { 162, 0 }, 128, 128, 128, 1, 0 }, + + /* 160 - Song 160 */ + { { 163, 0 }, 128, 128, 128, 1, 0 }, + + /* 161 - Song 161 */ + { { 164, 0 }, 128, 128, 128, 1, 0 }, + + /* 162 - Piranhas Swim */ + { { 165, 0 }, 128, 128, 128, 0, 1 }, + + /* 163 - Prison/Dungeon Door */ + { { 166, 0 }, 128, 128, 128, 0, 1 }, + + /* 164 - Fight Explosion */ + { { 167, 0 }, 128, 128, 128, 0, 1 }, + + /* 165 - Press Button */ + { { 168, 0 }, 128, 128, 128, 2, 1 }, + + /* 166 - Pull Lever */ + { { 169, 0 }, 128, 128, 128, 0, 1 }, + + /* 167 - Wrong Code */ + { { 170, 0 }, 128, 128, 128, 0, 1 }, + + /* 168 - Correct Code */ + { { 171, 0 }, 128, 128, 128, 0, 1 }, + + /* 169 - Sizzle */ + { { 172, 0 }, 128, 128, 128, 0, 1 }, + + /* 170 - Money In Slot */ + { { 173, 0 }, 128, 128, 128, 0, 1 }, + + /* 171 - Lightning Crack */ + { { 174, 0 }, 128, 128, 128, 0, 1 }, + + /* 172 - Machine Gun Fire */ + { { 175, 0 }, 128, 128, 128, 0, 1 }, + + /* 173 - Cage Descends */ + { { 176, 0 }, 128, 128, 128, 0, 1 }, + + /* 174 - Chair Activates */ + { { 177, 0 }, 128, 128, 128, 0, 1 }, + + /* 175 - Robot Powers On */ + { { 178, 0 }, 128, 128, 128, 0, 1 }, + + /* 176 - Grow Big */ + { { 179, 0 }, 128, 128, 128, 0, 1 }, + + /* 177 - Eat Food */ + { { 180, 0 }, 128, 128, 128, 0, 1 }, + + /* 178 - Head Shrink */ + { { 181, 0 }, 128, 128, 128, 0, 1 }, + + /* 179 - Grinding Gears */ + { { 182, 0 }, 128, 128, 128, 0, 1 }, + + /* 180 - Chair Splash */ + { { 183, 0 }, 128, 128, 128, 0, 1 }, + + /* 181 - Deflect Laser */ + { { 184, 0 }, 128, 128, 128, 0, 1 }, + + /* 182 - Zap Frank */ + { { 185, 0 }, 128, 128, 128, 0, 1 }, + + /* 183 - Frank Transforms */ + { { 186, 0 }, 128, 128, 128, 0, 1 }, + + /* 184 - Alarm Clock */ + { { 187, 0 }, 128, 128, 128, 0, 1 }, + + /* 185 - Slide Chute */ + { { 188, 0 }, 128, 128, 128, 0, 1 }, + + /* 186 - Puff */ + { { 189, 0 }, 128, 128, 128, 0, 1 }, + + /* 187 - Bite */ + { { 190, 0 }, 128, 128, 128, 0, 0 }, + + /* 188 - Stone Door Grind 2 */ + { { 191, 0 }, 128, 128, 128, 0, 1 }, + + /* 189 - Prequel 2 */ + { { 192, 0 }, 128, 128, 128, 1, 0 }, + + /* 190 - Prequel 3 */ + { { 193, 0 }, 128, 128, 128, 1, 0 }, + + /* 191 - Prequel 4 */ + { { 194, 0 }, 128, 128, 128, 1, 0 }, + + /* 192 - Stop Music */ + { { 195, 0 }, 128, 128, 128, 1, 0 }, + + /* 193 - Plane Flyby */ + { { 196, 0 }, 128, 128, 128, 0, 1 }, + + /* 194 - Commander Rocket 2 */ + { { 197, 0 }, 128, 128, 128, 1, 0 }, + + /* 195 - Commander Rocket 3 */ + { { 198, 0 }, 128, 128, 128, 1, 0 }, + + /* 196 - Rescue */ + { { 199, 0 }, 128, 128, 128, 1, 0 }, + + /* 197 - Slow Fanfare */ + { { 200, 0 }, 128, 128, 128, 1, 0 }, + + /* 198 - Plane Crash */ + { { 201, 0 }, 128, 128, 128, 1, 0 }, + + /* 199 - Plane Engine 1 */ + { { 202, 0 }, 128, 128, 128, 0, 1 }, + + /* 200 - Plane Engine 2 */ + { { 203, 0 }, 128, 128, 128, 0, 1 }, + + /* 201 - Boat In */ + { { 204, 0 }, 128, 128, 128, 0, 1 }, + + /* 202 - Boat Out */ + { { 205, 0 }, 128, 128, 128, 0, 1 }, + + /* 203 - Final Fanfare! */ + { { 206, 0 }, 128, 128, 128, 1, 0 }, + + /* 204 - Frank Destroyed */ + { { 207, 0 }, 128, 128, 128, 1, 0 }, + + /* 205 - Jaspar Eats */ + { { 208, 0 }, 128, 128, 128, 0, 1 }, + + /* 206 - Compy Scream 1 */ + { { 209, 0 }, 128, 128, 128, 0, 1 }, + + /* 207 - Compy Scream 2 */ + { { 210, 0 }, 128, 128, 128, 0, 1 }, + + /* 208 - Punch Klunk Fanfare */ + { { 211, 0 }, 128, 128, 128, 1, 0 }, + + /* 209 - Talk Frank */ + { { 212, 0 }, 128, 128, 128, 1, 0 } +}; + +const tuneData Sound::_tuneDemo[] = { + /* 1 - Hotel Gangsters */ + { { 32, 0 }, { 0, 0 }, 1, 0 }, + + /* 2 - Hotel General */ + { { 26, 0 }, { 0, 0 }, 1, 0 }, + + /* 3 - Jungle */ + { { 15, 0 }, { 0, 0 }, 1, 0 }, + + /* 4 - Jungle */ + { { 17, 0 }, { 0, 0 }, 1, 0 }, + + /* 5 - Jungle */ + { { 18, 0 }, { 0, 0 }, 1, 0 }, + + /* 6 - Jungle */ + { { 7, 8, 9, 10, 11, 12, 13, 14, 0 }, { 0, 0 }, 0, 0 }, + + /* 7 - Waterfall On */ + { { 3, 0 }, { 0, 0 }, 1, 0 }, + + /* 8 - Vnormal */ + { { 1, 0 }, { 0, 0 }, 1, 0 }, + + /* 9 - Trader Bob */ + { { 1, 0 }, { 0, 0 }, 1, 0 }, + + /* 10 - Jetty Music */ + { { 37, 0 }, { 0, 0 }, 1, 0 }, + + /* 11 - Ferry Music */ + { { 38, 0 }, { 0, 0 }, 1, 0 }, + + /* 12 - Temple Upstairs */ + { { 30, 0 }, { 0, 0 }, 1, 0 }, + + /* 13 - Temple Downstairs */ + { { 34, 0 }, { 0, 0 }, 1, 0 }, + + /* 14 - Temple Maze */ + { { 35, 0 }, { 0, 0 }, 1, 0 }, + + /* 15 - Temple Skull */ + { { 36, 0 }, { 0, 0 }, 1, 0 }, + + /* 16 - Johns Theme (Love Story) */ + { { 43, 0 }, { 0, 0 }, 1, 0 }, + + /* 17 - Vmedium */ + { { 28, 0 }, { 0, 0 }, 1, 0 }, + + /* 18 - Vsoft */ + { { 28, 0 }, { 0, 0 }, 1, 0 }, + + /* 19 - Floda Upstairs */ + { { 28, 0 }, { 0, 0 }, 1, 0 }, + + /* 20 - Floda General */ + { { 29, 0 }, { 0, 0 }, 1, 0 }, + + /* 21 - Klunks Room */ + { { 39, 0 }, { 0, 0 }, 1, 0 }, + + /* 22 - Hotel Lola */ + { { 31, 0 }, { 0, 0 }, 1, 0 }, + + /* 23 - Hotel Escape 1 */ + { { 33, 0 }, { 0, 0 }, 1, 0 }, + + /* 24 - Amazon Fortress */ + { { 40, 0 }, { 0, 0 }, 1, 0 }, + + /* 25 - Waterfall Off */ + { { -3, 0 }, { 0, 0 }, 1, 0 }, + + /* 26 - Wave Torch */ + { { 22, 0 }, { 121, 0 }, 2, 0 }, + + /* 27 - Zombies Rez Out */ + { { 25, 0 }, { 20, 0 }, 2, 0 }, + + /* 28 - Open Door (standard) */ + { { 20, 0 }, { 1, 0 }, 2, 0 }, + + /* 29 - Close Door (standard) */ + { { 21, 0 }, { 2, 0 }, 2, 0 }, + + /* 30 - Cloth Unrolls */ + { { 23, 0 }, { 51, 0 }, 2, 0 }, + + /* 31 - Snake Slithers Off */ + { { 24, 0 }, { 122, 0 }, 2, 0 }, + + /* 32 - Hotel Fanfare 2 */ + { { 69, 1003, 0 }, { 0, 0 }, 2, 0 }, + + /* 33 - Floda Secret */ + { { 28, 0 }, { 0, 0 }, 1, 0 }, + + /* 34 - Temple Fanfare 1 */ + { { 60, 162, 0 }, { 0, 0 }, 2, 0 }, + + /* 35 - Commander Rocket 1 */ + { { 46, 0 }, { 0, 0 }, 1, 0 }, +}; + +const tuneData Sound::_tune[] = { + /* 1 - Hotel Gangsters */ + { { 32, 0 }, { 0, 0 }, 1, 0 }, + + /* 2 - Hotel General */ + { { 41, 0 }, { 0, 0 }, 1, 0 }, + + /* 3 - Jungle */ + { { 15, 16, 0 }, { 0, 0 }, 1, 0 }, + + /* 4 - Jungle */ + { { 17, 0 }, { 0, 0 }, 1, 0 }, + + /* 5 - Jungle */ + { { 18, 0 }, { 0, 0 }, 1, 0 }, + + /* 6 - Jungle */ + { { 7, 8, 9, 10, 11, 12, 13, 14, 0 }, { 0, 0 }, 0, -10 }, + + /* 7 - Waterfall On */ + { { 3, 0 }, { 0, 0 }, 1, 0 }, + + /* 8 - Vnormal */ + { { 23, 0 }, { 0, 0 }, 1, 0 }, + + /* 9 - Trader Bob */ + { { 23, 0 }, { 0, 0 }, 1, 0 }, + + /* 10 - Jetty Music */ + { { 37, 0 }, { 0, 0 }, 1, 0 }, + + /* 11 - Ferry Music */ + { { 38, 0 }, { 0, 0 }, 1, 0 }, + + /* 12 - Temple Upstairs */ + { { 30, 0 }, { 0, 0 }, 1, 0 }, + + /* 13 - Temple Downstairs */ + { { 34, 36, 56, 0 }, { 0, 0 }, 0, 0 }, + + /* 14 - Temple Maze */ + { { 87, 35, 0 }, { 0, 0 }, 1, 0 }, + + /* 15 - Temple Skull */ + { { 76, 0 }, { 0, 0 }, 1, 0 }, + + /* 16 - Johns Theme (Love Story) */ + { { 44, 0 }, { 0, 0 }, 1, 0 }, + + /* 17 - Vmedium */ + { { 28, 0 }, { 0, 0 }, 1, 0 }, + + /* 18 - Vsoft */ + { { 28, 0 }, { 0, 0 }, 1, 0 }, + + /* 19 - Floda Upstairs */ + { { 28, 39, 0 }, { 0, 0 }, 0, 0 }, + + /* 20 - Floda General */ + { { 89, 63, 64, 65, 0 }, { 0, 0 }, 0, 0 }, + + /* 21 - Klunks Room */ + { { 43, 0 }, { 0, 0 }, 1, 0 }, + + /* 22 - Hotel Lola */ + { { 31, 0 }, { 0, 0 }, 1, 0 }, + + /* 23 - Hotel Escape 1 */ + { { 52, 0 }, { 0, 0 }, 1, 0 }, + + /* 24 - Amazon Fortress */ + { { 40, 0 }, { 0, 0 }, 1, 0 }, + + /* 25 - Waterfall Off */ + { {-3, 0 }, { 0, 0 }, 1, 0 }, + + /* 26 - Wave Torch */ + { { 0, 0 }, { 121, 0 }, 2, 0 }, + + /* 27 - Zombies Rez Out */ + { { 0, 0 }, { 20, 0 }, 2, 0 }, + + /* 28 - Open Door (standard) */ + { { 0, 0 }, { 1, 0 }, 2, 0 }, + + /* 29 - Close Door (standard) */ + { { 0, 0 }, { 2, 0 }, 2, 0 }, + + /* 30 - Cloth Unrolls */ + { { 0, 0 }, { 51, 0 }, 2, 0 }, + + /* 31 - Snake Slithers Off */ + { { 0, 0 }, { 122, 0 }, 2, 0 }, + + /* 32 - Hotel Fanfare 2 */ + { { 69, 2000, 0 }, { 0, 0 }, 2, 0 }, + + /* 33 - Floda Secret */ + { { 29, 42, 0 }, { 0, 0 }, 1, 0 }, + + /* 34 - Temple Fanfare 1 */ + { { 70, 2000, 0 }, { 0, 0 }, 2, 0 }, + + /* 35 - Commander Rocket 1 */ + { { 45, 0 }, { 0, 0 }, 2, 0 }, + + /* 36 - Hotel Escape 2 */ + { { 52, 0 }, { 0, 0 }, 1, 0 }, + + /* 37 - Back of Truck */ + { { 51, 48, 33, 54, 52, 0 }, { 0, 0 }, 1, 0 }, + + /* 38 - Hotel Fanfare 1 */ + { { 67, 2000, 0 }, { 0, 0 }, 2, 0 }, + + /* 39 - Truck Fanfare */ + { { 67, 177, 0 }, { 0, 0 }, 2, 0 }, + + /* 40 - Airport */ + { { 81, 0 }, { 0, 0 }, 1, 0 }, + + /* 41 - Plane Leaves */ + { { 68, 1198, 0 }, { 0, 0 }, 2, 0 }, + + /* 42 - Arrive Hotel */ + { { 26, 0 }, { 0, 0 }, 1, 0 }, + + /* 43 - Jungle Fanfare */ + { { 68, 2000, 0 }, { 0, 0 }, 2, 0 }, + + /* 44 - General Fanfare */ + { { 57, 2000, 0 }, { 0, 0 }, 2, 0 }, + + /* 45 - Johns Room */ + { { 90, 0 }, { 0, 0 }, 1, 0 }, + + /* 46 - Floda Lab */ + { { 92, 0 }, { 0, 0 }, 1, 0 }, + + /* 47 - Azura's Theme */ + { { 80, 0 }, { 0, 0 }, 1, 0 }, + + /* 48 - Use Record */ + { { 91, 2000, 0 }, { 0, 0 }, 2, 0 }, + + /* 49 - Franks Theme */ + { { 77, 0 }, { 0, 0 }, 1, 0 }, + + /* 50 - Anderson Doubts */ + { { 75, 0 }, { 0, 0 }, 1, 0 }, + + /* 51 - Bud and Lou Theme */ + { { 94, 1003, 0 }, { 0, 0 }, 1, 0 }, + + /* 52 - Gorilla Theme */ + { { 97, 1003, 0 }, { 0, 0 }, 1, 0 }, + + /* 53 - Missionaries Theme */ + { { 98, 1003, 0 }, { 0, 0 }, 1, 0 }, + + /* 54 - Sloth Theme */ + { { 100, 1003, 0 }, { 0, 0 }, 1, 0 }, + + /* 55 - Amazon Dungeon */ + { { 96, 0 }, { 0, 0 }, 1, 0 }, + + /* 56 - Throne Room */ + { { 78, 0 }, { 0, 0 }, 1, 0 }, + + /* 57 - Temple Puzzle */ + { { 88, 0 }, { 0, 0 }, 1, 0 }, + + /* 58 - Temple Fountain Room */ + { { 55, 0 }, { 0, 0 }, 1, 0 }, + + /* 59 - Light Switch */ + { { 0, 0 }, { 4, 0 }, 2, 0 }, + + /* 60 - Hydraulic Open */ + { { 0, 0 }, { 7, 0 }, 2, 0 }, + + /* 61 - Hydraulic Close */ + { { 0, 0 }, { 8, 0 }, 2, 0 }, + + /* 62 - Close Door (metal) */ + { { 0, 0 }, { 9, 0 }, 2, 0 }, + + /* 63 - Small Hatch Close */ + { { 0, 0 }, { 10, 0 }, 2, 0 }, + + /* 64 - Scissors Snip */ + { { 0, 0 }, { 5, 0 }, 2, 0 }, + + /* 65 - Pick up Sticky */ + { { 0, 0 }, { 6, 0 }, 2, 0 }, + + /* 66 - Oracle Rezzes In */ + { { 0, 0 }, { 11, 0 }, 2, 0 }, + + /* 67 - Sparkle SFX */ + { { 0, 0 }, { 12, 0 }, 2, 0 }, + + /* 68 - Splorch! */ + { { 0, 0 }, { 13, 0 }, 2, 0 }, + + /* 69 - Pour Liquid */ + { { 0, 0 }, { 3, 0 }, 2, 0 }, + + /* 70 - End Credit Medley */ + { { 95, 0 }, { 0, 0 }, 1, 0 }, + + /* 71 - Dino Ray */ + { { 0, 0 }, { 14, 0 }, 2, 0 }, + + /* 72 - Squish! */ + { { 0, 0 }, { 15, 0 }, 2, 0 }, + + /* 73 - Robot Laser */ + { { 0, 0 }, { 16, 0 }, 2, 0 }, + + /* 74 - Thud wood light */ + { { 0, 0 }, { 17, 0 }, 2, 0 }, + + /* 75 - Thud wood deep */ + { { 0, 0 }, { 18, 0 }, 2, 0 }, + + /* 76 - Thud metallic */ + { { 0, 0 }, { 19, 0 }, 2, 0 }, + + /* 77 - Cut Coconut */ + { { 0, 0 }, { 22, 0 }, 2, 0 }, + + /* 78 - Thud Stone */ + { { 0, 0 }, { 23, 0 }, 2, 0 }, + + /* 79 - Cloth Slide 1 */ + { { 0, 0 }, { 24, 0 }, 2, 0 }, + + /* 80 - Open Chest */ + { { 0, 0 }, { 25, 0 }, 2, 0 }, + + /* 81 - Close Chest */ + { { 0, 0 }, { 26, 0 }, 2, 0 }, + + /* 82 - Open Drawer */ + { { 0, 0 }, { 27, 0 }, 2, 0 }, + + /* 83 - Truck door closes */ + { { 0, 0 }, { 28, 0 }, 2, 0 }, + + /* 84 - Truck Starts */ + { { 0, 0 }, { 29, 0 }, 2, 0 }, + + /* 85 - Truck Drives Off */ + { { 0, 0 }, { 30, 0 }, 2, 0 }, + + /* 86 - Fish Splash */ + { { 0, 0 }, { 31, 0 }, 2, 0 }, + + /* 87 - Close Drawer/Push Ladder */ + { { 0, 0 }, { 33, 0 }, 2, 0 }, + + /* 88 - Agression Enhancer */ + { { 0, 0 }, { 32, 0 }, 2, 0 }, + + /* 89 - Stone Door Grind 1 */ + { { 0, 0 }, { 78, 0 }, 2, 0 }, + + /* 90 - Prequel 1 */ + { { 20, 0 }, { 0, 0 }, 1, 0 }, + + /* 91 - Intro Credits */ + { { 21, 0 }, { 0, 0 }, 2, 0 }, + + /* 92 - Valley 1 */ + { { 71, 0 }, { 0, 0 }, 1, 0 }, + + /* 93 - Valley 3 */ + { { 73, 0 }, { 0, 0 }, 1, 0 }, + + /* 94 - Fight Music */ + { { 72, 0 }, { 0, 0 }, 1, 0 }, + + /* 95 - Confrontation 1 */ + { { 93, 0 }, { 0, 0 }, 1, 0 }, + + /* 96 - Confrontation 2 */ + { { 74, 0 }, { 0, 0 }, 1, 0 }, + + /* 97 - Plane Hatch Open */ + { { 0, 0 }, { 35, 0 }, 2, 0 }, + + /* 98 - Plane Hatch Close */ + { { 0, 0 }, { 36, 0 }, 2, 0 }, + + /* 99 - Tie Vines */ + { { 0, 0 }, { 37, 0 }, 2, 0 }, + + /* 100 - Pterodactyl */ + { { 0, 0 }, { 38, 0 }, 2, 0 }, + + /* 101 - Beef Jerky Splash */ + { { 0, 0 }, { 39, 0 }, 2, 0 }, + + /* 102 - Piranha Burp */ + { { 0, 0 }, { 40, 0 }, 2, 0 }, + + /* 103 - Falling Vine */ + { { 0, 0 }, { 41, 0 }, 2, 0 }, + + /* 104 - Stone Door Grind 2 */ + { { 0, 0 }, { 79, 0 }, 2, 0 }, + + /* 105 - Stone Grind (light) */ + { { 0, 0 }, { 82, 0 }, 2, 0 }, + + /* 106 - Ape Takes Off Mask */ + { { 0, 0 }, { 44, 0 }, 2, 0 }, + + /* 107 - Bark Breaks */ + { { 0, 0 }, { 45, 0 }, 2, 0 }, + + /* 108 - Stone Click */ + { { 0, 0 }, { 46, 0 }, 2, 0 }, + + /* 109 - Sproing! */ + { { 0, 0 }, { 42, 0 }, 2, 0 }, + + /* 110 - Cash Register */ + { { 0, 0 }, { 48, 0 }, 2, 0 }, + + /* 111 - Squeaky Toy */ + { { 0, 0 }, { 49, 0 }, 2, 0 }, + + /* 112 - Falling Chains */ + { { 0, 0 }, { 50, 0 }, 2, 0 }, + + /* 113 - Open Locker Door */ + { { 0, 0 }, { 52, 0 }, 2, 0 }, + + /* 114 - Close Locker Door */ + { { 0, 0 }, { 53, 0 }, 2, 0 }, + + /* 115 - Rub Pencil */ + { { 0, 0 }, { 54, 0 }, 2, 0 }, + + /* 116 - Open Safe */ + { { 0, 0 }, { 55, 0 }, 2, 0 }, + + /* 117 - Close Safe */ + { { 0, 0 }, { 56, 0 }, 2, 0 }, + + /* 118 - Push Chair */ + { { 0, 0 }, { 57, 0 }, 2, 0 }, + + /* 119 - Snake Hiss */ + { { 0, 0 }, { 58, 0 }, 2, 0 }, + + /* 120 - Oracle Rezzes Out */ + { { 0, 0 }, { 59, 0 }, 2, 0 }, + + /* 121 - Wall Crumbles */ + { { 0, 0 }, { 60, 0 }, 2, 0 }, + + /* 122 - Crypt Crumbles */ + { { 0, 0 }, { 61, 0 }, 2, 0 }, + + /* 123 - Joe Sucked Up */ + { { 0, 0 }, { 63, 0 }, 2, 0 }, + + /* 124 - Rocket Pack Zoom */ + { { 0, 0 }, { 47, 0 }, 2, 0 }, + + /* 125 - Piranha Splash */ + { { 0, 0 }, { 83, 0 }, 2, 0 }, + + /* 126 - Snap Branch */ + { { 0, 0 }, { 66, 0 }, 2, 0 }, + + /* 127 - Dino Horn */ + { { 0, 0 }, { 67, 0 }, 2, 0 }, + + /* 128 - Tyre Screech */ + { { 0, 0 }, { 68, 0 }, 2, 0 }, + + /* 129 - Oil Splat */ + { { 0, 0 }, { 70, 0 }, 2, 0 }, + + /* 130 - Punch */ + { { 0, 0 }, { 71, 0 }, 2, 0 }, + + /* 131 - Body Hits Ground */ + { { 0, 0 }, { 72, 0 }, 2, 0 }, + + /* 132 - Chicken */ + { { 0, 0 }, { 69, 0 }, 2, 0 }, + + /* 133 - Open Sarcophagus */ + { { 0, 0 }, { 21, 0 }, 2, 0 }, + + /* 134 - Close Sarcophagus */ + { { 0, 0 }, { 21, 0 }, 2, 0 }, + + /* 135 - Creaking Stick */ + { { 0, 0 }, { 62, 0 }, 2, 0 }, + + /* 136 - Pick Hits Stone */ + { { 0, 0 }, { 73, 0 }, 2, 0 }, + + /* 137 - Stalactite Crumbles */ + { { 0, 0 }, { 74, 0 }, 2, 0 }, + + /* 138 - Tic-Toc */ + { { 0, 0 }, { 76, 0 }, 2, 0 }, + + /* 139 - Stone Grind (heavy) */ + { { 0, 0 }, { 81, 0 }, 2, 0 }, + + /* 140 - Explosion */ + { { 0, 0 }, { 77, 0 }, 2, 0 }, + + /* 141 - Cloth Slide 2 */ + { { 0, 0 }, { 84, 0 }, 2, 0 }, + + /* 142 - Temple Laser */ + { { 0, 0 }, { 85, 0 }, 2, 0 }, + + /* 143 - Dino Transformation */ + { { 0, 0 }, { 86, 0 }, 2, 0 }, + + /* 144 - Experimental Laser */ + { { 0, 0 }, { 87, 0 }, 2, 0 }, + + /* 145 - Stone Grind (medium) */ + { { 0, 0 }, { 88, 0 }, 2, 0 }, + + /* 146 - Weeping God Grind */ + { { 0, 0 }, { 89, 0 }, 2, 0 }, + + /* 147 - Alien Hum */ + { { 0, 0 }, { 90, 0 }, 2, 0 }, + + /* 148 - Alien Puzzle */ + { { 0, 0 }, { 91, 0 }, 2, 0 }, + + /* 149 - Vacuum On */ + { { 0, 0 }, { 92, 0 }, 2, 0 }, + + /* 150 - Vacuum Off */ + { { 0, 0 }, { 93, 0 }, 2, 0 }, + + /* 151 - Elevator Starts */ + { { 0, 0 }, { 94, 0 }, 2, 0 }, + + /* 152 - Mummy Crumbles */ + { { 0, 0 }, { 95, 0 }, 2, 0 }, + + /* 153 - Temple Green Circle */ + { { 0, 0 }, { 96, 0 }, 2, 0 }, + + /* 154 - Rattle Bars */ + { { 0, 0 }, { 97, 0 }, 2, 0 }, + + /* 155 - Door Dissolves */ + { { 0, 0 }, { 98, 0 }, 2, 0 }, + + /* 156 - Altar Slides */ + { { 0, 0 }, { 99, 0 }, 2, 0 }, + + /* 157 - Light Torch */ + { { 0, 0 }, { 100, 0 }, 2, 0 }, + + /* 158 - Stamp Sound */ + { { 0, 0 }, { 34, 0 }, 2, 0 }, + + /* 159 - Plaster Loud */ + { { 0, 0 }, { 102, 0 }, 2, 0 }, + + /* 160 - Sparky Bathtub */ + { { 0, 0 }, { 103, 0 }, 2, 0 }, + + /* 161 - Ape Rezzes Out */ + { { 0, 0 }, { 104, 0 }, 2, 0 }, + + /* 162 - Song 159 */ + { { 0, 0 }, { 0, 0 }, 2, 0 }, + + /* 163 - Song 160 */ + { { 0, 0 }, { 0, 0 }, 2, 0 }, + + /* 164 - Song 161 */ + { { 0, 0 }, { 0, 0 }, 2, 0 }, + + /* 165 - Piranhas Swim */ + { { 0, 0 }, { 105, 0 }, 2, 0 }, + + /* 166 - Prison/Dungeon Door */ + { { 0, 0 }, { 43, 0 }, 2, 0 }, + + /* 167 - Fight Explosion */ + { { 0, 0 }, { 80, 0 }, 2, 0 }, + + /* 168 - Press Button */ + { { 0, 0 }, { 65, 0 }, 2, 0 }, + + /* 169 - Pull Lever */ + { { 0, 0 }, { 107, 0 }, 2, 0 }, + + /* 170 - Wrong Code */ + { { 0, 0 }, { 108, 0 }, 2, 0 }, + + /* 171 - Correct Code */ + { { 0, 0 }, { 109, 0 }, 2, 0 }, + + /* 172 - Sizzle */ + { { 0, 0 }, { 110, 0 }, 2, 0 }, + + /* 173 - Money In Slot */ + { { 0, 0 }, { 111, 0 }, 2, 0 }, + + /* 174 - Lightning Crack */ + { { 0, 0 }, { 112, 0 }, 2, 0 }, + + /* 175 - Machine Gun Fire */ + { { 0, 0 }, { 113, 0 }, 2, 0 }, + + /* 176 - Cage Descends */ + { { 0, 0 }, { 114, 0 }, 2, 0 }, + + /* 177 - Chair Activates */ + { { 0, 0 }, { 115, 0 }, 2, 0 }, + + /* 178 - Robot Powers On */ + { { 0, 0 }, { 116, 0 }, 2, 0 }, + + /* 179 - Grow Big */ + { { 0, 0 }, { 117, 0 }, 2, 0 }, + + /* 180 - Eat Food */ + { { 0, 0 }, { 118, 0 }, 2, 0 }, + + /* 181 - Head Shrink */ + { { 0, 0 }, { 119, 0 }, 2, 0 }, + + /* 182 - Grinding Gears */ + { { 0, 0 }, { 120, 0 }, 2, 0 }, + + /* 183 - Chair Splash */ + { { 0, 0 }, { 123, 0 }, 2, 0 }, + + /* 184 - Deflect Laser */ + { { 0, 0 }, { 124, 0 }, 2, 0 }, + + /* 185 - Zap Frank */ + { { 0, 0 }, { 125, 0 }, 2, 0 }, + + /* 186 - Frank Transforms */ + { { 0, 0 }, { 126, 0 }, 2, 0 }, + + /* 187 - Alarm Clock */ + { { 0, 0 }, { 127, 0 }, 2, 0 }, + + /* 188 - Slide Chute */ + { { 0, 0 }, { 64, 0 }, 2, 0 }, + + /* 189 - Puff */ + { { 0, 0 }, { 128, 0 }, 2, 0 }, + + /* 190 - Bite */ + { { 0, 0 }, { 129, 0 }, 2, 0 }, + + /* 191 - Stone Door Grind 2 */ + { { 0, 0 }, { 79, 0 }, 2, 0 }, + + /* 192 - Prequel 2 */ + { { 22, 0 }, { 0, 0 }, 1, 0 }, + + /* 193 - Prequel 3 */ + { { 24, 0 }, { 0, 0 }, 1, 0 }, + + /* 194 - Prequel 4 */ + { { 25, 0 }, { 0, 0 }, 1, 0 }, + + /* 195 - Stop Music */ + { { 1, 0 }, { 0, 0 }, 1, 0 }, + + /* 196 - Plane Flyby */ + { { 0, 0 }, { 101, 0 }, 2, 0 }, + + /* 197 - Commander Rocket 2 */ + { { 46, 0 }, { 0, 0 }, 1, 0 }, + + /* 198 - Commander Rocket 3 */ + { { 47, 0 }, { 0, 0 }, 1, 0 }, + + /* 199 - Rescue */ + { { 99, 0 }, { 0, 0 }, 1, 0 }, + + /* 200 - Slow Fanfare */ + { { 0, 0 }, { 0, 0 }, 1, 0 }, + + /* 201 - Plane Crash */ + { { 93, 0 }, { 0, 0 }, 1, 0 }, + + /* 202 - Plane Engine 1 */ + { { 0, 0 }, { 130, 0 }, 2, 0 }, + + /* 203 - Plane Engine 2 */ + { { 0, 0 }, { 131, 0 }, 2, 0 }, + + /* 204 - Boat In */ + { { 0, 0 }, { 132, 0 }, 2, 0 }, + + /* 205 - Boat Out */ + { { 0, 0 }, { 133, 0 }, 2, 0 }, + + /* 206 - Final Fanfare! */ + { { 21, 95, 21, 0 }, { 0, 0 }, 1, 0 }, + + /* 207 - Frank Destroyed */ + { { 25, 1044, 0 }, { 0, 0 }, 1, 0 }, + + + /* 208 - Jaspar Eats */ + { { 0, 0 }, { 134, 0 }, 2, 0 }, + + /* 209 - Compy Scream 1 */ + { { 0, 0 }, { 135, 0 }, 2, 0 }, + + /* 210 - Compy Scream 2 */ + { { 0, 0 }, { 136, 0 }, 2, 0 }, + + /* 211 - Punch Klunk Fanfare */ + { { 69, 1017, 0 }, { 0, 0 }, 1, 0 }, + + /* 212 - Talk Frank */ + { { 77, 1017, 0 }, { 0, 0 }, 1, 0 } +}; + +const char *Sound::_sfxName[] = { + /* 1 - Door Open (standard) */ + "116Bssss", + + /* 2 - Door Close (standard) */ + "105assss", + + /* 3 - Pour Liquid */ + "133sssss", + + /* 4 - Light Switch */ + "27ssssss", + + /* 5 - Scissor Snip */ + "15ssssss", + + /* 6 - Pick up Sticky */ + "79ssssss", + + /* 7 - Hydraulic Doors Open */ + "96ssssss", + + /* 8 - Hydraulic Doors Close */ + "97ssssss", + + /* 9 - Metallic Door Slams */ + "105sssss", + + /* 10 - Small Hatch Close */ + "106sssss", + + /* 11 - Oracle Rezzes In */ + "132sssss", + + /* 12 - Polish Sparkle */ + "132Cssss", + + /* 13 - Splorch! */ + "137Bssss", + + /* 14 - Dino Ray Gun */ + "138sssss", + + /* 15 - Squish! */ + "137Assss", + + /* 16 - Robot Laser */ + "61ssssss", + + /* 17 - Thud wood light */ + "109sssss", + + /* 18 - Thud wood deep */ + "110sssss", + + /* 19 - Thud metallic */ + "111sssss", + + /* 20 - Zombies Rez Out */ + "77ssssss", + + /* 21 - Sarc Door Closes */ + "58ssssss", + + /* 22 - Thud breadboard fruit/Coconut */ + "131Assss", + + /* 23 - Thud stone */ + "75ssssss", + + /* 24 - Cloth Slide 1 */ + "135sssss", + + /* 25 - Open Chest */ + "112sssss", + + /* 26 - Close Chest */ + "121sssss", + + /* 27 - Open Drawer */ + "120sssss", + + /* 28 - Truck door closes */ + "122sssss", + + /* 29 - Truck Starts */ + "123Assss", + + /* 30 - Truck Drives Off */ + "123Bssss", + + /* 31 - Fish Splash */ + "18ssssss", + + /* 32 - Agression Enhancer */ + "138Bssss", + + /* 33 - Close Drawer/Push Ladder */ + "113sssss", + + /* 34 - *Stamp Sound */ + "40ssssss", + + /* 35 - plane hatch open */ + "3sssssss", + + /* 36 - plane hatch close */ + "4sssssss", + + /* 37 - tie vines */ + "11ssssss", + + /* 38 - Pterodactyl */ + "10ssssss", + + /* 39 - Beef Jerky Splash */ + "6sssssss", + + /* 40 - Piranha Burp */ + "7sssssss", + + /* 41 - Falling Vine */ + "13ssssss", + + /* 42 - Sproing! */ + "29ssssss", + + /* 43 - Prison/Dungeon Door */ + "33ssssss", + + /* 44 - Ape takes off mask */ + "24ssssss", + + /* 45 - Bark breaks */ + "25ssssss", + + /* 46 - Stone Click */ + "136sssss", + + /* 47 - Rocket Pack Zoom */ + "1006ssss", + + /* 48 - Cash Register */ + "36ssssss", + + /* 49 - Squeaky Toy */ + "37ssssss", + + /* 50 - Falling Chains */ + "38ssssss", + + /* 51 - Cloth Unravels */ + "64ssssss", + + /* 52 - Open Locker Door */ + "48ssssss", + + /* 53 - Close Locker Door */ + "49ssssss", + + /* 54 - Rub Pencil on Pad */ + "50ssssss", + + /* 55 - Open Safe */ + "51ssssss", + + /* 56 - Close Safe */ + "52ssssss", + + /* 57 - Push Chair */ + "59ssssss", + + /* 58 - Snake Hiss */ + "83ssssss", + + /* 59 - Oracle Rezzes Out */ + "70ssssss", + + /* 60 - Wall Crumbles */ + "73Asssss", + + /* 61 - Crypt Crumbles */ + "76ssssss", + + /* 62 - Creaking Stick */ + "74Asssss", + + /* 63 - Joe Sucked Up */ + "80ssssss", + + /* 64 - Slide Chute */ + "114assss", + + /* 65 - Press Button */ + "1007ssss", + + /* 66 - Snap Branch */ + "101sssss", + + /* 67 - Dino Horn */ + "103sssss", + + /* 68 - Tyre Screech */ + "125sssss", + + /* 69 - Chicken */ + "126sssss", + + /* 70 - Oil Splat */ + "127sssss", + + /* 71 - Punch */ + "128sssss", + + /* 72 - Body Hits Ground */ + "129sssss", + + /* 73 - Pick Hits Stone */ + "71ssssss", + + /* 74 - Stalactite Crumbles */ + "119sssss", + + /* 75 - *Drip */ + "93ssssss", + + /* 76 - Tic-Toc */ + "42Bsssss", + + /* 77 - Explosion */ + "88ssssss", + + /* 78 - Stone Door Grind 1 */ + "1001ssss", + + /* 79 - Stone Door Grind 2 */ + "1002ssss", + + /* 80 - *Fight Explosion */ + "1000ssss", + + /* 81 - Stone Grind (heavy) */ + "1003ssss", + + /* 82 - Stone Grind (light) */ + "89ssssss", + + /* 83 - Piranha Splash */ + "5sssssss", + + /* 84 - Cloth Slide 2 */ + "1005ssss", + + /* 85 - Temple Laser */ + "87ssssss", + + /* 86 - Dino Transformation */ + "55Bsssss", + + /* 87 - Experimental Laser */ + "55ssssss", + + /* 88 - Stone Grind (medium) */ + "134sssss", + + /* 89 - Weeping God Grind */ + "94ssssss", + + /* 90 - Alien Hum */ + "95ssssss", + + /* 91 - Alien Puzzle */ + "103Assss", + + /* 92 - Vacuum On */ + "21ssssss", + + /* 93 - Vacuum Off */ + "21Csssss", + + /* 94 - Elevator Starts */ + "44ssssss", + + /* 95 - Mummy Crumbles */ + "68ssssss", + + /* 96 - Temple Green Circle */ + "60Bsssss", + + /* 97 - Rattle Bars */ + "115sssss", + + /* 98 - Door Dissolves */ + "56ssssss", + + /* 99 - Altar Slides */ + "85ssssss", + + /* 100 - Light Torch */ + "81ssssss", + + /* 101 - Plane Flyby */ + "1027ssss", + + /* 102 - Plaster Loud */ + "41Bsssss", + + /* 103 - Sparky Bathtub */ + "73ssssss", + + /* 104 - Ape Rezzes Out */ + "14ssssss", + + /* 105 - Piranhas Swim */ + "17ssssss", + + /* 106 - *Gun Shot */ + "1004ssss", + + /* 107 - Pull Lever */ + "1008ssss", + + /* 108 - Wrong Code */ + "1009ssss", + + /* 109 - Correct Code */ + "1010ssss", + + /* 110 - Sizzle */ + "1011ssss", + + /* 111 - Money In Slot */ + "1012ssss", + + /* 112 - Lightning */ + "1013ssss", + + /* 113 - Machine Gun Fire */ + "1014ssss", + + /* 114 - Cage Descends */ + "1015ssss", + + /* 115 - Temple Chair Activates */ + "1016ssss", + + /* 116 - Robot Powers On */ + "1017ssss", + + /* 117 - Grow Big */ + "1018ssss", + + /* 118 - Eat Food */ + "1019ssss", + + /* 119 - Head Shrink */ + "1020ssss", + + /* 120 - Grinding Gears */ + "84ssssss", + + /* 121 - Wave Torch */ + "1021ssss", + + /* 122 - Snake Slithers Off */ + "1022ssss", + + /* 123 - Chair Splash */ + "26ssssss", + + /* 124 - Deflect Laser */ + "60ssssss", + + /* 125 - Zap Frank */ + "1023ssss", + + /* 126 - Frank Transforms */ + "1024ssss", + + /* 127 - Alarm Clock */ + "1025ssss", + + /* 128 - Puff */ + "35ssssss", + + /* 129 - Bite */ + "1026ssss", + + /* 130 - Plane Engine 1 */ + "1028ssss", + + /* 131 - Plane Engine 2 */ + "1029ssss", + + /* 132 - Boat In */ + "1030ssss", + + /* 133 - Boat Out */ + "1031ssss", + + /* 134 - Jaspar Eats */ + "1032ssss", + + /* 135 - Compy Scream 1 */ + "1033ssss", + + /* 136 - Compy Scream 2 */ + "1034ssss" +}; + +const int16 Sound::_jungleList[] = { 15, 16, 17, 18, 7, 8, 9, 10, 11, 12, 13, 14, 0 }; +#endif + +} // End of namespace Queen + +#ifdef PALMOS_68K +#include "scumm_globals.h" + +_GINIT(Queen_Musicdata) +_GSETPTR(Queen::Sound::_songDemo, GBVARS_MUSICDATASONGDEMO_INDEX, Queen::songData, GBVARS_QUEEN) +_GSETPTR(Queen::Sound::_song, GBVARS_MUSICDATASONG_INDEX, Queen::songData, GBVARS_QUEEN) +_GSETPTR(Queen::Sound::_tuneDemo, GBVARS_MUSICDATATUNEDEMO_INDEX, Queen::tuneData, GBVARS_QUEEN) +_GSETPTR(Queen::Sound::_tune, GBVARS_MUSICDATATUNE_INDEX, Queen::tuneData, GBVARS_QUEEN) +_GSETPTR(Queen::Sound::_sfxName, GBVARS_MUSICDATASFXNAME_INDEX, char, GBVARS_QUEEN) +_GSETPTR(Queen::Sound::_jungleList, GBVARS_MUSICDATAJUNGLELIST_INDEX, int16, GBVARS_QUEEN) +_GEND + +_GRELEASE(Queen_Musicdata) +_GRELEASEPTR(GBVARS_MUSICDATASONGDEMO_INDEX, GBVARS_QUEEN) +_GRELEASEPTR(GBVARS_MUSICDATASONG_INDEX, GBVARS_QUEEN) +_GRELEASEPTR(GBVARS_MUSICDATATUNEDEMO_INDEX, GBVARS_QUEEN) +_GRELEASEPTR(GBVARS_MUSICDATATUNE_INDEX, GBVARS_QUEEN) +_GRELEASEPTR(GBVARS_MUSICDATASFXNAME_INDEX, GBVARS_QUEEN) +_GRELEASEPTR(GBVARS_MUSICDATAJUNGLELIST_INDEX, GBVARS_QUEEN) +_GEND + +#endif diff --git a/engines/queen/queen.cpp b/engines/queen/queen.cpp new file mode 100644 index 0000000000..e6c6f39269 --- /dev/null +++ b/engines/queen/queen.cpp @@ -0,0 +1,454 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "backends/fs/fs.h" + +#include "base/gameDetector.h" +#include "base/plugins.h" + +#include "common/config-manager.h" +#include "common/file.h" +#include "common/savefile.h" +#include "common/system.h" + +#include "queen/queen.h" +#include "queen/bankman.h" +#include "queen/command.h" +#include "queen/cutaway.h" +#include "queen/debug.h" +#include "queen/display.h" +#include "queen/graphics.h" +#include "queen/grid.h" +#include "queen/input.h" +#include "queen/logic.h" +#include "queen/music.h" +#include "queen/resource.h" +#include "queen/sound.h" +#include "queen/talk.h" +#include "queen/walk.h" + +#include "sound/mididrv.h" + +#ifdef _WIN32_WCE +bool isSmartphone(); +#endif + +/* Flight of the Amazon Queen */ +static const GameSettings queen_setting[] = { + { "queen", "Flight of the Amazon Queen", 0 }, + { "queen", "Flight of the Amazon Queen (Demo)", 0 }, + { "queen", "Flight of the Amazon Queen (Interview)", 0 }, + { 0, 0, 0 } +}; + +GameList Engine_QUEEN_gameList() { + GameList games; + const GameSettings *g = queen_setting; + + while (g->gameid) { + games.push_back(*g); + g++; + } + return games; +} + +GameSettings determineTarget(uint32 size) { + switch (size) { + case 3724538: //regular demo + case 3732177: + return queen_setting[1]; + break; + case 1915913: //interview demo + return queen_setting[2]; + break; + default: //non-demo + return queen_setting[0]; + break; + } + return queen_setting[0]; +} + +DetectedGameList Engine_QUEEN_detectGames(const FSList &fslist) { + DetectedGameList detectedGames; + + // Iterate over all files in the given directory + for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { + if (!file->isDirectory()) { + const char *gameName = file->displayName().c_str(); + + if (0 == scumm_stricmp("queen.1", gameName) || 0 == scumm_stricmp("queen.1c", gameName)) { + Common::File dataFile; + dataFile.open(file->path().c_str()); + assert(dataFile.isOpen()); + + if (0 == scumm_stricmp("queen.1", gameName)) { //an unmodified file + detectedGames.push_back(determineTarget(dataFile.size())); + } else if (0 == scumm_stricmp("queen.1c", gameName)) { //oh joy, it's a rebuilt file + char header[9]; + dataFile.read(header, 9); + if (0 == scumm_strnicmp("QTBL", header, 4)) { //check validity + uint8 version = 0; //default to full/normal version + + if (0 == scumm_strnicmp("PE100", header + 4, 5)) //One of the 2 regular demos + version = 1; + if (0 == scumm_strnicmp("PEint", header + 4, 5)) //Interview demo + version = 2; + + detectedGames.push_back(queen_setting[version]); + } + } + + dataFile.close(); + break; + } + } + } + return detectedGames; +} + +Engine *Engine_QUEEN_create(GameDetector *detector, OSystem *syst) { + return new Queen::QueenEngine(detector, syst); +} + +REGISTER_PLUGIN(QUEEN, "Flight of the Amazon Queen") + +namespace Queen { + +QueenEngine::QueenEngine(GameDetector *detector, OSystem *syst) + : Engine(syst), _debugger(0) { +} + +QueenEngine::~QueenEngine() { + delete _bam; + delete _resource; + delete _bankMan; + delete _command; + delete _debugger; + delete _display; + delete _graphics; + delete _grid; + delete _input; + delete _logic; + delete _music; + delete _sound; + delete _walk; +} + +void QueenEngine::registerDefaultSettings() { + ConfMan.registerDefault("music_mute", false); + ConfMan.registerDefault("sfx_mute", false); + ConfMan.registerDefault("talkspeed", Logic::DEFAULT_TALK_SPEED); + ConfMan.registerDefault("speech_mute", _resource->isDemo() || _resource->isInterview()); + ConfMan.registerDefault("subtitles", true); +} + +void QueenEngine::checkOptionSettings() { + // check talkspeed value + if (_talkSpeed < MIN_TEXT_SPEED) { + _talkSpeed = MIN_TEXT_SPEED; + } else if (_talkSpeed > MAX_TEXT_SPEED) { + _talkSpeed = MAX_TEXT_SPEED; + } + + // ensure text is always on when voice is off + if (!_sound->speechOn()) { + _subtitles = true; + } + + // demo and interview versions don't have speech at all + if (_sound->speechOn() && (_resource->isDemo() || _resource->isInterview())) { + _sound->speechToggle(false); + } +} + +void QueenEngine::readOptionSettings() { + _music->setVolume(ConfMan.getInt("music_volume")); + _sound->musicToggle(!ConfMan.getBool("music_mute")); + _sound->sfxToggle(!ConfMan.getBool("sfx_mute")); + _talkSpeed = ConfMan.getInt("talkspeed"); + _sound->speechToggle(!ConfMan.getBool("speech_mute")); + _subtitles = ConfMan.getBool("subtitles"); + checkOptionSettings(); +} + +void QueenEngine::writeOptionSettings() { + ConfMan.set("music_volume", _music->volume()); + ConfMan.set("music_mute", !_sound->musicOn()); + ConfMan.set("sfx_mute", !_sound->sfxOn()); + ConfMan.set("talkspeed", _talkSpeed); + ConfMan.set("speech_mute", !_sound->speechOn()); + ConfMan.set("subtitles", _subtitles); + ConfMan.flushToDisk(); +} + +void QueenEngine::update(bool checkPlayerInput) { + if (_debugger->isAttached()) { + _debugger->onFrame(); + } + + _graphics->update(_logic->currentRoom()); + _logic->update(); + + _input->delay(); + + if (!_resource->isInterview()) { + _display->palCustomScroll(_logic->currentRoom()); + } + BobSlot *joe = _graphics->bob(0); + _display->update(joe->active, joe->x, joe->y); + + _input->checkKeys(); + if (_input->debugger()) { + _input->debuggerReset(); + _debugger->attach(); + } + if (canLoadOrSave()) { + if (_input->quickSave()) { + _input->quickSaveReset(); + saveGameState(0, "Quicksave"); + } + if (_input->quickLoad()) { + _input->quickLoadReset(); + loadGameState(0); + } + if (shouldPerformAutoSave(_lastSaveTime)) { + saveGameState(AUTOSAVE_SLOT, "Autosave"); + _lastSaveTime = _system->getMillis(); + } + } + if (!_input->cutawayRunning()) { + if (checkPlayerInput) { + _command->updatePlayer(); + } + if (_input->idleTime() >= Input::DELAY_SCREEN_BLANKER) { + _display->blankScreen(); + } + } +} + +bool QueenEngine::canLoadOrSave() const { + return !_input->cutawayRunning() && !(_resource->isDemo() || _resource->isInterview()); +} + +void QueenEngine::saveGameState(uint16 slot, const char *desc) { + debug(3, "Saving game to slot %d", slot); + char name[20]; + makeGameStateName(slot, name); + Common::OutSaveFile *file = _saveFileMan->openForSaving(name); + if (file) { + // save data + byte *saveData = new byte[SAVESTATE_MAX_SIZE]; + byte *p = saveData; + _bam->saveState(p); + _grid->saveState(p); + _logic->saveState(p); + _sound->saveState(p); + uint32 dataSize = p - saveData; + assert(dataSize < SAVESTATE_MAX_SIZE); + + // write header + GameStateHeader header; + memset(&header, 0, sizeof(header)); + file->writeUint32BE('SCVM'); + header.version = TO_BE_32(SAVESTATE_CUR_VER); + header.flags = TO_BE_32(0); + header.dataSize = TO_BE_32(dataSize); + strncpy(header.description, desc, sizeof(header.description) - 1); + file->write(&header, sizeof(header)); + + // write save data + file->write(saveData, dataSize); + file->flush(); + + // check for errors + if (file->ioFailed()) { + warning("Can't write file '%s'. (Disk full?)", name); + } + delete[] saveData; + delete file; + } else { + warning("Can't create file '%s', game not saved", name); + } +} + +void QueenEngine::loadGameState(uint16 slot) { + debug(3, "Loading game from slot %d", slot); + GameStateHeader header; + Common::InSaveFile *file = readGameStateHeader(slot, &header); + if (file && header.dataSize != 0) { + byte *saveData = new byte[header.dataSize]; + byte *p = saveData; + if (file->read(saveData, header.dataSize) != header.dataSize) { + warning("Error reading savegame file"); + } else { + _bam->loadState(header.version, p); + _grid->loadState(header.version, p); + _logic->loadState(header.version, p); + _sound->loadState(header.version, p); + if (header.dataSize != (uint32)(p - saveData)) { + warning("Corrupted savegame file"); + } else { + _logic->setupRestoredGame(); + } + } + delete[] saveData; + delete file; + } +} + +Common::InSaveFile *QueenEngine::readGameStateHeader(uint16 slot, GameStateHeader *gsh) { + char name[20]; + makeGameStateName(slot, name); + Common::InSaveFile *file = _saveFileMan->openForLoading(name); + if (file && file->readUint32BE() == 'SCVM') { + gsh->version = file->readUint32BE(); + gsh->flags = file->readUint32BE(); + gsh->dataSize = file->readUint32BE(); + file->read(gsh->description, sizeof(gsh->description)); + } else { + memset(gsh, 0, sizeof(GameStateHeader)); + } + return file; +} + +void QueenEngine::makeGameStateName(uint16 slot, char *buf) { + if (slot == AUTOSAVE_SLOT) { + strcpy(buf, "queen.asd"); + } else { + sprintf(buf, "queen.s%02d", slot); + } +} + +void QueenEngine::findGameStateDescriptions(char descriptions[100][32]) { + char filename[20]; + makeGameStateName(0, filename); + filename[strlen(filename) - 2] = 0; + bool marks[SAVESTATE_MAX_NUM]; + _saveFileMan->listSavefiles(filename, marks, SAVESTATE_MAX_NUM); + for (int i = 0; i < SAVESTATE_MAX_NUM; ++i) { + if (marks[i]) { + GameStateHeader header; + Common::InSaveFile *f = readGameStateHeader(i, &header); + strcpy(descriptions[i], header.description); + delete f; + } + } +} + +void QueenEngine::errorString(const char *buf1, char *buf2) { + strcpy(buf2, buf1); + +#ifdef _WIN32_WCE + if (isSmartphone()) + return; +#endif + + // Unless an error -originated- within the debugger, spawn the + // debugger. Otherwise exit out normally. + if (_debugger && !_debugger->isAttached()) { + // (Print it again in case debugger segfaults) + printf("%s\n", buf2); + _debugger->attach(buf2); + _debugger->onFrame(); + } +} + +int QueenEngine::go() { + _logic->start(); + if (ConfMan.hasKey("save_slot") && canLoadOrSave()) { + loadGameState(ConfMan.getInt("save_slot")); + } + _lastSaveTime = _system->getMillis(); + _quit = false; + while (!_quit) { + if (_logic->newRoom() > 0) { + _logic->update(); + _logic->oldRoom(_logic->currentRoom()); + _logic->currentRoom(_logic->newRoom()); + _logic->changeRoom(); + _display->fullscreen(false); + if (_logic->currentRoom() == _logic->newRoom()) { + _logic->newRoom(0); + } + } else if (_logic->joeWalk() == JWM_EXECUTE) { + _logic->joeWalk(JWM_NORMAL); + _command->executeCurrentAction(); + } else { + _logic->joeWalk(JWM_NORMAL); + update(true); + } + } + return 0; +} + +int QueenEngine::init(GameDetector &detector) { + _system->beginGFXTransaction(); + initCommonGFX(detector); + _system->initSize(GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT); + _system->endGFXTransaction(); + + _bam = new BamScene(this); + _resource = new Resource(); + _bankMan = new BankManager(_resource); + _command = new Command(this); + _debugger = new Debugger(this); + _display = new Display(this, _system); + _graphics = new Graphics(this); + _grid = new Grid(this); + _input = new Input(_resource->getLanguage(), _system); + + if (_resource->isDemo()) { + _logic = new LogicDemo(this); + } else if (_resource->isInterview()) { + _logic = new LogicInterview(this); + } else { + _logic = new LogicGame(this); + } + + if (!_mixer->isReady()) + warning("Sound initialisation failed"); + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); + // Set mixer music volume to maximum, since music volume is regulated by MusicPlayer's MIDI messages + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, Audio::Mixer::kMaxMixerVolume); + + int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI); + bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32")); + + MidiDriver *driver = MidiDriver::createMidi(midiDriver); + if (native_mt32) + driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); + + _music = new Music(driver, this); + _music->hasNativeMT32(native_mt32); + + _sound = Sound::giveSound(_mixer, this, _resource->compression()); + _walk = new Walk(this); + + registerDefaultSettings(); + readOptionSettings(); + + return 0; +} + +} // End of namespace Queen diff --git a/engines/queen/queen.h b/engines/queen/queen.h new file mode 100644 index 0000000000..91c657cf14 --- /dev/null +++ b/engines/queen/queen.h @@ -0,0 +1,164 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEEN_H +#define QUEEN_H + +#include "base/engine.h" + +class GameDetector; +namespace Common { + class InSaveFile; +} + +#if defined(_WIN32_WCE) && (_WIN32_WCE <= 300) + +FORCEINLINE int16 READ_BE_INT16(const void *ptr) { + uint16 result; + char dummy[2]; + result = READ_BE_UINT16(ptr); + strcpy(dummy, "x"); // Hello, I'm a drunk optimizer. Thanks for helping me. + return result; +} + +#else + +#define READ_BE_INT16 READ_BE_UINT16 + +#endif + +namespace Queen { + +#if !defined(__GNUC__) + #pragma START_PACK_STRUCTS +#endif + +struct GameStateHeader { + uint32 version; + uint32 flags; + uint32 dataSize; + char description[32]; +} GCC_PACK; + +#if !defined(__GNUC__) + #pragma END_PACK_STRUCTS +#endif + +class BamScene; +class BankManager; +class Command; +class Debugger; +class Display; +class Graphics; +class Grid; +class Input; +class Logic; +class Music; +class Resource; +class Sound; +class Walk; + +class QueenEngine : public Engine { +public: + + QueenEngine(GameDetector *detector, OSystem *syst); + virtual ~QueenEngine(); + + BamScene *bam() const { return _bam; } + BankManager *bankMan() const { return _bankMan; } + Command *command() const { return _command; } + Debugger *debugger() const { return _debugger; } + Display *display() const { return _display; } + Graphics *graphics() const { return _graphics; } + Grid *grid() const { return _grid; } + Input *input() const { return _input; } + Logic *logic() const { return _logic; } + Music *music() const { return _music; } + Resource *resource() const { return _resource; } + Sound *sound() const { return _sound; } + Walk *walk() const { return _walk; } + + Common::RandomSource randomizer; + + void registerDefaultSettings(); + void checkOptionSettings(); + void readOptionSettings(); + void writeOptionSettings(); + + int talkSpeed() const { return _talkSpeed; } + void talkSpeed(int speed) { _talkSpeed = speed; } + bool subtitles() const { return _subtitles; } + void subtitles(bool enable) { _subtitles = enable; } + void quitGame() { _quit = true; } + + void update(bool checkPlayerInput = false); + + bool canLoadOrSave() const; + void saveGameState(uint16 slot, const char *desc); + void loadGameState(uint16 slot); + void makeGameStateName(uint16 slot, char *buf); + void findGameStateDescriptions(char descriptions[100][32]); + Common::InSaveFile *readGameStateHeader(uint16 slot, GameStateHeader *gsh); + + enum { + SAVESTATE_CUR_VER = 1, + SAVESTATE_MAX_NUM = 100, + SAVESTATE_MAX_SIZE = 30000, + + AUTOSAVE_SLOT = 0xFF, + + MIN_TEXT_SPEED = 4, + MAX_TEXT_SPEED = 100, + MAX_MUSIC_VOLUME = 255 + }; + +protected: + + void errorString(const char *buf_input, char *buf_output); + + int go(); + int init(GameDetector &detector); + + + int _talkSpeed; + bool _subtitles; + bool _quit; + uint32 _lastSaveTime; + + BamScene *_bam; + BankManager *_bankMan; + Command *_command; + Debugger *_debugger; + Display *_display; + Graphics *_graphics; + Grid *_grid; + Input *_input; + Logic *_logic; + Music *_music; + Resource *_resource; + Sound *_sound; + Walk *_walk; +}; + +} // End of namespace Queen + +#endif diff --git a/engines/queen/resource.cpp b/engines/queen/resource.cpp new file mode 100644 index 0000000000..51fa47b687 --- /dev/null +++ b/engines/queen/resource.cpp @@ -0,0 +1,287 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/resource.h" + +namespace Queen { + +#ifdef PALMOS_68K +static ResourceEntry *_resourceTablePEM10; +#endif + +const char *Resource::_tableFilename = "queen.tbl"; + +const GameVersion Resource::_gameVersions[] = { + { "PEM10", 0x00000008, 22677657 }, + { "CEM10", 0x0000584E, 190787021 }, + { "PFM10", 0x0002CD93, 22157304 }, + { "CFM10", 0x00032585, 186689095 }, + { "PGM10", 0x00059ACA, 22240013 }, + { "CGM10", 0x0005F2A7, 217648975 }, + { "PIM10", 0x000866B1, 22461366 }, + { "CIM10", 0x0008BEE2, 190795582 }, + { "CSM10", 0x000B343C, 190730602 }, + { "CHM10", 0x000DA981, 190705558 }, + { "PE100", 0x00101EC6, 3724538 }, + { "PE100", 0x00102B7F, 3732177 }, + { "PEint", 0x00103838, 1915913 } +}; + +static int compareResourceEntry(const void *a, const void *b) { + const char *filename = (const char *)a; + const ResourceEntry *entry = (const ResourceEntry *)b; + return strcmp(filename, entry->filename); +} + +Resource::Resource() + : _resourceEntries(0), _resourceTable(NULL) { + _resourceFile = new Common::File(); + if (!findCompressedVersion() && !findNormalVersion()) + error("Could not open resource file '%s'", "queen.1"); + checkJASVersion(); + debug(5, "Detected game version: %s, which has %d resource entries", _versionString, _resourceEntries); +} + +Resource::~Resource() { + _resourceFile->close(); + delete _resourceFile; + + if (_resourceTable != _resourceTablePEM10) + delete[] _resourceTable; +} + +ResourceEntry *Resource::resourceEntry(const char *filename) const { + assert(filename[0] && strlen(filename) < 14); + + char entryName[14]; + char *ptr = entryName; + + strcpy(entryName, filename); + do + *ptr = toupper(*ptr); + while (*ptr++); + + ResourceEntry *re = NULL; +#ifndef PALMOS_MODE + re = (ResourceEntry *)bsearch(entryName, _resourceTable, _resourceEntries, sizeof(ResourceEntry), compareResourceEntry); +#else + // PALMOS FIXME (?) : still doesn't work for me (????) use this instead + uint32 cur = 0; + do { + if (!strcmp(entryName, _resourceTable[cur].filename)) { + re = &_resourceTable[cur]; + break; + } + } while (cur++ < _resourceEntries); +#endif + return re; +} + +uint8 *Resource::loadFile(const char *filename, uint32 skipBytes, uint32 *size, bool useMalloc) { + ResourceEntry *re = resourceEntry(filename); + assert(re != NULL); + uint32 sz = re->size - skipBytes; + if (size != NULL) { + *size = sz; + } + + byte *dstBuf; + if (useMalloc) { + dstBuf = (byte *)malloc(sz); + } else { + dstBuf = new byte[sz]; + } + + _resourceFile->seek(re->offset + skipBytes); + _resourceFile->read(dstBuf, sz); + return dstBuf; +} + +bool Resource::findNormalVersion() { + _resourceFile->open("queen.1"); + if (!_resourceFile->isOpen()) { + return false; + } + + _compression = COMPRESSION_NONE; + + // detect game version based on resource file size ; we try to + // verify that it is indeed the version we think it is later on + const GameVersion *gameVersion = detectGameVersion(_resourceFile->size()); + if (gameVersion == NULL) + error("Unknown/unsupported FOTAQ version"); + + strcpy(_versionString, gameVersion->versionString); + if (!readTableFile(gameVersion)) { + // check if it is the english floppy version, for which we have a hardcoded version of the table + if (!strcmp(gameVersion->versionString, _gameVersions[VER_ENG_FLOPPY].versionString)) { + _resourceEntries = 1076; + _resourceTable = _resourceTablePEM10; + } else { + error("Could not find tablefile '%s'", _tableFilename); + } + } + return true; +} + +bool Resource::findCompressedVersion() { + _resourceFile->open("queen.1c"); + if (!_resourceFile->isOpen()) { + return false; + } + readTableCompResource(); + return true; +} + +void Resource::checkJASVersion() { + ResourceEntry *re = resourceEntry("QUEEN.JAS"); + assert(re != NULL); + uint32 offset = re->offset; + if (isDemo()) + offset += JAS_VERSION_OFFSET_DEMO; + else if (isInterview()) + offset += JAS_VERSION_OFFSET_INTV; + else + offset += JAS_VERSION_OFFSET_PC; + _resourceFile->seek(offset); + + char versionStr[6]; + _resourceFile->read(versionStr, 6); + if (strcmp(_versionString, versionStr)) + error("Verifying game version failed! (expected: '%s', found: '%s')", _versionString, versionStr); +} + +Language Resource::getLanguage() const { + switch (_versionString[1]) { + case 'E': + return ENGLISH; + case 'G': + return GERMAN; + case 'F': + return FRENCH; + case 'I': + return ITALIAN; + case 'S': + return SPANISH; + case 'H': + return HEBREW; + default: + return ENGLISH; + } +} + +bool Resource::readTableFile(const GameVersion *gameVersion) { + Common::File tableFile; + tableFile.open(_tableFilename); + if (tableFile.isOpen() && tableFile.readUint32BE() == 'QTBL') { + if (tableFile.readUint32BE() != CURRENT_TBL_VERSION) + warning("Incorrect version of queen.tbl, please update it"); + tableFile.seek(gameVersion->tableOffset); + readTableEntries(&tableFile); + return true; + } + return false; +} + +void Resource::readTableCompResource() { + if (_resourceFile->readUint32BE() != 'QTBL') + error("Invalid table header"); + + _resourceFile->read(_versionString, 6); + _resourceFile->readByte(); // obsolete + _resourceFile->readByte(); // obsolete + _compression = _resourceFile->readByte(); + + readTableEntries(_resourceFile); +} + +void Resource::readTableEntries(Common::File *file) { + _resourceEntries = file->readUint16BE(); + _resourceTable = new ResourceEntry[_resourceEntries]; + for (uint16 i = 0; i < _resourceEntries; ++i) { + ResourceEntry *re = &_resourceTable[i]; + file->read(re->filename, 12); + re->filename[12] = '\0'; + re->bundle = file->readByte(); + re->offset = file->readUint32BE(); + re->size = file->readUint32BE(); + } +} + +const GameVersion *Resource::detectGameVersion(uint32 size) const { + const GameVersion *pgv = _gameVersions; + for (int i = 0; i < VER_COUNT; ++i, ++pgv) { + if (pgv->dataFileSize == size) { + return pgv; + } + } + return NULL; +} + +Common::File *Resource::giveCompressedSound(const char *filename, uint32 *size) { + assert(strstr(filename, ".SB")); + Common::File *f = NULL; + ResourceEntry *re = resourceEntry(filename); + if (re) { + if (size != NULL) { + *size = re->size; + } + _resourceFile->seek(re->offset); + f = _resourceFile; + } + return f; +} + +LineReader::LineReader(char *buffer, uint32 bufsize) : _buffer(buffer), _bufSize(bufsize), _current(0) { +} + +LineReader::~LineReader() { + delete[] _buffer; +} + +char *LineReader::nextLine() { + char *startOfLine = _buffer + _current; + char *curPos = startOfLine; + while (curPos < _buffer + _bufSize && *curPos++ != 0xd) ; + *(curPos - 1) = '\0'; // '\r' + if (curPos < _buffer + _bufSize) { + *curPos = '\0'; // '\n' + _current = (curPos - _buffer) + 1; + } + return startOfLine; +} + +} // End of namespace Queen + +#ifdef PALMOS_68K +#include "scumm_globals.h" + +_GINIT(Queen_Restables) +_GSETPTR(Queen::_resourceTablePEM10, GBVARS_RESOURCETABLEPM10_INDEX, Queen::ResourceEntry, GBVARS_QUEEN) +_GEND + +_GRELEASE(Queen_Restables) +_GRELEASEPTR(GBVARS_RESOURCETABLEPM10_INDEX, GBVARS_QUEEN) +_GEND + +#endif diff --git a/engines/queen/resource.h b/engines/queen/resource.h new file mode 100644 index 0000000000..e205490eca --- /dev/null +++ b/engines/queen/resource.h @@ -0,0 +1,169 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENRESOURCE_H +#define QUEENRESOURCE_H + +#include "common/file.h" +#include "common/util.h" +#include "queen/defs.h" + +namespace Queen { + +struct ResourceEntry { + char filename[13]; + uint8 bundle; + uint32 offset; + uint32 size; +}; + +struct GameVersion { + char versionString[6]; + uint32 tableOffset; + uint32 dataFileSize; +}; + +class LineReader { +public: + + LineReader(char *buffer, uint32 bufsize); + ~LineReader(); + char *nextLine(); + +private: + + char *_buffer; + uint32 _bufSize; + int _current; +}; + +class Resource { +public: + + Resource(); + ~Resource(); + + //! loads the specified from the resource file + uint8 *loadFile(const char *filename, uint32 skipBytes = 0, uint32 *size = NULL, bool useMalloc = false); + + //! returns true if the file is present in the resource + bool fileExists(const char *filename) const { return resourceEntry(filename) != NULL; } + + //! returns a reference to a sound file + Common::File *giveCompressedSound(const char *filename, uint32 *size); + + bool isDemo() const { return !strcmp(_versionString, "PE100"); } + bool isInterview() const { return !strcmp(_versionString, "PEint"); } + bool isFloppy() const { return _versionString[0] == 'P'; } + bool isCD() const { return _versionString[0] == 'C'; } + + //! returns compression type for audio files + uint8 compression() const { return _compression; } + + //! returns JAS version string (contains language, platform and version information) + const char *JASVersion() const { return _versionString; } + + //! returns language of the game + Language getLanguage() const; + + enum Version { + VER_ENG_FLOPPY = 0, + VER_ENG_TALKIE = 1, + VER_FRE_FLOPPY = 2, + VER_FRE_TALKIE = 3, + VER_GER_FLOPPY = 4, + VER_GER_TALKIE = 5, + VER_ITA_FLOPPY = 6, + VER_ITA_TALKIE = 7, + VER_SPA_TALKIE = 8, + VER_HEB_TALKIE = 9, + VER_DEMO_PCGAMES = 10, + VER_DEMO = 11, + VER_INTERVIEW = 12, + + VER_COUNT = 13 + }; + + enum { + CURRENT_TBL_VERSION = 1 + }; + + enum { + JAS_VERSION_OFFSET_DEMO = 0x119A8, + JAS_VERSION_OFFSET_INTV = 0xCF8, + JAS_VERSION_OFFSET_PC = 0x12484 + }; + +protected: + + Common::File *_resourceFile; + + //! compression type for audio files + uint8 _compression; + + //! JAS version string of the game + char _versionString[6]; + + //! number of entries in resource table + uint32 _resourceEntries; + + ResourceEntry *_resourceTable; + + //! look for a normal queen version (ie. queen.1) + bool findNormalVersion(); + + //! look for a compressed/rebuilt queen version (ie. queen.1c) + bool findCompressedVersion(); + + //! verify the version of the selected game + void checkJASVersion(); + + //! returns a reference to the ReseourceEntry for the specified filename + ResourceEntry *resourceEntry(const char *filename) const; + + //! extarct the resource table for the specified game version + bool readTableFile(const GameVersion *gameVersion); + + //! reads the resource table from a rebuilt datafile (ie. queen.1c) + void readTableCompResource(); + + //! read the resource table from the specified file + void readTableEntries(Common::File *file); + + //! detect game version based on queen.1 datafile size + const GameVersion *detectGameVersion(uint32 size) const; + + //! resource table filename (queen.tbl) + static const char *_tableFilename; + + //! known FOTAQ versions + static const GameVersion _gameVersions[]; + +#ifndef PALMOS_68K + //! resource table for english floppy version + static ResourceEntry _resourceTablePEM10[]; +#endif +}; + +} // End of namespace Queen + +#endif diff --git a/engines/queen/restables.cpp b/engines/queen/restables.cpp new file mode 100644 index 0000000000..8cebaccf49 --- /dev/null +++ b/engines/queen/restables.cpp @@ -0,0 +1,1108 @@ +/* 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$ + * $Id$ + * + */ + +#include "queen/resource.h" + +namespace Queen { + +#ifndef PALMOS_68K +//English Floppy Version +ResourceEntry Resource::_resourceTablePEM10[] = { + { "1000SSSS.SB", 1, 0x00000000, 0x000027fe }, + { "1001SSSS.SB", 1, 0x000027fe, 0x00007af8 }, + { "1002SSSS.SB", 1, 0x0000a2f6, 0x000049e2 }, + { "1003SSSS.SB", 1, 0x0000ecd8, 0x00001d42 }, + { "1004SSSS.SB", 1, 0x00010a1a, 0x00001a1c }, + { "1005SSSS.SB", 1, 0x00012436, 0x00001a8a }, + { "1006SSSS.SB", 1, 0x00013ec0, 0x00009d41 }, + { "1007SSSS.SB", 1, 0x0001dc01, 0x00001372 }, + { "1008SSSS.SB", 1, 0x0001ef73, 0x00002e4a }, + { "1009SSSS.SB", 1, 0x00021dbd, 0x0000220c }, + { "1010SSSS.SB", 1, 0x00023fc9, 0x00003b1e }, + { "1011SSSS.SB", 1, 0x00027ae7, 0x00000dd8 }, + { "1012SSSS.SB", 1, 0x000288bf, 0x0000444c }, + { "1013SSSS.SB", 1, 0x0002cd0b, 0x00007e66 }, + { "1014SSSS.SB", 1, 0x00034b71, 0x00006e14 }, + { "1015SSSS.SB", 1, 0x0003b985, 0x0000760c }, + { "1016SSSS.SB", 1, 0x00042f91, 0x00004f8b }, + { "1017SSSS.SB", 1, 0x00047f1c, 0x00004848 }, + { "1018SSSS.SB", 1, 0x0004c764, 0x00007e94 }, + { "1019SSSS.SB", 1, 0x000545f8, 0x00003a70 }, + { "101SSSSS.SB", 1, 0x00058068, 0x0000191a }, + { "1020SSSS.SB", 1, 0x00059982, 0x00004d90 }, + { "1021SSSS.SB", 1, 0x0005e712, 0x00003dcc }, + { "1023SSSS.SB", 1, 0x000624de, 0x00003d7f }, + { "1024SSSS.SB", 1, 0x0006625d, 0x00004f8a }, + { "1025SSSS.SB", 1, 0x0006b1e7, 0x00006488 }, + { "1026SSSS.SB", 1, 0x0007166f, 0x00002022 }, + { "1027SSSS.SB", 1, 0x00073691, 0x00009e76 }, + { "1028SSSS.SB", 1, 0x0007d507, 0x0001b37c }, + { "1029SSSS.SB", 1, 0x00098883, 0x0000ce2c }, + { "1030SSSS.SB", 1, 0x000a56af, 0x0001e6e4 }, + { "1031SSSS.SB", 1, 0x000c3d93, 0x00011532 }, + { "1032SSSS.SB", 1, 0x000d52c5, 0x000034d4 }, + { "1033SSSS.SB", 1, 0x000d8799, 0x00002de6 }, + { "1034SSSS.SB", 1, 0x000db57f, 0x000099a1 }, + { "103ASSSS.SB", 1, 0x000e4f20, 0x00005040 }, + { "103SSSSS.SB", 1, 0x000e9f60, 0x00006a94 }, + { "105ASSSS.SB", 1, 0x000f09f4, 0x00001d04 }, + { "105SSSSS.SB", 1, 0x000f26f8, 0x000024de }, + { "106SSSSS.SB", 1, 0x000f4bd6, 0x00000ac4 }, + { "109SSSSS.SB", 1, 0x000f569a, 0x00000d96 }, + { "10SSSSSS.SB", 1, 0x000f6430, 0x000013f6 }, + { "110SSSSS.SB", 1, 0x000f7826, 0x00000c96 }, + { "111SSSSS.SB", 1, 0x000f84bc, 0x00000f72 }, + { "112SSSSS.SB", 1, 0x000f942e, 0x00000f1a }, + { "113SSSSS.SB", 1, 0x000fa348, 0x00003f5e }, + { "114ASSSS.SB", 1, 0x000fe2a6, 0x00001e54 }, + { "115SSSSS.SB", 1, 0x001000fa, 0x00002580 }, + { "116BSSSS.SB", 1, 0x0010267a, 0x00002350 }, + { "119SSSSS.SB", 1, 0x001049ca, 0x00003808 }, + { "11SSSSSS.SB", 1, 0x001081d2, 0x0000157e }, + { "120SSSSS.SB", 1, 0x00109750, 0x00002be4 }, + { "121SSSSS.SB", 1, 0x0010c334, 0x00001340 }, + { "122SSSSS.SB", 1, 0x0010d674, 0x0000173e }, + { "123ASSSS.SB", 1, 0x0010edb2, 0x00009e9d }, + { "123BSSSS.SB", 1, 0x00118c4f, 0x0000f613 }, + { "125SSSSS.SB", 1, 0x00128262, 0x0000e628 }, + { "126SSSSS.SB", 1, 0x0013688a, 0x0000372e }, + { "127SSSSS.SB", 1, 0x00139fb8, 0x0000ff1e }, + { "128SSSSS.SB", 1, 0x00149ed6, 0x00000e34 }, + { "129SSSSS.SB", 1, 0x0014ad0a, 0x00000b02 }, + { "130SSSSS.SB", 1, 0x0014b80c, 0x00000a10 }, + { "131ASSSS.SB", 1, 0x0014c21c, 0x000022d4 }, + { "132CSSSS.SB", 1, 0x0014e4f0, 0x000094c3 }, + { "132SSSSS.SB", 1, 0x001579b3, 0x00006183 }, + { "133SSSSS.SB", 1, 0x0015db36, 0x00006a13 }, + { "134SSSSS.SB", 1, 0x00164549, 0x00004ea3 }, + { "135SSSSS.SB", 1, 0x001693ec, 0x000017c8 }, + { "136SSSSS.SB", 1, 0x0016abb4, 0x000004a0 }, + { "137ASSSS.SB", 1, 0x0016b054, 0x00001826 }, + { "137BSSSS.SB", 1, 0x0016c87a, 0x00001d46 }, + { "138BSSSS.SB", 1, 0x0016e5c0, 0x000065f9 }, + { "138SSSSS.SB", 1, 0x00174bb9, 0x000053c3 }, + { "13SSSSSS.SB", 1, 0x00179f7c, 0x00001f32 }, + { "14SSSSSS.SB", 1, 0x0017beae, 0x00004921 }, + { "15SSSSSS.SB", 1, 0x001807cf, 0x000007aa }, + { "17SSSSSS.SB", 1, 0x00180f79, 0x00005080 }, + { "18SSSSSS.SB", 1, 0x00185ff9, 0x000015e2 }, + { "21CSSSSS.SB", 1, 0x001875db, 0x000048ec }, + { "21SSSSSS.SB", 1, 0x0018bec7, 0x0000bb40 }, + { "24SSSSSS.SB", 1, 0x00197a07, 0x000023fc }, + { "25SSSSSS.SB", 1, 0x00199e03, 0x000016d8 }, + { "26SSSSSS.SB", 1, 0x0019b4db, 0x000049dd }, + { "27SSSSSS.SB", 1, 0x0019feb8, 0x000010a0 }, + { "29SSSSSS.SB", 1, 0x001a0f58, 0x00004e50 }, + { "33SSSSSS.SB", 1, 0x001a5da8, 0x00005218 }, + { "35SSSSSS.SB", 1, 0x001aafc0, 0x00002c6c }, + { "36SSSSSS.SB", 1, 0x001adc2c, 0x000031d2 }, + { "37SSSSSS.SB", 1, 0x001b0dfe, 0x0000197a }, + { "38SSSSSS.SB", 1, 0x001b2778, 0x00002162 }, + { "3SSSSSSS.SB", 1, 0x001b48da, 0x00000d04 }, + { "40SSSSSS.SB", 1, 0x001b55de, 0x00000268 }, + { "41BSSSSS.SB", 1, 0x001b5846, 0x000005fe }, + { "42BSSSSS.SB", 1, 0x001b5e44, 0x00000308 }, + { "44SSSSSS.SB", 1, 0x001b614c, 0x000052c0 }, + { "48SSSSSS.SB", 1, 0x001bb40c, 0x000010a8 }, + { "49SSSSSS.SB", 1, 0x001bc4b4, 0x00001dd4 }, + { "4SSSSSSS.SB", 1, 0x001be288, 0x000009d2 }, + { "50SSSSSS.SB", 1, 0x001bec5a, 0x00003f8a }, + { "51SSSSSS.SB", 1, 0x001c2be4, 0x000015da }, + { "52SSSSSS.SB", 1, 0x001c41be, 0x0000125a }, + { "55BSSSSS.SB", 1, 0x001c5418, 0x00003d7f }, + { "55SSSSSS.SB", 1, 0x001c9197, 0x00004508 }, + { "56SSSSSS.SB", 1, 0x001cd69f, 0x0000520b }, + { "58SSSSSS.SB", 1, 0x001d28aa, 0x00003399 }, + { "59SSSSSS.SB", 1, 0x001d5c43, 0x000032b8 }, + { "5SSSSSSS.SB", 1, 0x001d8efb, 0x00004ae9 }, + { "60BSSSSS.SB", 1, 0x001dd9e4, 0x000050f2 }, + { "60SSSSSS.SB", 1, 0x001e2ad6, 0x00003c57 }, + { "61SSSSSS.SB", 1, 0x001e672d, 0x0000513d }, + { "64SSSSSS.SB", 1, 0x001eb86a, 0x00005a98 }, + { "68SSSSSS.SB", 1, 0x001f1302, 0x00003e85 }, + { "6SSSSSSS.SB", 1, 0x001f5187, 0x00001650 }, + { "70SSSSSS.SB", 1, 0x001f67d7, 0x00009645 }, + { "71SSSSSS.SB", 1, 0x001ffe1c, 0x00000fa3 }, + { "73ASSSSS.SB", 1, 0x00200dbf, 0x00008812 }, + { "73SSSSSS.SB", 1, 0x002095d1, 0x00006706 }, + { "74ASSSSS.SB", 1, 0x0020fcd7, 0x000029b2 }, + { "75SSSSSS.SB", 1, 0x00212689, 0x00000920 }, + { "76SSSSSS.SB", 1, 0x00212fa9, 0x00004ef8 }, + { "77SSSSSS.SB", 1, 0x00217ea1, 0x00003358 }, + { "79SSSSSS.SB", 1, 0x0021b1f9, 0x000016a8 }, + { "7SSSSSSS.SB", 1, 0x0021c8a1, 0x000010d4 }, + { "80SSSSSS.SB", 1, 0x0021d975, 0x0000441e }, + { "81SSSSSS.SB", 1, 0x00221d93, 0x0000517f }, + { "83SSSSSS.SB", 1, 0x00226f12, 0x000043e3 }, + { "84SSSSSS.SB", 1, 0x0022b2f5, 0x00003baa }, + { "85SSSSSS.SB", 1, 0x0022ee9f, 0x00004470 }, + { "87SSSSSS.SB", 1, 0x0023330f, 0x00005113 }, + { "88SSSSSS.SB", 1, 0x00238422, 0x00005275 }, + { "89SSSSSS.SB", 1, 0x0023d697, 0x00001a69 }, + { "93SSSSSS.SB", 1, 0x0023f100, 0x00000470 }, + { "94SSSSSS.SB", 1, 0x0023f570, 0x000051e4 }, + { "95SSSSSS.SB", 1, 0x00244754, 0x0000343b }, + { "96SSSSSS.SB", 1, 0x00247b8f, 0x00005110 }, + { "97SSSSSS.SB", 1, 0x0024cc9f, 0x00004972 }, + { "AMAZON.ACT", 1, 0x00251611, 0x0000a97a }, + { "AND1.DOG", 1, 0x0025bf8b, 0x000009ca }, + { "ANDERSON.ACT", 1, 0x0025c955, 0x00007c0a }, + { "ANDSON2.ACT", 1, 0x0026455f, 0x00007c0a }, + { "ANDSON_E.ACT", 1, 0x0026c169, 0x00003c42 }, + { "APE.ACT", 1, 0x0026fdab, 0x000084cd }, + { "APE.DOG", 1, 0x00278278, 0x000011d4 }, + { "APE2.DOG", 1, 0x0027944c, 0x000008da }, + { "APE3.DOG", 1, 0x00279d26, 0x00000788 }, + { "AQ.RL", 1, 0x0027a4ae, 0x00063f3a }, + { "AQ8.RL", 1, 0x002de3e8, 0x000167f8 }, + { "AQB2.MUS", 1, 0x002f4be0, 0x00039972 }, + { "AQBANK.MUS", 1, 0x0032e552, 0x0004fe89 }, + { "AQBANK.RL", 1, 0x0037e3db, 0x000059e9 }, + { "AZURA_E.ACT", 1, 0x00383dc4, 0x0000425a }, + { "AZURA_H.ACT", 1, 0x0038801e, 0x00008a7a }, + { "AZURA_H.BBK", 1, 0x00390a98, 0x0001a712 }, + { "AZURA_H.PCX", 1, 0x003ab1aa, 0x0000accb }, + { "B1.BBK", 1, 0x003b5e75, 0x000051fb }, + { "B1.LUM", 1, 0x003bb070, 0x00000018 }, + { "B1.MSK", 1, 0x003bb088, 0x00001f40 }, + { "B1.PCX", 1, 0x003bcfc8, 0x000162a8 }, + { "B2.BBK", 1, 0x003d3270, 0x000034b1 }, + { "B2.LUM", 1, 0x003d6721, 0x00000018 }, + { "B2.MSK", 1, 0x003d6739, 0x00001f40 }, + { "B2.PCX", 1, 0x003d8679, 0x0000a521 }, + { "BAT.SAM", 1, 0x003e2b9a, 0x00009d9a }, + { "BEETLE.ACT", 1, 0x003ec934, 0x00001e42 }, + { "BIGAM.ACT", 1, 0x003ee776, 0x00002d12 }, + { "BLANK000.SB", 1, 0x003f1488, 0x00000076 }, + { "BLUEP.CUT", 1, 0x003f14fe, 0x00000148 }, + { "BOB1.DOG", 1, 0x003f1646, 0x000010bc }, + { "BOB2.DOG", 1, 0x003f2702, 0x00000e5a }, + { "BOB3.DOG", 1, 0x003f355c, 0x00000df8 }, + { "BOB4.DOG", 1, 0x003f4354, 0x00000dea }, + { "BOB5.DOG", 1, 0x003f513e, 0x00000bf0 }, + { "BUD.ACT", 1, 0x003f5d2e, 0x00006582 }, + { "BUD1.DOG", 1, 0x003fc2b0, 0x0000129c }, + { "BUD2.DOG", 1, 0x003fd54c, 0x00000c0e }, + { "C1.BBK", 1, 0x003fe15a, 0x000026fd }, + { "C1.LUM", 1, 0x00400857, 0x00000018 }, + { "C1.MSK", 1, 0x0040086f, 0x00001f40 }, + { "C1.PCX", 1, 0x004027af, 0x00004888 }, + { "C10.BBK", 1, 0x00407037, 0x0003949f }, + { "C10.PCX", 1, 0x004404d6, 0x0000e6fd }, + { "C100A.CUT", 1, 0x0044ebd3, 0x000003be }, + { "C100B.CUT", 1, 0x0044ef91, 0x00000346 }, + { "C100C.CUT", 1, 0x0044f2d7, 0x0000019a }, + { "C100D.CUT", 1, 0x0044f471, 0x00000774 }, + { "C101A.CUT", 1, 0x0044fbe5, 0x0000021a }, + { "C101B.CUT", 1, 0x0044fdff, 0x000000e6 }, + { "C101C.CUT", 1, 0x0044fee5, 0x00000200 }, + { "C101D.CUT", 1, 0x004500e5, 0x00000144 }, + { "C101E.CUT", 1, 0x00450229, 0x00000144 }, + { "C102A.CUT", 1, 0x0045036d, 0x0000040e }, + { "C102B.CUT", 1, 0x0045077b, 0x0000040e }, + { "C102C.CUT", 1, 0x00450b89, 0x0000047e }, + { "C102D.CUT", 1, 0x00451007, 0x000003b8 }, + { "C102E.CUT", 1, 0x004513bf, 0x0000037e }, + { "C103A.CUT", 1, 0x0045173d, 0x000000b4 }, + { "C103B.CUT", 1, 0x004517f1, 0x00000104 }, + { "C103C.CUT", 1, 0x004518f5, 0x00000090 }, + { "C103D.CUT", 1, 0x00451985, 0x00000070 }, + { "C103E.CUT", 1, 0x004519f5, 0x000000da }, + { "C103F.CUT", 1, 0x00451acf, 0x0000084a }, + { "C103G.CUT", 1, 0x00452319, 0x0000068e }, + { "C103H.CUT", 1, 0x004529a7, 0x000003be }, + { "C103I.CUT", 1, 0x00452d65, 0x000001f2 }, + { "C103J.CUT", 1, 0x00452f57, 0x0000041c }, + { "C103K.CUT", 1, 0x00453373, 0x0000016a }, + { "C103L.CUT", 1, 0x004534dd, 0x00000458 }, + { "C11.BBK", 1, 0x00453935, 0x0000bf94 }, + { "C11.PCX", 1, 0x0045f8c9, 0x0000c01a }, + { "C11A.CUT", 1, 0x0046b8e3, 0x00000346 }, + { "C12A.CUT", 1, 0x0046bc29, 0x000004d0 }, + { "C13A.CUT", 1, 0x0046c0f9, 0x00000164 }, + { "C13B.CUT", 1, 0x0046c25d, 0x0000016c }, + { "C13C.CUT", 1, 0x0046c3c9, 0x000001fa }, + { "C13D.CUT", 1, 0x0046c5c3, 0x00000174 }, + { "C13E.CUT", 1, 0x0046c737, 0x000001d0 }, + { "C13F.CUT", 1, 0x0046c907, 0x000002d6 }, + { "C13G.CUT", 1, 0x0046cbdd, 0x00000152 }, + { "C13H.CUT", 1, 0x0046cd2f, 0x00000116 }, + { "C13I.CUT", 1, 0x0046ce45, 0x00000080 }, + { "C13J.CUT", 1, 0x0046cec5, 0x00000272 }, + { "C13K.CUT", 1, 0x0046d137, 0x00000192 }, + { "C13L.CUT", 1, 0x0046d2c9, 0x000001dc }, + { "C13M.CUT", 1, 0x0046d4a5, 0x00000468 }, + { "C13N.CUT", 1, 0x0046d90d, 0x0000015e }, + { "C14A.CUT", 1, 0x0046da6b, 0x000002fe }, + { "C14B.CUT", 1, 0x0046dd69, 0x00000126 }, + { "C14C.CUT", 1, 0x0046de8f, 0x00000150 }, + { "C14D.CUT", 1, 0x0046dfdf, 0x00000108 }, + { "C15A.CUT", 1, 0x0046e0e7, 0x00000264 }, + { "C15B.CUT", 1, 0x0046e34b, 0x00000136 }, + { "C15C.CUT", 1, 0x0046e481, 0x00000380 }, + { "C15D.CUT", 1, 0x0046e801, 0x000002d2 }, + { "C15E.CUT", 1, 0x0046ead3, 0x000000f4 }, + { "C16A.CUT", 1, 0x0046ebc7, 0x000005d6 }, + { "C16B.CUT", 1, 0x0046f19d, 0x0000057e }, + { "C17A.CUT", 1, 0x0046f71b, 0x00000156 }, + { "C18A.CUT", 1, 0x0046f871, 0x00000a60 }, + { "C18B.CUT", 1, 0x004702d1, 0x000012b8 }, + { "C18C.CUT", 1, 0x00471589, 0x00001202 }, + { "C18D.CUT", 1, 0x0047278b, 0x000009fa }, + { "C18E.CUT", 1, 0x00473185, 0x000009e4 }, + { "C19A.CUT", 1, 0x00473b69, 0x00001076 }, + { "C19B.CUT", 1, 0x00474bdf, 0x0000015c }, + { "C1A.CUT", 1, 0x00474d3b, 0x0000034e }, + { "C2.BBK", 1, 0x00475089, 0x00005908 }, + { "C2.LUM", 1, 0x0047a991, 0x00000018 }, + { "C2.MSK", 1, 0x0047a9a9, 0x00001f40 }, + { "C2.PCX", 1, 0x0047c8e9, 0x00007f3a }, + { "C2.SAM", 1, 0x00484823, 0x000147ac }, + { "C20A.CUT", 1, 0x00498fcf, 0x00000500 }, + { "C20B.CUT", 1, 0x004994cf, 0x00000168 }, + { "C20C.CUT", 1, 0x00499637, 0x00000170 }, + { "C20D.CUT", 1, 0x004997a7, 0x00000388 }, + { "C20E.CUT", 1, 0x00499b2f, 0x00000394 }, + { "C20F.CUT", 1, 0x00499ec3, 0x0000073a }, + { "C20G.CUT", 1, 0x0049a5fd, 0x0000029c }, + { "C21A.CUT", 1, 0x0049a899, 0x000000da }, + { "C21B.CUT", 1, 0x0049a973, 0x000000ee }, + { "C21C.CUT", 1, 0x0049aa61, 0x0000025e }, + { "C21D.CUT", 1, 0x0049acbf, 0x000002ec }, + { "C21E.CUT", 1, 0x0049afab, 0x00000602 }, + { "C21F.CUT", 1, 0x0049b5ad, 0x000003e2 }, + { "C21G.CUT", 1, 0x0049b98f, 0x00000136 }, + { "C21H.CUT", 1, 0x0049bac5, 0x00000172 }, + { "C21I.CUT", 1, 0x0049bc37, 0x0000024c }, + { "C21J.CUT", 1, 0x0049be83, 0x00000122 }, + { "C21K.CUT", 1, 0x0049bfa5, 0x00000584 }, + { "C21L.CUT", 1, 0x0049c529, 0x00000522 }, + { "C21M.CUT", 1, 0x0049ca4b, 0x000001ea }, + { "C21N.CUT", 1, 0x0049cc35, 0x00000610 }, + { "C21O.CUT", 1, 0x0049d245, 0x000000f4 }, + { "C21P.CUT", 1, 0x0049d339, 0x00000150 }, + { "C21Q.CUT", 1, 0x0049d489, 0x000002ec }, + { "C21R.CUT", 1, 0x0049d775, 0x00000106 }, + { "C21S.CUT", 1, 0x0049d87b, 0x0000012a }, + { "C21T.CUT", 1, 0x0049d9a5, 0x0000015e }, + { "C21U.CUT", 1, 0x0049db03, 0x000000c6 }, + { "C22A.CUT", 1, 0x0049dbc9, 0x00000156 }, + { "C22B.CUT", 1, 0x0049dd1f, 0x000000fc }, + { "C22C.CUT", 1, 0x0049de1b, 0x00000166 }, + { "C24A.CUT", 1, 0x0049df81, 0x000000ec }, + { "C24B.CUT", 1, 0x0049e06d, 0x000005ca }, + { "C25A.CUT", 1, 0x0049e637, 0x000002d0 }, + { "C25B.CUT", 1, 0x0049e907, 0x000002e4 }, + { "C25C.CUT", 1, 0x0049ebeb, 0x0000023e }, + { "C25D.CUT", 1, 0x0049ee29, 0x00000182 }, + { "C25E.CUT", 1, 0x0049efab, 0x00000126 }, + { "C25F.CUT", 1, 0x0049f0d1, 0x0000017c }, + { "C25G.CUT", 1, 0x0049f24d, 0x000001e2 }, + { "C25H.CUT", 1, 0x0049f42f, 0x00000218 }, + { "C25I.CUT", 1, 0x0049f647, 0x000001da }, + { "C26A.CUT", 1, 0x0049f821, 0x0000030e }, + { "C26B.CUT", 1, 0x0049fb2f, 0x000000be }, + { "C26C.CUT", 1, 0x0049fbed, 0x000001a4 }, + { "C2A.CUT", 1, 0x0049fd91, 0x000008d0 }, + { "C2B.CUT", 1, 0x004a0661, 0x000004b8 }, + { "C2C.CUT", 1, 0x004a0b19, 0x0000023e }, + { "C3.BBK", 1, 0x004a0d57, 0x00006875 }, + { "C3.LUM", 1, 0x004a75cc, 0x00000018 }, + { "C3.MSK", 1, 0x004a75e4, 0x00001f40 }, + { "C3.PCX", 1, 0x004a9524, 0x0000c40c }, + { "C3.SAM", 1, 0x004b5930, 0x00011c72 }, + { "C30A.CUT", 1, 0x004c75a2, 0x00000254 }, + { "C30B.CUT", 1, 0x004c77f6, 0x000003c4 }, + { "C30C.CUT", 1, 0x004c7bba, 0x00000722 }, + { "C30D.CUT", 1, 0x004c82dc, 0x00000206 }, + { "C30E.CUT", 1, 0x004c84e2, 0x00000976 }, + { "C30F.CUT", 1, 0x004c8e58, 0x0000010e }, + { "C30G.CUT", 1, 0x004c8f66, 0x000001e6 }, + { "C30H.CUT", 1, 0x004c914c, 0x0000014c }, + { "C31A.CUT", 1, 0x004c9298, 0x0000020c }, + { "C31B.CUT", 1, 0x004c94a4, 0x00000596 }, + { "C31C.CUT", 1, 0x004c9a3a, 0x000000e2 }, + { "C31D.CUT", 1, 0x004c9b1c, 0x000004e4 }, + { "C31E.CUT", 1, 0x004ca000, 0x000000ee }, + { "C31F.CUT", 1, 0x004ca0ee, 0x0000010e }, + { "C32A.CUT", 1, 0x004ca1fc, 0x000000ac }, + { "C32B.CUT", 1, 0x004ca2a8, 0x0000010c }, + { "C32C.CUT", 1, 0x004ca3b4, 0x000000e8 }, + { "C35A.CUT", 1, 0x004ca49c, 0x0000042a }, + { "C35B.CUT", 1, 0x004ca8c6, 0x000001c0 }, + { "C36A.CUT", 1, 0x004caa86, 0x0000078c }, + { "C39A.CUT", 1, 0x004cb212, 0x00000ed4 }, + { "C3A.CUT", 1, 0x004cc0e6, 0x00000610 }, + { "C3B.CUT", 1, 0x004cc6f6, 0x000000a0 }, + { "C3C.CUT", 1, 0x004cc796, 0x000005d8 }, + { "C3D.CUT", 1, 0x004ccd6e, 0x00000344 }, + { "C3E.CUT", 1, 0x004cd0b2, 0x0000013a }, + { "C3F.CUT", 1, 0x004cd1ec, 0x000000e0 }, + { "C3G.CUT", 1, 0x004cd2cc, 0x00000102 }, + { "C3H.CUT", 1, 0x004cd3ce, 0x00000120 }, + { "C4.BBK", 1, 0x004cd4ee, 0x00009420 }, + { "C4.LUM", 1, 0x004d690e, 0x00000018 }, + { "C4.MSK", 1, 0x004d6926, 0x00001f40 }, + { "C4.PCX", 1, 0x004d8866, 0x00009487 }, + { "C4.SAM", 1, 0x004e1ced, 0x00002754 }, + { "C40A.CUT", 1, 0x004e4441, 0x0000094a }, + { "C40B.CUT", 1, 0x004e4d8b, 0x000002c2 }, + { "C41A.CUT", 1, 0x004e504d, 0x00000b84 }, + { "C41B.CUT", 1, 0x004e5bd1, 0x00001ba0 }, + { "C41C.CUT", 1, 0x004e7771, 0x00000170 }, + { "C41D.CUT", 1, 0x004e78e1, 0x0000025c }, + { "C41E.CUT", 1, 0x004e7b3d, 0x000001b8 }, + { "C41F.CUT", 1, 0x004e7cf5, 0x0000009c }, + { "C41G.CUT", 1, 0x004e7d91, 0x000000fe }, + { "C41H.CUT", 1, 0x004e7e8f, 0x000000ba }, + { "C42A.CUT", 1, 0x004e7f49, 0x000003ce }, + { "C42B.CUT", 1, 0x004e8317, 0x00000802 }, + { "C42C.CUT", 1, 0x004e8b19, 0x000001ae }, + { "C42D.CUT", 1, 0x004e8cc7, 0x0000010e }, + { "C42E.CUT", 1, 0x004e8dd5, 0x00000106 }, + { "C42F.CUT", 1, 0x004e8edb, 0x00000166 }, + { "C44A.CUT", 1, 0x004e9041, 0x000004f8 }, + { "C44B.CUT", 1, 0x004e9539, 0x0000079e }, + { "C44C.CUT", 1, 0x004e9cd7, 0x0000030a }, + { "C45A.CUT", 1, 0x004e9fe1, 0x000000f8 }, + { "C45B.CUT", 1, 0x004ea0d9, 0x000000f8 }, + { "C45C.CUT", 1, 0x004ea1d1, 0x000000f8 }, + { "C45D.CUT", 1, 0x004ea2c9, 0x000000f8 }, + { "C45E.CUT", 1, 0x004ea3c1, 0x00000186 }, + { "C46A.CUT", 1, 0x004ea547, 0x000000f8 }, + { "C46B.CUT", 1, 0x004ea63f, 0x000000f8 }, + { "C46C.CUT", 1, 0x004ea737, 0x000000f8 }, + { "C46D.CUT", 1, 0x004ea82f, 0x000000f8 }, + { "C47A.CUT", 1, 0x004ea927, 0x00000640 }, + { "C48A.CUT", 1, 0x004eaf67, 0x00000656 }, + { "C48C.CUT", 1, 0x004eb5bd, 0x00000516 }, + { "C49B.CUT", 1, 0x004ebad3, 0x00000398 }, + { "C49C.CUT", 1, 0x004ebe6b, 0x00000132 }, + { "C49D.CUT", 1, 0x004ebf9d, 0x000004e4 }, + { "C4A.CUT", 1, 0x004ec481, 0x00000094 }, + { "C4B.CUT", 1, 0x004ec515, 0x000003f4 }, + { "C5.BBK", 1, 0x004ec909, 0x00004171 }, + { "C5.LUM", 1, 0x004f0a7a, 0x00000018 }, + { "C5.MSK", 1, 0x004f0a92, 0x00001f40 }, + { "C5.PCX", 1, 0x004f29d2, 0x000099f2 }, + { "C5.SAM", 1, 0x004fc3c4, 0x00003016 }, + { "C50A.CUT", 1, 0x004ff3da, 0x000004f2 }, + { "C50B.CUT", 1, 0x004ff8cc, 0x000001f8 }, + { "C50C.CUT", 1, 0x004ffac4, 0x0000086e }, + { "C50D.CUT", 1, 0x00500332, 0x000000e2 }, + { "C50E.CUT", 1, 0x00500414, 0x00000618 }, + { "C50F.CUT", 1, 0x00500a2c, 0x000003b8 }, + { "C50G.CUT", 1, 0x00500de4, 0x000008da }, + { "C50H.CUT", 1, 0x005016be, 0x000000ba }, + { "C50I.CUT", 1, 0x00501778, 0x000009d4 }, + { "C50J.CUT", 1, 0x0050214c, 0x000000f2 }, + { "C51A.CUT", 1, 0x0050223e, 0x0000080e }, + { "C51B.CUT", 1, 0x00502a4c, 0x000001d8 }, + { "C51C.CUT", 1, 0x00502c24, 0x000001d8 }, + { "C51D.CUT", 1, 0x00502dfc, 0x000000ec }, + { "C53A.CUT", 1, 0x00502ee8, 0x00000566 }, + { "C53B.CUT", 1, 0x0050344e, 0x0000018a }, + { "C54A.CUT", 1, 0x005035d8, 0x0000028c }, + { "C55A.CUT", 1, 0x00503864, 0x000006c8 }, + { "C56A.CUT", 1, 0x00503f2c, 0x000003ba }, + { "C56B.CUT", 1, 0x005042e6, 0x00000678 }, + { "C58A.CUT", 1, 0x0050495e, 0x000001cc }, + { "C59A.CUT", 1, 0x00504b2a, 0x0000014a }, + { "C5A.CUT", 1, 0x00504c74, 0x0000040c }, + { "C5C.CUT", 1, 0x00505080, 0x0000050c }, + { "C6.BBK", 1, 0x0050558c, 0x0000711b }, + { "C6.LUM", 1, 0x0050c6a7, 0x00000018 }, + { "C6.MSK", 1, 0x0050c6bf, 0x00001f40 }, + { "C6.PCX", 1, 0x0050e5ff, 0x0000c6c0 }, + { "C6.SAM", 1, 0x0051acbf, 0x00004a87 }, + { "C60A.CUT", 1, 0x0051f746, 0x00000170 }, + { "C61A.CUT", 1, 0x0051f8b6, 0x00000170 }, + { "C62A.CUT", 1, 0x0051fa26, 0x00000dde }, + { "C62B.CUT", 1, 0x00520804, 0x00000268 }, + { "C62C.CUT", 1, 0x00520a6c, 0x00000164 }, + { "C62D.CUT", 1, 0x00520bd0, 0x000002a2 }, + { "C63A.CUT", 1, 0x00520e72, 0x000002d0 }, + { "C63B.CUT", 1, 0x00521142, 0x0000098e }, + { "C63C.CUT", 1, 0x00521ad0, 0x000005d8 }, + { "C63D.CUT", 1, 0x005220a8, 0x00000194 }, + { "C63E.CUT", 1, 0x0052223c, 0x00000336 }, + { "C63F.CUT", 1, 0x00522572, 0x0000041a }, + { "C63G.CUT", 1, 0x0052298c, 0x00000170 }, + { "C63H.CUT", 1, 0x00522afc, 0x0000039a }, + { "C63I.CUT", 1, 0x00522e96, 0x000003ac }, + { "C63J.CUT", 1, 0x00523242, 0x000002f0 }, + { "C63K.CUT", 1, 0x00523532, 0x00000634 }, + { "C64A.CUT", 1, 0x00523b66, 0x00000128 }, + { "C66A.CUT", 1, 0x00523c8e, 0x000002e8 }, + { "C67A.CUT", 1, 0x00523f76, 0x000005b6 }, + { "C67B.CUT", 1, 0x0052452c, 0x0000054c }, + { "C69A.CUT", 1, 0x00524a78, 0x00000700 }, + { "C69B.CUT", 1, 0x00525178, 0x00000670 }, + { "C69C.CUT", 1, 0x005257e8, 0x00000688 }, + { "C69D.CUT", 1, 0x00525e70, 0x000006ac }, + { "C69E.CUT", 1, 0x0052651c, 0x000009cc }, + { "C69F.CUT", 1, 0x00526ee8, 0x00000aa2 }, + { "C69G.CUT", 1, 0x0052798a, 0x000019ac }, + { "C69H.CUT", 1, 0x00529336, 0x0000075a }, + { "C69I.CUT", 1, 0x00529a90, 0x000003f0 }, + { "C69J.CUT", 1, 0x00529e80, 0x0000008a }, + { "C69K.CUT", 1, 0x00529f0a, 0x000005c8 }, + { "C69L.CUT", 1, 0x0052a4d2, 0x0000062a }, + { "C69M.CUT", 1, 0x0052aafc, 0x000005ba }, + { "C69N.CUT", 1, 0x0052b0b6, 0x0000012c }, + { "C69O.CUT", 1, 0x0052b1e2, 0x000001e4 }, + { "C69Z.CUT", 1, 0x0052b3c6, 0x000017a4 }, + { "C6A.CUT", 1, 0x0052cb6a, 0x00000220 }, + { "C6B.CUT", 1, 0x0052cd8a, 0x000000da }, + { "C6C.CUT", 1, 0x0052ce64, 0x00000138 }, + { "C7.BBK", 1, 0x0052cf9c, 0x00013c13 }, + { "C7.PCX", 1, 0x00540baf, 0x0000a75d }, + { "C70A.CUT", 1, 0x0054b30c, 0x000002b8 }, + { "C70B.CUT", 1, 0x0054b5c4, 0x00000384 }, + { "C70B.SAM", 1, 0x0054b948, 0x00009a4e }, + { "C70C.CUT", 1, 0x00555396, 0x00000292 }, + { "C70D.CUT", 1, 0x00555628, 0x00000952 }, + { "C70E.CUT", 1, 0x00555f7a, 0x0000038c }, + { "C70F.CUT", 1, 0x00556306, 0x0000034c }, + { "C70F.SAM", 1, 0x00556652, 0x000076bf }, + { "C70G.CUT", 1, 0x0055dd11, 0x00000348 }, + { "C70G.SAM", 1, 0x0055e059, 0x00008d7d }, + { "C70H.CUT", 1, 0x00566dd6, 0x00000322 }, + { "C70I.CUT", 1, 0x005670f8, 0x000003d8 }, + { "C70J.CUT", 1, 0x005674d0, 0x00000184 }, + { "C70K.CUT", 1, 0x00567654, 0x000002c0 }, + { "C70L.CUT", 1, 0x00567914, 0x000002c0 }, + { "C70M.CUT", 1, 0x00567bd4, 0x000002ba }, + { "C70N.CUT", 1, 0x00567e8e, 0x000002ba }, + { "C71A.CUT", 1, 0x00568148, 0x00000094 }, + { "C71B.CUT", 1, 0x005681dc, 0x00000094 }, + { "C71C.CUT", 1, 0x00568270, 0x000000b2 }, + { "C72A.CUT", 1, 0x00568322, 0x00000730 }, + { "C72B.CUT", 1, 0x00568a52, 0x00000632 }, + { "C72C.CUT", 1, 0x00569084, 0x0000007c }, + { "C73A.CUT", 1, 0x00569100, 0x0000038a }, + { "C73B.CUT", 1, 0x0056948a, 0x000002b8 }, + { "C73C.CUT", 1, 0x00569742, 0x00000192 }, + { "C73D.CUT", 1, 0x005698d4, 0x000000c8 }, + { "C73E.CUT", 1, 0x0056999c, 0x00000330 }, + { "C73F.CUT", 1, 0x00569ccc, 0x00000344 }, + { "C73G.CUT", 1, 0x0056a010, 0x00000210 }, + { "C74A.CUT", 1, 0x0056a220, 0x0000075e }, + { "C74B.CUT", 1, 0x0056a97e, 0x0000018c }, + { "C74C.CUT", 1, 0x0056ab0a, 0x000001dc }, + { "C74D.CUT", 1, 0x0056ace6, 0x0000018c }, + { "C74F.CUT", 1, 0x0056ae72, 0x000000e0 }, + { "C75B.CUT", 1, 0x0056af52, 0x00000d00 }, + { "C76A.CUT", 1, 0x0056bc52, 0x00000072 }, + { "C76B.CUT", 1, 0x0056bcc4, 0x00000f28 }, + { "C8.BBK", 1, 0x0056cbec, 0x0000b5ce }, + { "C8.PCX", 1, 0x005781ba, 0x0000c31f }, + { "C8A.CUT", 1, 0x005844d9, 0x000007e4 }, + { "C8B.CUT", 1, 0x00584cbd, 0x000002a8 }, + { "C9.BBK", 1, 0x00584f65, 0x0000f68b }, + { "C9.PCX", 1, 0x005945f0, 0x0000a787 }, + { "C97A.CUT", 1, 0x0059ed77, 0x0000017e }, + { "C97B.CUT", 1, 0x0059eef5, 0x0000013c }, + { "C99D.CUT", 1, 0x0059f031, 0x00000094 }, + { "C99E.CUT", 1, 0x0059f0c5, 0x000002e6 }, + { "C99F.CUT", 1, 0x0059f3ab, 0x000002e6 }, + { "C9A.CUT", 1, 0x0059f691, 0x000001fa }, + { "C9B.CUT", 1, 0x0059f88b, 0x000000f8 }, + { "C9C.CUT", 1, 0x0059f983, 0x00000136 }, + { "C9D.CUT", 1, 0x0059fab9, 0x000000a0 }, + { "CDCLO.CUT", 1, 0x0059fb59, 0x000001f6 }, + { "CDINT.CUT", 1, 0x0059fd4f, 0x000017fe }, + { "CDINT061.SB", 1, 0x005a154d, 0x0000334c }, + { "CDINT063.SB", 1, 0x005a4899, 0x0000798b }, + { "CDINT072.SB", 1, 0x005ac224, 0x00005f03 }, + { "CDINT081.SB", 1, 0x005b2127, 0x000062b3 }, + { "CDINT091.SB", 1, 0x005b83da, 0x00008a0e }, + { "CDINT102.SB", 1, 0x005c0de8, 0x0000673a }, + { "CDINT111.SB", 1, 0x005c7522, 0x00008453 }, + { "CDINT141.SB", 1, 0x005cf975, 0x000031be }, + { "CDINT151.SB", 1, 0x005d2b33, 0x00002a7c }, + { "CDINT191.SB", 1, 0x005d55af, 0x00001fc8 }, + { "CDINT201.SB", 1, 0x005d7577, 0x00002cd8 }, + { "CDINT212.SB", 1, 0x005da24f, 0x00004b03 }, + { "CDINT231.SB", 1, 0x005ded52, 0x00008dc0 }, + { "CDINT241.SB", 1, 0x005e7b12, 0x000032cf }, + { "CDINT281.SB", 1, 0x005eade1, 0x000053a6 }, + { "CDINT291.SB", 1, 0x005f0187, 0x00002138 }, + { "CDINT301.SB", 1, 0x005f22bf, 0x00003dae }, + { "CDINT321.SB", 1, 0x005f606d, 0x00007e92 }, + { "CDINT351.SB", 1, 0x005fdeff, 0x00002000 }, + { "CDINT361.SB", 1, 0x005ffeff, 0x00005a8b }, + { "CDINT381.SB", 1, 0x0060598a, 0x00003882 }, + { "CDINT442.SB", 1, 0x0060920c, 0x0000471b }, + { "CDINT451.SB", 1, 0x0060d927, 0x00005929 }, + { "CDINT452.SB", 1, 0x00613250, 0x00007915 }, + { "CDINT461.SB", 1, 0x0061ab65, 0x00001c6a }, + { "CDINT463.SB", 1, 0x0061c7cf, 0x000030ae }, + { "CDINT464.SB", 1, 0x0061f87d, 0x0000c980 }, + { "CDINT471.SB", 1, 0x0062c1fd, 0x00003593 }, + { "CDINT481.SB", 1, 0x0062f790, 0x00004e60 }, + { "CDINT492.SB", 1, 0x006345f0, 0x00004594 }, + { "CDRES.CUT", 1, 0x00638b84, 0x000001f6 }, + { "CHANGE.SAM", 1, 0x00638d7a, 0x00005ef2 }, + { "CHEF.ACT", 1, 0x0063ec6c, 0x00005362 }, + { "CHEF.DOG", 1, 0x00643fce, 0x00000af0 }, + { "CHIEF1.DOG", 1, 0x00644abe, 0x000009ca }, + { "CHIEF2.DOG", 1, 0x00645488, 0x0000121e }, + { "CHIEF3.DOG", 1, 0x006466a6, 0x00000be4 }, + { "CHIEF4.DOG", 1, 0x0064728a, 0x000002c4 }, + { "CHORN.CUT", 1, 0x0064754e, 0x0000034a }, + { "CINTR.CUT", 1, 0x00647898, 0x00001768 }, + { "CLOGO.CUT", 1, 0x00649000, 0x000000aa }, + { "CMASK.CUT", 1, 0x006490aa, 0x00000386 }, + { "COCON.CUT", 1, 0x00649430, 0x000003d8 }, + { "COCONUT.SAM", 1, 0x00649808, 0x0000a047 }, + { "COMIC.CUT", 1, 0x0065384f, 0x0000094e }, + { "COMPY.ACT", 1, 0x0065419d, 0x00005533 }, + { "CONTROL.BBK", 1, 0x006596d0, 0x00000e0e }, + { "COPY.BBK", 1, 0x0065a4de, 0x00000002 }, + { "COPY.CUT", 1, 0x0065a4e0, 0x00000086 }, + { "COPY.LBM", 1, 0x0065a566, 0x000053ca }, + { "COPY.PCX", 1, 0x0065f930, 0x0000646f }, + { "CRAP.PCX", 1, 0x00665d9f, 0x0000724c }, + { "CRED.CUT", 1, 0x0066cfeb, 0x000003ae }, + { "CREDIT1.CRD", 1, 0x0066d399, 0x00000604 }, + { "CREDIT2.CRD", 1, 0x0066d99d, 0x000010d3 }, + { "CROWBAR.SAM", 1, 0x0066ea70, 0x00009aca }, + { "CUDRS.CUT", 1, 0x0067853a, 0x000001f6 }, + { "D1.BBK", 1, 0x00678730, 0x000061b7 }, + { "D1.LUM", 1, 0x0067e8e7, 0x00000018 }, + { "D1.MSK", 1, 0x0067e8ff, 0x00001f40 }, + { "D1.PCX", 1, 0x0068083f, 0x0000a331 }, + { "D1C.SAM", 1, 0x0068ab70, 0x0000ddd7 }, + { "D2.BBK", 1, 0x00698947, 0x00004630 }, + { "D2.LUM", 1, 0x0069cf77, 0x00000018 }, + { "D2.MSK", 1, 0x0069cf8f, 0x00001f40 }, + { "D2.PCX", 1, 0x0069eecf, 0x00008dc3 }, + { "D3.BBK", 1, 0x006a7c92, 0x00004b34 }, + { "D3.LUM", 1, 0x006ac7c6, 0x00000018 }, + { "D3.MSK", 1, 0x006ac7de, 0x00001f40 }, + { "D3.PCX", 1, 0x006ae71e, 0x0000a662 }, + { "D3.SAM", 1, 0x006b8d80, 0x0000c55a }, + { "D4.BBK", 1, 0x006c52da, 0x00013e0d }, + { "D4.LUM", 1, 0x006d90e7, 0x00000018 }, + { "D4.MSK", 1, 0x006d90ff, 0x00001f40 }, + { "D4.PCX", 1, 0x006db03f, 0x0000a69b }, + { "D5.BBK", 1, 0x006e56da, 0x000088a1 }, + { "D5.PCX", 1, 0x006edf7b, 0x00007bc5 }, + { "D5.SAM", 1, 0x006f5b40, 0x00002ee7 }, + { "D5B.SAM", 1, 0x006f8a27, 0x0001128e }, + { "D6.BBK", 1, 0x00709cb5, 0x0000a480 }, + { "D6.LUM", 1, 0x00714135, 0x00000018 }, + { "D6.MSK", 1, 0x0071414d, 0x00001f40 }, + { "D6.PCX", 1, 0x0071608d, 0x000099a6 }, + { "D9.BBK", 1, 0x0071fa33, 0x00008461 }, + { "D9.PCX", 1, 0x00727e94, 0x0000b551 }, + { "DATA", 1, 0x007333e5, 0x00001434 }, + { "DEATH.ACT", 1, 0x00734819, 0x0000d83a }, + { "DEATH1.DOG", 1, 0x00742053, 0x000017ca }, + { "DEATH2.DOG", 1, 0x0074381d, 0x0000081e }, + { "DEINO.ACT", 1, 0x0074403b, 0x0000e199 }, + { "DFRANK.ACT", 1, 0x007521d4, 0x000052e2 }, + { "DISK1.SAM", 1, 0x007574b6, 0x000033da }, + { "DOG.ACT", 1, 0x0075a890, 0x00002a62 }, + { "DOG.DOG", 1, 0x0075d2f2, 0x0000023a }, + { "E1.BBK", 1, 0x0075d52c, 0x0001765b }, + { "E1.PCX", 1, 0x00774b87, 0x0000dcc7 }, + { "E2.BBK", 1, 0x0078284e, 0x0000cefc }, + { "E2.PCX", 1, 0x0078f74a, 0x0000f5f2 }, + { "E3.BBK", 1, 0x0079ed3c, 0x00011042 }, + { "E3.PCX", 1, 0x007afd7e, 0x0000a4d0 }, + { "EQDATA.LHA", 1, 0x007ba24e, 0x00031a7a }, + { "F1.BBK", 1, 0x007ebcc8, 0x00004716 }, + { "F1.LUM", 1, 0x007f03de, 0x00000018 }, + { "F1.MSK", 1, 0x007f03f6, 0x00001f40 }, + { "F1.PCX", 1, 0x007f2336, 0x0000991d }, + { "F1.SAM", 1, 0x007fbc53, 0x0000fd04 }, + { "F1B.PCX", 1, 0x0080b957, 0x00005739 }, + { "F2.BBK", 1, 0x00811090, 0x00006a18 }, + { "F2.LUM", 1, 0x00817aa8, 0x00000018 }, + { "F2.MSK", 1, 0x00817ac0, 0x00001f40 }, + { "F2.PCX", 1, 0x00819a00, 0x00009f28 }, + { "F2B.PCX", 1, 0x00823928, 0x00004abb }, + { "F3.BBK", 1, 0x008283e3, 0x00002c59 }, + { "F3.LUM", 1, 0x0082b03c, 0x00000018 }, + { "F3.MSK", 1, 0x0082b054, 0x00001f40 }, + { "F3.PCX", 1, 0x0082cf94, 0x0000aa0c }, + { "F3.SAM", 1, 0x008379a0, 0x0000a940 }, + { "F4.BBK", 1, 0x008422e0, 0x0000b561 }, + { "F4.LUM", 1, 0x0084d841, 0x00000018 }, + { "F4.MSK", 1, 0x0084d859, 0x00001f40 }, + { "F4.PCX", 1, 0x0084f799, 0x00014df0 }, + { "F4B.PCX", 1, 0x00864589, 0x000117d8 }, + { "F4S.PCX", 1, 0x00875d61, 0x0000833e }, + { "FALL.SAM", 1, 0x0087e09f, 0x0000cbda }, + { "FAYE.ACT", 1, 0x0088ac79, 0x00013fda }, + { "FAYE2.DOG", 1, 0x0089ec53, 0x0000098e }, + { "FAYE3.DOG", 1, 0x0089f5e1, 0x000005d8 }, + { "FAYE4.DOG", 1, 0x0089fbb9, 0x00000c40 }, + { "FAYE5.CUT", 1, 0x008a07f9, 0x0000007a }, + { "FAYE5.DOG", 1, 0x008a0873, 0x000009ce }, + { "FAYE6.DOG", 1, 0x008a1241, 0x00000466 }, + { "FAYE_E.ACT", 1, 0x008a16a7, 0x00002d4a }, + { "FAYE_H.ACT", 1, 0x008a43f1, 0x0000807a }, + { "FAYE_H.BBK", 1, 0x008ac46b, 0x0000e9fb }, + { "FAYE_H.PCX", 1, 0x008bae66, 0x000096b5 }, + { "FRANK.ACT", 1, 0x008c451b, 0x00008342 }, + { "FRANK.DOG", 1, 0x008cc85d, 0x00000e48 }, + { "FRANK_H.ACT", 1, 0x008cd6a5, 0x0000ad60 }, + { "FRANK_H.BBK", 1, 0x008d8405, 0x00016369 }, + { "FRANK_H.PCX", 1, 0x008ee76e, 0x00008dd6 }, + { "GET_GEM.SAM", 1, 0x008f7544, 0x000099d2 }, + { "GET_HORN.BBK", 1, 0x00900f16, 0x000045d8 }, + { "GET_HORN.SAM", 1, 0x009054ee, 0x00005dde }, + { "GM.CUT", 1, 0x0090b2cc, 0x0000009c }, + { "GOONS.ACT", 1, 0x0090b368, 0x0000728a }, + { "GUARDS.ACT", 1, 0x009125f2, 0x0000504a }, + { "HENRY.ACT", 1, 0x0091763c, 0x0000b102 }, + { "HENRY.DOG", 1, 0x0092273e, 0x00001280 }, + { "HENRY2.DOG", 1, 0x009239be, 0x00000b24 }, + { "HORN.SAM", 1, 0x009244e2, 0x000067b2 }, + { "HUGH.ACT", 1, 0x0092ac94, 0x0000e8da }, + { "I1.SAM", 1, 0x0093956e, 0x0000cbf2 }, + { "IAN.ACT", 1, 0x00946160, 0x0000a542 }, + { "IAN1.DOG", 1, 0x009506a2, 0x00000518 }, + { "IAN2.DOG", 1, 0x00950bba, 0x00000a9a }, + { "J1.BBK", 1, 0x00951654, 0x000025fc }, + { "J1.LUM", 1, 0x00953c50, 0x00000018 }, + { "J1.MSK", 1, 0x00953c68, 0x00001f40 }, + { "J1.PCX", 1, 0x00955ba8, 0x00008d84 }, + { "J1.SAM", 1, 0x0095e92c, 0x00008442 }, + { "J2.BBK", 1, 0x00966d6e, 0x00000e97 }, + { "J2.LUM", 1, 0x00967c05, 0x00000018 }, + { "J2.MSK", 1, 0x00967c1d, 0x00001f40 }, + { "J2.PCX", 1, 0x00969b5d, 0x0000c33c }, + { "J2.SAM", 1, 0x00975e99, 0x0000a1e3 }, + { "J3.BBK", 1, 0x0098007c, 0x00003289 }, + { "J3.LUM", 1, 0x00983305, 0x00000018 }, + { "J3.MSK", 1, 0x0098331d, 0x00001f40 }, + { "J3.PCX", 1, 0x0098525d, 0x000094b4 }, + { "J4.BBK", 1, 0x0098e711, 0x00006e28 }, + { "J4.LUM", 1, 0x00995539, 0x00000018 }, + { "J4.MSK", 1, 0x00995551, 0x00001f40 }, + { "J4.PCX", 1, 0x00997491, 0x00008e90 }, + { "J5.BBK", 1, 0x009a0321, 0x00004521 }, + { "J5.LUM", 1, 0x009a4842, 0x00000018 }, + { "J5.MSK", 1, 0x009a485a, 0x00001f40 }, + { "J5.PCX", 1, 0x009a679a, 0x00009b37 }, + { "J5.SAM", 1, 0x009b02d1, 0x000042f2 }, + { "J6.BBK", 1, 0x009b45c3, 0x000043d7 }, + { "J6.LUM", 1, 0x009b899a, 0x00000018 }, + { "J6.MSK", 1, 0x009b89b2, 0x00001f40 }, + { "J6.PCX", 1, 0x009ba8f2, 0x0000a5c5 }, + { "J7.BBK", 1, 0x009c4eb7, 0x00003234 }, + { "J7.LUM", 1, 0x009c80eb, 0x00000018 }, + { "J7.MSK", 1, 0x009c8103, 0x00001f40 }, + { "J7.PCX", 1, 0x009ca043, 0x0000cd63 }, + { "J7.SAM", 1, 0x009d6da6, 0x000083aa }, + { "J8.BBK", 1, 0x009df150, 0x0000320d }, + { "J8.LUM", 1, 0x009e235d, 0x00000018 }, + { "J8.MSK", 1, 0x009e2375, 0x00001f40 }, + { "J8.PCX", 1, 0x009e42b5, 0x0000c0e2 }, + { "J8.SAM", 1, 0x009f0397, 0x0000024e }, + { "JASPAR.ACT", 1, 0x009f05e5, 0x00004fb2 }, + { "JIM1.DOG", 1, 0x009f5597, 0x00001276 }, + { "JIM2.DOG", 1, 0x009f680d, 0x00001282 }, + { "JIM3.DOG", 1, 0x009f7a8f, 0x00000df6 }, + { "JIMTAM.ACT", 1, 0x009f8885, 0x0000a08a }, + { "JOE.BBK", 1, 0x00a0290f, 0x00014b8a }, + { "JOE1.BBK", 1, 0x00a17499, 0x00012a5a }, + { "JOED_A.BBK", 1, 0x00a29ef3, 0x00009b8a }, + { "JOED_B.BBK", 1, 0x00a33a7d, 0x0000a50a }, + { "JOEU_A.BBK", 1, 0x00a3df87, 0x00009b8a }, + { "JOEU_B.BBK", 1, 0x00a47b11, 0x0000a50a }, + { "JOE_A.BBK", 1, 0x00a5201b, 0x00009b8a }, + { "JOE_B.BBK", 1, 0x00a5bba5, 0x0000dc5a }, + { "JOE_E.ACT", 1, 0x00a697ff, 0x0000388a }, + { "JOE_H.ACT", 1, 0x00a6d089, 0x0000dace }, + { "JOHN.ACT", 1, 0x00a7ab57, 0x00006312 }, + { "JOHN1.DOG", 1, 0x00a80e69, 0x0000067c }, + { "JOURNAL.BBK", 1, 0x00a814e5, 0x0000a318 }, + { "JOURNAL.PCX", 1, 0x00a8b7fd, 0x00009c70 }, + { "KISS1.SAM", 1, 0x00a9546d, 0x0000f722 }, + { "KLUNK.ACT", 1, 0x00aa4b8f, 0x00007c30 }, + { "KLUNK1.DOG", 1, 0x00aac7bf, 0x00000e6a }, + { "KLUNK2.DOG", 1, 0x00aad629, 0x00000ed0 }, + { "KLUNK2.SAM", 1, 0x00aae4f9, 0x0001737c }, + { "L1.BBK", 1, 0x00ac5875, 0x00000002 }, + { "L1.PCX", 1, 0x00ac5877, 0x000043f7 }, + { "L2.BBK", 1, 0x00ac9c6e, 0x00000002 }, + { "L2.PCX", 1, 0x00ac9c70, 0x0000ba60 }, + { "LARIS.ACT", 1, 0x00ad56d0, 0x0000355a }, + { "LARIS.DOG", 1, 0x00ad8c2a, 0x00000f32 }, + { "LARIS3.DOG", 1, 0x00ad9b5c, 0x0000079a }, + { "LITTLEP.ACT", 1, 0x00ada2f6, 0x000002e2 }, + { "LOLA.ACT", 1, 0x00ada5d8, 0x0001342d }, + { "LOLA1.DOG", 1, 0x00aeda05, 0x00000ad6 }, + { "LOU.ACT", 1, 0x00aee4db, 0x00005552 }, + { "LOU1.DOG", 1, 0x00af3a2d, 0x000010ac }, + { "LOU2.DOG", 1, 0x00af4ad9, 0x00000bae }, + { "M1.BBK", 1, 0x00af5687, 0x00006d5c }, + { "M1.PCX", 1, 0x00afc3e3, 0x000193ce }, + { "M2.BBK", 1, 0x00b157b1, 0x00001a4a }, + { "M2.PCX", 1, 0x00b171fb, 0x0000a9d3 }, + { "M2.SAM", 1, 0x00b21bce, 0x0001433c }, + { "MAN1.DOG", 1, 0x00b35f0a, 0x0000101e }, + { "MAN2.DOG", 1, 0x00b36f28, 0x000007a4 }, + { "MASK.SAM", 1, 0x00b376cc, 0x000081ea }, + { "N1.BBK", 1, 0x00b3f8b6, 0x00002d74 }, + { "N1.LUM", 1, 0x00b4262a, 0x00000018 }, + { "N1.MSK", 1, 0x00b42642, 0x00001f40 }, + { "N1.PCX", 1, 0x00b44582, 0x0000cbe7 }, + { "N10.BBK", 1, 0x00b51169, 0x00000002 }, + { "N10.PCX", 1, 0x00b5116b, 0x0000a28d }, + { "N10.SAM", 1, 0x00b5b3f8, 0x000147f8 }, + { "N11.BBK", 1, 0x00b6fbf0, 0x00000d48 }, + { "N11.PCX", 1, 0x00b70938, 0x00008e85 }, + { "N12.BBK", 1, 0x00b797bd, 0x000049aa }, + { "N12.LUM", 1, 0x00b7e167, 0x00000018 }, + { "N12.MSK", 1, 0x00b7e17f, 0x00001f40 }, + { "N12.PCX", 1, 0x00b800bf, 0x000074ea }, + { "N13.BBK", 1, 0x00b875a9, 0x000046b7 }, + { "N13.LUM", 1, 0x00b8bc60, 0x00000018 }, + { "N13.MSK", 1, 0x00b8bc78, 0x00001f40 }, + { "N13.PCX", 1, 0x00b8dbb8, 0x00008640 }, + { "N13.SAM", 1, 0x00b961f8, 0x00025a26 }, + { "N13B.SAM", 1, 0x00bbbc1e, 0x0000250a }, + { "N14.BBK", 1, 0x00bbe128, 0x000073ec }, + { "N14.PCX", 1, 0x00bc5514, 0x0000b3f3 }, + { "N14.SAM", 1, 0x00bd0907, 0x0002550a }, + { "N14B.PCX", 1, 0x00bf5e11, 0x0000b933 }, + { "N15.BBK", 1, 0x00c01744, 0x00000002 }, + { "N15.PCX", 1, 0x00c01746, 0x0000ae43 }, + { "N16.BBK", 1, 0x00c0c589, 0x000014c0 }, + { "N16.LUM", 1, 0x00c0da49, 0x00000018 }, + { "N16.MSK", 1, 0x00c0da61, 0x00001f40 }, + { "N16.PCX", 1, 0x00c0f9a1, 0x0000e038 }, + { "N2.BBK", 1, 0x00c1d9d9, 0x00007377 }, + { "N2.LUM", 1, 0x00c24d50, 0x00000018 }, + { "N2.MSK", 1, 0x00c24d68, 0x00001f40 }, + { "N2.PCX", 1, 0x00c26ca8, 0x00007d65 }, + { "N3.BBK", 1, 0x00c2ea0d, 0x00004fe2 }, + { "N3.LUM", 1, 0x00c339ef, 0x00000018 }, + { "N3.MSK", 1, 0x00c33a07, 0x00001f40 }, + { "N3.PCX", 1, 0x00c35947, 0x0000a6de }, + { "N4.BBK", 1, 0x00c40025, 0x0000acc0 }, + { "N4.LUM", 1, 0x00c4ace5, 0x00000018 }, + { "N4.MSK", 1, 0x00c4acfd, 0x00001f40 }, + { "N4.PCX", 1, 0x00c4cc3d, 0x00006e0b }, + { "N5.BBK", 1, 0x00c53a48, 0x0000202a }, + { "N5.LUM", 1, 0x00c55a72, 0x00000018 }, + { "N5.MSK", 1, 0x00c55a8a, 0x00001f40 }, + { "N5.PCX", 1, 0x00c579ca, 0x000092f0 }, + { "N6.BBK", 1, 0x00c60cba, 0x00001d97 }, + { "N6.LUM", 1, 0x00c62a51, 0x00000018 }, + { "N6.MSK", 1, 0x00c62a69, 0x00001f40 }, + { "N6.PCX", 1, 0x00c649a9, 0x000086e2 }, + { "N7.BBK", 1, 0x00c6d08b, 0x000025f8 }, + { "N7.LUM", 1, 0x00c6f683, 0x00000018 }, + { "N7.MSK", 1, 0x00c6f69b, 0x00001f40 }, + { "N7.PCX", 1, 0x00c715db, 0x00008e79 }, + { "N8.BBK", 1, 0x00c7a454, 0x00007bb7 }, + { "N8.LUM", 1, 0x00c8200b, 0x00000018 }, + { "N8.MSK", 1, 0x00c82023, 0x00001f40 }, + { "N8.PCX", 1, 0x00c83f63, 0x0000be5f }, + { "N9.BBK", 1, 0x00c8fdc2, 0x0000c9bf }, + { "N9.LUM", 1, 0x00c9c781, 0x00000018 }, + { "N9.MSK", 1, 0x00c9c799, 0x00001f40 }, + { "N9.PCX", 1, 0x00c9e6d9, 0x0000aa4b }, + { "N9.SAM", 1, 0x00ca9124, 0x0000e902 }, + { "NAOMI.DOG", 1, 0x00cb7a26, 0x000013b6 }, + { "NAOMI2.DOG", 1, 0x00cb8ddc, 0x00000a40 }, + { "OBJECTS.BBK", 1, 0x00cb981c, 0x00019322 }, + { "ORACLE.ACT", 1, 0x00cd2b3e, 0x00004042 }, + { "ORACLE1.DOG", 1, 0x00cd6b80, 0x00001088 }, + { "ORACLE2.DOG", 1, 0x00cd7c08, 0x000003c0 }, + { "ORACLE3.DOG", 1, 0x00cd7fc8, 0x00000806 }, + { "PANEL.PCX", 1, 0x00cd87ce, 0x00002279 }, + { "PRIN1.CUT", 1, 0x00cdaa47, 0x0000007a }, + { "PRIN1.DOG", 1, 0x00cdaac1, 0x00000aea }, + { "PRIN2.DOG", 1, 0x00cdb5ab, 0x0000055a }, + { "PRIN4.DOG", 1, 0x00cdbb05, 0x00000720 }, + { "PRINCESS.ACT", 1, 0x00cdc225, 0x0000d732 }, + { "PRISON.ACT", 1, 0x00ce9957, 0x00006f22 }, + { "PUNCH.SAM", 1, 0x00cf0879, 0x00007e9a }, + { "PUSH.SAM", 1, 0x00cf8713, 0x00009308 }, + { "PYGMY.ACT", 1, 0x00d01a1b, 0x00010a63 }, + { "QUEEN.JAS", 1, 0x00d1247e, 0x0001371a }, + { "QUEEN2.JAS", 1, 0x00d25b98, 0x00008c00 }, + { "R1.BBK", 1, 0x00d2e798, 0x00001a4a }, + { "R1.PCX", 1, 0x00d301e2, 0x000065c0 }, + { "R2.BBK", 1, 0x00d367a2, 0x00001a4a }, + { "R2.PCX", 1, 0x00d381ec, 0x00005f32 }, + { "R3.BBK", 1, 0x00d3e11e, 0x00001a4a }, + { "R3.PCX", 1, 0x00d3fb68, 0x000061a1 }, + { "R4.BBK", 1, 0x00d45d09, 0x00001a4a }, + { "R4.PCX", 1, 0x00d47753, 0x00009ec3 }, + { "RASH.SAM", 1, 0x00d51616, 0x0000431a }, + { "RENEGADE.BBK", 1, 0x00d55930, 0x0000117a }, + { "RENEGADE.PCX", 1, 0x00d56aaa, 0x0000296d }, + { "RITA.ACT", 1, 0x00d59417, 0x000020ea }, + { "RITA_H.ACT", 1, 0x00d5b501, 0x00015cdb }, + { "ROCKET.SAM", 1, 0x00d711dc, 0x00027e20 }, + { "SEC.ACT", 1, 0x00d98ffc, 0x00006e6a }, + { "SEC1.DOG", 1, 0x00d9fe66, 0x00001672 }, + { "SEC2.DOG", 1, 0x00da14d8, 0x00000944 }, + { "SHEET.SAM", 1, 0x00da1e1c, 0x0000ad00 }, + { "SHIELD.SAM", 1, 0x00dacb1c, 0x00002afb }, + { "SHOWER.ACT", 1, 0x00daf617, 0x0000762a }, + { "SHOWERAM.DOG", 1, 0x00db6c41, 0x000005fc }, + { "SKULL.ACT", 1, 0x00db723d, 0x00001973 }, + { "SPARKY.ACT", 1, 0x00db8bb0, 0x0000f912 }, + { "SPARKY1.DOG", 1, 0x00dc84c2, 0x00000986 }, + { "SPARKY2.DOG", 1, 0x00dc8e48, 0x00000402 }, + { "SPARKY3.DOG", 1, 0x00dc924a, 0x0000126e }, + { "SPARKY4.DOG", 1, 0x00dca4b8, 0x0000043a }, + { "SPARKY5.DOG", 1, 0x00dca8f2, 0x0000091c }, + { "SPARKY6.DOG", 1, 0x00dcb20e, 0x000007b6 }, + { "SPARKY7.DOG", 1, 0x00dcb9c4, 0x0000095e }, + { "SPARKY8.DOG", 1, 0x00dcc322, 0x0000072a }, + { "SPARKY_H.ACT", 1, 0x00dcca4c, 0x0000280a }, + { "T1.BBK", 1, 0x00dcf256, 0x0000f492 }, + { "T1.LUM", 1, 0x00dde6e8, 0x00000018 }, + { "T1.MSK", 1, 0x00dde700, 0x00001f40 }, + { "T1.PCX", 1, 0x00de0640, 0x0000aa3d }, + { "T1.SAM", 1, 0x00deb07d, 0x0000711a }, + { "T10.BBK", 1, 0x00df2197, 0x0000a957 }, + { "T10.LUM", 1, 0x00dfcaee, 0x00000018 }, + { "T10.MSK", 1, 0x00dfcb06, 0x00001f40 }, + { "T10.PCX", 1, 0x00dfea46, 0x0001198a }, + { "T10.SAM", 1, 0x00e103d0, 0x0000bc49 }, + { "T11.BBK", 1, 0x00e1c019, 0x00001e84 }, + { "T11.LUM", 1, 0x00e1de9d, 0x00000018 }, + { "T11.MSK", 1, 0x00e1deb5, 0x00001f40 }, + { "T11.PCX", 1, 0x00e1fdf5, 0x00008e7e }, + { "T12.BBK", 1, 0x00e28c73, 0x000059b5 }, + { "T12.LUM", 1, 0x00e2e628, 0x00000018 }, + { "T12.MSK", 1, 0x00e2e640, 0x00001f40 }, + { "T12.PCX", 1, 0x00e30580, 0x00007b3c }, + { "T12.SAM", 1, 0x00e380bc, 0x0000fe4b }, + { "T13.BBK", 1, 0x00e47f07, 0x000043b4 }, + { "T13.LUM", 1, 0x00e4c2bb, 0x00000018 }, + { "T13.MSK", 1, 0x00e4c2d3, 0x00001f40 }, + { "T13.PCX", 1, 0x00e4e213, 0x00007591 }, + { "T14.BBK", 1, 0x00e557a4, 0x0000468b }, + { "T14.LUM", 1, 0x00e59e2f, 0x00000018 }, + { "T14.MSK", 1, 0x00e59e47, 0x00001f40 }, + { "T14.PCX", 1, 0x00e5bd87, 0x00007df5 }, + { "T14.SAM", 1, 0x00e63b7c, 0x00016db3 }, + { "T15.BBK", 1, 0x00e7a92f, 0x000096e6 }, + { "T15.LUM", 1, 0x00e84015, 0x00000018 }, + { "T15.MSK", 1, 0x00e8402d, 0x00001f40 }, + { "T15.PCX", 1, 0x00e85f6d, 0x00008b28 }, + { "T15.SAM", 1, 0x00e8ea95, 0x0000a7d0 }, + { "T15B.SAM", 1, 0x00e99265, 0x000164d7 }, + { "T16.BBK", 1, 0x00eaf73c, 0x000027ff }, + { "T16.LUM", 1, 0x00eb1f3b, 0x00000018 }, + { "T16.MSK", 1, 0x00eb1f53, 0x00001f40 }, + { "T16.PCX", 1, 0x00eb3e93, 0x00009110 }, + { "T17.BBK", 1, 0x00ebcfa3, 0x000056df }, + { "T17.LUM", 1, 0x00ec2682, 0x00000018 }, + { "T17.MSK", 1, 0x00ec269a, 0x00001f40 }, + { "T17.PCX", 1, 0x00ec45da, 0x00007db6 }, + { "T18.BBK", 1, 0x00ecc390, 0x00006174 }, + { "T18.LUM", 1, 0x00ed2504, 0x00000018 }, + { "T18.MSK", 1, 0x00ed251c, 0x00001f40 }, + { "T18.PCX", 1, 0x00ed445c, 0x000087d6 }, + { "T19.BBK", 1, 0x00edcc32, 0x0000aec8 }, + { "T19.LUM", 1, 0x00ee7afa, 0x00000018 }, + { "T19.MSK", 1, 0x00ee7b12, 0x00001f40 }, + { "T19.PCX", 1, 0x00ee9a52, 0x0000768e }, + { "T19.SAM", 1, 0x00ef10e0, 0x000167ea }, + { "T1B.SAM", 1, 0x00f078ca, 0x0002fa9f }, + { "T2.BBK", 1, 0x00f37369, 0x00004dea }, + { "T2.LUM", 1, 0x00f3c153, 0x00000018 }, + { "T2.MSK", 1, 0x00f3c16b, 0x00001f40 }, + { "T2.PCX", 1, 0x00f3e0ab, 0x00011404 }, + { "T2.SAM", 1, 0x00f4f4af, 0x0000aa32 }, + { "T20.BBK", 1, 0x00f59ee1, 0x0000611c }, + { "T20.LUM", 1, 0x00f5fffd, 0x00000018 }, + { "T20.MSK", 1, 0x00f60015, 0x00001f40 }, + { "T20.PCX", 1, 0x00f61f55, 0x0000852a }, + { "T20.SAM", 1, 0x00f6a47f, 0x00007f9c }, + { "T20B.SAM", 1, 0x00f7241b, 0x0000a2bc }, + { "T21.BBK", 1, 0x00f7c6d7, 0x00000002 }, + { "T21.PCX", 1, 0x00f7c6d9, 0x0000b7b0 }, + { "T22.BBK", 1, 0x00f87e89, 0x00000002 }, + { "T22.PCX", 1, 0x00f87e8b, 0x0000a982 }, + { "T23.BBK", 1, 0x00f9280d, 0x00005bca }, + { "T23.LUM", 1, 0x00f983d7, 0x00000018 }, + { "T23.MSK", 1, 0x00f983ef, 0x00001f40 }, + { "T23.PCX", 1, 0x00f9a32f, 0x00008200 }, + { "T24.BBK", 1, 0x00fa252f, 0x0000aaf1 }, + { "T24.LUM", 1, 0x00fad020, 0x00000018 }, + { "T24.MSK", 1, 0x00fad038, 0x00001f40 }, + { "T24.PCX", 1, 0x00faef78, 0x00006f7e }, + { "T25.BBK", 1, 0x00fb5ef6, 0x0000a631 }, + { "T25.LUM", 1, 0x00fc0527, 0x00000018 }, + { "T25.MSK", 1, 0x00fc053f, 0x00001f40 }, + { "T25.PCX", 1, 0x00fc247f, 0x00008881 }, + { "T25.SAM", 1, 0x00fcad00, 0x000091ac }, + { "T26.BBK", 1, 0x00fd3eac, 0x00014578 }, + { "T26.LUM", 1, 0x00fe8424, 0x00000018 }, + { "T26.MSK", 1, 0x00fe843c, 0x00001f40 }, + { "T26.PCX", 1, 0x00fea37c, 0x00012570 }, + { "T26A.SAM", 1, 0x00ffc8ec, 0x000126a6 }, + { "T27.BBK", 1, 0x0100ef92, 0x0000a73e }, + { "T27.LUM", 1, 0x010196d0, 0x00000018 }, + { "T27.MSK", 1, 0x010196e8, 0x00001f40 }, + { "T27.PCX", 1, 0x0101b628, 0x000085fa }, + { "T28.BBK", 1, 0x01023c22, 0x00000002 }, + { "T28.PCX", 1, 0x01023c24, 0x000017d2 }, + { "T2B.SAM", 1, 0x010253f6, 0x00021df6 }, + { "T3.BBK", 1, 0x010471ec, 0x00004b24 }, + { "T3.LUM", 1, 0x0104bd10, 0x00000018 }, + { "T3.MSK", 1, 0x0104bd28, 0x00001f40 }, + { "T3.PCX", 1, 0x0104dc68, 0x0000724c }, + { "T3.SAM", 1, 0x01054eb4, 0x00006042 }, + { "T4.BBK", 1, 0x0105aef6, 0x00002dca }, + { "T4.MSK", 1, 0x0105dcc0, 0x00001f40 }, + { "T4.PCX", 1, 0x0105fc00, 0x00007566 }, + { "T5.BBK", 1, 0x01067166, 0x00001ac0 }, + { "T5.LUM", 1, 0x01068c26, 0x00000018 }, + { "T5.MSK", 1, 0x01068c3e, 0x00001f40 }, + { "T5.PCX", 1, 0x0106ab7e, 0x00009509 }, + { "T5.SAM", 1, 0x01074087, 0x000049aa }, + { "T5B.SAM", 1, 0x01078a31, 0x00022018 }, + { "T5C.SAM", 1, 0x0109aa49, 0x00011612 }, + { "T6.BBK", 1, 0x010ac05b, 0x00007db0 }, + { "T6.LUM", 1, 0x010b3e0b, 0x00000018 }, + { "T6.MSK", 1, 0x010b3e23, 0x00001f40 }, + { "T6.PCX", 1, 0x010b5d63, 0x000096b4 }, + { "T6.SAM", 1, 0x010bf417, 0x0000f04d }, + { "T6A.SAM", 1, 0x010ce464, 0x000199ee }, + { "T6B.PCX", 1, 0x010e7e52, 0x0000ad10 }, + { "T6B.SAM", 1, 0x010f2b62, 0x00010cba }, + { "T6C.SAM", 1, 0x0110381c, 0x00015041 }, + { "T7.BBK", 1, 0x0111885d, 0x0000c781 }, + { "T7.PCX", 1, 0x01124fde, 0x00006da0 }, + { "T7.SAM", 1, 0x0112bd7e, 0x000172ea }, + { "T8.BBK", 1, 0x01143068, 0x00002762 }, + { "T8.LUM", 1, 0x011457ca, 0x00000018 }, + { "T8.MSK", 1, 0x011457e2, 0x00001f40 }, + { "T8.PCX", 1, 0x01147722, 0x0000831b }, + { "T8.SAM", 1, 0x0114fa3d, 0x00012c01 }, + { "T9.BBK", 1, 0x0116263e, 0x000029f3 }, + { "T9.LUM", 1, 0x01165031, 0x00000018 }, + { "T9.MSK", 1, 0x01165049, 0x00001f40 }, + { "T9.PCX", 1, 0x01166f89, 0x0000735b }, + { "T9.SAM", 1, 0x0116e2e4, 0x0000d9e6 }, + { "TABLET.BBK", 1, 0x0117bcca, 0x00013902 }, + { "TABLET.PCX", 1, 0x0118f5cc, 0x0000af16 }, + { "TALLPYG.DOG", 1, 0x0119a4e2, 0x0000034a }, + { "TAM1.DOG", 1, 0x0119a82c, 0x00001e8a }, + { "TAM2.DOG", 1, 0x0119c6b6, 0x0000076c }, + { "TAM3.DOG", 1, 0x0119ce22, 0x000007c2 }, + { "TAM4.DOG", 1, 0x0119d5e4, 0x0000083c }, + { "TEMPLE.ACT", 1, 0x0119de20, 0x00005052 }, + { "TMPD.ACT", 1, 0x011a2e72, 0x0000b00c }, + { "TRADER.ACT", 1, 0x011ade7e, 0x0001424a }, + { "V1.BBK", 1, 0x011c20c8, 0x00006724 }, + { "V1.PCX", 1, 0x011c87ec, 0x000091ea }, + { "V1.SAM", 1, 0x011d19d6, 0x000061e5 }, + { "V10.BBK", 1, 0x011d7bbb, 0x000094e8 }, + { "V10.PCX", 1, 0x011e10a3, 0x0000946c }, + { "V11.BBK", 1, 0x011ea50f, 0x0000e122 }, + { "V11.PCX", 1, 0x011f8631, 0x0000946c }, + { "V2.BBK", 1, 0x01201a9d, 0x00007dfb }, + { "V2.LUM", 1, 0x01209898, 0x00000018 }, + { "V2.MSK", 1, 0x012098b0, 0x00001f40 }, + { "V2.PCX", 1, 0x0120b7f0, 0x0000876c }, + { "V3.BBK", 1, 0x01213f5c, 0x0000d716 }, + { "V3.LUM", 1, 0x01221672, 0x00000018 }, + { "V3.MSK", 1, 0x0122168a, 0x00001f40 }, + { "V3.PCX", 1, 0x012235ca, 0x00005efa }, + { "V4.BBK", 1, 0x012294c4, 0x0000571a }, + { "V4.PCX", 1, 0x0122ebde, 0x00010cd4 }, + { "V5.BBK", 1, 0x0123f8b2, 0x0001c43f }, + { "V5.MSK", 1, 0x0125bcf1, 0x00001f40 }, + { "V5.PCX", 1, 0x0125dc31, 0x00009148 }, + { "V5.SAM", 1, 0x01266d79, 0x0003953d }, + { "V5B.SAM", 1, 0x012a02b6, 0x0000ce6f }, + { "V5C.SAM", 1, 0x012ad125, 0x0000f142 }, + { "V5D.SAM", 1, 0x012bc267, 0x00000f50 }, + { "V5E.SAM", 1, 0x012bd1b7, 0x00009352 }, + { "V5X.SAM", 1, 0x012c6509, 0x0001d7c2 }, + { "V6.BBK", 1, 0x012e3ccb, 0x0000d716 }, + { "V6.LUM", 1, 0x012f13e1, 0x00000018 }, + { "V6.MSK", 1, 0x012f13f9, 0x00001f40 }, + { "V6.PCX", 1, 0x012f3339, 0x000074ce }, + { "V7.BBK", 1, 0x012fa807, 0x000177cd }, + { "V7.PCX", 1, 0x01311fd4, 0x0000a3b4 }, + { "V8.BBK", 1, 0x0131c388, 0x00006724 }, + { "V8.PCX", 1, 0x01322aac, 0x0000a8d0 }, + { "VACUUM.SAM", 1, 0x0132d37c, 0x00009516 }, + { "WATER.ACT", 1, 0x01336892, 0x00001c02 }, + { "WEDGE.ACT", 1, 0x01338494, 0x0000390e }, + { "WEDGE.DOG", 1, 0x0133bda2, 0x000002c4 }, + { "WEENIE.SAM", 1, 0x0133c066, 0x0000b4d2 }, + { "WITCH1.DOG", 1, 0x01347538, 0x000012e4 }, + { "WITCH2.DOG", 1, 0x0134881c, 0x0000088e }, + { "WITCH3.DOG", 1, 0x013490aa, 0x00000df8 }, + { "WITCH4.DOG", 1, 0x01349ea2, 0x000002b2 }, + { "X1.BBK", 1, 0x0134a154, 0x00010e5d }, + { "X1.PCX", 1, 0x0135afb1, 0x0000cc4b }, + { "X10.BBK", 1, 0x01367bfc, 0x00009907 }, + { "X10.PCX", 1, 0x01371503, 0x0000a1b3 }, + { "X10_JOE.ACT", 1, 0x0137b6b6, 0x0000943a }, + { "X10_RITA.ACT", 1, 0x01384af0, 0x000076d9 }, + { "X11.BBK", 1, 0x0138c1c9, 0x00016966 }, + { "X11.PCX", 1, 0x013a2b2f, 0x0000c160 }, + { "X11_JOE.ACT", 1, 0x013aec8f, 0x0000872e }, + { "X11_RITA.ACT", 1, 0x013b73bd, 0x0000a6f2 }, + { "X2.BBK", 1, 0x013c1aaf, 0x0000df2b }, + { "X2.PCX", 1, 0x013cf9da, 0x00013ed5 }, + { "X2_JOE.ACT", 1, 0x013e38af, 0x00008042 }, + { "X2_RITA.ACT", 1, 0x013eb8f1, 0x0000df02 }, + { "X3.BBK", 1, 0x013f97f3, 0x00000002 }, + { "X3.PCX", 1, 0x013f97f5, 0x0000d165 }, + { "X3_RITA.ACT", 1, 0x0140695a, 0x0000a0fa }, + { "X4.BBK", 1, 0x01410a54, 0x00004b53 }, + { "X4.PCX", 1, 0x014155a7, 0x0000b51b }, + { "X4A.SAM", 1, 0x01420ac2, 0x0001b456 }, + { "X4B.SAM", 1, 0x0143bf18, 0x0002a1b4 }, + { "X4_JOE.ACT", 1, 0x014660cc, 0x000088a5 }, + { "X4_RITA.ACT", 1, 0x0146e971, 0x0000398a }, + { "X5.BBK", 1, 0x014722fb, 0x000075a0 }, + { "X5.PCX", 1, 0x0147989b, 0x0000adeb }, + { "X5_SPARK.ACT", 1, 0x01484686, 0x00006e5a }, + { "X6.BBK", 1, 0x0148b4e0, 0x0001889e }, + { "X6.PCX", 1, 0x014a3d7e, 0x0000be75 }, + { "X6_HUGH.ACT", 1, 0x014afbf3, 0x0000c25a }, + { "X7.BBK", 1, 0x014bbe4d, 0x00002ada }, + { "X7.PCX", 1, 0x014be927, 0x00009456 }, + { "X7A.SAM", 1, 0x014c7d7d, 0x0001b7cb }, + { "X7B.SAM", 1, 0x014e3548, 0x0003b107 }, + { "X8.BBK", 1, 0x0151e64f, 0x00032a14 }, + { "X8.PCX", 1, 0x01551063, 0x00013d4f }, + { "X9.BBK", 1, 0x01564db2, 0x00028337 }, + { "X9.PCX", 1, 0x0158d0e9, 0x0000a31c }, + { "ZOMBIE.ACT", 1, 0x01597405, 0x000078ea }, + { "ZOMBIE1.DOG", 1, 0x0159ecef, 0x00000f6a }, + { "ZOMBIE2.DOG", 1, 0x0159fc59, 0x00000c40 } +}; +#endif +} // End of namespace Queen diff --git a/engines/queen/sound.cpp b/engines/queen/sound.cpp new file mode 100644 index 0000000000..a5c756e366 --- /dev/null +++ b/engines/queen/sound.cpp @@ -0,0 +1,239 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/sound.h" + +#include "queen/input.h" +#include "queen/music.h" +#include "queen/queen.h" +#include "queen/resource.h" + +#include "sound/flac.h" +#include "sound/mp3.h" +#include "sound/vorbis.h" + +#define SB_HEADER_SIZE 110 +#define STOP_MUSIC -1 + +namespace Queen { + +Sound::Sound(Audio::Mixer *mixer, QueenEngine *vm) : + _mixer(mixer), _vm(vm), _sfxToggle(true), _speechToggle(true), _musicToggle(true), _lastOverride(0) { +} + +Sound::~Sound() { +} + +Sound *Sound::giveSound(Audio::Mixer *mixer, QueenEngine *vm, uint8 compression) { + if (!mixer->isReady()) + return new SilentSound(mixer, vm); + + switch (compression) { + case COMPRESSION_NONE: + return new SBSound(mixer, vm); + break; + case COMPRESSION_MP3: +#ifndef USE_MAD + warning("Using MP3 compressed datafile, but MP3 support not compiled in"); + return new SilentSound(mixer, vm); +#else + return new MP3Sound(mixer, vm); +#endif + break; + case COMPRESSION_OGG: +#ifndef USE_VORBIS + warning("Using OGG compressed datafile, but OGG support not compiled in"); + return new SilentSound(mixer, vm); +#else + return new OGGSound(mixer, vm); +#endif + break; + case COMPRESSION_FLAC: +#ifndef USE_FLAC + warning("Using FLAC compressed datafile, but FLAC support not compiled in"); + return new SilentSound(mixer, vm); +#else + return new FLACSound(mixer, vm); +#endif + break; + default: + warning("Unknown compression type"); + return new SilentSound(mixer, vm); + } +} + +void Sound::waitFinished(bool isSpeech) { + if (isSpeech) + while (_mixer->isSoundHandleActive(_speechHandle)) + _vm->input()->delay(10); + else + while (_mixer->isSoundHandleActive(_sfxHandle)) + _vm->input()->delay(10); +} + +void Sound::playSfx(uint16 sfx, bool isSpeech) { + if (isSpeech && !speechOn()) return; + else if (!sfxOn()) return; + + if (sfx != 0) { + char name[13]; +#ifndef PALMOS_68K + strcpy(name, _sfxName[sfx - 1]); +#else + strncpy(name, _sfxName + 10 * (sfx - 1), 10); // saved as 8char + /0/0 +#endif + strcat(name, ".SB"); + waitFinished(isSpeech); + if (sfxPlay(name, isSpeech ? &_speechHandle : &_sfxHandle)) { + _speechSfxExists = isSpeech; + } else { + _speechSfxExists = false; + } + } +} + +void Sound::playSfx(const char *base, bool isSpeech) { + if (isSpeech && !speechOn()) return; + else if (!sfxOn()) return; + + char name[13]; + strcpy(name, base); + // alter filename to add zeros and append ".SB" + for (int i = 0; i < 8; i++) { + if (name[i] == ' ') + name[i] = '0'; + } + strcat(name, ".SB"); + waitFinished(isSpeech); + if (sfxPlay(name, isSpeech ? &_speechHandle : &_sfxHandle)) { + _speechSfxExists = isSpeech; + } else { + _speechSfxExists = false; + } +} + +void Sound::playSong(int16 songNum) { + if (songNum <= 0) { + _vm->music()->stopSong(); + return; + } + + int16 newTune; + if (_vm->resource()->isDemo()) { + if (songNum == 17) { + _vm->music()->stopSong(); + return; + } + newTune = _songDemo[songNum - 1].tuneList[0] - 1; + } else { + newTune = _song[songNum - 1].tuneList[0] - 1; + } + + if (_tune[newTune].sfx[0]) { + if (sfxOn()) + playSfx(_tune[newTune].sfx[0], false); + return; + } + + if (!musicOn()) + return; + + int override = (_vm->resource()->isDemo()) ? _songDemo[songNum - 1].override : _song[songNum - 1].override; + switch (override) { + // Override all songs + case 1: + break; + // Alter song settings (such as volume) and exit + case 2: + _vm->music()->toggleVChange(); + default: + return; + break; + } + + _lastOverride = songNum; + + _vm->music()->queueTuneList(newTune); + _vm->music()->playMusic(); +} + +void Sound::saveState(byte *&ptr) { + WRITE_BE_UINT16(ptr, _lastOverride); ptr += 2; +} + +void Sound::loadState(uint32 ver, byte *&ptr) { + _lastOverride = (int16)READ_BE_INT16(ptr); ptr += 2; +} + +bool SilentSound::sfxPlay(const char *name, Audio::SoundHandle *soundHandle) { + return false; +} + +bool SBSound::sfxPlay(const char *name, Audio::SoundHandle *soundHandle) { + if (_vm->resource()->fileExists(name)) { + uint32 size; + uint8 *sound = _vm->resource()->loadFile(name, SB_HEADER_SIZE, &size, true); + byte flags = Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE; + _mixer->playRaw(soundHandle, sound, size, 11025, flags); + return true; + } + return false; +} + +#ifdef USE_MAD +bool MP3Sound::sfxPlay(const char *name, Audio::SoundHandle *soundHandle) { + uint32 size; + Common::File *f = _vm->resource()->giveCompressedSound(name, &size); + if (f) { + _mixer->playInputStream(Audio::Mixer::kSFXSoundType, soundHandle, makeMP3Stream(f, size)); + return true; + } + return false; +} +#endif + +#ifdef USE_VORBIS +bool OGGSound::sfxPlay(const char *name, Audio::SoundHandle *soundHandle) { + uint32 size; + Common::File *f = _vm->resource()->giveCompressedSound(name, &size); + if (f) { + _mixer->playInputStream(Audio::Mixer::kSFXSoundType, soundHandle, makeVorbisStream(f, size)); + return true; + } + return false; +} +#endif + +#ifdef USE_FLAC +bool FLACSound::sfxPlay(const char *name, Audio::SoundHandle *soundHandle) { + uint32 size; + Common::File *f = _vm->resource()->giveCompressedSound(name, &size); + if (f) { + _mixer->playInputStream(Audio::Mixer::kSFXSoundType, soundHandle, makeFlacStream(f, size)); + return true; + } + return false; +} +#endif + +} //End of namespace Queen diff --git a/engines/queen/sound.h b/engines/queen/sound.h new file mode 100644 index 0000000000..d12032e994 --- /dev/null +++ b/engines/queen/sound.h @@ -0,0 +1,158 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENSOUND_H +#define QUEENSOUND_H + +#include "common/util.h" +#include "sound/mixer.h" +#include "queen/defs.h" + +namespace Queen { + +class Input; +class Resource; + +struct songData { + int16 tuneList[5]; + int16 volume; + int16 tempo; + int16 reverb; + int16 override; + int16 ignore; +}; + +struct tuneData { + int16 tuneNum[9]; + int16 sfx[2]; + int16 mode; + int16 delay; +}; + +class QueenEngine; + +class Sound { +public: + Sound(Audio::Mixer *mixer, QueenEngine *vm); + virtual ~Sound(); + virtual bool sfxPlay(const char *name, Audio::SoundHandle *soundHandle) = 0; + static Sound *giveSound(Audio::Mixer *mixer, QueenEngine *vm, uint8 compression); + void playSfx(uint16 sfx, bool isSpeech); + void playSfx(const char *base, bool isSpeech); + void playSong(int16 songNum); + void playLastSong() { playSong(_lastOverride); } + void stopSpeech() { _mixer->stopHandle(_speechHandle); } + void stopSfx() { _mixer->stopHandle(_sfxHandle); } + + bool sfxOn() const { return _sfxToggle; } + void sfxToggle(bool val) { _sfxToggle = val; } + void toggleSfx() { _sfxToggle ^= true; } + + bool speechOn() const { return _speechToggle; } + void speechToggle(bool val) { _speechToggle = val; } + void toggleSpeech() { _speechToggle ^= true; } + + bool musicOn() const { return _musicToggle; } + void musicToggle(bool val) { _musicToggle = val; } + void toggleMusic() { _musicToggle ^= true; } + + bool isSpeechActive() const { return _mixer->isSoundHandleActive(_speechHandle); } + bool isSfxActive() const { return _mixer->isSoundHandleActive(_sfxHandle); } + + bool speechSfxExists() const { return _speechSfxExists; } + + int16 lastOverride() const { return _lastOverride; } + + void saveState(byte *&ptr); + void loadState(uint32 ver, byte *&ptr); + +#ifndef PALMOS_68K + static const songData _songDemo[]; + static const songData _song[]; + static const tuneData _tuneDemo[]; + static const tuneData _tune[]; + static const char *_sfxName[]; + static const int16 _jungleList[]; +#else + static const songData *_songDemo; + static const songData *_song; + static const tuneData *_tuneDemo; + static const tuneData *_tune; + static const char *_sfxName; + static const int16 *_jungleList; +#endif + +protected: + void waitFinished(bool isSpeech); + + Audio::Mixer *_mixer; + QueenEngine *_vm; + + bool _sfxToggle; + bool _speechToggle; + bool _musicToggle; + bool _speechSfxExists; + + int16 _lastOverride; + Audio::SoundHandle _sfxHandle; + Audio::SoundHandle _speechHandle; +}; + +class SilentSound : public Sound { +public: + SilentSound(Audio::Mixer *mixer, QueenEngine *vm) : Sound(mixer, vm) {}; + bool sfxPlay(const char *name, Audio::SoundHandle *soundHandle); +}; + +class SBSound : public Sound { +public: + SBSound(Audio::Mixer *mixer, QueenEngine *vm) : Sound(mixer, vm) {}; + bool sfxPlay(const char *name, Audio::SoundHandle *soundHandle); +}; + +#ifdef USE_MAD +class MP3Sound : public Sound { +public: + MP3Sound(Audio::Mixer *mixer, QueenEngine *vm) : Sound(mixer, vm) {}; + bool sfxPlay(const char *name, Audio::SoundHandle *soundHandle); +}; +#endif + +#ifdef USE_VORBIS +class OGGSound : public Sound { +public: + OGGSound(Audio::Mixer *mixer, QueenEngine *vm) : Sound(mixer, vm) {}; + bool sfxPlay(const char *name, Audio::SoundHandle *soundHandle); +}; +#endif + +#ifdef USE_FLAC +class FLACSound : public Sound { +public: + FLACSound(Audio::Mixer *mixer, QueenEngine *vm) : Sound(mixer, vm) {}; + bool sfxPlay(const char *name, Audio::SoundHandle *soundHandle); +}; +#endif // #ifdef USE_FLAC + +} // End of namespace Queen + +#endif diff --git a/engines/queen/state.cpp b/engines/queen/state.cpp new file mode 100644 index 0000000000..5709bb07c6 --- /dev/null +++ b/engines/queen/state.cpp @@ -0,0 +1,130 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/state.h" + +namespace Queen { + +Direction State::findDirection(uint16 state) { + static const Direction sd[] = { + DIR_BACK, + DIR_RIGHT, + DIR_LEFT, + DIR_FRONT + }; + return sd[(state >> 2) & 3]; +} + +StateTalk State::findTalk(uint16 state) { + return (state & (1 << 9)) ? STATE_TALK_TALK : STATE_TALK_MUTE; +} + +StateGrab State::findGrab(uint16 state) { + static const StateGrab sg[] = { + STATE_GRAB_NONE, + STATE_GRAB_DOWN, + STATE_GRAB_UP, + STATE_GRAB_MID + }; + return sg[state & 3]; +} + +StateOn State::findOn(uint16 state) { + return (state & (1 << 8)) ? STATE_ON_ON : STATE_ON_OFF; +} + +Verb State::findDefaultVerb(uint16 state) { + static const Verb sdv[] = { + VERB_NONE, + VERB_OPEN, + VERB_NONE, + VERB_CLOSE, + + VERB_NONE, + VERB_NONE, + VERB_LOOK_AT, + VERB_MOVE, + + VERB_GIVE, + VERB_TALK_TO, + VERB_NONE, + VERB_NONE, + + VERB_USE, + VERB_NONE, + VERB_PICK_UP, + VERB_NONE + }; + return sdv[(state >> 4) & 0xF]; +} + +StateUse State::findUse(uint16 state) { + return (state & (1 << 10)) ? STATE_USE : STATE_USE_ON; +} + +void State::alterOn(uint16 *objState, StateOn state) { + switch (state) { + case STATE_ON_ON: + *objState |= (1 << 8); + break; + case STATE_ON_OFF: + *objState &= ~(1 << 8); + break; + } +} + +void State::alterDefaultVerb(uint16 *objState, Verb v) { + uint16 val; + switch (v) { + case VERB_OPEN: + val = 1; + break; + case VERB_CLOSE: + val = 3; + break; + case VERB_MOVE: + val = 7; + break; + case VERB_GIVE: + val = 8; + break; + case VERB_USE: + val = 12; + break; + case VERB_PICK_UP: + val = 14; + break; + case VERB_TALK_TO: + val = 9; + break; + case VERB_LOOK_AT: + val = 6; + break; + default: + val = 0; + break; + } + *objState = (*objState & ~0xF0) | (val << 4); +} + +} // End of namespace Queen diff --git a/engines/queen/state.h b/engines/queen/state.h new file mode 100644 index 0000000000..d7be47fbc8 --- /dev/null +++ b/engines/queen/state.h @@ -0,0 +1,113 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENSTATE_H +#define QUEENSTATE_H + +#include "common/util.h" +#include "queen/defs.h" + +namespace Queen { + + +enum StateTalk { + STATE_TALK_TALK, + STATE_TALK_MUTE +}; + +enum StateGrab { + STATE_GRAB_NONE, + STATE_GRAB_DOWN, + STATE_GRAB_UP, + STATE_GRAB_MID +}; + +enum StateOn { + STATE_ON_ON, + STATE_ON_OFF +}; + +enum StateUse { + STATE_USE, + STATE_USE_ON +}; + + +/*! + Each object/item in game has a state field. + (refer to ObjectData and ItemData). + + <table> + <tr> + <td>Name</td> + <td>Bits</td> + <td>Description</td> + </tr> + <tr> + <td>USE</td> + <td>10</td> + <td>Use</td> + </tr> + <tr> + <td>TALK</td> + <td>9</td> + <td>Talk</td> + </tr> + <tr> + <td>ON</td> + <td>8</td> + <td>On/Off</td> + </tr> + <tr> + <td>DEF</td> + <td>7,6,5,4</td> + <td>Default verb command</td> + </tr> + <tr> + <td>DIR</td> + <td>3,2</td> + <td>Direction to face for the object</td> + </tr> + <tr> + <td>GRAB</td> + <td>1,0</td> + <td>Grab Direction</td> + </tr> + </table> +*/ +struct State { + + static Direction findDirection(uint16 state); + static StateTalk findTalk(uint16 state); + static StateGrab findGrab(uint16 state); + static StateOn findOn(uint16 state); + static Verb findDefaultVerb(uint16 state); + static StateUse findUse(uint16 state); + + static void alterOn(uint16 *objState, StateOn state); + static void alterDefaultVerb(uint16 *objState, Verb v); +}; + + +} // End of namespace Queen + +#endif diff --git a/engines/queen/structs.h b/engines/queen/structs.h new file mode 100644 index 0000000000..4f43950fa4 --- /dev/null +++ b/engines/queen/structs.h @@ -0,0 +1,580 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENSTRUCTS_H +#define QUEENSTRUCTS_H + +#include "queen/defs.h" + +namespace Queen { + +struct Box { + int16 x1, y1, x2, y2; + + Box() + : x1(0), y1(0), x2(0), y2(0) { + } + + Box(int16 xx1, int16 yy1, int16 xx2, int16 yy2) + : x1(xx1), y1(yy1), x2(xx2), y2(yy2) { + } + + void readFromBE(byte *&ptr) { + x1 = (int16)READ_BE_UINT16(ptr); ptr += 2; + y1 = (int16)READ_BE_UINT16(ptr); ptr += 2; + x2 = (int16)READ_BE_UINT16(ptr); ptr += 2; + y2 = (int16)READ_BE_UINT16(ptr); ptr += 2; + } + + void writeToBE(byte *&ptr) { + WRITE_BE_UINT16(ptr, x1); ptr += 2; + WRITE_BE_UINT16(ptr, y1); ptr += 2; + WRITE_BE_UINT16(ptr, x2); ptr += 2; + WRITE_BE_UINT16(ptr, y2); ptr += 2; + } + + int16 xDiff() const { + return x2 - x1; + } + + int16 yDiff() const { + return y2 - y1; + } + + bool intersects(int16 x, int16 y, uint16 w, uint16 h) const { + return (x + w > x1) && (y + h > y1) && (x <= x2) && (y <= y2); + } + + bool contains(int16 x, int16 y) const { + return (x >= x1) && (x <= x2) && (y >= y1) && (y <= y2); + } + + bool operator==(const Box &b) const { + return (x1 == b.x1) && (x2 == b.x2) && (y1 == b.y1) && (y2 == b.y2); + } +}; + + +struct Area { + //! bitmask of connected areas + int16 mapNeighbours; + //! coordinates defining area limits + Box box; + //! scaling factors for bobs actors + uint16 bottomScaleFactor, topScaleFactor; + //! entry in ObjectData, object lying in this area + uint16 object; + + void readFromBE(byte *&ptr) { + mapNeighbours = (int16)READ_BE_UINT16(ptr); ptr += 2; + box.readFromBE(ptr); + bottomScaleFactor = READ_BE_UINT16(ptr); ptr += 2; + topScaleFactor = READ_BE_UINT16(ptr); ptr += 2; + object = READ_BE_UINT16(ptr); ptr += 2; + } + + void writeToBE(byte *&ptr) { + WRITE_BE_UINT16(ptr, mapNeighbours); ptr += 2; + box.writeToBE(ptr); + WRITE_BE_UINT16(ptr, bottomScaleFactor); ptr += 2; + WRITE_BE_UINT16(ptr, topScaleFactor); ptr += 2; + WRITE_BE_UINT16(ptr, object); ptr += 2; + } + + uint16 calcScale(int16 y) const { + uint16 dy = box.yDiff(); + int16 ds = scaleDiff(); + uint16 scale = 0; + + if (dy) // Prevent division-by-zero + scale = ((((y - box.y1) * 100) / dy) * ds) / 100 + bottomScaleFactor; + + if (scale == 0) + scale = 100; + + return scale; + } + + int16 scaleDiff() const { + return (int16)(topScaleFactor - bottomScaleFactor); + } +}; + + +struct WalkOffData { + //! entry in ObjectData + int16 entryObj; + //! coordinates to reach + uint16 x, y; + + void readFromBE(byte *&ptr) { + entryObj = (int16)READ_BE_UINT16(ptr); ptr += 2; + x = READ_BE_UINT16(ptr); ptr += 2; + y = READ_BE_UINT16(ptr); ptr += 2; + } + + void writeToBE(byte *&ptr) { + WRITE_BE_UINT16(ptr, entryObj); ptr += 2; + WRITE_BE_UINT16(ptr, x); ptr += 2; + WRITE_BE_UINT16(ptr, y); ptr += 2; + } +}; + + +struct GraphicData { + //! coordinates of object + uint16 x, y; + //! bank bobframes + /*! + <table> + <tr> + <td>lastFrame == 0</td> + <td>non-animated bob (one frame)</td> + </tr> + <tr> + <td>lastFrame < 0</td> + <td>rebound animation</td> + </tr> + <tr> + <td>firstFrame < 0</td> + <td>BobSlot::animString (animation is described by a string)</td> + </tr> + <tr> + <td>firstFrame > 0</td> + <td>BobSlot::animNormal (animation is a sequence of frames)</td> + </tr> + </table> + */ + int16 firstFrame, lastFrame; + //! moving speed of object + uint16 speed; + + void readFromBE(byte *&ptr) { + x = READ_BE_UINT16(ptr); ptr += 2; + y = READ_BE_UINT16(ptr); ptr += 2; + firstFrame = (int16)READ_BE_UINT16(ptr); ptr += 2; + lastFrame = (int16)READ_BE_UINT16(ptr); ptr += 2; + speed = READ_BE_UINT16(ptr); ptr += 2; + } +}; + + +struct ObjectData { + //! entry in OBJECT_NAME (<0: object is hidden, 0: object has been deleted) + int16 name; + //! coordinates of object + uint16 x, y; + //! entry in OBJECT_DESCR + uint16 description; + //! associated object + int16 entryObj; + //! room in which this object is available + uint16 room; + //! state of the object (grab direction, on/off, default command...) + uint16 state; + //! entry in GraphicData + /*! + <table> + <tr> + <td>value</td> + <td>description</td> + </tr> + <tr> + <td>]-4000..-10]</td> + <td>graphic image turned off</td> + </tr> + <tr> + <td>-4</td> + <td>person object (right facing)</td> + </tr> + <tr> + <td>-3</td> + <td>person object (left facing)</td> + </tr> + <tr> + <td>-2</td> + <td>animated bob (off)</td> + </tr> + <tr> + <td>-1</td> + <td>static bob (off)</td> + </tr> + <tr> + <td>0</td> + <td>object deleted</td> + </tr> + <tr> + <td>]0..5000]</td> + <td>static or animated bob (on)</td> + </tr> + <tr> + <td>]5000.. [</td> + <td>'paste down' bob</td> + </tr> + </table> + */ + int16 image; + + void readFromBE(byte *&ptr) { + name = (int16)READ_BE_UINT16(ptr); ptr += 2; + x = READ_BE_UINT16(ptr); ptr += 2; + y = READ_BE_UINT16(ptr); ptr += 2; + description = READ_BE_UINT16(ptr); ptr += 2; + entryObj = (int16)READ_BE_UINT16(ptr); ptr += 2; + room = READ_BE_UINT16(ptr); ptr += 2; + state = READ_BE_UINT16(ptr); ptr += 2; + image = (int16)READ_BE_UINT16(ptr); ptr += 2; + } + + void writeToBE(byte *&ptr) { + WRITE_BE_UINT16(ptr, name); ptr += 2; + WRITE_BE_UINT16(ptr, x); ptr += 2; + WRITE_BE_UINT16(ptr, y); ptr += 2; + WRITE_BE_UINT16(ptr, description); ptr += 2; + WRITE_BE_UINT16(ptr, entryObj); ptr += 2; + WRITE_BE_UINT16(ptr, room); ptr += 2; + WRITE_BE_UINT16(ptr, state); ptr += 2; + WRITE_BE_UINT16(ptr, image); ptr += 2; + } +}; + + +struct ObjectDescription { + //! entry in ObjectData or ItemData + uint16 object; + //! type of the description + /*! + refer to select.c l.75-101 + <table> + <tr> + <td>value</td> + <td>description</td> + </tr> + <tr> + <td>0</td> + <td>random but starts at first description</td> + <tr> + <td>1</td> + <td>random</td> + </tr> + <tr> + <td>2</td> + <td>sequential with loop</td> + </tr> + <tr> + <td>3</td> + <td>sequential and set description to last</td> + </tr> + </table> + */ + uint16 type; + //! last entry possible in OBJECT_DESCR for this object + uint16 lastDescription; + //! last description number used (in order to avoid re-using it) + uint16 lastSeenNumber; + + void readFromBE(byte *&ptr) { + object = READ_BE_UINT16(ptr); ptr += 2; + type = READ_BE_UINT16(ptr); ptr += 2; + lastDescription = READ_BE_UINT16(ptr); ptr += 2; + lastSeenNumber = READ_BE_UINT16(ptr); ptr += 2; + } + + void writeToBE(byte *&ptr) { + WRITE_BE_UINT16(ptr, object); ptr += 2; + WRITE_BE_UINT16(ptr, type); ptr += 2; + WRITE_BE_UINT16(ptr, lastDescription); ptr += 2; + WRITE_BE_UINT16(ptr, lastSeenNumber); ptr += 2; + } +}; + + +struct ItemData { + //! entry in OBJECT_NAME + int16 name; + //! entry in OBJECT_DESCR + uint16 description; + //! state of the object + uint16 state; + //! bank bobframe + uint16 frame; + //! entry in OBJECT_DESCR (>0 if available) + int16 sfxDescription; + + void readFromBE(byte *&ptr) { + name = (int16)READ_BE_UINT16(ptr); ptr += 2; + description = READ_BE_UINT16(ptr); ptr += 2; + state = READ_BE_UINT16(ptr); ptr += 2; + frame = READ_BE_UINT16(ptr); ptr += 2; + sfxDescription = (int16)READ_BE_UINT16(ptr); ptr += 2; + } + + void writeToBE(byte *&ptr) { + WRITE_BE_UINT16(ptr, name); ptr += 2; + WRITE_BE_UINT16(ptr, description); ptr += 2; + WRITE_BE_UINT16(ptr, state); ptr += 2; + WRITE_BE_UINT16(ptr, frame); ptr += 2; + WRITE_BE_UINT16(ptr, sfxDescription); ptr += 2; + } +}; + + +struct ActorData { + //! room in which the actor is + int16 room; + //! bob number associated to this actor + int16 bobNum; + //! entry in ACTOR_NAME + uint16 name; + //! gamestate entry/value, actor is valid if GAMESTATE[slot] == value + int16 gsSlot, gsValue; + //! spoken text color + uint16 color; + //! bank bobframe for standing position of the actor + uint16 bobFrameStanding; + //! initial coordinates in the room + uint16 x, y; + //! entry in ACTOR_ANIM + uint16 anim; + //! bank to use to load the actor file + uint16 bankNum; + //! entry in ACTOR_FILE + uint16 file; + + void readFromBE(byte *&ptr) { + room = (int16)READ_BE_UINT16(ptr); ptr += 2; + bobNum = (int16)READ_BE_UINT16(ptr); ptr += 2; + name = READ_BE_UINT16(ptr); ptr += 2; + gsSlot = (int16)READ_BE_UINT16(ptr); ptr += 2; + gsValue = (int16)READ_BE_UINT16(ptr); ptr += 2; + color = READ_BE_UINT16(ptr); ptr += 2; + bobFrameStanding = READ_BE_UINT16(ptr); ptr += 2; + x = READ_BE_UINT16(ptr); ptr += 2; + y = READ_BE_UINT16(ptr); ptr += 2; + anim = READ_BE_UINT16(ptr); ptr += 2; + bankNum = READ_BE_UINT16(ptr); ptr += 2; + file = READ_BE_UINT16(ptr); ptr += 2; + // Fix the actor data (see queen.c - l.1518-1519). When there is no + // valid actor file, we must load the data from the objects room bank. + // This bank has number 15 (not 10 as in the data files). + if (file == 0) { + bankNum = 15; + } + } +}; + + +struct CmdListData { + //! action to perform + Verb verb; + //! first object used in the action + int16 nounObj1; + //! second object used in the action + int16 nounObj2; + //! song to play (>0: playbefore, <0: playafter) + int16 song; + //! if set, P2_SET_AREAS must be called (using CmdArea) + bool setAreas; + //! if set, P3_SET_OBJECTS must be called (using CmdObject) + bool setObjects; + //! if set, P4_SET_ITEMS must be called (using CmdInventory) + bool setItems; + //! if set, P1_SET_CONDITIONS must be called (using CmdGameState) + bool setConditions; + //! graphic image order + int16 imageOrder; + //! special section to execute (refer to execute.c l.423-451) + int16 specialSection; + + void readFromBE(byte *&ptr) { + verb = (Verb)READ_BE_UINT16(ptr); ptr += 2; + nounObj1 = (int16)READ_BE_UINT16(ptr); ptr += 2; + nounObj2 = (int16)READ_BE_UINT16(ptr); ptr += 2; + song = (int16)READ_BE_UINT16(ptr); ptr += 2; + setAreas = READ_BE_UINT16(ptr) != 0; ptr += 2; + setObjects = READ_BE_UINT16(ptr) != 0; ptr += 2; + setItems = READ_BE_UINT16(ptr) != 0; ptr += 2; + setConditions = READ_BE_UINT16(ptr) != 0; ptr += 2; + imageOrder = (int16)READ_BE_UINT16(ptr); ptr += 2; + specialSection = (int16)READ_BE_UINT16(ptr); ptr += 2; + } + + bool match(const Verb& v, int16 obj1, int16 obj2) const { + return verb == v && nounObj1 == obj1 && nounObj2 == obj2; + } +}; + + +struct CmdArea { + //! CmdListData number + int16 id; + //! area to turn off/on (<0: off, >0: on) + int16 area; + //! room in which the area must be changed + uint16 room; + + void readFromBE(byte *&ptr) { + id = (int16)READ_BE_UINT16(ptr); ptr += 2; + area = (int16)READ_BE_UINT16(ptr); ptr += 2; + room = READ_BE_UINT16(ptr); ptr += 2; + } +}; + + +struct CmdObject { + //! CmdListData number + int16 id; + //! >0: show, <0: hide + int16 dstObj; + //! >0: copy from srcObj, 0: nothing, -1: delete dstObj + int16 srcObj; + + void readFromBE(byte *&ptr) { + id = (int16)READ_BE_UINT16(ptr); ptr += 2; + dstObj = (int16)READ_BE_UINT16(ptr); ptr += 2; + srcObj = (int16)READ_BE_UINT16(ptr); ptr += 2; + } +}; + + +struct CmdInventory { + //! CmdListData number + int16 id; + //! <0: delete, >0: add + int16 dstItem; + //! >0: valid + int16 srcItem; + + void readFromBE(byte *&ptr) { + id = (int16)READ_BE_UINT16(ptr); ptr += 2; + dstItem = (int16)READ_BE_UINT16(ptr); ptr += 2; + srcItem = (int16)READ_BE_UINT16(ptr); ptr += 2; + } +}; + + +struct CmdGameState { + //! CmdListData number + int16 id; + int16 gameStateSlot; + int16 gameStateValue; + uint16 speakValue; + + void readFromBE(byte *&ptr) { + id = (int16)READ_BE_UINT16(ptr); ptr += 2; + gameStateSlot = (int16)READ_BE_UINT16(ptr); ptr += 2; + gameStateValue = (int16)READ_BE_UINT16(ptr); ptr += 2; + speakValue = READ_BE_UINT16(ptr); ptr += 2; + } +}; + + +struct FurnitureData { + //! room in which the furniture are + int16 room; + //! furniture object number + /*! + <table> + <tr> + <td>range</td> + <td>type</td> + </tr> + <tr> + <td>]0..5000]</td> + <td>static or animated</td> + </tr> + <tr> + <td>]5000..[</td> + <td>paste down</td> + </tr> + </table> + */ + int16 objNum; + + void readFromBE(byte *&ptr) { + room = (int16)READ_BE_UINT16(ptr); ptr += 2; + objNum = (int16)READ_BE_UINT16(ptr); ptr += 2; + } +}; + + +struct GraphicAnim { + int16 keyFrame; + int16 frame; + uint16 speed; + + void readFromBE(byte *&ptr) { + keyFrame = (int16)READ_BE_UINT16(ptr); ptr += 2; + frame = (int16)READ_BE_UINT16(ptr); ptr += 2; + speed = READ_BE_UINT16(ptr); ptr += 2; + } +}; + + +struct AnimFrame { + uint16 frame; + uint16 speed; +}; + + +struct Person { + //! actor settings to use + const ActorData *actor; + //! actor name + const char *name; + //! animation string + const char *anim; + //! current frame + uint16 bobFrame; +}; + + +struct TalkSelected { + bool hasTalkedTo; + int16 values[4]; + + void readFromBE(byte *&ptr) { + hasTalkedTo = READ_BE_UINT16(ptr) != 0; ptr += 2; + for (int i = 0; i < 4; i++) { + values[i] = (int16)READ_BE_UINT16(ptr); ptr += 2; + } + } + + void writeToBE(byte *&ptr) { + WRITE_BE_UINT16(ptr, (uint16)hasTalkedTo); ptr += 2; + for (int i = 0; i < 4; i++) { + WRITE_BE_UINT16(ptr, values[i]); ptr += 2; + } + } +}; + + +struct BobFrame { + uint16 width, height; + uint16 xhotspot, yhotspot; + uint8 *data; +}; + + +} // End of namespace Queen + +#endif diff --git a/engines/queen/talk.cpp b/engines/queen/talk.cpp new file mode 100644 index 0000000000..27399ab0cf --- /dev/null +++ b/engines/queen/talk.cpp @@ -0,0 +1,1812 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/talk.h" + +#include "queen/bankman.h" +#include "queen/display.h" +#include "queen/graphics.h" +#include "queen/grid.h" +#include "queen/input.h" +#include "queen/logic.h" +#include "queen/queen.h" +#include "queen/resource.h" +#include "queen/sound.h" +#include "queen/state.h" +#include "queen/walk.h" + +#include "common/file.h" + +namespace Queen { + +#ifdef PALMOS_68K +static const Talk::SpeechParameters *_speechParameters; +#endif + +void Talk::talk( + const char *filename, + int personInRoom, + char *cutawayFilename, + QueenEngine *vm) { + Talk *talk = new Talk(vm); + talk->talk(filename, personInRoom, cutawayFilename); + delete talk; +} + +bool Talk::speak( + const char *sentence, + Person *person, + const char *voiceFilePrefix, + QueenEngine *vm) { + Talk *talk = new Talk(vm); + bool result; + if (sentence) + result = talk->speak(sentence, person, voiceFilePrefix); + else + result = false; + delete talk; + return result; +} + +Talk::Talk(QueenEngine *vm) + : _vm(vm), _fileData(NULL) { + _vm->input()->talkQuitReset(); +} + +Talk::~Talk() { + delete[] _fileData; +} + +void Talk::talk(const char *filename, int personInRoom, char *cutawayFilename) { + int i; + _oldSelectedSentenceIndex = 0; + _oldSelectedSentenceValue = 0; + + debug(6, "----- talk(\"%s\") -----", filename); + + cutawayFilename[0] = '\0'; + + load(filename); + + Person person; + memset(&person, 0, sizeof(Person)); + _vm->logic()->initPerson(personInRoom, "", false, &person); + + if (NULL == person.name) { + error("Invalid person object"); + } + + int16 oldLevel = 0; + bool personWalking = false; + + // Lines 828-846 in talk.c + for (i = 1; i <= 4; i++) { + if (selectedValue(i) > 0) { + // This option has been redefined so display new dialogue option + _dialogueTree[1][i].head = selectedValue(i); + } else if (selectedValue(i) == -1) { + // Already selected so don't redisplay + if (_dialogueTree[1][i].gameStateIndex >= 0) { + _dialogueTree[1][i].head = -1; + _dialogueTree[1][i].dialogueNodeValue1 = -1; + _dialogueTree[1][i].gameStateIndex = -1; + _dialogueTree[1][i].gameStateValue = -1; + } + } + } + + initialTalk(); + + // Lines 906-? in talk.c + _vm->display()->showMouseCursor(true); + + int16 level=1, retval=0; + int16 head = _dialogueTree[level][0].head; + + // TODO: split this loop in several functions + while (retval != -1) { + char otherVoiceFilePrefix[MAX_STRING_SIZE]; + + _talkString[0][0] = '\0'; + + if (hasTalkedTo() && head == 1) + strcpy(_talkString[0], _person2String); + else + findDialogueString(_person1PtrOff, head, _pMax, _talkString[0]); + + if (hasTalkedTo() && head == 1) + sprintf(otherVoiceFilePrefix, "%2dXXXXP", _talkKey); + else + sprintf(otherVoiceFilePrefix, "%2d%4xP", _talkKey, head); + + if (_talkString[0][0] == '\0' && retval > 1) { + findDialogueString(_person1PtrOff, retval, _pMax, _talkString[0]); + sprintf(otherVoiceFilePrefix,"%2d%4xP", _talkKey, retval); + } + + // Joe dialogue + + for (i = 1; i <= 4; i++) { + findDialogueString(_joePtrOff, _dialogueTree[level][i].head, _jMax, _talkString[i]); + + int16 index = _dialogueTree[level][i].gameStateIndex; + + if (index < 0 && _vm->logic()->gameState(ABS(index)) != _dialogueTree[level][i].gameStateValue) + _talkString[i][0] = '\0'; + + sprintf(_joeVoiceFilePrefix[i], "%2d%4xJ", _talkKey, _dialogueTree[level][i].head); + } + + // Check to see if (all the dialogue options have been selected. + // if this is the case, and the last one left is the exit option, + // then automatically set S to that and exit. + + int choicesLeft = 0; + int selectedSentence = 0; + + for (i = 1; i <= 4; i++) { + if (_talkString[i][0] != '\0') { + choicesLeft++; + selectedSentence = i; + } + } + + debug(6, "choicesLeft = %i", choicesLeft); + + if (1 == choicesLeft) { + // Automatically run the final dialogue option + if (speak(_talkString[0], &person, otherVoiceFilePrefix)) + personWalking = true; + + if (_vm->input()->talkQuit()) + break; + + speak(_talkString[selectedSentence], NULL, _joeVoiceFilePrefix[selectedSentence]); + } else { + if (person.actor->bobNum > 0) { + speak(_talkString[0], &person, otherVoiceFilePrefix); + selectedSentence = selectSentence(); + } else { + warning("bobBum is %i", person.actor->bobNum); + selectedSentence = 0; + } + } + + if (_vm->input()->talkQuit()) + break; + + retval = _dialogueTree[level][selectedSentence].dialogueNodeValue1; + head = _dialogueTree[level][selectedSentence].head; + oldLevel = level; + level = 0; + + // Set LEVEL to the selected child in dialogue tree + + for (i = 1; i <= _levelMax; i++) + if (_dialogueTree[i][0].head == head) + level = i; + + if (0 == level) { + // No new level has been selected, so lets set LEVEL to the + // tree path pointed to by the RETVAL + + for (i = 1; i <= _levelMax; i++) + for (int j = 0; j <= 5; j++) + if (_dialogueTree[i][j].head == retval) + level = i; + + disableSentence(oldLevel, selectedSentence); + } else { // 0 != level + // Check to see if Person Return value is positive, if it is, then + // change the selected dialogue option to the Return value + + if (_dialogueTree[level][0].dialogueNodeValue1 > 0) { + if (1 == oldLevel) { + _oldSelectedSentenceIndex = selectedSentence; + _oldSelectedSentenceValue = selectedValue(selectedSentence); + selectedValue(selectedSentence, _dialogueTree[level][0].dialogueNodeValue1); + } + + _dialogueTree[oldLevel][selectedSentence].head = _dialogueTree[level][0].dialogueNodeValue1; + _dialogueTree[level][0].dialogueNodeValue1 = -1; + } else { + disableSentence(oldLevel, selectedSentence); + } + } + + // Check selected person to see if any Gamestates need setting + + int16 index = _dialogueTree[level][0].gameStateIndex; + if (index > 0) + _vm->logic()->gameState(index, _dialogueTree[level][0].gameStateValue); + + // if the selected dialogue line has a POSITIVE game state value + // then set gamestate to Value = TALK(OLDLEVEL,S,3) + + index = _dialogueTree[oldLevel][selectedSentence].gameStateIndex; + if (index > 0) + _vm->logic()->gameState(index, _dialogueTree[oldLevel][selectedSentence].gameStateValue); + + // check to see if person has something final to say + if (-1 == retval) { + findDialogueString(_person1PtrOff, head, _pMax, _talkString[0]); + if (_talkString[0][0] != '\0') { + sprintf(otherVoiceFilePrefix, "%2d%4xP", _talkKey, head); + if (speak(_talkString[0], &person, otherVoiceFilePrefix)) + personWalking = true; + } + } + } + + cutawayFilename[0] = '\0'; + + for (i = 0; i < 2; i++) { + if (_gameState[i] > 0) { + if (_vm->logic()->gameState(_gameState[i]) == _testValue[i]) { + if (_itemNumber[i] > 0) + _vm->logic()->inventoryInsertItem(_itemNumber[i]); + else + _vm->logic()->inventoryDeleteItem(ABS(_itemNumber[i])); + } + } + } + + _vm->grid()->setupPanel(); + + uint16 offset = _cutawayPtrOff; + + int16 cutawayGameState = (int16)READ_BE_INT16(_fileData + offset); offset += 2; + int16 cutawayTestValue = (int16)READ_BE_INT16(_fileData + offset); offset += 2; + + if (_vm->logic()->gameState(cutawayGameState) == cutawayTestValue) { + getString(_fileData, offset, cutawayFilename, 20); + if (cutawayFilename[0]) { + //CR 2 - 7/3/95, If we're executing a cutaway scene, then make sure + // Joe can talk, so set TALKQUIT to 0 just in case we exit on the + // line that set's the cutaway game states. + _vm->input()->talkQuitReset(); + } + } + if (_vm->input()->talkQuit()) { + if (_oldSelectedSentenceIndex > 0) + selectedValue(_oldSelectedSentenceIndex, _oldSelectedSentenceValue); + _vm->input()->talkQuitReset(); + _vm->display()->clearTexts(0, 198); + _vm->logic()->makeJoeSpeak(15, false); + } else { + setHasTalkedTo(); + } + + _vm->logic()->joeFace(); + + if (cutawayFilename[0] == '\0') { + BobSlot *pbs = _vm->graphics()->bob(person.actor->bobNum); + + pbs->x = person.actor->x; + pbs->y = person.actor->y; + + // Better kick start the persons anim sequence + _vm->graphics()->resetPersonAnim(person.actor->bobNum); + } + + _vm->logic()->joeWalk(JWM_NORMAL); +} + +void Talk::disableSentence(int oldLevel, int selectedSentence) { + // Mark off selected option + + if (1 == oldLevel) { + if (_dialogueTree[oldLevel][selectedSentence].dialogueNodeValue1 != -1) { + // Make sure choice is not exit option + _oldSelectedSentenceIndex = selectedSentence; + _oldSelectedSentenceValue = selectedValue(selectedSentence); + selectedValue(selectedSentence, -1); + } + } + + // Cancel selected dialogue line, so that its no longer displayed + _dialogueTree[oldLevel][selectedSentence].head = -1; + _dialogueTree[oldLevel][selectedSentence].dialogueNodeValue1 = -1; +} + +void Talk::findDialogueString(uint16 offset, int16 id, int16 max, char *str) { + str[0] = '\0'; + for (int i = 1; i <= max; i++) { + offset += 2; + int16 currentId = (int16)READ_BE_INT16(_fileData + offset); + offset += 2; + if (id == currentId) { + getString(_fileData, offset, str, MAX_STRING_LENGTH, 4); + break; + } else { + getString(_fileData, offset, NULL, MAX_STRING_LENGTH, 4); + } + } +} + +byte *Talk::loadDialogFile(const char *filename) { + static const struct { + const char *filename; + Language lang; + } dogFiles[] = { + { "chief1.dog", FRENCH }, + { "chief2.dog", FRENCH }, + { "bud1.dog", ITALIAN } + }; + for (int i = 0; i < ARRAYSIZE(dogFiles); ++i) { + if (!scumm_stricmp(filename, dogFiles[i].filename) && + _vm->resource()->getLanguage() == dogFiles[i].lang) { + Common::File fdog; + fdog.open(filename); + if (fdog.isOpen()) { + debug(6, "Loading dog file '%s' from game data path", filename); + uint32 size = fdog.size() - DOG_HEADER_SIZE; + byte *buf = new byte[size]; + fdog.seek(DOG_HEADER_SIZE); + fdog.read(buf, size); + return buf; + } + } + } + return _vm->resource()->loadFile(filename, DOG_HEADER_SIZE); +} + +void Talk::load(const char *filename) { + int i; + byte *ptr = _fileData = loadDialogFile(filename); + bool canQuit; + + // Load talk header + + _levelMax = (int16)READ_BE_INT16(ptr); ptr += 2; + + if (_levelMax < 0) { + _levelMax = -_levelMax; + canQuit = false; + } else { + canQuit = true; + } + + _uniqueKey = (int16)READ_BE_INT16(ptr); ptr += 2; + _talkKey = (int16)READ_BE_INT16(ptr); ptr += 2; + _jMax = (int16)READ_BE_INT16(ptr); ptr += 2; + _pMax = (int16)READ_BE_INT16(ptr); ptr += 2; + + for (i = 0; i < 2; i++) { + _gameState [i] = (int16)READ_BE_INT16(ptr); ptr += 2; + _testValue [i] = (int16)READ_BE_INT16(ptr); ptr += 2; + _itemNumber[i] = (int16)READ_BE_INT16(ptr); ptr += 2; + } + + _person1PtrOff = READ_BE_UINT16(ptr); ptr += 2; + _cutawayPtrOff = READ_BE_UINT16(ptr); ptr += 2; + _person2PtrOff = READ_BE_UINT16(ptr); ptr += 2; + _joePtrOff = 32 + _levelMax * 96; + + // Load dialogue tree + ptr = _fileData + 32; + memset(&_dialogueTree[0], 0, sizeof(_dialogueTree[0])); + for (i = 1; i <= _levelMax; i++) + for (int j = 0; j <= 5; j++) { + ptr += 2; + _dialogueTree[i][j].head = (int16)READ_BE_INT16(ptr); ptr += 2; + ptr += 2; + _dialogueTree[i][j].dialogueNodeValue1 = (int16)READ_BE_INT16(ptr); ptr += 2; + ptr += 2; + _dialogueTree[i][j].gameStateIndex = (int16)READ_BE_INT16(ptr); ptr += 2; + ptr += 2; + _dialogueTree[i][j].gameStateValue = (int16)READ_BE_INT16(ptr); ptr += 2; + } +} + +void Talk::initialTalk() { + // Lines 848-903 in talk.c + + uint16 offset = _joePtrOff + 2; + uint16 hasNotString = READ_BE_UINT16(_fileData + offset); offset += 2; + + char joeString[MAX_STRING_SIZE]; + if (!hasNotString) { + getString(_fileData, offset, joeString, MAX_STRING_LENGTH); + } else { + joeString[0] = '\0'; + } + + offset = _person2PtrOff; + char joe2String[MAX_STRING_SIZE]; + getString(_fileData, offset, _person2String, MAX_STRING_LENGTH); + getString(_fileData, offset, joe2String, MAX_STRING_LENGTH); + + if (!hasTalkedTo()) { + // Not yet talked to this person + if (joeString[0] != '0') { + char voiceFilePrefix[MAX_STRING_SIZE]; + sprintf(voiceFilePrefix, "%2dSSSSJ", _talkKey); + speak(joeString, NULL, voiceFilePrefix); + } + } else { + // Already spoken to them, choose second response + if (joe2String[0] != '0') { + char voiceFilePrefix[MAX_STRING_SIZE]; + sprintf(voiceFilePrefix, "%2dXXXXJ", _talkKey); + speak(joe2String, NULL, voiceFilePrefix); + } + } +} + +int Talk::getSpeakCommand(const Person *person, const char *sentence, unsigned &index) { + // Lines 1299-1362 in talk.c + int commandCode = SPEAK_DEFAULT; + uint16 id = (sentence[index] << 8) | sentence[index + 1]; + switch (id) { + case 'AO': + commandCode = SPEAK_AMAL_ON; + break; + case 'FL': + commandCode = SPEAK_FACE_LEFT; + break; + case 'FF': + commandCode = SPEAK_FACE_FRONT; + break; + case 'FB': + commandCode = SPEAK_FACE_BACK; + break; + case 'FR': + commandCode = SPEAK_FACE_RIGHT; + break; + case 'GD': + _vm->logic()->joeGrab(STATE_GRAB_DOWN); + commandCode = SPEAK_NONE; + break; + case 'GM': + _vm->logic()->joeGrab(STATE_GRAB_MID); + commandCode = SPEAK_NONE; + break; + case 'WT': + commandCode = SPEAK_PAUSE; + break; + case 'XY': + // For example *XY00(237,112) + { + commandCode = atoi(sentence + index + 2); + int x = atoi(sentence + index + 5); + int y = atoi(sentence + index + 9); + if (0 == strcmp(person->name, "JOE")) + _vm->walk()->moveJoe(0, x, y, _vm->input()->cutawayRunning()); + else + _vm->walk()->movePerson(person, x, y, _vm->graphics()->numFrames(), 0); + index += 11; + // if (JOEWALK==3) CUTQUIT=0; + // XXX personWalking = true; + } + break; + default: + if (sentence[index + 0] >= '0' && sentence[index + 0] <= '9' && + sentence[index + 1] >= '0' && sentence[index + 1] <= '9') { + commandCode = (sentence[index] - '0') * 10 + (sentence[index + 1] - '0'); + } else + warning("Unknown command string: '%2s'", sentence + index); + } + + index += 2; + + return commandCode; +} + + +bool Talk::speak(const char *sentence, Person *person, const char *voiceFilePrefix) { + // Function SPEAK, lines 1266-1384 in talk.c + bool personWalking = false; + unsigned segmentIndex = 0; + unsigned segmentStart = 0; + unsigned i; + + Person joe_person; + ActorData joe_actor; + + _vm->logic()->joeWalk(JWM_SPEAK); + + if (!person) { + // Fill in values for use by speakSegment() etc. + memset(&joe_person, 0, sizeof(Person)); + memset(&joe_actor, 0, sizeof(ActorData)); + + joe_actor.bobNum = 0; + joe_actor.color = 14; + joe_actor.bankNum = 7; + + joe_person.actor = &joe_actor; + joe_person.name = "JOE"; + + person = &joe_person; + } + + debug(6, "Sentence '%s' is said by person '%s' and voice files with prefix '%s' played", + sentence, person->name, voiceFilePrefix); + + if (sentence[0] == '\0') { + return personWalking; + } + + if (0 == strcmp(person->name, "FAYE-H" ) || + 0 == strcmp(person->name, "FRANK-H") || + 0 == strcmp(person->name, "AZURA-H") || + 0 == strcmp(person->name, "X3_RITA") || + (0 == strcmp(person->name, "JOE") && _vm->logic()->currentRoom() == FAYE_HEAD ) || + (0 == strcmp(person->name, "JOE") && _vm->logic()->currentRoom() == AZURA_HEAD) || + (0 == strcmp(person->name, "JOE") && _vm->logic()->currentRoom() == FRANK_HEAD)) + _talkHead = true; + else + _talkHead = false; + + for (i = 0; i < strlen(sentence); ) { + if (sentence[i] == '*') { + int segmentLength = i - segmentStart; + + i++; + int command = getSpeakCommand(person, sentence, i); + + if (SPEAK_NONE != command) { + speakSegment( + sentence + segmentStart, + segmentLength, + person, + command, + voiceFilePrefix, + segmentIndex); + // XXX if (JOEWALK == 2) break + } + + segmentIndex++; + segmentStart = i; + } else + i++; + + if (_vm->input()->cutawayQuit() || _vm->input()->talkQuit()) + return personWalking; + } + + if (segmentStart != i) { + speakSegment( + sentence + segmentStart, + i - segmentStart, + person, + 0, + voiceFilePrefix, + segmentIndex); + } + + return personWalking; +} + +int Talk::countSpaces(const char *segment) { + int tmp = 0; + + while (*segment++) + tmp++; + + if (tmp < 10) + tmp = 10; + + return (tmp * 2) / (_vm->talkSpeed() / 3); +} + +void Talk::headStringAnimation(const SpeechParameters *parameters, int bobNum, int bankNum) { + // talk.c lines 1612-1635 + BobSlot *bob2 = _vm->graphics()->bob(2); + + if (parameters->animation[0] == 'E') { + int offset = 1; + + BobSlot *bob = _vm->graphics()->bob(bobNum); + int16 x = bob->x; + int16 y = bob->y; + + for (;;) { + uint16 frame; + + frame = atoi(parameters->animation + offset); + if (!frame) + break; + + offset += 4; + + _vm->bankMan()->unpack(frame, _vm->graphics()->numFrames(), bankNum); + + bob2->frameNum = _vm->graphics()->numFrames(); + bob2->scale = 100; + bob2->active = true; + bob2->x = x; + bob2->y = y; + + _vm->update(); + } + } else + bob2->active = false; +} + +void Talk::stringAnimation(const SpeechParameters *parameters, int startFrame, int bankNum) { + // lines 1639-1690 in talk.c + + int offset = 0; + bool torso; + + if (parameters->animation[0] == 'T') { + // Torso animation + torso = true; + _vm->bankMan()->overpack(parameters->body, startFrame, bankNum); + offset++; + } else if (parameters->animation[0] == 'E') { + // Talking head animation + return; + } else if (!isdigit(parameters->animation[0])) { + debug(6, "Error in speak string animation: '%s'", parameters->animation); + return; + } else + torso = false; + + for (;;) { + uint16 frame; + + frame = atoi(parameters->animation + offset); + if (!frame) + break; + + offset += 4; + + if (frame > 500) { + frame -= 500; + _vm->sound()->playSfx(_vm->logic()->currentRoomSfx(), false); + } + + if (torso) { + _vm->bankMan()->overpack(frame, startFrame, bankNum); + } else { + _vm->bankMan()->unpack(frame, startFrame, bankNum); + // XXX bobs[BNUM].scale=SF; + } + + _vm->update(); + } +} + +void Talk::defaultAnimation( + const char *segment, + bool isJoe, + const SpeechParameters *parameters, + int startFrame, + int bankNum) { + // lines 1730-1823 in talk.c + + if (segment[0] != 0) { + + // Why on earth would someone name a variable qzx? + short qzx = 0; + + int len = countSpaces(segment); + while (1) { + if (parameters != NULL) { + + int bf; + if (segment[0] == ' ') + bf = 0; + else + bf = parameters->bf; + + int head; + if (parameters->rf > 0) + head = bf + _vm->randomizer.getRandomNumber(parameters->rf); + else + head = bf; + + if (bf > 0) { + // Make the head move + qzx ^= 1; + if (parameters->af && qzx) + _vm->bankMan()->overpack(parameters->af + head, startFrame, bankNum); + else { + _vm->bankMan()->overpack(head, startFrame, bankNum); + } + } else { + debug(6, "[Talk::defaultAnimation] Body action!"); + // Just do a body action + _vm->bankMan()->overpack(parameters->body, startFrame, bankNum); + } + + if (!_talkHead) + _vm->update(); + } else { // (_talkHead && isJoe) + _vm->update(); + } + + if (_vm->input()->talkQuit()) + break; + + if (_vm->logic()->joeWalk() == JWM_SPEAK) { + _vm->update(); + } else { + _vm->update(true); + if (_vm->logic()->joeWalk() == JWM_EXECUTE) + // Selected a command, so exit + break; + } + + // Skip through text more quickly + if (_vm->input()->keyVerb() == VERB_SKIP_TEXT) { + _vm->input()->clearKeyVerb(); + _vm->sound()->stopSpeech(); + break; + } + + if (_vm->sound()->speechOn() && _vm->sound()->speechSfxExists()) { + // sfx is finished, stop the speak animation + if (!_vm->sound()->isSpeechActive()) { + break; + } + } else { + // no sfx, stop the animation when speak segment 'length' is 0 + --len; + if (len <= 0) { + break; + } + } + } + } + + // Make sure that Person closes their mouth + if (!isJoe && parameters->ff > 0) + _vm->bankMan()->overpack(parameters->ff, startFrame, bankNum); +} + + +void Talk::speakSegment( + const char *segmentStart, + int length, + Person *person, + int command, + const char *voiceFilePrefix, + int index) +{ + int i; + char segment[MAX_STRING_SIZE]; + memcpy(segment, segmentStart, length); + segment[length] = '\0'; + + char voiceFileName[MAX_STRING_SIZE]; + sprintf(voiceFileName, "%s%1x", voiceFilePrefix, index + 1); + + // FIXME - it seems the french talkie version has a useless voice file ; + // the c30e_102 file is very similar to c30e_101, so there is no need to + // play it. This voice was used in room 30 (N8) when talking to Klunk. + if (!(_vm->resource()->getLanguage() == FRENCH && !strcmp(voiceFileName, "c30e_102")) + && _vm->sound()->speechOn()) + _vm->sound()->playSfx(voiceFileName, true); + + int faceDirectionCommand = 0; + + switch (command) { + case SPEAK_PAUSE: + for (i = 0; i < 10 && !_vm->input()->talkQuit(); i++) { + _vm->update(); + } + return; + + case SPEAK_FACE_LEFT: + case SPEAK_FACE_RIGHT: + case SPEAK_FACE_FRONT: + case SPEAK_FACE_BACK: + faceDirectionCommand = command; + command = 0; + break; + } + + bool isJoe = (0 == person->actor->bobNum); + + int16 bobNum = person->actor->bobNum; + uint16 color = person->actor->color; + uint16 bankNum = person->actor->bankNum; + + BobSlot *bob = _vm->graphics()->bob(bobNum); + + bool oracle = false; + int textX = 0; + int textY = 0; + + if (!isJoe) { + if (SPEAK_AMAL_ON == command) { + // It's the oracle! + // Don't turn AMAL animation off, and don't manually anim person + command = SPEAK_ORACLE; + oracle = true; + uint16 frameNum = _vm->graphics()->personFrames(bobNum); + for (i = 5; i <= 8; ++i) { + _vm->bankMan()->unpack(i, frameNum, bankNum); + ++frameNum; + } + } else { + bob->animating = false; + bob->frameNum = 31 + bobNum; + } + } + + if (_talkHead) { + // talk.c lines 1491-1533 + switch (_vm->logic()->currentRoom()) { + case FAYE_HEAD: + case AZURA_HEAD: + textX = 15; + break; + default: // FRANK_HEAD + textX = 150; + break; + } + textY = isJoe ? 30 : 60; + } else { + textX = bob->x; + textY = bob->y; + } + + //int SF = _vm->grid()->findScale(textX, textY); + + const SpeechParameters *parameters = NULL; + int startFrame = 0; + + if (_talkHead && isJoe) { + if (_vm->subtitles()) + _vm->graphics()->setBobText(bob, segment, textX, textY, color, true); + defaultAnimation(segment, isJoe, parameters, startFrame, bankNum); + } else { + if (SPEAK_UNKNOWN_6 == command) + return; + + if (isJoe) { + if (_vm->logic()->currentRoom() == 108) + parameters = findSpeechParameters("JOE-E", command, 0); + else + parameters = findSpeechParameters("JOE", command, _vm->logic()->joeFacing()); + } + else + parameters = findSpeechParameters(person->name, command, 0); + + startFrame = 31 + bobNum; + int faceDirection = 0; + + if (isJoe && _vm->logic()->joeFacing() == DIR_LEFT) + faceDirection = DIR_LEFT; + else if (!isJoe) { + ObjectData *data = _vm->logic()->objectData(_vm->logic()->objectForPerson(bobNum)); + + if (data->image == -3) + faceDirection = DIR_LEFT; + + if (faceDirectionCommand == SPEAK_FACE_LEFT) + data->image = -3; + else if (faceDirectionCommand == SPEAK_FACE_RIGHT) + data->image = -4; + } + + if (faceDirectionCommand) { + switch (faceDirectionCommand) { + case SPEAK_FACE_LEFT: + faceDirection = DIR_LEFT; + break; + case SPEAK_FACE_RIGHT: + faceDirection = DIR_RIGHT; + break; + case SPEAK_FACE_FRONT: + faceDirection = DIR_FRONT; + break; + case SPEAK_FACE_BACK: + faceDirection = DIR_BACK; + break; + } + if (isJoe) + _vm->logic()->joeFacing(faceDirection); + } + + if (!isJoe) { + bob->xflip = (faceDirection == DIR_LEFT); + } + + // Run animated sequence if SANIMstr is primed + + if (_talkHead) { + // talk.c lines 1612-1635 + headStringAnimation(parameters, bobNum, bankNum); + } + + if (_vm->subtitles()) + _vm->graphics()->setBobText(bob, segment, textX, textY, color, _talkHead); + + if (parameters->animation[0] != '\0' && parameters->animation[0] != 'E') { + stringAnimation(parameters, startFrame, bankNum); + } else { + _vm->bankMan()->unpack(parameters->body, startFrame, bankNum); + + if (length == 0 && !isJoe && parameters->bf > 0) { + _vm->bankMan()->overpack(parameters->bf, startFrame, bankNum); + _vm->update(); + } + + if (-1 == parameters->rf) { + // Setup the Torso frames + _vm->bankMan()->overpack(parameters->bf, startFrame, bankNum); + if (isJoe) + parameters = findSpeechParameters(person->name, 0, _vm->logic()->joeFacing()); + else + parameters = findSpeechParameters(person->name, 0, 0); + } + + if (-2 == parameters->rf) { + // Setup the Torso frames + _vm->bankMan()->overpack(parameters->bf, startFrame, bankNum); + if (isJoe) + parameters = findSpeechParameters(person->name, 14, _vm->logic()->joeFacing()); + else + parameters = findSpeechParameters(person->name, 14, 0); + } + + defaultAnimation(segment, isJoe, parameters, startFrame, bankNum); + } + } + + // Moved here so that Text is cleared when a Torso command done! + _vm->display()->clearTexts(0,198); + + if (oracle) { + uint16 frameNum = _vm->graphics()->personFrames(bobNum); + for (i = 1; i <= 4; ++i) { + _vm->bankMan()->unpack(i, frameNum, bankNum); + ++frameNum; + } + } + + // Ensure that the correct buffer frame is selected + + if (isJoe && !_talkHead) { + if (_vm->logic()->joeFacing() == DIR_FRONT || + _vm->logic()->joeFacing() == DIR_BACK) { + // Joe is facing either Front or Back! + // - Don't FACE_JOE in room 69, because Joe is probably + // holding the Dino Ray gun. + if (_vm->logic()->currentRoom() != 69) + _vm->logic()->joeFace(); + } else { + if (command == SPEAK_DEFAULT || + command == 6 || + command == 7) { + _vm->logic()->joeFace(); + } else if (command != 5) { + // 7/11/94, Ensure that correct mouth closed frame is used! + if (parameters->rf != -1) + // XXX should really be just "bf", but it is not always calculated... :-( + _vm->bankMan()->overpack(parameters->bf, startFrame, bankNum); + + if (parameters->ff == 0) + _vm->bankMan()->overpack(10, startFrame, bankNum); + else + _vm->bankMan()->overpack(parameters->ff, startFrame, bankNum); + } + } + } + + _vm->update(); +} + +const Talk::SpeechParameters *Talk::findSpeechParameters( + const char *name, + int state, + int faceDirection) { + const SpeechParameters *iterator = _speechParameters; + if (faceDirection == DIR_RIGHT) + faceDirection = DIR_LEFT; + + while (iterator->name[0] != '*') { + if (0 == scumm_stricmp(iterator->name, name) && + iterator->state == state && + iterator->faceDirection == faceDirection) + break; + iterator++; + } + + return iterator; +} + +void Talk::getString(const byte *ptr, uint16 &offset, char *str, int maxLength, int align) { + assert((align & 1) == 0); + int length = *(ptr + offset); + ++offset; + + if (length > maxLength) { + error("String too long. Length = %i, maxLength = %i", length, maxLength); + } else if (length) { + if (str) { + memcpy(str, ptr + offset, length); + str[length] = '\0'; + } + offset = (offset + length + (align - 1)) & ~(align - 1); + } else { + if (str) { + str[0] = '\0'; + } + } +} + +TalkSelected *Talk::talkSelected() { + return _vm->logic()->talkSelected(_uniqueKey); +} + +int Talk::splitOption(const char *str, char optionText[5][MAX_STRING_SIZE]) { + char option[MAX_STRING_SIZE]; + strcpy(option, str); + // option text ends at '*' char + char *p = strchr(option, '*'); + if (p) { + *p = '\0'; + } + int lines; + memset(optionText, 0, 5 * MAX_STRING_SIZE); + if (_vm->resource()->getLanguage() == ENGLISH || _vm->display()->textWidth(option) <= MAX_TEXT_WIDTH) { + strcpy(optionText[0], option); + lines = 1; + } else if (_vm->resource()->getLanguage() == HEBREW) { + lines = splitOptionHebrew(option, optionText); + } else { + lines = splitOptionDefault(option, optionText); + } + return lines; +} + +int Talk::splitOptionHebrew(const char *str, char optionText[5][MAX_STRING_SIZE]) { + char tmpString[MAX_STRING_SIZE] = ""; + uint16 len = 0; + uint16 spaceCharWidth = _vm->display()->textWidth(" "); + uint16 width = 0; + uint16 optionLines = 0; + uint16 maxTextLen = MAX_TEXT_WIDTH; + const char *p = strchr(str, '\0'); + while (p != str - 1) { + while (*p != ' ' && p != str - 1) { + --p; + ++len; + } + if (p != str - 1) { + uint16 wordWidth = _vm->display()->textWidth(p, len); + width += wordWidth; + if (width > maxTextLen) { + ++optionLines; + strncpy(optionText[optionLines], p, len); + optionText[optionLines][len] = '\0'; + width = wordWidth; + maxTextLen = MAX_TEXT_WIDTH - OPTION_TEXT_MARGIN; + } else { + strcpy(tmpString, optionText[optionLines]); + strncpy(optionText[optionLines], p, len); + optionText[optionLines][len] = '\0'; + strcat(optionText[optionLines], tmpString); + } + --p; + len = 1; + width += spaceCharWidth; + } else { + if (len > 1) { + if (width + _vm->display()->textWidth(p + 1, len) > maxTextLen) { + ++optionLines; + } + strcpy(tmpString, optionText[optionLines]); + strncpy(optionText[optionLines], p + 1, len); + optionText[optionLines][len] = '\0'; + strcat(optionText[optionLines], tmpString); + } + ++optionLines; + } + } + return optionLines; +} + +int Talk::splitOptionDefault(const char *str, char optionText[5][MAX_STRING_SIZE]) { + // Split up multiple line option at closest space character + uint16 spaceCharWidth = _vm->display()->textWidth(" "); + uint16 width = 0; + uint16 optionLines = 0; + uint16 maxTextLen = MAX_TEXT_WIDTH; + const char *p = str; + while (p) { + p = strchr(str, ' '); + if (p) { + uint16 len = p - str; + uint16 wordWidth = _vm->display()->textWidth(str, len); + width += wordWidth; + if (width > maxTextLen) { + ++optionLines; + strncpy(optionText[optionLines], str, len + 1); + width = wordWidth; + maxTextLen = MAX_TEXT_WIDTH - OPTION_TEXT_MARGIN; + } else { + strncat(optionText[optionLines], str, len + 1); + } + width += spaceCharWidth; + str = p + 1; + } else { + if (str[0]) { + if (width + _vm->display()->textWidth(str) > maxTextLen) { + ++optionLines; + } + strcat(optionText[optionLines], str); + } + ++optionLines; + } + } + return optionLines; +} + +int16 Talk::selectSentence() { + int selectedSentence = 0; + + int startOption = 1; + int optionLines = 0; + char optionText[5][MAX_STRING_SIZE]; + int talkZone[5]; + int i; + + _vm->display()->textCurrentColor(INK_TALK_NORMAL); + + _vm->graphics()->setupArrows(); + BobSlot *arrowBobUp = _vm->graphics()->bob(Graphics::ARROW_BOB_UP); + arrowBobUp->active = false; + BobSlot *arrowBobDown = _vm->graphics()->bob(Graphics::ARROW_BOB_DOWN); + arrowBobDown->active = false; + + bool rezone = true; + + while (rezone) { + rezone = false; + + // Set zones for UP/DOWN text arrows when not English version + + _vm->grid()->clear(GS_PANEL); + + if (_vm->resource()->getLanguage() != ENGLISH) { + _vm->grid()->setZone(GS_PANEL, ARROW_ZONE_UP, MAX_TEXT_WIDTH + 1, 0, 319, 24); + _vm->grid()->setZone(GS_PANEL, ARROW_ZONE_DOWN, MAX_TEXT_WIDTH + 1, 25, 319, 49); + } + + _vm->display()->clearTexts(151, 199); + + int sentenceCount = 0; + int yOffset = 1; + + for (i = startOption; i <= 4; i++) { + talkZone[i] = 0; + + if (_talkString[i][0] != '\0') { + sentenceCount++; + optionLines = splitOption(_talkString[i], optionText); + + if (yOffset < 5) { + _vm->grid()->setZone( + GS_PANEL, + i, + 0, + yOffset * LINE_HEIGHT - PUSHUP, + (_vm->resource()->getLanguage() == ENGLISH) ? 319 : MAX_TEXT_WIDTH, + (yOffset + optionLines) * LINE_HEIGHT - PUSHUP); + } + + int j; + for (j = 0; j < optionLines; j++) { + if (yOffset < 5) { + _vm->display()->setText( + (j == 0) ? 0 : OPTION_TEXT_MARGIN, + 150 - PUSHUP + yOffset * LINE_HEIGHT, + optionText[j]); + } + yOffset++; + } + + talkZone[i] = sentenceCount; + } + } + + yOffset--; + + // Up and down dialogue arrows + + if (_vm->resource()->getLanguage() != ENGLISH) { + arrowBobUp->active = (startOption > 1); + arrowBobDown->active = (yOffset > 4); + } + + _vm->input()->clearKeyVerb(); + + if (sentenceCount > 0) { + int zone = 0; + int oldZone = 0; + + while (0 == selectedSentence) { + + if (_vm->input()->talkQuit()) + break; + + _vm->update(); + + zone = _vm->grid()->findZoneForPos(GS_PANEL, _vm->input()->mousePosX(), _vm->input()->mousePosY()); + + int mouseButton = _vm->input()->mouseButton(); + _vm->input()->clearMouseButton(); + + if (ARROW_ZONE_UP == zone || ARROW_ZONE_DOWN == zone) { + if (oldZone > 0) { + int16 y; + const Box *b = _vm->grid()->zone(GS_PANEL, oldZone); + for (y = b->y1; y < b->y2; y += 10) + _vm->display()->textColor(150 + y, INK_TALK_NORMAL); + oldZone = 0; + } + if (mouseButton != 0) { + if (zone == ARROW_ZONE_UP && arrowBobUp->active) { + startOption--; + } else if (zone == ARROW_ZONE_DOWN && arrowBobDown->active) { + startOption++; + } + } + rezone = true; + break; + } else { + if (oldZone != zone) { + // Changed zone, change text colors + int y; + + debug(6, "Changed zone. oldZone = %i, zone = %i", + oldZone, zone); + + if (zone > 0) { + const Box *b = _vm->grid()->zone(GS_PANEL, zone); + for (y = b->y1; y < b->y2; y += 10) + _vm->display()->textColor(150 + y, INK_JOE); + } + + if (oldZone > 0) { + const Box *b = _vm->grid()->zone(GS_PANEL, oldZone); + for (y = b->y1; y < b->y2; y += 10) + _vm->display()->textColor(150 + y, INK_TALK_NORMAL); + } + + oldZone = zone; + } + + } + + Verb v = _vm->input()->keyVerb(); + if (v >= VERB_DIGIT_FIRST && v <= VERB_DIGIT_LAST) { + int n = v - VERB_DIGIT_FIRST + 1; + for (i = 1; i <= 4; i++) { + if (talkZone[i] == n) { + selectedSentence = i; + break; + } + } + + _vm->input()->clearKeyVerb(); + } + else if (mouseButton) { + selectedSentence = zone; + } + + } // while () + } + } + + debug(6, "Selected sentence %i", selectedSentence); + + arrowBobUp->active = false; + arrowBobDown->active = false; + + if (selectedSentence > 0) { + _vm->display()->clearTexts(0, 198); + + speak(_talkString[selectedSentence], NULL, _joeVoiceFilePrefix[selectedSentence]); + } + + _vm->display()->clearTexts(151, 151); + + return selectedSentence; +} + +#ifndef PALMOS_68K +const Talk::SpeechParameters Talk::_speechParameters[] = { + { "JOE", 0, 1, 1, 10, 2, 3, "", 0 }, + { "JOE", 0, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 0, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 1, 1, 1, 45, -1, 0, "", 0 }, + { "JOE", 1, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 1, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 2, 1, 1, 46, -1, 0, "", 0 }, + { "JOE", 2, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 2, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 3, 1, 1, 47, -1, 0, "", 0 }, + { "JOE", 3, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 3, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 4, 1, 1, 50, -1, 0, "", 0 }, + { "JOE", 4, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 4, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 5, 1, 2, 0, 0, 0, "", 0 }, + { "JOE", 5, 3, 4, 0, 0, 0, "", 0 }, + { "JOE", 5, 4, 6, 0, 0, 0, "", 0 }, + + { "JOE", 6, 1, 1, 48, 0, 1, "", 0 }, + { "JOE", 6, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 6, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 7, 1, 1, 51, 0, 1, "", 0 }, + { "JOE", 7, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 7, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 8, 1, 1, 26, 0, 0, "", 0 }, + { "JOE", 8, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 8, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 9, 1, 1, 29, 0, 0, "", 0 }, + { "JOE", 9, 3, 3, 28, 0, 0, "", 0 }, + { "JOE", 9, 4, 5, 38, 0, 0, "", 0 }, + + { "JOE", 10, 1, 1, 12, 0, 0, "T046,010,010,010,012,012,012,012,012,012,012,012,012,012,012,012,012,012,010,000", 0 }, + { "JOE", 10, 3, 3, 18, 0, 0, "", 0 }, + { "JOE", 10, 4, 5, 44, 0, 0, "", 0 }, + + { "JOE", 11, 1, 1, 53, -1, 0, "", 0 }, + { "JOE", 11, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 11, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 12, 1, 1, 10, 2, 3, "", 0 }, + { "JOE", 12, 3, 3, 28, 2, 0, "", 0 }, + { "JOE", 12, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 13, 1, 1, 10, 2, 3, "T012,013,019,019,019,019,019,019,019,019,019,019,013,010,000", 0 }, + { "JOE", 13, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 13, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 14, 1, 1, 16, 2, 3, "", 16 }, + { "JOE", 14, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 14, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 15, 1, 1, 58, -1, 0, "", 0 }, + { "JOE", 15, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 15, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 16, 1, 1, 59, -1, 0, "", 0 }, + { "JOE", 16, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 16, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 17, 1, 1, 56, -1, 0, "", 0 }, + { "JOE", 17, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 17, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 18, 1, 56, 16, 2, 3, "T056,057,057,057,056,056,000", 0 }, + { "JOE", 18, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 18, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 19, 1, 54, 16, 2, 3, "T054,055,057,056,000", 0 }, + { "JOE", 19, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 19, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 20, 1, 56, 16, 2, 3, "T056,057,055,054,001,000", 0 }, + { "JOE", 20, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 20, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 21, 1, 1, 60, -1, 0, "", 0 }, + { "JOE", 21, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 21, 4, 61, 38, 1, 0, "", 0 }, + + { "JOE", 22, 1, 1, 16, 2, 3, "T063,060,000", 0 }, + { "JOE", 22, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 22, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 23, 1, 1, 16, 2, 3, "T060,063,001,000", 0 }, + { "JOE", 23, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 23, 4, 5, 38, 1, 0, "", 0 }, + + { "JOE", 24, 1, 1, 47, -2, 0, "", 0 }, + { "JOE", 24, 3, 3, 28, 2, 3, "", 0 }, + { "JOE", 24, 4, 5, 38, 1, 0, "", 0 }, + + { "RICO", 0, 0, 1, 7, 1, 3, "", 7 }, + { "RICO", 1, 0, 1, 5, -1, 0, "", 7 }, + { "RICO", 2, 0, 1, 9, 0, 3, "", 7 }, + { "RICO", 3, 0, 1, 4, -1, 0, "", 7 }, + + { "EDDY", 0, 0, 14, 18, 1, 3, "", 18 }, + { "EDDY", 1, 0, 14, 0, 0, 0, "T016,017,017,016,016,017,017,016,016,017,017,000", 18 }, + + { "MIKE", 0, 0, 1, 2, 2, 3, "", 2 }, + + { "LOLA", 0, 0, 30, 33, 2, 3, "", 33 }, + { "LOLA", 1, 0, 9, 10, 2, 3, "", 33 }, + { "LOLA", 2, 0, 30, 33, 2, 3, "", 33 }, + { "LOLA", 3, 0, 32, 33, 2, 3, "", 33 }, + { "LOLA", 4, 0, 8, 0, 0, 0, "", 33 }, + { "LOLA", 5, 0, 31, 0, 0, 0, "", 0 }, + { "LOLA", 6, 0, 31, 0, 0, 0, "047,048,049,050,000", 33 }, + + { "LOLA_SHOWER", 0, 0, 7, 10, 2, 3, "", 10 }, + { "LOLA_SHOWER", 1, 0, 9, 10, 2, 3, "", 10 }, + { "LOLA_SHOWER", 2, 0, 30, 33, 2, 3, "", 10 }, + { "LOLA_SHOWER", 3, 0, 32, 33, 2, 3, "", 10 }, + { "LOLA_SHOWER", 4, 0, 8, 0, 0, 0, "", 0 }, + { "LOLA_SHOWER", 5, 0, 61, 0, 0, 0, "", 0 }, + { "LOLA_SHOWER", 6, 0, 64, 10, 2, 3, "", 0 }, + { "LOLA_SHOWER", 7, 0, 31, 0, 0, 0, "062,063,064,000", 0 }, + + { "SECRETARY", 0, 0, 1, 12, 2, 3, "", 12 }, + { "SECRETARY", 1, 0, 1, 12, 2, 0, "", 12 }, + { "SECRETARY", 2, 0, 1, 12, 2, 0, "", 12 }, + + { "SPARKY", 0, 0, 21, 23, 2, 3, "", 23 }, + { "SPARKY", 1, 0, 21, 22, 0, 0, "", 0 }, + { "SPARKY", 2, 0, 21, 22, 0, 0, "021,042,043,000", 0 }, + { "SPARKY", 3, 0, 21, 22, 0, 0, "043,042,021,000", 0 }, + { "SPARKY", 4, 0, 43, 43, 1, 0, "", 0 }, + { "SPARKY", 14, 0, 21, 29, 5, 0, "", 29 }, + + { "SPARKY-F", 0, 0, 45, 23, 5, 0, "", 23 }, + { "SPARKY-F", 1, 0, 45, 47, 0, 0, "", 0 }, + { "SPARKY-F", 2, 0, 45, 23, 5, 0, "045,046,046,045,000", 23 }, + { "SPARKY-F", 14, 0, 45, 29, 5, 0, "", 29 }, + + { "FAYE", 0, 0, 19, 35, 2, 3, "", 35 }, + { "FAYE", 1, 0, 19, 41, 2, 3, "", 35 }, + { "FAYE", 2, 0, 19, 28, -1, 0, "", 35 }, + { "FAYE", 3, 0, 19, 20, 0, 0, "", 0 }, + { "FAYE", 4, 0, 19, 27, -1, 0, "", 35 }, + { "FAYE", 5, 0, 19, 29, -1, 0, "", 35 }, + { "FAYE", 6, 0, 59, 35, 2, 3, "", 35 }, + { "FAYE", 7, 0, 19, 30, -1, 0, "", 35 }, + { "FAYE", 8, 0, 19, 31, -1, 0, "", 35 }, + + { "BOB", 0, 0, 27, 34, 2, 3, "", 34 }, + { "BOB", 1, 0, 27, 28, -1, 0, "", 34 }, + { "BOB", 2, 0, 30, 0, 0, 0, "", 0 }, + { "BOB", 3, 0, 31, 0, 0, 0, "", 0 }, + { "BOB", 4, 0, 27, 61, -1, 0, "", 34 }, + { "BOB", 5, 0, 27, 42, 1, 0, "", 42 }, + + { "PYGMY", 0, 0, 20, 21, 2, 6, "", 0 }, + { "PYGMY", 1, 0, 20, 21, 2, 6, "020,068,068,068,068,068,068,068,068,020,000", 0 }, + { "PYGMY", 2, 0, 20, 21, 2, 6, "T028,029,030,031,031,031,031,030,029,028,035,000", 0 }, + { "PYGMY", 3, 0, 20, 21, 2, 6, "T035,036,037,038,037,038,037,038,036,035,000", 0 }, + { "PYGMY", 4, 0, 20, 21, 2, 6, "T032,033,032,033,032,033,035,000", 0 }, + { "PYGMY", 5, 0, 20, 21, 2, 6, "T023,022,021,022,023,024,025,026,027,026,025,024,023,000", 0 }, + { "PYGMY", 6, 0, 20, 21, 2, 6, "T034,034,034,035,000", 0 }, + + { "WITCH", 0, 0, 39, 40, 2, 6, "", 40 }, + { "WITCH", 1, 0, 39, 40, 2, 6, "073,074,000", 40 }, + { "WITCH", 2, 0, 39, 40, 2, 6, "074,073,000", 40 }, + { "WITCH", 3, 0, 39, 40, 2, 6, "T047,048,049,050,051,000", 40 }, + { "WITCH", 4, 0, 39, 40, 2, 6, "T052,053,054,055,056,057,058,057,056,056,056,055,054,053,052,000", 40 }, + { "WITCH", 5, 0, 39, 40, 2, 6, "069,070,071,072,073,074,000", 40 }, + { "WITCH", 6, 0, 39, 40, 2, 6, "074,073,072,071,070,069,070,000", 40 }, + { "WITCH", 7, 0, 39, 51, -1, 0, "", 40 }, + { "WITCH", 8, 0, 39, 40, 2, 6, "T051,050,049,048,047,000", 40 }, + + { "CHIEF", 0, 0, 1, 7, 1, 7, "", 3 }, + { "CHIEF", 1, 0, 1, 2, 2, 6, "062,063,064,065,066,000", 0 }, + { "CHIEF", 2, 0, 1, 2, 2, 6, "066,065,064,063,062,000", 0 }, + { "CHIEF", 3, 0, 1, 17, -1, 0, "", 3 }, + { "CHIEF", 4, 0, 1, 18, -1, 0, "", 3 }, + { "CHIEF", 5, 0, 1, 19, -1, 0, "", 3 }, + + { "NAOMI", 0, 0, 1, 2, 2, 3, "", 2 }, + { "NAOMI", 1, 0, 1, 2, 2, 6, "048,049,050,051,052,053,054,055,000", 0 }, + { "NAOMI", 2, 0, 1, 2, 2, 6, "055,054,053,052,051,050,049,048,000", 0 }, + { "NAOMI", 3, 0, 1, 13, -1, 0, "", 2 }, + { "NAOMI", 4, 0, 1, 14, -1, 0, "", 2 }, + { "NAOMI", 5, 0, 1, 10, -1, 0, "", 2 }, + { "NAOMI", 6, 0, 1, 12, -1, 0, "", 2 }, + { "NAOMI", 7, 0, 1, 12, -1, 0, "T008,008,008,002,000", 2 }, + + { "WEDGEWOOD", 0, 0, 8, 1, 2, 0, "", 8 }, + { "WEDGEWOOD", 1, 0, 1, 1, 3, 0, "", 1 }, + + { "BUD", 0, 0, 1, 2, 3, 2, "", 2 }, + { "BUD", 1, 0, 1, 2, 4, 2, "T017,018,000", 2 }, + { "BUD", 2, 0, 1, 21, -1, 0, "", 2 }, + { "BUD", 3, 0, 1, 14, -1, 0, "", 2 }, + { "BUD", 4, 0, 1, 15, -1, 0, "", 2 }, + { "BUD", 5, 0, 1, 20, -1, 0, "", 2 }, + { "BUD", 6, 0, 1, 16, -1, 0, "", 2 }, + { "BUD", 7, 0, 1, 19, -1, 0, "", 2 }, + { "BUD", 8, 0, 1, 17, -1, 0, "", 2 }, + { "BUD", 9, 0, 1, 14, -1, 0, "T014,008,008,003,003,008,008,003,003,010,010,012,012,000", 2 }, + + { "LOU", 0, 0, 1, 2, 2, 3, "", 2 }, + { "LOU", 1, 0, 1, 2, 4, 2, "013,014,015,016,017,018,000", 2 }, + { "LOU", 2, 0, 1, 2, 4, 2, "018,017,016,015,014,013,000", 2 }, + + { "JIMMY", 0, 0, 16, 17, 2, 3, "", 17 }, + { "JIMMY", 1, 0, 16, 25, -1, 0, "", 17 }, + { "JIMMY", 2, 0, 16, 26, -1, 0, "", 17 }, + { "JIMMY", 3, 0, 16, 27, -1, 0, "", 17 }, + { "JIMMY", 4, 0, 16, 28, -1, 0, "", 17 }, + { "JIMMY", 5, 0, 16, 29, -1, 0, "", 17 }, + + { "TAMMY", 0, 0, 1, 2, 2, 3, "", 2 }, + { "TAMMY", 1, 0, 1, 2, 2, 3, "T008,008,009,009,008,008,009,009,008,008,009,009,002,000", 2 }, + { "TAMMY", 2, 0, 1, 2, 2, 3, "T002,010,010,010,002,000", 2 }, + { "TAMMY", 3, 0, 1, 2, 2, 3, "T011,011,011,011,011,002,000", 2 }, + { "TAMMY", 4, 0, 1, 2, 2, 3, "T013,014,015,013,014,015,013,009,001,000", 2 }, + + { "SKULL", 0, 0, 9, 9, 4, 0, "", 0 }, + { "SKULL", 1, 0, 1, 9, 4, 0, "001,002,003,004,005,006,007,008,009,000", 0 }, + { "SKULL", 2, 0, 1, 9, 4, 0, "009,008,007,006,005,004,003,002,001,000", 0 }, + + { "APE", 0, 0, 1, 6, 7, 0, "", 6 }, + { "APE", 1, 0, 1, 6, 7, 0, "002,001,000", 6 }, + { "APE", 2, 0, 1, 6, 7, 0, "002,003,001,000", 6 }, + { "APE", 3, 0, 1, 6, 7, 0, "004,005,004,005,004,001,000", 6 }, + { "APE", 4, 0, 1, 6, 7, 0, "001,003,005,004,005,004,001,000", 6 }, + + { "APE1", 0, 0, 15, 16, 7, 0, "", 16 }, + { "APE2", 0, 0, 14, 6, 7, 0, "", 6 }, + + { "SHOWERAM", 0, 0, 1, 2, 3, 0, "", 2 }, + { "SHOWERAM", 1, 0, 1, 2, 3, 0, "026,027,028,029,001,000", 2 }, + { "SHOWERAM", 2, 0, 1, 2, 3, 0, "001,029,028,027,026,000", 2 }, + + { "PRINCESS1", 0, 0, 19, 23, 2, 3, "", 23 }, + { "PRINCESS1", 1, 0, 19, 41, -1, 0, "", 23 }, + { "PRINCESS1", 2, 0, 19, 42, -1, 0, "", 23 }, + { "PRINCESS1", 3, 0, 19, 45, -1, 0, "", 23 }, + { "PRINCESS1", 4, 0, 19, 40, -1, 0, "", 23 }, + { "PRINCESS1", 5, 0, 19, 45, 2, 3, "T40,043,044,045,000", 45 }, + { "PRINCESS1", 6, 0, 19, 45, -1, 0, "T041,038,000", 38 }, + { "PRINCESS1", 7, 0, 22, 0, 0, 0, "", 0 }, + { "PRINCESS1", 8, 0, 19, 45, 2, 3, "T045,044,043,040,039,000", 39 }, + + { "PRINCESS2", 0, 0, 46, 23, 2, 3, "", 23 }, + { "PRINCESS2", 1, 0, 46, 29, 2, 3, "", 29 }, + { "PRINCESS2", 2, 0, 46, 29, 2, 3, "T029,036,035,000", 35 }, + + { "GUARDS", 0, 0, 7, 7, 0, 0, "", 7 }, + + { "AMGUARD", 0, 0, 19, 22, 2, 3, "", 22 }, + + { "MAN1", 0, 0, 2, 3, 2, 3, "", 3 }, + { "MAN2", 0, 0, 9, 10, 1, 2, "", 10 }, + + { "DOG", 0, 0, 6, 6, 1, 0, "", 0 }, + { "DOG", 1, 0, 6, 6, 1, 0, "010,009,008,000", 0 }, + { "DOG", 2, 0, 6, 6, 1, 0, "008,009,010,000", 0 }, + + { "CHEF", 0, 0, 5, 6, 2, 3, "", 6 }, + + { "HENRY", 0, 0, 7, 9, 2, 3, "", 9 }, + { "HENRY", 1, 0, 7, 21, -1, 0, "", 9 }, + { "HENRY", 2, 0, 7, 19, -1, 0, "", 9 }, + { "HENRY", 3, 0, 7, 20, -1, 0, "", 9 }, + { "HENRY", 4, 0, 8, 9, 2, 3, "", 9 }, + { "HENRY", 5, 0, 23, 9, -1, 0, "", 9 }, + { "HENRY", 6, 0, 7, 9, 2, 3, "T019,015,017,017,017,017,017,017,017,015,009,000", 9 }, + { "HENRY", 7, 0, 7, 9, 2, 3, "T018,010,000", 10 }, + { "HENRY", 8, 0, 7, 9, 2, 3, "T018,016,000", 16 }, + { "HENRY", 9, 0, 7, 9, 2, 3, "T018,011,000", 11 }, + { "HENRY", 10, 0, 29, 33, 1, 1, "", 33 }, + { "HENRY", 11, 0, 7, 30, 2, 0, "", 9 }, + { "HENRY", 12, 0, 7, 9, 2, 3, "025,026,000", 26 }, + { "HENRY", 13, 0, 7, 9, 2, 3, "027,028,027,028,000", 28 }, + { "HENRY", 14, 0, 7, 9, 2, 3, "026,025,007,000", 9 }, + + { "JOHAN", 0, 0, 1, 15, 2, 3, "", 15 }, + { "JOHAN", 1, 0, 1, 0, 0, 0, "T006,007,008,000", 15 }, + { "JOHAN", 2, 0, 1, 15, 2, 3, "T002,003,004,005,004,005,004,005,004,005,004,005,004,003,002,000", 15 }, + { "JOHAN", 3, 0, 1, 8, -1, 0, "", 15 }, + { "JOHAN", 4, 0, 1, 0, 0, 0, "T008,007,006,001,000", 15 }, + + { "KLUNK", 0, 0, 1, 2, 2, 3, "", 2 }, + { "KLUNK", 1, 0, 1, 2, 2, 3, "019,020,021,022,001,000", 2 }, + { "KLUNK", 2, 0, 1, 2, 2, 3, "001,022,021,020,019,016,517,000", 2 }, + { "KLUNK", 3, 0, 1, 2, 2, 3, "T010,011,010,011,010,011,009,000", 2 }, + + { "FRANK", 0, 0, 13, 14, 2, 3, "", 14 }, + { "FRANK", 1, 0, 13, 20, 0, 1, "", 14 }, + { "FRANK", 2, 0, 13, 14, 2, 3, "025,026,027,027,027,026,026,026,027,027,026,026,027,025,013,000", 14 }, + { "FRANK", 3, 0, 28, 14, 2, 3, "", 14 }, + + { "DEATH", 0, 0, 1, 2, 2, 3, "", 2 }, + { "DEATH", 1, 0, 1, 2, 2, 3, "013,014,015,016,017,001,000", 0 }, + { "DEATH", 2, 0, 1, 2, 2, 3, "001,017,016,015,014,013,000", 0 }, + { "DEATH", 3, 0, 1, 2, 2, 3, "T018,019,020,021,021,022,022,020,021,022,020,021,022,023,024,524,000", 2 }, + { "DEATH", 4, 0, 1, 2, 2, 3, "T025,026,027,028,028,028,028,028,028,028,028,028,029,035,000", 2 }, + { "DEATH", 5, 0, 1, 2, 2, 3, "T030,031,032,033,033,033,033,033,033,033,033,033,034,035,000", 2 }, + { "DEATH", 6, 0, 1, 2, 2, 3, "T023,022,020,019,018,001,000", 2 }, + + { "JASPAR", 0, 0, 1, 1, 22, 0, "026,027,028,029,028,029,028,029,030,023,000", 0 }, + { "JASPAR", 1, 0, 1, 1, 22, 0, "023,026,000", 0 }, + + { "ORACLE", 0, 0, 1, 5, 3, 0, "", 0 }, + + { "ZOMBIE", 0, 0, 1, 5, 2, 3, "", 5 }, + { "ZOMBIE", 1, 0, 1, 12, -1, 0, "", 5 }, + { "ZOMBIE", 2, 0, 1, 13, -1, 0, "", 5 }, + { "ZOMBIE", 3, 0, 1, 1, 5, 5, "", 5 }, + + { "ZOMBIE2", 0, 0, 14, 14, 0, 0, "", 0 }, + { "ZOMBIE3", 0, 0, 18, 18, 0, 0, "", 0 }, + + { "ANDERSON", 0, 0, 7, 8, 2, 3, "", 8 }, + { "ANDERSON", 1, 0, 7, 8, 1, 0, "", 8 }, + { "ANDERSON", 2, 0, 7, 16, -1, 0, "", 8 }, + { "ANDERSON", 3, 0, 7, 18, -1, 0, "", 8 }, + { "ANDERSON", 4, 0, 7, 19, -1, 0, "", 8 }, + { "ANDERSON", 5, 0, 7, 20, -1, 0, "", 8 }, + { "ANDERSON", 6, 0, 7, 21, 1, 0, "", 8 }, + + { "COMPY", 0, 0, 12, 12, -1, 0, "", 0 }, + { "COMPY", 1, 0, 10, 10, 10, 0, "010,011,012,012,013,014,014,000", 0 }, + { "COMPY", 2, 0, 10, 10, 10, 0, "014,013,012,000", 0 }, + + { "DEINO", 0, 0, 13, 13, -1, 0, "", 0 }, + { "DEINO", 1, 0, 9, 9, 9, 0, "009,010,000", 0 }, + + { "TMPD", 0, 0, 19, 22, 2, 3, "", 22 }, + + { "IAN", 0, 0, 7, 9, 2, 3, "", 9 }, + { "IAN", 1, 0, 8, 25, 3, 0, "", 25 }, + { "IAN", 2, 0, 7, 21, -1, 0, "", 9 }, + { "IAN", 3, 0, 7, 22, 1, 0, "", 9 }, + { "IAN", 4, 0, 7, 22, -1, 0, "", 9 }, + { "IAN", 5, 0, 7, 24, -1, 0, "", 9 }, + { "IAN", 6, 0, 7, 9, 2, 3, "034,034,034,035,035,036,036,035,035,036,035,036,035,000", 9 }, + { "IAN", 7, 0, 7, 31, -1, 0, "", 9 }, + + { "FAYE-H", 0, 0, 1, 1, 4, 1, "", 1 }, + { "FAYE-H", 1, 0, 1, 1, 4, 1, "007,000", 7 }, + { "FAYE-H", 2, 0, 1, 1, 4, 1, "009,010,011,009,001,000", 1 }, + { "FAYE-H", 3, 0, 1, 1, 4, 1, "E012,013,000", 1 }, + { "FAYE-H", 4, 0, 1, 1, 4, 1, "E015,000", 1 }, + { "FAYE-H", 5, 0, 1, 1, 4, 1, "E014,000", 1 }, + + { "AZURA-H", 0, 0, 1, 1, 4, 1, "", 1 }, + { "AZURA-H", 1, 0, 1, 1, 4, 1, "007,000", 7 }, + { "AZURA-H", 2, 0, 1, 1, 4, 1, "009,010,011,009,001,000", 1 }, + { "AZURA-H", 3, 0, 1, 1, 4, 1, "E012,013, 000", 1 }, + { "AZURA-H", 4, 0, 1, 1, 4, 1, "E015,000", 1 }, + { "AZURA-H", 5, 0, 1, 1, 4, 1, "E014,000", 1 }, + + { "FRANK-H", 0, 0, 1, 1, 4, 1, "", 1 }, + { "FRANK-H", 1, 0, 1, 1, 4, 1, "E009,000", 1 }, + { "FRANK-H", 2, 0, 1, 1, 4, 1, "E007,000", 1 }, + { "FRANK-H", 3, 0, 1, 1, 4, 1, "010,011,012,013,014,015,010,000", 1 }, + + { "JOE-E", 0, 0, 1, 2, 4, 1, "", 2 }, + { "JOE-E", 6, 0, 1, 2, 4, 1, "008,009,008,002,000", 2 }, + + { "AZURA-E", 0, 0, 1, 1, 5, 1, "", 1 }, + { "AZURA-E", 1, 0, 1, 1, 5, 1, "009,010,009,008,000", 1 }, + + { "FAYE-E", 0, 0, 1, 4, 4, 1, "", 1 }, + { "FAYE-E", 1, 0, 1, 4, 4, 1, "002,003,002,001,000", 1 }, + + { "ANDSON-E", 0, 0, 1, 3, 4, 1, "", 1 }, + { "ANDSON-E", 1, 0, 1, 3, 4, 1, "002,001,000", 1 }, + + { "JOE-H", 0, 0, 1, 1, 4, 4, "", 1 }, + { "JOE-H", 1, 0, 1, 1, 2, 3, "012,013,014,000", 14 }, + { "JOE-H", 2, 0, 1, 1, 2, 3, "010,011,000", 11 }, + { "JOE-H", 3, 0, 1, 1, 2, 3, "014,013,012,001,000", 1 }, + { "JOE-H", 4, 0, 1, 13, 1, 0, "", 13 }, + + { "RITA-H", 0, 0, 7, 1, 2, 3, "", 1 }, + { "RITA-H", 1, 0, 7, 0, 0, 0, "009,010,011,012,013,000", 13 }, + { "RITA-H", 2, 0, 7, 0, 0, 0, "014,015,016,000", 16 }, + { "RITA-H", 3, 0, 7, 0, 0, 0, "013,012,011,010,000", 10 }, + { "RITA-H", 4, 0, 7, 0, 0, 0, "009,007,008,007,009,000", 9 }, + { "RITA-H", 5, 0, 7, 0, 0, 0, "016,015,014,000", 14 }, + + { "RITA", 0, 0, 1, 4, 2, 3, "", 4 }, + { "RITA", 1, 0, 2, 4, 2, 3, "", 4 }, + + { "SPARKY-H", 0, 0, 1, 1, 2, 3, "", 1 }, + + { "HUGH", 0, 0, 1, 1, 2, 3, "", 1 }, + { "HUGH", 1, 0, 7, 7, 2, 3, "", 7 }, + + { "X2_JOE", 0, 0, 1, 1, 2, 3, "", 1 }, + { "X2_JOE", 1, 0, 1, 1, 2, 3, "001,007,008,008,007,001,000", 1 }, + + { "X2_RITA", 0, 0, 1, 1, 2, 3, "", 1 }, + { "X2_RITA", 1, 0, 1, 1, 2, 3, "001,007,008,008,007,001,000", 1 }, + + { "X3_RITA", 0, 0, 1, 1, 4, 1, "", 1 }, + { "X3_RITA", 1, 0, 1, 1, 4, 1, "007,000", 7 }, + { "X3_RITA", 2, 0, 1, 1, 4, 1, "009,010,011,009,001,000", 1 }, + { "X3_RITA", 3, 0, 1, 1, 4, 1, "E012,013,000", 1 }, + { "X3_RITA", 4, 0, 1, 1, 4, 1, "E015,000", 1 }, + { "X3_RITA", 5, 0, 1, 1, 4, 1, "E014,000", 1 }, + + { "X4_JOE", 0, 0, 1, 1, 3, 4, "", 1 }, + { "X4_JOE", 1, 0, 1, 13, 2, 3, "", 13 }, + { "X4_JOE", 2, 0, 1, 1, 3, 4, "009, 010, 011, 012, 013, 000", 13 }, + { "X4_JOE", 3, 0, 1, 1, 3, 4, "012, 011, 010, 009, 000", 9 }, + { "X4_JOE", 4, 0, 1, 1, 3, 4, "001, 019, 000", 19 }, + + { "X4_RITA", 0, 0, 1, 1, 0, 1, "", 1 }, + { "X4_RITA", 1, 0, 1, 7, 0, 1, "", 7 }, + { "X4_RITA", 2, 0, 1, 1, 3, 4, "004,005,006,006,006,006,007,000", 7 }, + { "X4_RITA", 3, 0, 1, 1, 3, 4, "005,004,001,000", 1 }, + { "X4_RITA", 4, 0, 1, 1, 3, 4, "001,003,000", 3 }, + + { "X5_SPARKY", 0, 0, 1, 1, 2, 3, "", 1 }, + { "X5_SPARKY", 1, 0, 1, 1, 2, 3, "001,010,011,011,001,000", 1 }, + { "X5_SPARKY", 2, 0, 1, 1, 2, 3, "001,007,008,009,000", 9 }, + + { "X6_HUGH", 0, 0, 1, 1, 2, 3, "", 1 }, + { "X6_HUGH", 1, 0, 1, 1, 2, 3, "007,007,007,007,,001,000", 1 }, + { "X6_HUGH", 2, 0, 1, 1, 2, 3, "008,008,008,008,008,009,009,008,008,008,009,008,000", 8 }, + + { "X10_JOE", 0, 0, 1, 2, 2, 3, "", 2 }, + { "X10_JOE", 1, 0, 1, 8, 2, 3, "", 8 }, + { "X10_JOE", 2, 0, 1, 2, 2, 3, "014,014,014,015,015,014,014,015,015,000", 2 }, + + { "X10_RITA", 0, 0, 1, 2, 2, 3, "", 2 }, + + { "X11_JOE", 0, 0, 1, 2, 0, 1, "", 2 }, + + { "X11_RITA", 0, 0, 1, 2, 0, 1, "", 2 }, + { "X11_RITA", 1, 0, 1, 2, 1, 0, "003,004,000", 4 }, + + { "JOHN", 0, 0, 1, 2, 2, 3, "", 1 }, + { "JOHN", 1, 0, 1, 15, -1, 0, "", 1 }, + { "JOHN", 2, 0, 1, 16, -1, 0, "", 1 }, + { "JOHN", 3, 0, 1, 17, -1, 0, "", 1 }, + + { "STEVE", 0, 0, 8, 2, 2, 3, "", 2 }, + { "STEVE", 1, 0, 8, 16, -1, 0, "", 2 }, + { "STEVE", 2, 0, 9, 18, -1, 0, "T016,017,017,016,008,000", 2 }, + { "STEVE", 3, 0, 8, 18, -1, 0, "", 2 }, + + { "TONY", 0, 0, 1, 2, 2, 3, "", 1 }, + { "TONY", 1, 0, 1, 12, -1, 0, "", 1 }, + + { "*", 0, 0, 0, 0, 0, 0, "", 0 } +}; +#endif + +} // End of namespace Queen + +#ifdef PALMOS_68K +#include "scumm_globals.h" + +_GINIT(Queen_Talk) +_GSETPTR(Queen::_speechParameters, GBVARS_SPEECHPARAMETERS_INDEX, Queen::Talk::SpeechParameters, GBVARS_QUEEN) +_GEND + +_GRELEASE(Queen_Talk) +_GRELEASEPTR(GBVARS_SPEECHPARAMETERS_INDEX, GBVARS_QUEEN) +_GEND + +#endif diff --git a/engines/queen/talk.h b/engines/queen/talk.h new file mode 100644 index 0000000000..d1469e2fe8 --- /dev/null +++ b/engines/queen/talk.h @@ -0,0 +1,244 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEEN_TALK_H +#define QUEEN_TALK_H + +#include "common/util.h" +#include "queen/structs.h" + +namespace Queen { + +class QueenEngine; + +class Talk { +public: + + //! Public interface to run a talk from a file + static void talk(const char *filename, int personInRoom, char *cutawayFilename, QueenEngine *vm); + + //! Public interface to speak a sentence + static bool speak(const char *sentence, Person *person, const char *voiceFilePrefix, QueenEngine *vm); + + //! Read a string from ptr and update offset + static void getString(const byte *ptr, uint16 &offset, char *str, int maxLength, int align = 2); + +private: + + //! Collection of constants used by Talk + enum { + LINE_HEIGHT = 10, + MAX_STRING_LENGTH = 255, + MAX_STRING_SIZE = (MAX_STRING_LENGTH + 1), + MAX_TEXT_WIDTH = (320-18), + PUSHUP = 4, + ARROW_ZONE_UP = 5, + ARROW_ZONE_DOWN = 6, + DOG_HEADER_SIZE = 20, + OPTION_TEXT_MARGIN = 24 + }; + + //! Special commands for speech + enum { + SPEAK_DEFAULT = 0, + SPEAK_FACE_LEFT = -1, + SPEAK_FACE_RIGHT = -2, + SPEAK_FACE_FRONT = -3, + SPEAK_FACE_BACK = -4, + SPEAK_ORACLE = -5, + SPEAK_UNKNOWN_6 = -6, + SPEAK_AMAL_ON = -7, + SPEAK_PAUSE = -8, + SPEAK_NONE = -9 + }; + + struct DialogueNode { + int16 head; + int16 dialogueNodeValue1; + int16 gameStateIndex; + int16 gameStateValue; + }; + +#ifndef PALMOS_68K + struct SpeechParameters { + const char *name; + signed char state,faceDirection; + signed char body,bf,rf,af; + const char *animation; + signed char ff; + }; +#else +public: + struct SpeechParameters { + const char name[11]; + signed char state,faceDirection; + signed char body,bf,rf,af; + const char animation[80]; + signed char ff; + }; +private: +#endif + + QueenEngine *_vm; + + bool _wasFullscren; + + //! Raw .dog file data (without 20 byte header) + byte *_fileData; + + //! Number of dialogue levels + int16 _levelMax; + + //! Unique key for this dialogue + int16 _uniqueKey; + + //! Used to select voice files + int16 _talkKey; + + int16 _jMax; + + //! Used by findDialogueString + int16 _pMax; + + // Update game state efter dialogue + int16 _gameState[2]; + int16 _testValue[2]; + int16 _itemNumber[2]; + + //! String data + uint16 _person1PtrOff; + + //! Cutaway data + uint16 _cutawayPtrOff; + + //! Data used if we have talked to the person before + uint16 _person2PtrOff; + + //! Data used if we haven't talked to the person before + uint16 _joePtrOff; + + //! Is a talking head + bool _talkHead; + + //! IDs for sentences + DialogueNode _dialogueTree[18][6]; + + //! Greeting from person Joe has talked to before + char _person2String[MAX_STRING_SIZE]; + + int _oldSelectedSentenceIndex; + int _oldSelectedSentenceValue; + + char _talkString[5][MAX_STRING_SIZE]; + char _joeVoiceFilePrefix[5][MAX_STRING_SIZE]; + +#ifndef PALMOS_68K + static const SpeechParameters _speechParameters[]; +#endif + + Talk(QueenEngine *vm); + ~Talk(); + + //! Perform talk in file and return a cutaway filename + void talk(const char *filename, int personInRoom, char *cutawayFilename); + + byte *loadDialogFile(const char *filename); + + //! Load talk data from .dog file + void load(const char *filename); + + //! First things spoken + void initialTalk(); + + //! Find a string in the dialogue tree + void findDialogueString(uint16 offset, int16 id, int16 max, char *str); + + //! Get TalkSelected struct for this talk + TalkSelected *talkSelected(); + + //! Interface to the TalkSelected struct + bool hasTalkedTo() { return talkSelected()->hasTalkedTo; } + + //! Interface to the TalkSelected struct + void setHasTalkedTo() { talkSelected()->hasTalkedTo = true; } + + //! Get a selected value + int16 selectedValue(int index) { + return talkSelected()->values[index-1]; + } + + //! Set a selected value + void selectedValue(int index, int16 value) { + talkSelected()->values[index-1] = value; + } + + //! The sentence will not be displayed again + void disableSentence(int oldLevel, int selectedSentence); + + //! Select what to say + int16 selectSentence(); + + //! Speak sentence + bool speak(const char *sentence, Person *person, const char *voiceFilePrefix); + + //! Convert command in sentence to command code + int getSpeakCommand(const Person *person, const char *sentence, unsigned &index); + + //! Speak a part of a sentence + void speakSegment( + const char *segmentStart, + int length, + Person *person, + int command, + const char *voiceFilePrefix, + int index); + + void headStringAnimation(const SpeechParameters *parameters, int bobNum, int bankNum); + + void stringAnimation(const SpeechParameters *parameters, int startFrame, int bankNum); + + void defaultAnimation( + const char *segment, + bool isJoe, + const SpeechParameters *parameters, + int startFrame, + int bankNum); + + int countSpaces(const char *segment); + + //! Get special parameters for speech + const SpeechParameters *findSpeechParameters( + const char *name, + int state, + int faceDirection); + + int splitOption(const char *str, char optionText[5][MAX_STRING_SIZE]); + + int splitOptionHebrew(const char *str, char optionText[5][MAX_STRING_SIZE]); + + int splitOptionDefault(const char *str, char optionText[5][MAX_STRING_SIZE]); + +}; + +} // End of namespace Queen + +#endif diff --git a/engines/queen/walk.cpp b/engines/queen/walk.cpp new file mode 100644 index 0000000000..69854b373a --- /dev/null +++ b/engines/queen/walk.cpp @@ -0,0 +1,564 @@ +/* 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$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "queen/walk.h" + +#include "queen/bankman.h" +#include "queen/input.h" +#include "queen/logic.h" +#include "queen/graphics.h" +#include "queen/grid.h" +#include "queen/queen.h" + +namespace Queen { + +const MovePersonData Walk::_moveData[] = { + { "COMPY", -1, -6, 1, 6, 0, 0, 0, 0, 12, 12, 1, 14 }, + { "DEINO", -1, -8, 1, 8, 0, 0, 0, 0, 11, 11, 1, 10 }, + { "FAYE", -1, -6, 1, 6, 13, 18, 7, 12, 19, 22, 2, 5 }, + { "GUARDS", -1, -6, 1, 6, 0, 0, 0, 0, 7, 7, 2, 5 }, + { "PRINCESS1", -1, -6, 1, 6, 13, 18, 7, 12, 19, 21, 2, 5 }, + { "PRINCESS2", -1, -6, 1, 6, 13, 18, 7, 12, 19, 21, 2, 5 }, + { "AMGUARD", -1, -6, 1, 6, 13, 18, 7, 12, 19, 21, 2, 5 }, + { "SPARKY", -1, -6, 1, 6, 13, 18, 7, 12, 21, 20, 2, 5 }, + { "LOLA_SHOWER", -1, -6, 55, 60, 0, 0, 0, 0, 7, 7, 2, 5 }, + { "LOLA", -24, -29, 24, 29, 0, 0, 0, 0, 30, 30, 2, 5 }, + { "BOB", -15, -20, 15, 20, 21, 26, 0, 0, 27, 29, 2, 5 }, + { "CHEF", -1, -4, 1, 4, 0, 0, 0, 0, 1, 5, 2, 4 }, + { "HENRY", -1, -6, 1, 6, 0, 0, 0, 0, 7, 7, 2, 6 }, + { "ANDERSON", -1, -6, 1, 6, 0, 0, 0, 0, 7, 7, 2, 5 }, + { "JASPAR", -4, -9, 4, 9, 16, 21, 10, 15, 1, 3, 1, 10 }, + { "PYGMY", -7, -12, 7, 12, 0, 0, 0, 0, 27, 27, 2, 5 }, + { "FRANK", 7, 12, 1, 6, 0, 0, 0, 0, 13, 13, 2, 4 }, + { "WEDGEWOOD", -20, -25, 20, 25, 0, 0, 0, 0, 1, 1, 1, 5 }, + { "TMPD", -1, -6, 1, 6, 13, 18, 7, 12, 19, 21, 2, 5 }, + { "IAN", -1, -6, 1, 6, 0, 0, 0, 0, 7, 7, 2, 6 }, + { "*", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +Walk::Walk(QueenEngine *vm) + : _vm(vm) { +} + +void Walk::animateJoePrepare() { + // queen.c l.2748-2788 + uint16 i; + for (i = 1; i <= _walkDataCount; ++i) { + + WalkData *pwd = &_walkData[i]; + + if (pwd->dx < 0) { + pwd->anim.set(11, 18, DIR_LEFT); + } else { + pwd->anim.set(11, 18, DIR_RIGHT); + } + + int16 k = ABS(pwd->dy); + int16 ds = pwd->area->scaleDiff(); + if (ds > 0) { + k *= ((k * ds) / pwd->area->box.yDiff()) / 2; + } + + if (ABS(pwd->dx) < k) { + if (pwd->dy < 0) { + if (ds < 0) { + pwd->anim.set(19, 24, DIR_FRONT); + } else { + pwd->anim.set(25, 30, DIR_BACK); + } + } else if (pwd->dy > 0) { + if (ds < 0) { + pwd->anim.set(25, 30, DIR_BACK); + } else { + pwd->anim.set(19, 24, DIR_FRONT); + } + } + } + } +} + +void Walk::animateJoe() { + // queen.c l.2789-2835 + uint16 lastDirection = 0; + uint16 i; + BobSlot *pbs = _vm->graphics()->bob(0); + _vm->logic()->joeFacing(_walkData[1].anim.facing); + _vm->logic()->joeScale(_walkData[1].area->calcScale(pbs->y)); + _vm->logic()->joeFace(); + for (i = 1; i <= _walkDataCount && !_joeInterrupted; ++i) { + + WalkData *pwd = &_walkData[i]; + + // area has been turned off, see if we should execute a cutaway + if (pwd->area->mapNeighbours < 0) { + // queen.c l.2838-2911 + _vm->logic()->handleSpecialArea(pwd->anim.facing, pwd->areaNum, i); + _joeMoveBlock = true; + return; + } + if (lastDirection != pwd->anim.facing) { + pbs->animNormal(pwd->anim.firstFrame, pwd->anim.lastFrame, 1, false, false); + } + + uint16 moveSpeed = _vm->grid()->findScale(pbs->x, pbs->y) * 6 / 100; + pbs->move(pbs->x + pwd->dx, pbs->y + pwd->dy, moveSpeed); + pbs->xflip = (pbs->xdir < 0); + while (pbs->moving) { + // adjust Joe's movespeed according to scale + pbs->scale = pwd->area->calcScale(pbs->y); + _vm->logic()->joeScale(pbs->scale); + pbs->scaleWalkSpeed(6); + _vm->update(true); + if (_vm->input()->cutawayQuit() || _vm->logic()->joeWalk() == JWM_EXECUTE) { + stopJoe(); + break; + } + } + lastDirection = pwd->anim.facing; + } + _vm->logic()->joeFacing(lastDirection); +} + +void Walk::animatePersonPrepare(const MovePersonData *mpd, int direction) { + // queen.c l.2469-2572 + int i; + for (i = 1; i <= _walkDataCount; ++i) { + + WalkData *pwd = &_walkData[i]; + + if (pwd->dx < 0) { + pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_LEFT); + } else if (pwd->dx > 0) { + pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_RIGHT); + } else { + if (ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) { + pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_RIGHT); + } else { + // we have specific moves for this actor, see what direction they were last facing + if (direction == -3) { + // previously facing right + pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_LEFT); + } else { + // previously facing left + pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_RIGHT); + } + } + } + + int16 k = ABS(pwd->dy); + int16 ds = pwd->area->scaleDiff(); + if (ds > 0) { + k *= ((k * ds) / pwd->area->box.yDiff()) / 2; + } + + if (ABS(pwd->dx) < k) { + if (pwd->dy < 0) { + if (mpd->walkBack1 > 0) { + pwd->anim.set(mpd->walkBack1, mpd->walkBack2, DIR_BACK); + } else if (pwd->dx < 0) { + pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_BACK); + } else { + pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_BACK); + } + } else if (pwd->dy > 0) { + if (mpd->walkFront1 > 0) { + pwd->anim.set(mpd->walkFront1, mpd->walkFront2, DIR_FRONT); + } else if (ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) { + if (pwd->dx < 0) { + pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_FRONT); + } else { + pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_FRONT); + } + } else { + // we have a special move for left/right, so select that instead! + if (direction == -3) { + // previously facing right + pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_FRONT); + } else { + // previously facing left + pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_FRONT); + } + } + } + } + } +} + +void Walk::animatePerson(const MovePersonData *mpd, uint16 image, uint16 bobNum, uint16 bankNum, int direction) { + // queen.c l.2572-2651 + BobSlot *pbs = _vm->graphics()->bob(bobNum); + + // check to see which way person should be facing + if (mpd->walkLeft1 == mpd->walkRight1) { + pbs->xflip = (direction == -3); + } else { + // they have special walk for left and right, so don't flip + pbs->xflip = false; + } + + uint16 i; + for (i = 1; i <= _walkDataCount; ++i) { + WalkData *pwd = &_walkData[i]; + + // unpack necessary frames for bob animation + uint16 dstFrame = image; + uint16 srcFrame = ABS(pwd->anim.firstFrame); + while (srcFrame <= ABS(pwd->anim.lastFrame)) { + _vm->bankMan()->unpack(srcFrame, dstFrame, bankNum); + ++dstFrame; + ++srcFrame; + } + // pass across bobs direction ONLY if walk is a mirror flip! + if (ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) { + pbs->animNormal(image, dstFrame - 1, mpd->animSpeed, false, pbs->xflip); + } else { + pbs->animNormal(image, dstFrame - 1, mpd->animSpeed, false, false); + } + + // move other actors at correct speed relative to scale + uint16 moveSpeed = _vm->grid()->findScale(pbs->x, pbs->y) * mpd->moveSpeed / 100; + pbs->move(pbs->x + pwd->dx, pbs->y + pwd->dy, moveSpeed); + + // flip if one set of frames for actor + if (mpd->walkLeft1 < 0 || ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) { + pbs->xflip = pwd->dx < 0; + } + + while (pbs->moving) { + _vm->update(); + pbs->scale = pwd->area->calcScale(pbs->y); + pbs->scaleWalkSpeed(mpd->moveSpeed); + if (_vm->input()->cutawayQuit()) { + stopPerson(bobNum); + break; + } + } + } +} + +int16 Walk::moveJoe(int direction, int16 endx, int16 endy, bool inCutaway) { + _joeInterrupted = false; + _joeMoveBlock = false; + int16 can = 0; + initWalkData(); + + uint16 oldx = _vm->graphics()->bob(0)->x; + uint16 oldy = _vm->graphics()->bob(0)->y; + + _vm->logic()->joeWalk(JWM_MOVE); + + uint16 oldPos = _vm->grid()->findAreaForPos(GS_ROOM, oldx, oldy); + uint16 newPos = _vm->grid()->findAreaForPos(GS_ROOM, endx, endy); + + debug(9, "Walk::moveJoe(%d, %d, %d, %d, %d) - old = %d, new = %d", direction, oldx, oldy, endx, endy, oldPos, newPos); + + // if in cutaway, allow Joe to walk anywhere + if (newPos == 0 && inCutaway) { + incWalkData(oldx, oldy, endx, endy, oldPos); + } else { + if (calc(oldPos, newPos, oldx, oldy, endx, endy)) { + if (_walkDataCount > 0) { + animateJoePrepare(); + animateJoe(); + if (_joeInterrupted) { + can = -1; + } + } + } else { + // path has been blocked, make Joe say so + _vm->logic()->makeJoeSpeak(4); + can = -1; + } + } + + _vm->graphics()->bob(0)->animating = false; + if (_joeMoveBlock) { + can = -2; + _joeMoveBlock = false; + } else if (direction > 0) { + _vm->logic()->joeFacing(direction); + } + _vm->logic()->joePrevFacing(_vm->logic()->joeFacing()); + _vm->logic()->joeFace(); + return can; +} + +int16 Walk::movePerson(const Person *pp, int16 endx, int16 endy, uint16 curImage, int direction) { + if (endx == 0 && endy == 0) { + warning("Walk::movePerson() - endx == 0 && endy == 0"); + return 0; + } + + int16 can = 0; + initWalkData(); + + uint16 bobNum = pp->actor->bobNum; + uint16 bankNum = pp->actor->bankNum; + + uint16 oldx = _vm->graphics()->bob(bobNum)->x; + uint16 oldy = _vm->graphics()->bob(bobNum)->y; + + uint16 oldPos = _vm->grid()->findAreaForPos(GS_ROOM, oldx, oldy); + uint16 newPos = _vm->grid()->findAreaForPos(GS_ROOM, endx, endy); + + debug(9, "Walk::movePerson(%d, %d, %d, %d, %d) - old = %d, new = %d", direction, oldx, oldy, endx, endy, oldPos, newPos); + + // find MovePersonData associated to Person + const MovePersonData *mpd = _moveData; + while (mpd->name[0] != '*') { + if (scumm_stricmp(mpd->name, pp->name) == 0) { + break; + } + ++mpd; + } + + if (calc(oldPos, newPos, oldx, oldy, endx, endy)) { + if (_walkDataCount > 0) { + animatePersonPrepare(mpd, direction); + animatePerson(mpd, curImage, bobNum, bankNum, direction); + } + } else { + can = -1; + } + + uint16 standingFrame = 31 + bobNum; + + // make other person face the right direction + BobSlot *pbs = _vm->graphics()->bob(bobNum); + pbs->endx = endx; + pbs->endy = endy; + pbs->animating = false; + pbs->scale = _walkData[_walkDataCount].area->calcScale(endy); + if (_walkData[_walkDataCount].anim.facing == DIR_BACK) { + _vm->bankMan()->unpack(mpd->backStandingFrame, standingFrame, bankNum); + } else { + _vm->bankMan()->unpack(mpd->frontStandingFrame, standingFrame, bankNum); + } + uint16 obj = _vm->logic()->objectForPerson(bobNum); + if (_walkData[_walkDataCount].dx < 0) { + _vm->logic()->objectData(obj)->image = -3; + pbs->xflip = true; + } else { + _vm->logic()->objectData(obj)->image = -4; + pbs->xflip = false; + } + pbs->frameNum = standingFrame; + return can; +} + +void Walk::stopJoe() { + BobSlot *pbs = _vm->graphics()->bob(0); + pbs->moving = false; + _joeInterrupted = true; +} + +void Walk::stopPerson(uint16 bobNum) { + BobSlot *pbs = _vm->graphics()->bob(bobNum); + pbs->x = pbs->endx; + pbs->y = pbs->endy; + pbs->moving = false; +} + +bool Walk::calc(uint16 oldPos, uint16 newPos, int16 oldx, int16 oldy, int16 x, int16 y) { + // if newPos is outside of an AREA then traverse Y axis until an AREA is found + if (newPos == 0) { + newPos = findAreaPosition(&x, &y, true); + } + + // do the same for oldPos in case Joe somehow sits on the border of an AREA + // and does not register + if (oldPos == 0) { + oldPos = findAreaPosition(&oldx, &oldy, false); + } + + if (oldPos == newPos) { + incWalkData(oldx, oldy, x, y, newPos); + return true; + } else if (calcPath(oldPos, newPos)) { + uint16 i; + int16 px = oldx; + int16 py = oldy; + for (i = 2; i <= _areaListCount; ++i) { + uint16 a1 = _areaList[i - 1]; + uint16 a2 = _areaList[i]; + const Area *pa1 = &_roomArea[a1]; + const Area *pa2 = &_roomArea[a2]; + uint16 x1 = calcC(pa1->box.x1, pa1->box.x2, pa2->box.x1, pa2->box.x2, px); + uint16 y1 = calcC(pa1->box.y1, pa1->box.y2, pa2->box.y1, pa2->box.y2, py); + incWalkData(px, py, x1, y1, a1); + px = x1; + py = y1; + } + incWalkData(px, py, x, y, newPos); + return true; + } + return false; +} + +int16 Walk::calcC(int16 c1, int16 c2, int16 c3, int16 c4, int16 lastc) { + int16 s1 = MAX(c1, c3); + int16 s2 = MIN(c2, c4); + int16 c; + if ((lastc >= s1 && lastc <= s2) || (lastc >= s2 && lastc <= s1)) { + c = lastc; + } else { + c = (s1 + s2) / 2; + } + return c; +} + +int16 Walk::findAreaPosition(int16 *x, int16 *y, bool recalibrate) { + // FIXME - in order to locate the nearest available area, the original + // algorithm computes the X (or Y) closest face distance for each available + // area. We simply added the case where the pointer is neither lying in the + // X range nor in the Y one. + // To get an example of this in action, in the room D1, make Joe walking + // to the wall at the right of the window (just above the radiator). On the + // original game, Joe will go to the left door... + uint16 i; + uint16 pos = 1; + uint32 minDist = (uint32)~0; + const Box *b = &_roomArea[1].box; + for (i = 1; i <= _roomAreaCount; ++i) { + + b = &_roomArea[i].box; + + uint16 dx1 = ABS(b->x1 - *x); + uint16 dx2 = ABS(b->x2 - *x); + uint16 dy1 = ABS(b->y1 - *y); + uint16 dy2 = ABS(b->y2 - *y); + uint16 csx = MIN(dx1, dx2); + uint16 csy = MIN(dy1, dy2); + + bool inX = (*x >= b->x1) && (*x <= b->x2); + bool inY = (*y >= b->y1) && (*y <= b->y2); + + uint32 dist = minDist; + if (!inX && !inY) { + dist = csx * csx + csy * csy; + } else if (inX) { + dist = csy * csy; + } else if (inY) { + dist = csx * csx; + } + + if (dist < minDist) { + minDist = dist; + pos = i; + } + } + // we now have the closest area near X,Y, so we can recalibrate + // the X,Y coord to be in this area + if (recalibrate) { + b = &_roomArea[pos].box; + if (*x < b->x1) *x = b->x1; + if (*x > b->x2) *x = b->x2; + if (*y < b->y1) *y = b->y1; + if (*y > b->y2) *y = b->y2; + } + return pos; +} + +uint16 Walk::findFreeArea(uint16 area) const { + uint16 testArea; + uint16 freeArea = 0; + uint16 map = ABS(_roomArea[area].mapNeighbours); + for (testArea = 1; testArea <= _roomAreaCount; ++testArea) { + int b = _roomAreaCount - testArea; + if (map & (1 << b)) { + // connecting area, check if it's been struck off + if (!isAreaStruck(testArea)) { + // legitimate connecting area, keep it + freeArea = testArea; + break; + } + } + } + return freeArea; +} + +bool Walk::isAreaStruck(uint16 area) const { + uint16 i; + bool found = false; + for (i = 1; i <= _areaStrikeCount; ++i) { + if (_areaStrike[i] == area) { + found = true; + break; + } + } + return found; +} + +bool Walk::calcPath(uint16 oldArea, uint16 newArea) { + debug(9, "Walk::calcPath(%d, %d)", oldArea, newArea); + _areaList[1] = _areaStrike[1] = oldArea; + _areaListCount = _areaStrikeCount = 1; + uint16 area = oldArea; + while (_areaListCount > 0 && area != newArea) { + area = findFreeArea(area); + if (!area) { + // wrong path, rolling back + _areaList[_areaListCount] = 0; + --_areaListCount; + area = _areaList[_areaListCount]; + } else { + ++_areaListCount; + assert(_areaListCount < MAX_WALK_DATA); + _areaList[_areaListCount] = area; + if (!isAreaStruck(area)) { + ++_areaStrikeCount; + assert(_areaStrikeCount < MAX_WALK_DATA); + _areaStrike[_areaStrikeCount] = area; + } + } + } + return _areaList[1] != 0; +} + +void Walk::initWalkData() { + uint16 curRoom = _vm->logic()->currentRoom(); + _roomArea = _vm->grid()->area(curRoom, 0); + _roomAreaCount = _vm->grid()->areaMax(curRoom); + + _walkDataCount = 0; + memset(_walkData, 0, sizeof(_walkData)); + _areaStrikeCount = 0; + memset(_areaStrike, 0, sizeof(_areaStrike)); + _areaListCount = 0; + memset(_areaList, 0, sizeof(_areaList)); +} + +void Walk::incWalkData(int16 px, int16 py, int16 x, int16 y, uint16 areaNum) { + debug(9, "Walk::incWalkData(%d, %d, %d)", (x - px), (y - py), areaNum); + if (px != x || py != y) { + ++_walkDataCount; + assert(_walkDataCount < MAX_WALK_DATA); + WalkData *pwd = &_walkData[_walkDataCount]; + pwd->dx = x - px; + pwd->dy = y - py; + pwd->area = &_roomArea[areaNum]; + pwd->areaNum = areaNum; + } +} + +} // End of namespace Queen diff --git a/engines/queen/walk.h b/engines/queen/walk.h new file mode 100644 index 0000000000..02c8264db9 --- /dev/null +++ b/engines/queen/walk.h @@ -0,0 +1,144 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef QUEENWALK_H +#define QUEENWALK_H + +#include "common/util.h" +#include "queen/structs.h" + +namespace Queen { + +struct MovePersonAnim { + int16 firstFrame; + int16 lastFrame; + Direction facing; + + void set(int16 ff, int16 lf, Direction dir) { + firstFrame = ff; + lastFrame = lf; + facing = dir; + } +}; + +struct WalkData { + int16 dx, dy; + const Area *area; + uint16 areaNum; + MovePersonAnim anim; +}; + +struct MovePersonData { + const char *name; + int16 walkLeft1, walkLeft2; + int16 walkRight1, walkRight2; + int16 walkBack1, walkBack2; + int16 walkFront1, walkFront2; + uint16 frontStandingFrame; + uint16 backStandingFrame; + uint16 animSpeed; + uint16 moveSpeed; +}; + +class QueenEngine; + +class Walk { +public: + + Walk(QueenEngine *vm); + + int16 moveJoe(int direction, int16 endx, int16 endy, bool inCutaway); + int16 movePerson(const Person *pp, int16 endx, int16 endy, uint16 curImage, int direction); + + void stopJoe(); + void stopPerson(uint16 bobNum); + + enum { + MAX_WALK_DATA = 16 + }; + +private: + + void animateJoePrepare(); + void animateJoe(); + + void animatePersonPrepare(const MovePersonData *mpd, int direction); + void animatePerson(const MovePersonData *mpd, uint16 image, uint16 bobNum, uint16 bankNum, int direction); + + //! compute transition coordinate + static int16 calcC(int16 c1, int16 c2, int16 c3, int16 c4, int16 lastc); + + //! find area for position + int16 findAreaPosition(int16 *x, int16 *y, bool recalibrate); + + //! find an area not already struck + uint16 findFreeArea(uint16 area) const; + + //! return true if the area is already on the walking path + bool isAreaStruck(uint16 area) const; + + //! calculates the path list from oldArea to newArea + bool calcPath(uint16 oldArea, uint16 newArea); + + //! resets path computed in calcPath() + void initWalkData(); + + //! add an area to the path + void incWalkData(int16 px, int16 py, int16 x, int16 y, uint16 area); + + //! compute path (and populates _walkData) from current position to the new one + bool calc(uint16 oldPos, uint16 newPos, int16 oldx, int16 oldy, int16 x, int16 y); + + + //! areas for current room + const Area *_roomArea; + + //! number of areas for current room + uint16 _roomAreaCount; + + //! walking steps + WalkData _walkData[MAX_WALK_DATA]; + + //! number of walking steps + uint16 _walkDataCount; + + uint16 _areaStrike[MAX_WALK_DATA]; + uint16 _areaStrikeCount; + + uint16 _areaList[MAX_WALK_DATA]; + uint16 _areaListCount; + + //! set if stopJoe() is called + bool _joeInterrupted; + + //! set if handleSpecialArea() is called + bool _joeMoveBlock; + + QueenEngine *_vm; + + //! persons walking animation data + static const MovePersonData _moveData[]; +}; + +} // End of namespace Queen + +#endif diff --git a/engines/queen/xref.txt b/engines/queen/xref.txt new file mode 100644 index 0000000000..55067aa922 --- /dev/null +++ b/engines/queen/xref.txt @@ -0,0 +1,496 @@ +$Id$ + +Cross-reference for functions and variables for the original source code and +the ScummVM implementation. + + +BANKS +===== +erase() BankManager::close +freeallframes() BankManager::eraseFrames(true) +freeframes() BankManager::eraseFrames(false) +loadbank() BankManager::load +overpack() BankManager::overpack +unpack() BankManager::unpack + + +COMMAND +======= +ALTER_DEFAULT() *not needed* (use State::alterDefaultVerb) +CLEAR_COMMAND() Command::clear +EXECUTE_ACTION() Command::executeCurrentAction +FIND_DEFAULT() *not needed* (use State::findDefaultVerb) +LOOK() Command::lookAtSelectedObject +LOOK_ICON(),LOOK_ITEM() Command::lookForCurrentIcon +LOOK_ROOM() Command::lookForCurrentObject +OPEN_CLOSE_OTHER() Command::openOrCloseAssociatedObject +P1_SET_CONDITIONS() Command::setConditions +P2_SET_AREAS() Command::setAreas +P3_SET_OBJECTS() Command::setObjects +P4_SET_ITEMS() Command::setItems +SELECT() Command::grabCurrentSelection +SELECT_ITEM() Command::grabSelectedItem +SELECT_NOUN() Command::grabSelectedNoun +SELECT_VERB() Command::grabSelectedVerb +WALK() Command::makeJoeWalkTo +- +ACTION Command::_state.action +ACTION2 Command::_state.selAction +CLEVEL Command::_state.commandLevel +COM_A Command::_cmdArea +COM_A_MAX Command::_numCmdArea +COM_O Command::_cmdObject +COM_O_MAX Command::_numCmdObject +COM_G Command::_cmdGameState +COM_G_MAX Command::_numCmdGameState +COM_I Command::_cmdInventory +COM_I_MAX Command::_numCmdInventory +COM_LIST Command::_cmdList +COM_LIST_MAX Command::_numCmdList +COMMANDstr Command::_cmdText +DEFCOMM Command::_state.defaultVerb +MKEY Command::_mouseKey +OLDVERB,VERB Command::_state.*verb +OLDNOUN,NOUN Command::_state.*noun +NOUN2 Command::_state.selNoun +PARSE Command::_parse +SUBJ1,SUBJ2 Command::_state.subject* + + +CREDIT SCRIPTING SYSTEM +======================= +Cinit() Credits::Credits() +Ctext() *not needed* (included in Credits::update) +Cupdate() Credits::update +- +Ccol Credits::_color +Ccount Credits::_count +Cfp +Cflag Credits::_running +Cfontsize Credits::_fontSize +Cjustify Credits::_justify +Cpausecount Credits::_pause +Czone Credits::_zone + + +CUTAWAY +======= +action_special_move() Cutaway::actionSpecialMove +CUTAWAY() Cutaway::run +MAKE_COMPLEX_ANIM() Cutaway::makeComplexAnimation +SCENE_START() Logic::sceneStart +SCENE_END() Logic::sceneStop +- +CUTON Input::_cutawayRunning +CUTQUIT Input::_cutawayQuit +FINAL_ROOM Cutaway::_finalRoom +IROOM Cutaway::_initialRoom +OBJ_CUT +OBJ_ANIM +OLDBANK +PERSON_DATA +SCENE Logic::_scene +TROOM Cutaway::_temporaryRoom + + +DEBUG +===== +cd_sample_check() +debuginfo() Debugger::Cmd_Info +select_new_room() Debugger::Cmd_Room +- +AREAVAR (boolean, if true display objects/areas boxes) + + +GAME SETTINGS +============= +game_load() Logic::gameLoad +game_save() Logic::gameSave +- +config_request +MUSICTOGGLE Sound::_musicToggle / ConfMan.("music_mute") +SFXTOGGLE Sound::_sfxToggle / ConfMan.("sfx_mute") +TALKSPD QueenEngine::_talkSpeed / ConfMan.("talkspeed") +TEXTTOGGLE QueenEngine::_subtitles / ConfMan.("subtitles") +VersionStr GameVersion::versionString +VOICETOGGLE Sound::_speechToggle / ConfMan.("speech_mute") +VOLUME ? / ConfMan.("master_volume") + + +GRAPHICS +======== +bob() Graphics::drawBob +CHECK_PARALLAX() Graphics::handleParallax +clearallbobs() Graphics::clearBobs +clearbob() BobSlot::clear +DISP_OBJECTS() Graphics::setupRoomObjects +drawbobs() Graphics::drawBobs +invbob() Graphics::drawInventoryItem +loadbackdrop() *not needed* (included in Display::setupNewRoom) +loadpanel() Display::setupPanel +MAKE_SPEAK_BOB() Graphics::setBobText +makeanim() BobSlot::animNormal +movebob() BobSlot::move +pastebob() Graphics::pasteBob +REDISP_OBJECT() Graphics::refreshObject +requestor() +shrinkbob() Graphics::shrinkFrame +sortbobs() Graphics::sortBobs +stringanim() BobSlot::animString +- +bobs Graphics::_bobs +cambob Graphics::_cameraBob +sortedbobs Graphics::_sortedBobs + + +INPUT +===== +check_keys() Input::checkKeys() +get_key() *not needed* +- +drawmouseflag *not needed* (equivalent to _display->showMouseCursor(bool)) +key_commands Input::_currentCommandKeys +key_language Input::_commandKeys +KEYVERB Input::_keyVerb +MouseButton Input::_mouseButton +mouseflag *not needed* +no_check_keys Input::_noCheckKeys + + +INVENTORY +========= +DEL_ITEM_NUM() Logic::inventoryDeleteItem +INS_ITEM_NUM() Logic::inventoryInsertItem +INVDWN() Logic::inventoryScroll +INVENTORY() Logic::inventoryRefresh +INVUP() Logic::inventoryScroll +SETUP_ITEMS() Logic::inventorySetup +- +INV1,INV2,INV3,INV4 Logic::_inventoryItem + + +JOE +=== +FACE_JOE() Logic::joeFace +GRAB_DIR(),GRAB_JOE() Logic::joeGrab +SETUP_HERO() Logic::setupJoeInRoom +SETUP_JOE() Logic::setupJoe +USE_UNDERWEAR() Logic::joeUseUnderwear +USE_CLOTHES() Logic::joeUseClothes +USE_DRESS() Logic::joeUseDress +- +CUTJOEF Logic::_joe.cutFacing +JOE_RESPstr Logic::_joeResponse +JOEF,JX,JY,JDIR Logic::_joe.* +JOEWALK Logic::_joe.walk + + +JOURNAL +======= +clearlefttext() Journal::clearPanelTexts +drawnames() Journal::drawSaveDescriptions +findsaves() Journal::findSaveDescriptions +menutext() Journal::drawPanelText +predrawbobs() Journal::drawConfigPanel / Journal::drawNormalPanel +prompt_do() *not needed* +USE_JOURNAL() Logic::useJournal +waitmousezone() *not needed* +- +choice Journal::_currentSaveSlot +decbase Journal::_currentSavePage +in_journal *not needed* (the hack in puttext() seems useless and CHECK_PARALLAX() is never called) +save_descriptions Journal::_saveDescriptions +walkgameload *not needed ?* + + +LOGIC +===== +CHECK_PLAYER() QueenEngine::update +CUTAWAY_SPECIAL() Logic::removeHotelItemsFromInventory +DISP_ROOM() Logic::displayRoom +FIND_BOB() Logic::findBob +FIND_FRAME() Logic::findFrame +FIND_GRAPHIC() Logic::graphicData +P3_COPY_FROM() Logic::objectCopy +R_MAP() Logic::handlePinnacleRoom +restart_game() +SETUP_BOBS() Graphics::unpackControlBank / Graphics::setupMouseCursor +SETUP_FURNITURE() Graphics::setupRoomFurniture +SETUP_ROOM() Logic::changeRoom +SETUP_SCREENS() *not needed* (only calls Display::setupPanel) +SETUP_VARS() *not needed* (equivalent to Command::clear(), SCENE=0, clear(gamestate)) +update() QueenEngine::update +- +A_ANIMstr Logic::_aAnim +A_ANIM_MAX Logic::_numAAnim +A_NAMEstr Logic::_aName +A_NAME_MAX Logic::_numAName +A_FILEstr Logic::_aFile +A_FILE_MAX Logic::_numAFile +ACTOR_DATA_MAX Logic::_numActors +bamflag BamScene::_flag +bamindex BamScene::_index +DESCTOT Logic::_numDescriptions +ENTRY_OBJ Logic::_entryObj +FMAX Logic::_numFurnitureStatic +FMAXA Logic::_numFurnitureAnimated +FMAXLEN Logic::_numFurnitureAnimatedLen +FRAMES Logic::_numFrames +FURN_DATA_MAX Logic::_numFurniture +GAMESTATE Logic::_gameState +GRAPHIC_ANIM_MAX Logic::_numGraphicAnim +GRAPHIC_DATA Logic::_graphicData +GRAPHIC_MAX Logic::_numGraphics +ITEMTOT Logic::_numItems +ITEM_DATA Logic::_itemData +NAMETOT Logic::_numNames +OBJ_DESC_DATA Logic::_objectDescription +OBJ_DESC_MAX Logic::_numObjDesc +OBJECT_DATA Logic::_objectData +OBJECT_DESCRstr Logic::_objDescription +OBJECT_NAMEstr Logic::_objName +OBJTOT Logic::_numObjects +OLDROOM,ROOM,NEW_ROOM Logic::_*oom +ROOMTOT Logic::_numRooms +ROOM_DATA Logic::_roomData +ROOM_NAMEstr Logic::_roomName +SFACTOR Logic::_joe.scale +VERB_NAMEstr Logic::_verbName +WALK_OFF_DATA Logic::_walkOffData +WALK_OFF_MAX Logic::_numWalkOffs + + +PERSONS +======= +ALLOCATE_PERSON() Logic::allocatePersonFrames +CREATE_ANIM() Graphics::setupPersonAnim +SET_PERSON_DATA() Logic::initPerson +SETUP_PERSON() Logic::setupPersonInRoom +OBJ_PERSON() Logic::objectForPerson +- +NEW_ANIM Graphics::_newAnim +PERSON_FACE +PERSON_FACE_MAX +PERSON_FRAMES Logic::_personFrames +P_ANIMstr Person.anim +P_NAMEstr Person.name +P_STAND,P_BNUM,P_ROOM Person.actor->* +P_BANK,P_X,P_Y,P_COLOR Person.actor->* +P_VALUE,P_GAMES Person.actor->* +SFRAME Person.bobFrame + + +RESOURCE +======== +tflen() Resource::fileSize +topen() Resource::loadFile +tseek() *not needed* + + +SCREEN +====== +Box() Display::drawBox +calc_screen_scroll() Display::horizontalScrollUpdate +changejoepal() Display::palSetJoe* +check_colors() Display::palCustomColors +check_pal_scroll Display::palCustomScroll +clearpanel() Display::prepareUpdate +drawbackdrop() Display::prepareUpdate +drawpanel() Display::prepareUpdate +drawscreen() Display::update +dynalum() Display::dynalumUpdate +fade_panel() Display::palGreyPanel +fadein() Display::palFadeIn +fadeout() Display::palFadeOut +flashspecial() Display::palCustomFlash +loadfont() Display::initFont +palscroll() Display::palScroll +putcharacter() Display::drawChar +setpal() Display::palSet +- +BDxres Display::_bdWidth +BDyres Display::_bdHeight +clothespal Display::_palJoeClothes +COMPANEL *not needed* (argument) +dresspal Display::_palJoeDress +font Display::_font +font_sizes Display::_charWidth +FULLSCREEN Display::_fullscreen +nopalscroll Display::_pal.scrollable +palette Display::_pal.room +panelflag *not needed* (redundant with fullscreen) +scrollx Display::_horizontalScroll +tpal Display::_pal.screen + + +SOUND +===== +alter_current_volume() +playsong() Sound::playSong() +sfxbusy() Sound::waitSfxFinished() +sfxplay() Sound::playSfx() +- +song[] Sound::_song[] +tunelist[] Sound::_tune[] +CURRSONG Music::_currentSong +SFXNAME Sound::_sfxName +VOLUME + + +STATE +===== +ALTER_STATE() State::alterState* +FIND_STATE() State::findState* +- + + +TALK +==== +FIND_SACTION() Talk::findSpeechParameters +MOVE_SPEAK() *not needed* (included in Talk::getSpeakCommand) +SPEAK() Talk::speak +SPEAK_SUB() Talk::speakSegment +talk() Talk::talk +TALK_PROC() Talk::talk +- +A1,A12 +actiondata Talk::_speechParameters +HEAD +JMAX +JOEKEYstr +LEVEL +LEVELMAX +OLDLEVEL +OLDS +OWALK +PERstr +PKEYstr +TALKHEAD +TALKQUIT Input::_talkQuit +TALKstr +TALK_SELECTED Logic::_talkSelected + + +TEXTS +===== +blanktexts() Display::clearTexts +drawtext() Display::drawTexts +Ink() Display::textCurrentColor +MIDDLE() Display::textCenterX / Display::textSetCentered +text() Display::setText +textlen() Display::textWidth +- +textcol Display::_curTextColor +texts Display::_texts + + +WALK +==== +CALC_PATH() Walk::calcPath +CALC_WALK() Walk::incWalkData +CALC_X() Walk::calcC +CALC_Y() Walk::calcC +CALCSCALE() Area::calcScale +FIND_FREE_AREA Walk::findFreeArea +FIND_NEWP() Walk::findAreaPosition +FIND_OLDP() Walk::findAreaPosition +MOVE_JOE() Walk::moveJoe +MOVE_OTHER() Walk::movePerson +- +AREALIST Walk::_areaList +AREASTRIKE Walk::_areaStrike +movdata Walk::_moveData +WALK_DATA Walk::_walkData +WALKI Walk::_walkDataCount + + +ZONES +===== +ClearZones() Grid::clear +FIND_SCALE() Grid::findScale +FIND_VERB() Grid::findVerbUnderCursor +SETUP_PANEL_ZONES() Grid::setupPanel +SETUP_ZONES() Grid::setupNewRoom +SetZone() Grid::setZone +zone() Grid::findZoneForPos / Logic::findAreaForPos +- +AREA Grid::_area +AREAMAX Grid::_areaMax +OBJECT_BOX Grid::_objectBox +OBJMAX Grid::_objMax +zones Grid::_zones + + +(UNSORTED) +========== +in() Cutaway::inRange +find_cd_cut() findCdCut +find_cd_desc() *not needed* (included in Logic::joeSpeak) +- +Kstr +bank9 +NEWDEF, +M,A, +FRAME, +AM, +WX,WY, +PX,PY, +LD,FD +DESC2,DESC +PERSON_OBJ +FS,FE,FACE, +TY, +DY, +I2, +N,V, +ds,bs, +bx,by, +dx,dy, +SFAC,FDIR, +OBJ,E,T, +CH, +OLDG,S2,S1,ITEM,TYPE,C, +NAME,TL,TI,TS,WC,IMAGE, +D,P,LI,R +CANTQUIT !Input::_canQuit + + +(NO NEED TO BE GLOBAL) +====================== +Nstr,F1,F2,F3,F4,F5,F6,F7,F8,SF,BF,AS,MS // MOVE_OTHER (struct movdata *) +Nstr,S,F,BODY,BF,RF,AF,SANIMstr,FF // FIND_SACTION (struct action *) +CURRBOB // SETUP_FURNITURE, REDISP_OBJECT, DISP_OBJECTS +PSY,PSX,CSX,DX1,DX2,DY1,DY2,PCF,CCF,CSY // FIND_NEWP, FIND_OLDP +tx,ty,SFRAME,EFRAME,SPEED // FIND_GRAPHIC +AREAMAXv +CURRY +OMAX,OMAXA +TEMPA +BANK,BNUM +DIFF // LOOK local var +RET // P1_SET_CONDITIONS local var +BS,DS // CALC_SCALE +SX,SY, +NEWA // FIND_FREE_AREA local +IX,IY // Cutaway locals +COM // EXECUTE_ACTION local +COMMAX // EXECUTE_ACTION local +COND // EXECUTE_ACTION local +CURRCOM // EXECUTE_ACTION local +GSET // P1_SET_CONDITIONS local +A2 // EXECUTE_ACTION local +TEMPI // P1_SET_CONDITIONS local +MAPC // findFreeArea local var +NEWP,OLDP // locals in joeMove && personMove +OLDX,X,OLDY,Y // passed as arguments +X2,X1,XD,YD // incWalkData && findFreeArea locals +Gstr // not needed, grab state +Pstr // not needed, FIND_STATE result +OUTLINE // not needed, textSet() Graphics::parameter +FTOT // queen.c/SETUP_FURNITURE local var +OBJMAXv // == Logic::_objMax[Logic::_currentRoom] +TEMPstr +WORDstr +JOE2str,PERSON2str // locals in Talk::initialTalk +SUBJECT +tmpbamflag |