aboutsummaryrefslogtreecommitdiff
path: root/engines/draci/game.cpp
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/game.cpp
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/game.cpp')
-rw-r--r--engines/draci/game.cpp461
1 files changed, 379 insertions, 82 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;
}