aboutsummaryrefslogtreecommitdiff
path: root/engines/draci
diff options
context:
space:
mode:
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);