diff options
-rw-r--r-- | engines/draci/game.cpp | 461 | ||||
-rw-r--r-- | engines/draci/game.h | 49 | ||||
-rw-r--r-- | engines/draci/script.cpp | 89 | ||||
-rw-r--r-- | engines/draci/script.h | 1 |
4 files changed, 486 insertions, 114 deletions
diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp index 5d78011e8d..d641809dc2 100644 --- a/engines/draci/game.cpp +++ b/engines/draci/game.cpp @@ -40,7 +40,6 @@ const Common::String dialoguePath("ROZH"); static double real_to_double(byte real[6]); Game::Game(DraciEngine *vm) : _vm(vm) { - unsigned int i; BArchive *initArchive = _vm->_initArchive; @@ -91,12 +90,12 @@ Game::Game(DraciEngine *vm) : _vm(vm) { _info._startRoom = gameData.readByte() - 1; _info._mapRoom = gameData.readByte() - 1; _info._numObjects = gameData.readUint16LE(); - _info._numIcons = gameData.readUint16LE(); + _info._numItems = gameData.readUint16LE(); _info._numVariables = gameData.readByte(); _info._numPersons = gameData.readByte(); _info._numDialogues = gameData.readByte(); - _info._maxIconWidth = gameData.readUint16LE(); - _info._maxIconHeight = gameData.readUint16LE(); + _info._maxItemWidth = gameData.readUint16LE(); + _info._maxItemHeight = gameData.readUint16LE(); _info._musicLength = gameData.readUint16LE(); _info._crc[0] = gameData.readUint16LE(); _info._crc[1] = gameData.readUint16LE(); @@ -126,8 +125,9 @@ Game::Game(DraciEngine *vm) : _vm(vm) { // Read in item icon status file = initArchive->getFile(1); - _iconStatus = file->_data; - uint numIcons = file->_length; + _itemStatus = file->_data; + uint numItems = file->_length; + _items = new GameItem[numItems]; // Read in object status @@ -154,7 +154,7 @@ Game::Game(DraciEngine *vm) : _vm(vm) { assert(numPersons == _info._numPersons); assert(numVariables == _info._numVariables); assert(numObjects == _info._numObjects); - assert(numIcons == _info._numIcons); + assert(numItems == _info._numItems); } void Game::start() { @@ -218,13 +218,17 @@ void Game::init() { _animUnderCursor = kOverlayImage; - _currentIcon = kNoIcon; + _currentItem = kNoItem; + _itemUnderCursor = kNoItem; _vm->_mouse->setCursorType(kNormalCursor); _loopStatus = kStatusOrdinary; _objUnderCursor = kOverlayImage; + // Set the inventory to empty initially + memset(_inventory, kNoItem, kInventorySlots * sizeof (int)); + // Initialize animation for object / room titles Animation *titleAnim = _vm->_anims->addText(kTitleText, true); Text *title = new Text("", _vm->_smallFont, kTitleColour, 0, 0); @@ -235,8 +239,16 @@ void Game::init() { Text *speech = new Text("", _vm->_bigFont, kFontColour1, 0, 0); speechAnim->addFrame(speech); + // Initialize inventory animation + BAFile *f = _vm->_iconsArchive->getFile(13); + Animation *inventoryAnim = _vm->_anims->addAnimation(kInventorySprite, 255, false); + Sprite *inventorySprite = new Sprite(f->_data, f->_length, 0, 0, true); + inventoryAnim->addFrame(inventorySprite); + inventoryAnim->setRelative((kScreenWidth - inventorySprite->getWidth()) / 2, + (kScreenHeight - inventorySprite->getHeight()) / 2); + for (uint i = 0; i < kDialogueLines; ++i) { - _dialogueAnims[i] = _vm->_anims->addText(-10 - i, true); + _dialogueAnims[i] = _vm->_anims->addText(kDialogueLinesID - i, true); Text *dialogueLine = new Text("", _vm->_smallFont, kLineInactiveColour, 0, 0); _dialogueAnims[i]->addFrame(dialogueLine); @@ -248,6 +260,10 @@ void Game::init() { text->setText(""); } + for (uint i = 0; i < _info._numItems; ++i) { + loadItem(i); + } + loadObject(kDragonObject); GameObject *dragon = getObject(kDragonObject); @@ -273,11 +289,11 @@ void Game::loop() { Surface *surface = _vm->_screen->getSurface(); - debugC(6, kDraciLogicDebugLevel, "loopstatus: %d, loopsubstatus: %d", - _loopStatus, _loopSubstatus); - do { + debugC(4, kDraciLogicDebugLevel, "loopstatus: %d, loopsubstatus: %d", + _loopStatus, _loopSubstatus); + _vm->handleEvents(); // Fetch mouse coordinates @@ -286,9 +302,6 @@ void Game::loop() { if (_loopStatus == kStatusDialogue && _loopSubstatus == kSubstatusOrdinary) { - // Find animation under cursor - _animUnderCursor = _vm->_anims->getTopAnimationID(x, y); - Text *text; for (int i = 0; i < kDialogueLines; ++i) { text = reinterpret_cast<Text *>(_dialogueAnims[i]->getFrame()); @@ -307,33 +320,28 @@ void Game::loop() { } } - if (_currentRoom._mouseOn) { + if(_vm->_mouse->isCursorOn()) { // Fetch the dedicated objects' title animation / current frame Animation *titleAnim = _vm->_anims->getAnimation(kTitleText); Text *title = reinterpret_cast<Text *>(titleAnim->getFrame()); - if (_loopStatus == kStatusOrdinary && _loopSubstatus == kSubstatusOrdinary) { - if(_vm->_mouse->isCursorOn()) { - // Find the game object under the cursor - // (to be more precise, one that corresponds to the animation under the cursor) - _animUnderCursor = _vm->_anims->getTopAnimationID(x, y); - int curObject = getObjectWithAnimation(_animUnderCursor); + updateCursor(); + updateTitle(); - updateTitle(); + if (_loopStatus == kStatusOrdinary && _loopSubstatus == kSubstatusOrdinary) { - _objUnderCursor = curObject; - if (_objUnderCursor != _oldObjUnderCursor) { - _oldObjUnderCursor = _objUnderCursor; + if (_vm->_mouse->lButtonPressed()) { + _vm->_mouse->lButtonSet(false); + + if (_currentItem != kNoItem) { + putItem(_currentItem, 0); + _currentItem = kNoItem; updateCursor(); - } - - if (_vm->_mouse->lButtonPressed()) { - _vm->_mouse->lButtonSet(false); - + } else { if (_objUnderCursor != kObjectNotFound) { GameObject *obj = &_objects[_objUnderCursor]; - + _vm->_mouse->cursorOff(); titleAnim->markDirtyRect(surface); title->setText(""); @@ -353,51 +361,128 @@ void Game::loop() { walkHero(x, y); } } + } - if (_vm->_mouse->rButtonPressed()) { - _vm->_mouse->rButtonSet(false); + if (_vm->_mouse->rButtonPressed()) { + _vm->_mouse->rButtonSet(false); - if (_objUnderCursor != kObjectNotFound) { - GameObject *obj = &_objects[_objUnderCursor]; + if (_objUnderCursor != kObjectNotFound) { + GameObject *obj = &_objects[_objUnderCursor]; - if (_vm->_script->testExpression(obj->_program, obj->_canUse)) { - _vm->_mouse->cursorOff(); - titleAnim->markDirtyRect(surface); - title->setText(""); - _objUnderCursor = kObjectNotFound; - - if (!obj->_imUse) { - if (obj->_useDir == -1) { - walkHero(x, y); - } else { - walkHero(obj->_useX, obj->_useY); - } - } + if (_vm->_script->testExpression(obj->_program, obj->_canUse)) { + _vm->_mouse->cursorOff(); + titleAnim->markDirtyRect(surface); + title->setText(""); + _objUnderCursor = kObjectNotFound; - _vm->_script->run(obj->_program, obj->_use); - _vm->_mouse->cursorOn(); - } else { - walkHero(x, y); + if (!obj->_imUse) { + if (obj->_useDir == -1) { + walkHero(x, y); + } else { + walkHero(obj->_useX, obj->_useY); + } } + + _vm->_script->run(obj->_program, obj->_use); + _vm->_mouse->cursorOn(); } else { - if (_vm->_script->testExpression(_currentRoom._program, _currentRoom._canUse)) { - _vm->_mouse->cursorOff(); - titleAnim->markDirtyRect(surface); - title->setText(""); + walkHero(x, y); + } + } else { + if (_vm->_script->testExpression(_currentRoom._program, _currentRoom._canUse)) { + _vm->_mouse->cursorOff(); + titleAnim->markDirtyRect(surface); + title->setText(""); - _vm->_script->run(_currentRoom._program, _currentRoom._use); - _vm->_mouse->cursorOn(); - } else { - walkHero(x, y); - } + _vm->_script->run(_currentRoom._program, _currentRoom._use); + _vm->_mouse->cursorOn(); + } else { + walkHero(x, y); } } } } - } - debug(8, "Anim under cursor: %d", _animUnderCursor); + if (_loopStatus == kStatusInventory && _loopSubstatus == kSubstatusOrdinary) { + if (_inventoryExit) { + inventoryDone(); + } + + // If we are in inventory mode, all the animations except game items' + // images will necessarily be paused so we can safely assume that any + // animation under the cursor (a value returned by + // AnimationManager::getTopAnimationID()) will be an item animation or. + // an overlay, for which we check. Item animations have their IDs + // calculated by offseting their itemID from the ID of the last "special" + // animation ID. In this way, we obtain its itemID. + if (_animUnderCursor != kOverlayImage && _animUnderCursor != kInventorySprite) { + _itemUnderCursor = kInventoryItemsID - _animUnderCursor; + } else { + _itemUnderCursor = kNoItem; + } + + // If the user pressed the left mouse button + if (_vm->_mouse->lButtonPressed()) { + _vm->_mouse->lButtonSet(false); + + // If there is an inventory item under the cursor and we aren't + // holding any item, run its look GPL program + if (_itemUnderCursor != kNoItem && _currentItem == kNoItem) { + const GPL2Program &program = _items[_itemUnderCursor]._program; + const int lookOffset = _items[_itemUnderCursor]._look; + + _vm->_script->run(program, lookOffset); + // Otherwise, if we are holding an item, try to place it inside the + // inventory + } else if (_currentItem != kNoItem) { + // FIXME: This should place the item in the nearest inventory slot, + // not the first one available + putItem(_currentItem, 0); + + // Remove it from our hands + _currentItem = kNoItem; + } + } else if (_vm->_mouse->rButtonPressed()) { + _vm->_mouse->rButtonSet(false); + + Animation *inventoryAnim = _vm->_anims->getAnimation(kInventorySprite); + + // If we right-clicked outside the inventory, close it + if (!inventoryAnim->getFrame()->getRect().contains(x, y)) { + inventoryDone(); + + // If there is an inventory item under our cursor + } else if (_itemUnderCursor != kNoItem) { + + // Again, we have two possibilities: + + // The first is that there is no item in our hands. + // In that case, just take the inventory item from the inventory. + if (_currentItem == kNoItem) { + _currentItem = _itemUnderCursor; + removeItem(_itemUnderCursor); + + // The second is that there *is* an item in our hands. + // In that case, run the canUse script for the inventory item + // which will check if the two items are combinable and, finally, + // run the use script for the item. + } else { + const GPL2Program &program = _items[_itemUnderCursor]._program; + const int canUseOffset = _items[_itemUnderCursor]._canUse; + const int useOffset = _items[_itemUnderCursor]._use; + + if (_vm->_script->testExpression(program, canUseOffset)) { + _vm->_script->run(program, useOffset); + } + } + updateCursor(); + } + } + } + } + + debugC(5, kDraciLogicDebugLevel, "Anim under cursor: %d", _animUnderCursor); // Handle character talking (if there is any) if (_loopSubstatus == kSubstatusTalk) { @@ -425,9 +510,7 @@ void Game::loop() { return; // Advance animations and redraw screen - if (_loopStatus != kStatusInventory) { - _vm->_anims->drawScene(surface); - } + _vm->_anims->drawScene(surface); _vm->_screen->copyToScreen(); _vm->_system->delayMillis(20); @@ -440,40 +523,106 @@ void Game::loop() { void Game::updateCursor() { - _vm->_mouse->setCursorType(kNormalCursor); + // Fetch mouse coordinates + int x = _vm->_mouse->getPosX(); + int y = _vm->_mouse->getPosY(); + + // Find animation under cursor + _animUnderCursor = _vm->_anims->getTopAnimationID(x, y); + + // If we are inside a dialogue, all we need is to update the ID of the current + // animation under the cursor. This enables us to update the currently selected + // dialogue line (by recolouring it) but still leave the cursor unupdated when + // over background objects. + if (_loopStatus == kStatusDialogue) + return; + + // If we are in inventory mode, we do a different kind of updating that handles + // inventory items and return early + if (_loopStatus == kStatusInventory && _loopSubstatus == kSubstatusOrdinary) { + + if (_currentItem == kNoItem) { + _vm->_mouse->setCursorType(kNormalCursor); + } else { + _vm->_mouse->loadItemCursor(_currentItem); + } + + if (_itemUnderCursor != kNoItem) { + const GPL2Program &program = _items[_itemUnderCursor]._program; + const int canUseOffset = _items[_itemUnderCursor]._canUse; + + if (_vm->_script->testExpression(program, canUseOffset)) { + if (_currentItem == kNoItem) { + _vm->_mouse->setCursorType(kHighlightedCursor); + } else { + _vm->_mouse->loadItemCursor(_currentItem, true); + } + } + } - if (_currentIcon != kNoIcon) { - _vm->_mouse->loadItemCursor(_currentIcon); + return; } + // Find the game object under the cursor + // (to be more precise, one that corresponds to the animation under the cursor) + int curObject = getObjectWithAnimation(_animUnderCursor); + + // Update the game object under the cursor + _objUnderCursor = curObject; + if (_objUnderCursor != _oldObjUnderCursor) { + _oldObjUnderCursor = _objUnderCursor; + } + + // Load the appropriate cursor (item image if an item is held or ordinary cursor + // if not) + if (_currentItem == kNoItem) { + _vm->_mouse->setCursorType(kNormalCursor); + } else { + _vm->_mouse->loadItemCursor(_currentItem); + } + + // TODO: Handle main menu + + // If there is no game object under the cursor, try using the room itself if (_objUnderCursor == kObjectNotFound) { if (_vm->_script->testExpression(_currentRoom._program, _currentRoom._canUse)) { - if (_currentIcon == kNoIcon) { + if (_currentItem == kNoItem) { _vm->_mouse->setCursorType(kHighlightedCursor); } else { - _vm->_mouse->loadItemCursor(_currentIcon, true); + _vm->_mouse->loadItemCursor(_currentItem, true); } } + // If there *is* a game object under the cursor, update the cursor image } else { GameObject *obj = &_objects[_objUnderCursor]; - - _vm->_mouse->setCursorType((CursorType)obj->_walkDir); - if (!(obj->_walkDir > 0)) { + // If there is no walking direction set on the object (i.e. the object + // is not a gate / exit), test whether it can be used and, if so, + // update the cursor image (highlight it). + if (obj->_walkDir == 0) { if (_vm->_script->testExpression(obj->_program, obj->_canUse)) { - if (_currentIcon == kNoIcon) { + if (_currentItem == kNoItem) { _vm->_mouse->setCursorType(kHighlightedCursor); } else { - _vm->_mouse->loadItemCursor(_currentIcon, true); + _vm->_mouse->loadItemCursor(_currentItem, true); } } + // If the walking direction *is* set, the game object is a gate, so update + // the cursor image to the appropriate arrow. + } else { + _vm->_mouse->setCursorType((CursorType)obj->_walkDir); } } } void Game::updateTitle() { + // If we are inside a dialogue, don't update titles + if (_loopStatus == kStatusDialogue) + return; + + // Fetch current surface and height of the small font (used for titles) Surface *surface = _vm->_screen->getSurface(); const int smallFontHeight = _vm->_smallFont->getFontHeight(); @@ -488,6 +637,8 @@ void Game::updateTitle() { // Mark dirty rectangle to delete the previous text titleAnim->markDirtyRect(surface); + // If there is no object under the cursor, delete the title. + // Otherwise, show the object's title. if (_objUnderCursor == kObjectNotFound) { title->setText(""); } else { @@ -500,6 +651,8 @@ void Game::updateTitle() { int newY = surface->centerOnY(y - smallFontHeight / 2, title->getHeight() * 2); titleAnim->setRelative(newX, newY); + // If we are currently playing the title, mark it dirty so it gets updated. + // Otherwise, start playing the title animation. if (titleAnim->isPlaying()) { titleAnim->markDirtyRect(surface); } else { @@ -521,6 +674,115 @@ int Game::getObjectWithAnimation(int animID) { return kObjectNotFound; } +void Game::removeItem(int itemID) { + + for (uint i = 0; i < kInventorySlots; ++i) { + if (_inventory[i] == itemID) { + _inventory[i] = kNoItem; + _vm->_anims->stop(kInventoryItemsID - itemID); + break; + } + } +} + +void Game::putItem(int itemID, int position) { + + if (itemID == kNoItem) + return; + + uint i = position; + + if (position >= 0 && + position < kInventoryLines * kInventoryColumns && + _inventory[position] == kNoItem) { + _inventory[position] = itemID; + } else { + for (i = 0; i < kInventorySlots; ++i) { + if (_inventory[i] == kNoItem) { + _inventory[i] = itemID; + break; + } + } + } + + const int line = i / kInventoryColumns + 1; + const int column = i % kInventoryColumns + 1; + + Animation *anim = _vm->_anims->getAnimation(kInventoryItemsID - itemID); + Drawable *frame = anim->getFrame(); + + const int x = kInventoryX + + (column * kInventoryItemWidth) - + (kInventoryItemWidth / 2) - + (frame->getWidth() / 2); + + const int y = kInventoryY + + (line * kInventoryItemHeight) - + (kInventoryItemHeight / 2) - + (frame->getHeight() / 2); + + debug(2, "itemID: %d position: %d line: %d column: %d x: %d y: %d", itemID, position, line, column, x, y); + + anim->setRelative(x, y); + + // If we are in inventory mode, we need to play the item animation, immediately + // upon returning it to its slot but *not* in other modes because it should be + // invisible then (along with the inventory) + if (_loopStatus == kStatusInventory && _loopSubstatus == kSubstatusOrdinary) { + _vm->_anims->play(kInventoryItemsID - itemID); + } +} + +void Game::inventoryInit() { + + // Pause all "background" animations + _vm->_anims->pauseAnimations(); + + // Draw the inventory and the current items + inventoryDraw(); + + // Turn cursor on if it is off + _vm->_mouse->cursorOn(); + + // Set the appropriate loop status + _loopStatus = kStatusInventory; + + // TODO: This will be used for exiting the inventory automatically when the mouse + // is outside it for some time + _inventoryExit = false; +} + +void Game::inventoryDone() { + _vm->_mouse->cursorOn(); + _loopStatus = kStatusOrdinary; + + _vm->_anims->unpauseAnimations(); + + _vm->_anims->stop(kInventorySprite); + + for (uint i = 0; i < kInventorySlots; ++i) { + if (_inventory[i] != kNoItem) { + _vm->_anims->stop(kInventoryItemsID - _inventory[i]); + } + } + + // Reset item under cursor + _itemUnderCursor = kNoItem; + + // TODO: Handle main menu +} + +void Game::inventoryDraw() { + + _vm->_anims->play(kInventorySprite); + + for (uint i = 0; i < kInventorySlots; ++i) { + if (_inventory[i] != kNoItem) { + _vm->_anims->play(kInventoryItemsID - _inventory[i]); + } + } +} + void Game::dialogueMenu(int dialogueID) { int oldLines, hit; @@ -578,7 +840,6 @@ int Game::dialogueDraw() { GPL2Program blockTest; blockTest._bytecode = _dialogueBlocks[i]._canBlock; blockTest._length = _dialogueBlocks[i]._canLen; - debugC(3, kDraciLogicDebugLevel, "Testing dialogue block %d", i); if (_vm->_script->testExpression(blockTest, 1)) { anim = _dialogueAnims[_dialogueLines]; @@ -757,6 +1018,33 @@ void Game::walkHero(int x, int y) { _vm->_anims->play(animID); } +void Game::loadItem(int itemID) { + + BAFile *f = _vm->_itemsArchive->getFile(itemID * 3); + Common::MemoryReadStream itemReader(f->_data, f->_length); + + GameItem *item = _items + itemID; + + item->_init = itemReader.readSint16LE(); + item->_look = itemReader.readSint16LE(); + item->_use = itemReader.readSint16LE(); + item->_canUse = itemReader.readSint16LE(); + item->_imInit = itemReader.readByte(); + item->_imLook = itemReader.readByte(); + item->_imUse = itemReader.readByte(); + + f = _vm->_itemsArchive->getFile(itemID * 3 + 1); + + // The first byte is the length of the string + item->_title = Common::String((const char *)f->_data + 1, f->_length - 1); + assert(f->_data[0] == item->_title.size()); + + f = _vm->_itemsArchive->getFile(itemID * 3 + 2); + + item->_program._bytecode = f->_data; + item->_program._length = f->_length; +} + void Game::loadRoom(int roomNum) { BAFile *f; @@ -1135,12 +1423,20 @@ void Game::setVariable(int numVar, int value) { _variables[numVar] = value; } -int Game::getIconStatus(int iconID) { - return _iconStatus[iconID]; +int Game::getItemStatus(int itemID) { + return _itemStatus[itemID]; +} + +void Game::setItemStatus(int itemID, int status) { + _itemStatus[itemID] = status; +} + +int Game::getCurrentItem() { + return _currentItem; } -int Game::getCurrentIcon() { - return _currentIcon; +void Game::setCurrentItem(int itemID) { + _currentItem = itemID; } Person *Game::getPerson(int personID) { @@ -1186,6 +1482,7 @@ Game::~Game() { delete[] _variables; delete[] _dialogueOffsets; delete[] _objects; + delete[] _items; } diff --git a/engines/draci/game.h b/engines/draci/game.h index a95dbe7b52..5cefa5dec1 100644 --- a/engines/draci/game.h +++ b/engines/draci/game.h @@ -57,10 +57,10 @@ enum { kNoEscRoom = -1 }; -// Used as a value to Game::_currentIcon and means there is no icon (game item) selected +// Used as a value to Game::_currentIcon and means there is no item selected // and a "real" cursor image is used enum { - kNoIcon = -1 + kNoItem = -1 }; // Used as a default parameter in Game::loadWalkingMap() to specify that the default @@ -82,6 +82,17 @@ enum SpeechConstants { kSpeechTimeUnit = 400 }; +/** Inventory related magical constants */ +enum InventoryConstants { + kInventoryItemWidth = 25, + kInventoryItemHeight = 25, + kInventoryColumns = 7, + kInventoryLines = 5, + kInventoryX = 70, //!< Used for positioning of the inventory sprite on the X axis + kInventoryY = 30, //!< Used for positioning of the inventory sprite on the Y axis + kInventorySlots = kInventoryLines * kInventoryColumns +}; + class WalkingMap { public: @@ -140,11 +151,11 @@ struct GameInfo { int _startRoom; int _mapRoom; uint _numObjects; - uint _numIcons; + uint _numItems; byte _numVariables; byte _numPersons; byte _numDialogues; - uint _maxIconWidth, _maxIconHeight; + uint _maxItemWidth, _maxItemHeight; uint _musicLength; uint _crc[4]; uint _numDialogueBlocks; @@ -255,6 +266,7 @@ public: void loadOverlays(); void loadObject(uint numObj); void loadWalkingMap(int mapID = kDefaultRoomMap); + void loadItem(int itemID); uint getNumObjects(); GameObject *getObject(uint objNum); @@ -271,8 +283,13 @@ public: int getGateNum(); void setGateNum(int gate); - int getIconStatus(int iconID); - int getCurrentIcon(); + int getItemStatus(int itemID); + void setItemStatus(int itemID, int status); + int getCurrentItem(); + void setCurrentItem(int itemID); + void removeItem(int itemID); + void putItem(int itemID, int position); + void addItem(int itemID); int getEscRoom(); @@ -297,6 +314,10 @@ public: void updateTitle(); void updateCursor(); + void inventoryInit(); + void inventoryDraw(); + void inventoryDone(); + void dialogueMenu(int dialogueID); int dialogueDraw(); void dialogueInit(int dialogID); @@ -312,17 +333,23 @@ private: GameInfo _info; int *_variables; - byte *_iconStatus; Person *_persons; GameObject *_objects; + byte *_itemStatus; + GameItem *_items; + int _currentItem; + int _itemUnderCursor; + + int _inventory[kInventorySlots]; + bool _inventoryExit; + + Room _currentRoom; int _currentGate; int _newRoom; int _newGate; - int _currentIcon; - // HACK: remove public when tested and add getters instead public: uint *_dialogueOffsets; @@ -336,8 +363,8 @@ public: int _lastBlock; int _dialogueLines; int _blockNum; - int _lines[4]; - Animation *_dialogueAnims[4]; + int _lines[kDialogueLines]; + Animation *_dialogueAnims[kDialogueLines]; LoopStatus _loopStatus; LoopSubstatus _loopSubstatus; diff --git a/engines/draci/script.cpp b/engines/draci/script.cpp index fdc2624b73..70b982b6d6 100644 --- a/engines/draci/script.cpp +++ b/engines/draci/script.cpp @@ -52,7 +52,7 @@ void Script::setupCommandList() { { 6, 1, "Talk", 2, { 3, 2 }, &Script::talk }, { 7, 1, "ObjStat", 2, { 3, 3 }, &Script::objStat }, { 7, 2, "ObjStat_On", 2, { 3, 3 }, &Script::objStatOn }, - { 8, 1, "IcoStat", 2, { 3, 3 }, NULL }, + { 8, 1, "IcoStat", 2, { 3, 3 }, &Script::icoStat }, { 9, 1, "Dialogue", 1, { 2 }, &Script::dialogue }, { 9, 2, "ExitDialogue", 0, { 0 }, &Script::exitDialogue }, { 9, 3, "ResetDialogue", 0, { 0 }, &Script::resetDialogue }, @@ -248,33 +248,33 @@ int Script::funcNot(int n) { return !n; } -int Script::funcIsIcoOn(int iconID) { - iconID -= 1; +int Script::funcIsIcoOn(int itemID) { + itemID -= 1; - return _vm->_game->getIconStatus(iconID) == 1; + return _vm->_game->getItemStatus(itemID) == 1; } -int Script::funcIcoStat(int iconID) { - iconID -= 1; +int Script::funcIcoStat(int itemID) { + itemID -= 1; - int status = _vm->_game->getIconStatus(iconID); + int status = _vm->_game->getItemStatus(itemID); return (status == 1) ? 1 : 2; } -int Script::funcIsIcoAct(int iconID) { - iconID -= 1; +int Script::funcIsIcoAct(int itemID) { + itemID -= 1; - return _vm->_game->getCurrentIcon() == iconID; + return _vm->_game->getCurrentItem() == itemID; } -int Script::funcActIco(int iconID) { +int Script::funcActIco(int itemID) { // The parameter seems to be an omission in the original player since it's not // used in the implementation of the function. It's possible that the functions were // implemented in such a way that they had to have a single parameter so this is only // passed as a dummy. - return _vm->_game->getCurrentIcon(); + return _vm->_game->getCurrentItem(); } int Script::funcIsObjOn(int objID) { @@ -500,6 +500,53 @@ void Script::release(Common::Queue<int> ¶ms) { _vm->_anims->deleteAfterIndex(markedIndex); } +void Script::icoStat(Common::Queue<int> ¶ms) { + int status = params.pop(); + int itemID = params.pop() - 1; + + _vm->_game->setItemStatus(itemID, status == 1); + + if (_vm->_game->getItemStatus(itemID) == 0) { + + if (itemID != kNoItem) { + _vm->_anims->deleteAnimation(kInventoryItemsID - itemID); + } + + _vm->_game->removeItem(itemID); + + if (_vm->_game->getCurrentItem() == itemID) { + _vm->_game->setCurrentItem(kNoItem); + } + + if (_vm->_mouse->getCursorType() == kNormalCursor) { + if (_vm->_game->getLoopStatus() == kStatusInventory) { + _vm->_mouse->cursorOff(); + } + } + } + + if (_vm->_game->getItemStatus(itemID) == 1) { + + if (itemID != kNoItem) { + Animation *itemAnim = _vm->_anims->addItem(kInventoryItemsID - itemID); + BAFile *f = _vm->_itemImagesArchive->getFile(2 * itemID); + Sprite *sp = new Sprite(f->_data, f->_length, 0, 0, true); + itemAnim->addFrame(sp); + } + + _vm->_game->setCurrentItem(itemID); + + _vm->_mouse->loadItemCursor(itemID); + + // TODO: This is probably not needed but I'm leaving it to be sure for now + // The original engine needed to turn off the mouse temporarily when changing + // the cursor image. I'm just setting it to the final state of that transition. + if (_vm->_game->getLoopStatus() == kStatusInventory) { + _vm->_mouse->cursorOn(); + } + } +} + void Script::objStatOn(Common::Queue<int> ¶ms) { int objID = params.pop() - 1; int roomID = params.pop() - 1; @@ -750,7 +797,7 @@ int Script::handleMathExpression(Common::MemoryReadStream &reader) { GPL2Operator oper; GPL2Function func; - debugC(3, kDraciBytecodeDebugLevel, "\t<MATHEXPR>"); + debugC(4, kDraciBytecodeDebugLevel, "\t<MATHEXPR>"); // Read in initial math object obj = (mathExpressionObject)reader.readSint16LE(); @@ -774,7 +821,7 @@ int Script::handleMathExpression(Common::MemoryReadStream &reader) { case kMathNumber: value = reader.readSint16LE(); stk.push(value); - debugC(3, kDraciBytecodeDebugLevel, "\t\tnumber: %d", value); + debugC(4, kDraciBytecodeDebugLevel, "\t\tnumber: %d", value); break; case kMathOperator: @@ -791,7 +838,7 @@ int Script::handleMathExpression(Common::MemoryReadStream &reader) { // Push result stk.push(res); - debugC(3, kDraciBytecodeDebugLevel, "\t\t%d %s %d (res: %d)", + debugC(4, kDraciBytecodeDebugLevel, "\t\t%d %s %d (res: %d)", arg1, oper._name.c_str(), arg2, res); break; @@ -800,7 +847,7 @@ int Script::handleMathExpression(Common::MemoryReadStream &reader) { stk.push(_vm->_game->getVariable(value)); - debugC(3, kDraciBytecodeDebugLevel, "\t\tvariable: %d (%d)", value, + debugC(4, kDraciBytecodeDebugLevel, "\t\tvariable: %d (%d)", value, _vm->_game->getVariable(value)); break; @@ -817,7 +864,7 @@ int Script::handleMathExpression(Common::MemoryReadStream &reader) { // FIXME: Pushing dummy value for now, but should push return value stk.push(0); - debugC(3, kDraciBytecodeDebugLevel, "\t\tcall: %s (not implemented)", + debugC(4, kDraciBytecodeDebugLevel, "\t\tcall: %s (not implemented)", func._name.c_str()); } else { arg1 = stk.pop(); @@ -828,7 +875,7 @@ int Script::handleMathExpression(Common::MemoryReadStream &reader) { // Push the result on the evaluation stack stk.push(res); - debugC(3, kDraciBytecodeDebugLevel, "\t\tcall: %s(%d) (res: %d)", + debugC(4, kDraciBytecodeDebugLevel, "\t\tcall: %s(%d) (res: %d)", func._name.c_str(), arg1, res); } @@ -865,7 +912,7 @@ bool Script::testExpression(GPL2Program program, uint16 offset) { // Seek to the expression reader.seek(offset); - debugC(2, kDraciBytecodeDebugLevel, + debugC(4, kDraciBytecodeDebugLevel, "Evaluating (standalone) GPL expression at offset %d:", offset); return (bool)handleMathExpression(reader); @@ -962,7 +1009,7 @@ int Script::run(GPL2Program program, uint16 offset) { // Account for GPL jump that some commands set if (_jump != 0) { - debugC(6, kDraciBytecodeDebugLevel, + debugC(3, kDraciBytecodeDebugLevel, "Jumping from offset %d to %d (%d bytes)", reader.pos(), reader.pos() + _jump, _jump); reader.seek(_jump, SEEK_CUR); @@ -996,7 +1043,7 @@ int Script::run(GPL2Program program, uint16 offset) { for (int i = 0; i < cmd->_numParams; ++i) { if (cmd->_paramTypes[i] == 4) { - debugC(2, kDraciBytecodeDebugLevel, + debugC(3, kDraciBytecodeDebugLevel, "Evaluating (in-script) GPL expression at offset %d: ", offset); params.push(handleMathExpression(reader)); } diff --git a/engines/draci/script.h b/engines/draci/script.h index 7fe917eaf3..86abb5ee9b 100644 --- a/engines/draci/script.h +++ b/engines/draci/script.h @@ -109,6 +109,7 @@ private: void start(Common::Queue<int> ¶ms); void mark(Common::Queue<int> ¶ms); void release(Common::Queue<int> ¶ms); + void icoStat(Common::Queue<int> ¶ms); void objStat(Common::Queue<int> ¶ms); void objStatOn(Common::Queue<int> ¶ms); void execInit(Common::Queue<int> ¶ms); |