aboutsummaryrefslogtreecommitdiff
path: root/engines/draci
diff options
context:
space:
mode:
authorDenis Kasak2009-08-17 18:50:38 +0000
committerDenis Kasak2009-08-17 18:50:38 +0000
commitb0fea939f49c3b0c6287d5a165c5f8ad27b9d794 (patch)
tree9567e9c71a55bf6d0301a7d5c7a2f0a17f128193 /engines/draci
parente5774d2881fe769e31ead57870b1315c56a4ba21 (diff)
downloadscummvm-rg350-b0fea939f49c3b0c6287d5a165c5f8ad27b9d794.tar.gz
scummvm-rg350-b0fea939f49c3b0c6287d5a165c5f8ad27b9d794.tar.bz2
scummvm-rg350-b0fea939f49c3b0c6287d5a165c5f8ad27b9d794.zip
Added inventory and item handling support (monster patch, sorry). Items were previously called "icons" as in the original player. This commit also renamed every such instance to the proper "item".
svn-id: r43487
Diffstat (limited to 'engines/draci')
-rw-r--r--engines/draci/game.cpp461
-rw-r--r--engines/draci/game.h49
-rw-r--r--engines/draci/script.cpp89
-rw-r--r--engines/draci/script.h1
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> &params) {
_vm->_anims->deleteAfterIndex(markedIndex);
}
+void Script::icoStat(Common::Queue<int> &params) {
+ 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> &params) {
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> &params);
void mark(Common::Queue<int> &params);
void release(Common::Queue<int> &params);
+ void icoStat(Common::Queue<int> &params);
void objStat(Common::Queue<int> &params);
void objStatOn(Common::Queue<int> &params);
void execInit(Common::Queue<int> &params);