aboutsummaryrefslogtreecommitdiff
path: root/engines/access/inventory.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/access/inventory.cpp')
-rw-r--r--engines/access/inventory.cpp536
1 files changed, 536 insertions, 0 deletions
diff --git a/engines/access/inventory.cpp b/engines/access/inventory.cpp
new file mode 100644
index 0000000000..df499ba705
--- /dev/null
+++ b/engines/access/inventory.cpp
@@ -0,0 +1,536 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "access/inventory.h"
+#include "access/access.h"
+#include "access/resources.h"
+#include "access/amazon/amazon_resources.h"
+#include "access/martian/martian_resources.h"
+
+namespace Access {
+
+void InventoryEntry::load(const Common::String &name, const int *data) {
+ _value = ITEM_NOT_FOUND;
+ _name = name;
+ _otherItem1 = *data++;
+ _newItem1 = *data++;
+ _otherItem2 = *data++;
+ _newItem2 = *data;
+}
+
+int InventoryEntry::checkItem(int itemId) {
+ if (_otherItem1 == itemId)
+ return _newItem1;
+ else if (_otherItem2 == itemId)
+ return _newItem2;
+ else
+ return -1;
+}
+
+/*------------------------------------------------------------------------*/
+
+InventoryManager::InventoryManager(AccessEngine *vm) : Manager(vm) {
+ _startInvItem = 0;
+ _startInvBox = 0;
+ _invChangeFlag = true;
+ _invRefreshFlag = false;
+ _invModeFlag = false;
+ _startAboutItem = 0;
+ _startTravelItem = 0;
+ _iconDisplayFlag = true;
+ _boxNum = 0;
+
+ const char *const *names;
+ const int *combineP;
+
+ switch (vm->getGameID()) {
+ case GType_Amazon:
+ names = Amazon::INVENTORY_NAMES;
+ combineP = &Amazon::COMBO_TABLE[0][0];
+ _inv.resize(85);
+ break;
+ case GType_MartianMemorandum:
+ names = Martian::INVENTORY_NAMES;
+ combineP = &Martian::COMBO_TABLE[0][0];
+ _inv.resize(54);
+ break;
+ default:
+ error("Unknown game");
+ }
+
+ for (uint i = 0; i < _inv.size(); ++i, combineP += 4) {
+ _inv[i].load(names[i], combineP);
+ }
+
+ for (uint i = 0; i < 26; ++i) {
+ const int *r = INVCOORDS[i];
+ _invCoords.push_back(Common::Rect(r[0], r[2], r[1], r[3]));
+ }
+}
+
+int &InventoryManager::operator[](int idx) {
+ // WORKAROUND: At least in Amazon, some game scripts accidentally do reads
+ // beyond the length of the inventory array
+ static int invalid = 0;
+ return (idx >= (int)_inv.size()) ? invalid : _inv[idx]._value;
+}
+
+int InventoryManager::useItem() {
+ return _vm->_useItem;
+}
+
+void InventoryManager::setUseItem(int itemId) {
+ _vm->_useItem = itemId;
+}
+
+void InventoryManager::refreshInventory() {
+ // The original version was using pre-rendering for the inventory to spare some time.
+ // This is not needed on modern hardware, and it breaks a couple of things.
+ // Therefore it was removed in order to keep the same logic than for the CD version
+ // if (_vm->_screen->_vesaMode) {
+ // _invRefreshFlag = true;
+ // newDisplayInv();
+ // }
+}
+
+int InventoryManager::newDisplayInv() {
+ Screen &screen = *_vm->_screen;
+ EventsManager &events = *_vm->_events;
+ Room &room = *_vm->_room;
+ FileManager &files = *_vm->_files;
+
+ _invModeFlag = true;
+ _vm->_timers.saveTimers();
+
+ if (!room._tile && !_invRefreshFlag) {
+ saveScreens();
+ }
+
+ savedFields();
+ screen.setPanel(1);
+ events._cursorExitFlag = false;
+ getList();
+ initFields();
+
+ files.loadScreen(&_vm->_buffer1, 99, 0);
+ _vm->_buffer1.copyTo(&_vm->_buffer2);
+ _vm->copyBF2Vid();
+
+ // Set cells
+ Common::Array<CellIdent> cells;
+ cells.push_back(CellIdent(99, 99, 1));
+ _vm->loadCells(cells);
+
+ showAllItems();
+
+ if (!_invRefreshFlag) {
+ chooseItem();
+ if (_vm->_useItem != -1) {
+ int savedScale = _vm->_scale;
+ _vm->_scale = 153;
+ _vm->_screen->setScaleTable(_vm->_scale);
+ _vm->_buffer1.clearBuffer();
+
+ SpriteResource *spr = _vm->_objectsTable[99];
+ SpriteFrame *frame = spr->getFrame(_vm->_useItem);
+
+ int w = screen._scaleTable1[46];
+ int h = screen._scaleTable1[35];
+ _vm->_buffer1.sPlotF(frame, Common::Rect(0, 0, w, h));
+ events.setCursorData(&_vm->_buffer1, Common::Rect(0, 0, w, h));
+
+ _vm->_scale = savedScale;
+ screen.setScaleTable(_vm->_scale);
+ }
+ }
+
+ freeInvCells();
+ screen.setPanel(0);
+ events.debounceLeft();
+
+ restoreFields();
+ screen.restorePalette();
+ // The original was testing the vesa mode too.
+ // We removed this check as we don't use pre-rendering
+ if (!_invRefreshFlag) {
+ screen.clearScreen();
+ screen.setPalette();
+ }
+
+ if (!room._tile && !_invRefreshFlag) {
+ restoreScreens();
+ } else {
+ screen.setBufferScan();
+ room.buildScreen();
+
+ // The original was doing a check on the vesa mode at this point.
+ // We don't need it as we don't do inventory pre-rendering
+ screen.fadeOut();
+ _vm->copyBF2Vid();
+ }
+
+ events._cursorExitFlag = false;
+ screen._screenChangeFlag = false;
+ _invModeFlag = false;
+ events.debounceLeft();
+ _vm->_timers.restoreTimers();
+ _vm->_startup = 1;
+
+ int result = 0;
+ if (!_invRefreshFlag) {
+ if (_vm->_useItem == -1) {
+ result = 2;
+ events.forceSetCursor(CURSOR_CROSSHAIRS);
+ } else
+ events.forceSetCursor(CURSOR_INVENTORY);
+ }
+
+ _invRefreshFlag = false;
+ _invChangeFlag = false;
+ return result;
+}
+
+void InventoryManager::savedFields() {
+ Screen &screen = *_vm->_screen;
+ Room &room = *_vm->_room;
+
+ _fields._vWindowHeight = screen._vWindowHeight;
+ _fields._vWindowLinesTall = screen._vWindowLinesTall;
+ _fields._vWindowWidth = screen._vWindowWidth;
+ _fields._vWindowBytesWide = screen._vWindowBytesWide;
+ _fields._playFieldHeight = room._playFieldHeight;
+ _fields._playFieldWidth = room._playFieldWidth;
+ _fields._windowXAdd = screen._windowXAdd;
+ _fields._windowYAdd = screen._windowYAdd;
+ _fields._screenYOff = screen._screenYOff;
+ _fields._scrollX = _vm->_scrollX;
+ _fields._scrollY = _vm->_scrollY;
+ _fields._clipWidth = screen._clipWidth;
+ _fields._clipHeight = screen._clipHeight;
+ _fields._bufferStart = screen._bufferStart;
+ _fields._scrollCol = _vm->_scrollCol;
+ _fields._scrollRow = _vm->_scrollRow;
+}
+
+void InventoryManager::restoreFields() {
+ Screen &screen = *_vm->_screen;
+ Room &room = *_vm->_room;
+
+ screen._vWindowHeight = _fields._vWindowHeight;
+ screen._vWindowLinesTall = _fields._vWindowLinesTall;
+ screen._vWindowWidth = _fields._vWindowWidth;
+ screen._vWindowBytesWide = _fields._vWindowBytesWide;
+ room._playFieldHeight = _fields._playFieldHeight;
+ room._playFieldWidth = _fields._playFieldWidth;
+ screen._windowXAdd = _fields._windowXAdd;
+ screen._windowYAdd = _fields._windowYAdd;
+ screen._screenYOff = _fields._screenYOff;
+ _vm->_scrollX = _fields._scrollX;
+ _vm->_scrollY = _fields._scrollY;
+ screen._clipWidth = _fields._clipWidth;
+ screen._clipHeight = _fields._clipHeight;
+ screen._bufferStart = _fields._bufferStart;
+ _vm->_scrollCol = _fields._scrollCol;
+ _vm->_scrollRow = _fields._scrollRow;
+}
+
+void InventoryManager::initFields() {
+ Screen &screen = *_vm->_screen;
+ Room &room = *_vm->_room;
+
+ screen._vWindowHeight = screen.h;
+ room._playFieldHeight = screen.h;
+ screen._vWindowLinesTall = screen.h;
+ screen._clipHeight = screen.h;
+ room._playFieldWidth = screen.w;
+ screen._vWindowWidth = screen.w;
+ screen._vWindowBytesWide = screen.w;
+ screen._clipWidth = screen.w;
+
+ screen._windowXAdd = 0;
+ screen._windowYAdd = 0;
+ screen._screenYOff = 0;
+ screen._bufferStart.x = 0;
+ screen._bufferStart.y = 0;
+ _vm->_scrollX = _vm->_scrollY = 0;
+
+ _vm->_buffer1.clearBuffer();
+ _vm->_buffer2.clearBuffer();
+ // The original was doing at this point a check on vesa mode
+ // We don't need it as we don't do inventory pre-rendering
+ if (!_invRefreshFlag)
+ screen.clearBuffer();
+
+ screen.savePalette();
+}
+
+void InventoryManager::getList() {
+ _items.clear();
+ _tempLOff.clear();
+
+ for (uint i = 0; i < _inv.size(); ++i) {
+ if (_inv[i]._value == ITEM_IN_INVENTORY) {
+ _items.push_back(i);
+ _tempLOff.push_back(_inv[i]._name);
+ }
+ }
+}
+
+void InventoryManager::showAllItems() {
+ _iconDisplayFlag = true;
+
+ for (uint i = 0; i < _items.size(); ++i)
+ putInvIcon(i, _items[i]);
+}
+
+void InventoryManager::putInvIcon(int itemIndex, int itemId) {
+ SpriteResource *spr = _vm->_objectsTable[99];
+ assert(spr);
+ Common::Point pt((itemIndex % 6) * 46 + 23, (itemIndex / 6) * 35 + 15);
+ _vm->_buffer2.plotImage(spr, itemId, pt);
+
+ if (_iconDisplayFlag) {
+ _vm->_screen->copyBlock(&_vm->_buffer2, Common::Rect(pt.x, pt.y, pt.x + 46, pt.y + 35));
+ }
+}
+
+void InventoryManager::chooseItem() {
+ EventsManager &events = *_vm->_events;
+ _vm->_useItem = -1;
+
+ while (!_vm->shouldQuit()) {
+ // Check for events
+ events.pollEventsAndWait();
+
+ int selIndex;
+ // Poll events and wait for a click on a known area
+ if (!events._leftButton || ((selIndex = coordIndexOf()) == -1))
+ continue;
+
+ if (selIndex > 23) {
+ if (selIndex == 25)
+ _vm->_useItem = -1;
+ break;
+ } else if (selIndex < (int)_items.size() && _items[selIndex] != -1) {
+ _boxNum = selIndex;
+ _vm->copyBF2Vid();
+ combineItems();
+ _vm->copyBF2Vid();
+ outlineIcon(_boxNum);
+ _vm->_useItem = _items[_boxNum];
+ }
+ }
+}
+
+void InventoryManager::freeInvCells() {
+ delete _vm->_objectsTable[99];
+ _vm->_objectsTable[99] = nullptr;
+}
+
+int InventoryManager::coordIndexOf() {
+ const Common::Point pt = _vm->_events->_mousePos;
+
+ for (int i = 0; i < (int)_invCoords.size(); ++i) {
+ if (_invCoords[i].contains(pt))
+ return i;
+ }
+
+ return -1;
+}
+
+void InventoryManager::saveScreens() {
+ _vm->_buffer1.copyTo(&_savedBuffer1);
+ _vm->_screen->copyTo(&_savedScreen);
+ _vm->_newRects.push_back(Common::Rect(0, 0, _savedScreen.w, _savedScreen.h));
+
+}
+
+void InventoryManager::restoreScreens() {
+ _vm->_buffer1.w = _vm->_buffer1.pitch;
+ _savedBuffer1.copyTo(&_vm->_buffer1);
+ _savedScreen.copyTo(_vm->_screen);
+
+ _savedBuffer1.free();
+ _savedScreen.free();
+}
+
+void InventoryManager::outlineIcon(int itemIndex) {
+ Screen &screen = *_vm->_screen;
+ screen.frameRect(_invCoords[itemIndex], 7);
+
+ Common::String s = _tempLOff[itemIndex];
+ Font &font = _vm->_fonts._font2;
+ int strWidth = font.stringWidth(s);
+
+ font._fontColors[0] = 0;
+ font._fontColors[1] = 10;
+ font._fontColors[2] = 11;
+ font._fontColors[3] = 12;
+ font.drawString(&screen, s, Common::Point((screen.w - strWidth) / 2, 184));
+}
+
+void InventoryManager::combineItems() {
+ Screen &screen = *_vm->_screen;
+ EventsManager &events = *_vm->_events;
+ screen._leftSkip = screen._rightSkip = 0;
+ screen._topSkip = screen._bottomSkip = 0;
+ screen._screenYOff = 0;
+
+ Common::Point tempMouse = events._mousePos;
+ Common::Point lastMouse = events._mousePos;
+
+ Common::Rect &inv = _invCoords[_boxNum];
+ Common::Rect r(inv.left, inv.top, inv.left + 46, inv.top + 35);
+ Common::Point tempBox(inv.left, inv.top);
+ Common::Point lastBox(inv.left, inv.top);
+
+ _vm->_buffer2.copyBlock(&_vm->_buffer1, r);
+ SpriteResource *sprites = _vm->_objectsTable[99];
+ int invItem = _items[_boxNum];
+ events.pollEvents();
+
+ // Item drag handling loop if left button is held down
+ while (!_vm->shouldQuit() && events._leftButton) {
+ // Poll for events
+ events.pollEventsAndWait();
+
+ // Check positioning
+ if (lastMouse == events._mousePos)
+ continue;
+
+ lastMouse = events._mousePos;
+ Common::Rect lastRect(lastBox.x, lastBox.y, lastBox.x + 46, lastBox.y + 35);
+ screen.copyBlock(&_vm->_buffer2, lastRect);
+
+ Common::Point newPt;
+ newPt.x = MAX(events._mousePos.x - tempMouse.x + tempBox.x, 0);
+ newPt.y = MAX(events._mousePos.y - tempMouse.y + tempBox.y, 0);
+
+ screen.plotImage(sprites, invItem, newPt);
+ lastBox = newPt;
+ }
+
+ int destBox = events.checkMouseBox1(_invCoords);
+ if (destBox >= 0 && destBox != _boxNum && destBox < (int)_items.size()
+ && _items[destBox] != -1) {
+ int itemA = invItem;
+ int itemB = _items[destBox];
+
+ // Check whether the items can be combined
+ int combinedItem = _inv[itemA].checkItem(itemB);
+ if (combinedItem != -1) {
+ _inv[combinedItem]._value = 1;
+ _inv[itemA]._value = 2;
+ _inv[itemB]._value = 2;
+ _items[_boxNum] = -1;
+ _items[destBox] = combinedItem;
+ _tempLOff[destBox] = _inv[combinedItem]._name;
+ events.hideCursor();
+
+ // Shrink down the first item on top of the second item
+ zoomIcon(itemA, itemB, destBox, true);
+
+ // Shrink down the second item
+ Common::Rect destRect(_invCoords[destBox].left, _invCoords[destBox].top,
+ _invCoords[destBox].left + 46, _invCoords[destBox].top + 35);
+ _vm->_buffer2.copyBlock(&_vm->_buffer1, destRect);
+ screen._screenYOff = 0;
+ zoomIcon(itemB, -1, destBox, true);
+
+ // Exand up the new combined item from nothing to full size
+ zoomIcon(combinedItem, -1, destBox, false);
+
+ _boxNum = destBox;
+ events.showCursor();
+ return;
+ }
+ }
+
+ _iconDisplayFlag = true;
+ putInvIcon(_boxNum, invItem);
+}
+
+void InventoryManager::zoomIcon(int zoomItem, int backItem, int zoomBox, bool shrink) {
+ Screen &screen = *_vm->_screen;
+ screen._screenYOff = 0;
+ SpriteResource *sprites = _vm->_objectsTable[99];
+
+ int oldScale = _vm->_scale;
+ int zoomScale = shrink ? 255 : 1;
+ int zoomInc = shrink ? -1 : 1;
+ Common::Rect boxRect(_invCoords[zoomBox].left, _invCoords[zoomBox].top,
+ _invCoords[zoomBox].left + 46, _invCoords[zoomBox].top + 35);
+
+ while (!_vm->shouldQuit() && zoomScale != 0 && zoomScale != 256) {
+ _vm->_events->pollEventsAndWait();
+
+ _vm->_buffer2.copyBlock(&_vm->_buffer1, boxRect);
+ if (backItem != -1) {
+ _iconDisplayFlag = false;
+ putInvIcon(zoomBox, backItem);
+ }
+
+ _vm->_scale = zoomScale;
+ screen.setScaleTable(zoomScale);
+
+ int xv = screen._scaleTable1[boxRect.width() + 1];
+ if (xv) {
+ int yv = screen._scaleTable1[boxRect.height() + 1];
+ if (yv) {
+ // The zoomed size is positive in both directions, so show zoomed item
+ Common::Rect scaledBox(xv, yv);
+ scaledBox.moveTo(boxRect.left + (boxRect.width() - xv + 1) / 2,
+ boxRect.top + (boxRect.height() - yv + 1) / 2);
+
+ _vm->_buffer2.sPlotF(sprites->getFrame(zoomItem), scaledBox);
+ }
+ }
+
+ screen.copyBlock(&_vm->_buffer2, boxRect);
+
+ zoomScale += zoomInc;
+ }
+
+ if (!shrink) {
+ // Handle the final full-size version
+ _vm->_buffer2.copyBlock(&_vm->_buffer1, boxRect);
+ _vm->_buffer2.plotImage(sprites, zoomItem,
+ Common::Point(boxRect.left, boxRect.top));
+ screen.copyBlock(&_vm->_buffer2, boxRect);
+ }
+
+ _vm->_scale = oldScale;
+ screen.setScaleTable(oldScale);
+}
+
+void InventoryManager::synchronize(Common::Serializer &s) {
+ int count = _inv.size();
+ s.syncAsUint16LE(count);
+
+ if (!s.isSaving())
+ _inv.resize(count);
+
+ for (int i = 0; i < count; ++i)
+ s.syncAsUint16LE(_inv[i]._value);
+}
+
+} // End of namespace Access