aboutsummaryrefslogtreecommitdiff
path: root/engines/lure/menu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/lure/menu.cpp')
-rw-r--r--engines/lure/menu.cpp416
1 files changed, 416 insertions, 0 deletions
diff --git a/engines/lure/menu.cpp b/engines/lure/menu.cpp
new file mode 100644
index 0000000000..752b11f339
--- /dev/null
+++ b/engines/lure/menu.cpp
@@ -0,0 +1,416 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/menu.h"
+#include "lure/luredefs.h"
+#include "lure/decode.h"
+#include "lure/surface.h"
+#include "lure/system.h"
+#include "lure/res_struct.h"
+#include "lure/res.h"
+#include "lure/strings.h"
+
+namespace Lure {
+
+MenuRecord::MenuRecord(uint16 hsxstartVal, uint16 hsxendVal, uint16 xstartVal,
+ uint16 widthVal, const char *strings) {
+ _xstart = xstartVal; _width = widthVal;
+ _hsxstart = hsxstartVal; _hsxend = hsxendVal;
+
+ // Figure out the number of entries
+ const char *sPtr = strings;
+ _numEntries = 1;
+ while ((sPtr = strchr(sPtr, ',')) != NULL) {
+ ++_numEntries;
+ ++sPtr;
+ }
+
+ // Set up the list of entries
+ char *sCopy = strdup(strings);
+ char *s;
+ _entries = (char **) malloc(sizeof(char *) * _numEntries);
+ uint8 index = 0;
+ s = sCopy;
+ while (s != NULL) {
+ _entries[index++] = s;
+ s = strchr(s, ',');
+ if (s != NULL) *s++ = '\0'; // replace comma with NULL
+ }
+}
+
+MenuRecord::~MenuRecord() {
+ delete _entries[0]; // Delete string data for all the menu items
+ free(_entries); // Free the list
+}
+
+char *MenuRecord::getEntry(uint8 index) {
+ if (index >= _numEntries) error("Invalid menuitem index specified: %d", index);
+ return _entries[index];
+}
+
+/*--------------------------------------------------------------------------*/
+
+static Menu *int_menu = NULL;
+
+Menu::Menu(OSystem &system): _system(system), _screen(Screen::getReference()),
+ _events(Events::getReference()), _mouse(Mouse::getReference()) {
+ int_menu = this;
+
+ MemoryBlock *res = Disk::getReference().getEntry(5);
+ PictureDecoder decoder;
+ _menu = decoder.decode(res, SCREEN_SIZE);
+ delete res;
+
+ _menus[0] = new MenuRecord(40, 87, 20, 80, "Credits");
+ _menus[1] = new MenuRecord(127, 179, 100, 120, "Restart game,Save game,Restore game");
+ _menus[2] = new MenuRecord(224, 281, 210, 105, "Quit,Slow Text\x8b,Sound on ");
+ _selectedMenu = NULL;
+}
+
+Menu::~Menu() {
+ for (int ctr=0; ctr<NUM_MENUS; ++ctr) delete _menus[ctr];
+ delete _menu;
+}
+
+Menu &Menu::getReference() {
+ return *int_menu;
+}
+
+uint8 Menu::execute() {
+ _mouse.setCursorNum(CURSOR_ARROW);
+ _system.copyRectToScreen(_menu->data(), FULL_SCREEN_WIDTH, 0, 0, FULL_SCREEN_WIDTH, MENUBAR_Y_SIZE);
+ _system.updateScreen();
+
+ _selectedMenu = NULL;
+ _surfaceMenu = NULL;
+ _selectedIndex = 0;
+
+ while (_mouse.lButton()) {
+ while (_events.pollEvent()) {
+ // handle events
+ }
+
+ if (_mouse.y() < MENUBAR_Y_SIZE)
+ {
+ MenuRecord *p = getMenuAt(_mouse.x());
+
+ if (_selectedMenu != p) {
+ // If necessary, remove prior menu
+ if (_selectedMenu) {
+ toggleHighlight(_selectedMenu);
+ _screen.updateArea(_selectedMenu->xstart(), MENUBAR_Y_SIZE,
+ _surfaceMenu->width(), _surfaceMenu->height());
+ delete _surfaceMenu;
+ _surfaceMenu = NULL;
+ _selectedIndex = 0;
+ }
+
+ _selectedMenu = p;
+
+ // If a new menu is selected, show it
+ if (_selectedMenu) {
+ toggleHighlight(_selectedMenu);
+ _surfaceMenu = Surface::newDialog(
+ _selectedMenu->width(), _selectedMenu->numEntries(),
+ _selectedMenu->entries(), false, MENU_UNSELECTED_COLOUR);
+ _surfaceMenu->copyToScreen(_selectedMenu->xstart(), MENUBAR_Y_SIZE);
+ }
+
+ _system.copyRectToScreen(_menu->data(), FULL_SCREEN_WIDTH, 0, 0, FULL_SCREEN_WIDTH, MENUBAR_Y_SIZE);
+ _system.updateScreen();
+ }
+ }
+
+ // Check for changing selected index
+ uint8 index = getIndexAt(_mouse.x(), _mouse.y());
+ if (index != _selectedIndex) {
+ if (_selectedIndex != 0) toggleHighlightItem(_selectedIndex);
+ _selectedIndex = index;
+ if (_selectedIndex != 0) toggleHighlightItem(_selectedIndex);
+ }
+ _system.delayMillis(10);
+ }
+
+ if (_surfaceMenu) delete _surfaceMenu;
+
+ // Deselect the currently selected menu header
+ if (_selectedMenu)
+ toggleHighlight(_selectedMenu);
+
+ // Restore the previous screen
+ _screen.update();
+
+ if (_selectedMenu == NULL) return MENUITEM_NONE;
+ else if (_selectedMenu == _menus[0]) return MENUITEM_CREDITS;
+ else if (_selectedMenu == _menus[1]) {
+ switch (_selectedIndex) {
+ case 1: return MENUITEM_RESTART_GAME;
+ case 2: return MENUITEM_SAVE_GAME;
+ case 3: return MENUITEM_RESTORE_GAME;
+ }
+ } else {
+ switch (_selectedIndex) {
+ case 1: return MENUITEM_QUIT;
+ case 2: return MENUITEM_TEXT_SPEED;
+ case 3: return MENUITEM_SOUND;
+ }
+ }
+ return MENUITEM_NONE;
+}
+
+MenuRecord *Menu::getMenuAt(int x) {
+ for (int ctr = 0; ctr < NUM_MENUS; ++ctr)
+ if ((x >= _menus[ctr]->hsxstart()) && (x <= _menus[ctr]->hsxend()))
+ return _menus[ctr];
+
+ return NULL;
+}
+
+uint8 Menu::getIndexAt(uint16 x, uint16 y) {
+ if (!_selectedMenu) return 0;
+
+ int ys = MENUBAR_Y_SIZE + DIALOG_EDGE_SIZE + 3;
+ int ye = MENUBAR_Y_SIZE + _surfaceMenu->height() - DIALOG_EDGE_SIZE - 3;
+ if ((y < ys) || (y > ye)) return 0;
+
+ uint16 yRelative = y - ys;
+ uint8 index = (uint8) (yRelative / 8) + 1;
+ if (index > _selectedMenu->numEntries()) index = _selectedMenu->numEntries();
+ return index;
+}
+
+void Menu::toggleHighlight(MenuRecord *menuRec) {
+ byte *addr = _menu->data();
+
+ for (uint16 y=0; y<MENUBAR_Y_SIZE; ++y) {
+ for (uint16 x=menuRec->hsxstart(); x<=menuRec->hsxend(); ++x) {
+ if (addr[x] == MENUBAR_SELECTED_COLOUR) addr[x] = 0;
+ else if (addr[x] == 0) addr[x] = MENUBAR_SELECTED_COLOUR;
+ }
+ addr += FULL_SCREEN_WIDTH;
+ }
+}
+
+void Menu::toggleHighlightItem(uint8 index) {
+ byte *p = _surfaceMenu->data().data() + (DIALOG_EDGE_SIZE + 3 +
+ ((index - 1) * 8)) * _surfaceMenu->width();
+ uint32 numBytes = 8 * _surfaceMenu->width();
+
+ while (numBytes-- > 0) {
+ if (*p == MENU_UNSELECTED_COLOUR) *p = MENU_SELECTED_COLOUR;
+ else if (*p == MENU_SELECTED_COLOUR) *p = MENU_UNSELECTED_COLOUR;
+ ++p;
+ }
+
+ _surfaceMenu->copyToScreen(_selectedMenu->xstart(), MENUBAR_Y_SIZE);
+}
+
+/*--------------------------------------------------------------------------*/
+
+uint16 PopupMenu::ShowInventory() {
+ Resources &rsc = Resources::getReference();
+ StringData &strings = StringData::getReference();
+
+ uint16 numItems = rsc.numInventoryItems();
+ uint16 itemCtr = 0;
+ char **itemNames = (char **) Memory::alloc(sizeof(char *) * numItems);
+ uint16 *idList = (uint16 *) Memory::alloc(sizeof(uint16) * numItems);
+
+ HotspotDataList::iterator i;
+ for (i = rsc.hotspotData().begin(); i != rsc.hotspotData().end(); ++i) {
+ HotspotData *hotspot = *i;
+ if (hotspot->roomNumber == PLAYER_ID) {
+ idList[itemCtr] = hotspot->hotspotId;
+ char *hotspotName = itemNames[itemCtr++] = (char *) malloc(MAX_HOTSPOT_NAME_SIZE);
+ strings.getString(hotspot->nameId, hotspotName, NULL, NULL);
+ }
+ }
+
+ uint16 result = Show(numItems, (const char **) itemNames);
+ if (result != 0xffff) result = idList[result];
+
+ for (itemCtr = 0; itemCtr < numItems; ++itemCtr)
+ free(itemNames[itemCtr]);
+
+ delete itemNames;
+ delete idList;
+ return result;
+}
+
+Action PopupMenu::Show(uint32 actionMask) {
+ int numEntries = 0;
+ uint32 v = actionMask;
+ int index;
+
+ for (index = 1; index <= EXAMINE; ++index, v >>= 1) {
+ if (v & 1) ++numEntries;
+ }
+
+ const char **strList = (const char **) Memory::alloc(sizeof(char *) * numEntries);
+
+ v = actionMask;
+ int strIndex = 0;
+ for (index=1; index<=EXAMINE; ++index, v >>= 1) {
+ if (v & 1)
+ strList[strIndex++] = actionList[index];
+ }
+
+ uint16 result = Show(numEntries, strList);
+
+ if (result == 0xffff) return NONE;
+
+ v = actionMask;
+ for (index = 1; index <= EXAMINE; ++index, v >>= 1) {
+ if (v & 1)
+ if (result-- == 0) return (Action) index;
+ }
+
+ delete strList;
+ return NONE;
+}
+
+Action PopupMenu::Show(int numEntries, Action *actions) {
+ const char **strList = (const char **) Memory::alloc(sizeof(char *) * numEntries);
+ Action *actionPtr = actions;
+ for (int index = 0; index < numEntries; ++index)
+ strList[index] = actionList[*actionPtr++];
+ uint16 result = Show(numEntries, strList);
+
+ delete strList;
+ if (result == 0xffff) return NONE;
+ else return actions[result];
+}
+
+uint16 PopupMenu::Show(int numEntries, const char *actions[]) {
+ if (numEntries == 0) return 0xffff;
+ Events &e = Events::getReference();
+ Mouse &mouse = Mouse::getReference();
+ OSystem &system = System::getReference();
+ Screen &screen = Screen::getReference();
+ Rect r;
+
+ mouse.cursorOff();
+ uint16 oldX = mouse.x();
+ uint16 oldY = mouse.y();
+ const uint16 yMiddle = FULL_SCREEN_HEIGHT / 2;
+ mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle);
+
+ // Round up number of lines in dialog to next odd number
+ uint16 numLines = (numEntries / 2) * 2 + 1;
+ if (numLines > 5) numLines = 5;
+
+ // Figure out the character width
+ uint16 numCols = 0;
+ for (int ctr = 0; ctr < numEntries; ++ctr) {
+ int len = strlen(actions[ctr]);
+ if (len > numCols)
+ numCols = len;
+ }
+
+ // Create the dialog surface
+ Surface *s = new Surface(DIALOG_EDGE_SIZE * 2 + numCols * FONT_WIDTH,
+ DIALOG_EDGE_SIZE * 2 + numLines * FONT_HEIGHT);
+ s->createDialog();
+
+ int selectedIndex = 0;
+ bool refreshFlag = true;
+ r.left = DIALOG_EDGE_SIZE;
+ r.right = s->width() - DIALOG_EDGE_SIZE - 1;
+ r.top = DIALOG_EDGE_SIZE;
+ r.bottom = s->height() - DIALOG_EDGE_SIZE - 1;
+
+ for (;;) {
+ if (refreshFlag) {
+ // Set up the contents of the menu
+ s->fillRect(r, 0);
+
+ for (int index = 0; index < numLines; ++index) {
+ int actionIndex = selectedIndex - (numEntries / 2) + index;
+ if ((actionIndex >= 0) && (actionIndex < numEntries)) {
+ s->writeString(DIALOG_EDGE_SIZE, DIALOG_EDGE_SIZE + index * FONT_HEIGHT,
+ actions[actionIndex], true,
+ (index == (numLines / 2)) ? MENU_SELECTED_COLOUR : MENU_UNSELECTED_COLOUR,
+ false);
+ }
+ }
+
+ s->copyToScreen(0, yMiddle-(s->height() / 2));
+ system.updateScreen();
+ refreshFlag = false;
+ }
+
+ if (e.pollEvent()) {
+ if (e.quitFlag) {
+ selectedIndex = 0xffff;
+ break;
+ }
+
+ if (e.type() == OSystem::EVENT_KEYDOWN) {
+ byte ch = e.event().kbd.ascii;
+ uint16 keycode = e.event().kbd.keycode;
+
+ if (((keycode == 0x108) || (keycode == 0x111)) && (selectedIndex > 0)) {
+ --selectedIndex;
+ refreshFlag = true;
+ } else if (((keycode == 0x102) || (keycode == 0x112)) &&
+ (selectedIndex < numEntries-1)) {
+ ++selectedIndex;
+ refreshFlag = true;
+ } else if ((ch == '\xd') || (keycode == 0x10f)) {
+ break;
+ } else if (ch == '\x1b') {
+ selectedIndex = 0xffff;
+ break;
+ }
+
+ } else if (e.type() == OSystem::EVENT_MOUSEMOVE) {
+ if ((mouse.y() < yMiddle) && (selectedIndex > 0) &&
+ (yMiddle-mouse.y() >= POPMENU_CHANGE_SENSITIVITY)) {
+ --selectedIndex;
+ mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle);
+ refreshFlag = true;
+ } else if ((mouse.y() > yMiddle) && (selectedIndex < numEntries - 1) &&
+ (mouse.y()-yMiddle >= POPMENU_CHANGE_SENSITIVITY)) {
+ ++selectedIndex;
+ mouse.setPosition(FULL_SCREEN_WIDTH/2, yMiddle);
+ refreshFlag = true;
+ }
+
+ } else if (e.type() == OSystem::EVENT_LBUTTONDOWN) {
+ mouse.waitForRelease();
+ break;
+
+ } else if (e.type() == OSystem::EVENT_RBUTTONDOWN) {
+ mouse.waitForRelease();
+ selectedIndex = 0xffff;
+ break;
+ }
+ }
+ }
+
+ mouse.setPosition(oldX, oldY);
+ mouse.cursorOn();
+ screen.update();
+ return selectedIndex;
+}
+
+} // end of namespace Lure