From 60fa351d32a0d133583662bc4cd20057be418b7a Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sat, 5 Dec 2009 05:44:46 +0000 Subject: Expanded the actions view into a full user interface view, and added support for highlighting of user interface elements svn-id: r46262 --- engines/m4/globals.h | 2 +- engines/m4/m4.cpp | 4 +- engines/m4/m4.h | 1 - engines/m4/mads_menus.cpp | 6 +- engines/m4/scene.cpp | 270 ++++++++++++++++++++++++++++++---------------- engines/m4/scene.h | 46 ++++---- engines/m4/viewmgr.cpp | 8 ++ engines/m4/viewmgr.h | 1 + 8 files changed, 214 insertions(+), 124 deletions(-) (limited to 'engines') diff --git a/engines/m4/globals.h b/engines/m4/globals.h index ccab82ffcd..36b5a28b61 100644 --- a/engines/m4/globals.h +++ b/engines/m4/globals.h @@ -228,7 +228,7 @@ public: void loadMadsObjects(); uint32 getObjectsSize() { return _madsObjects.size(); } - const MadsObject *getObject(uint32 index) { return _madsObjects[index].get(); } + MadsObject *getObject(uint32 index) { return _madsObjects[index].get(); } }; #define PLAYER_FIELD_LENGTH 40 diff --git a/engines/m4/m4.cpp b/engines/m4/m4.cpp index 4d713f3de8..094a3124c8 100644 --- a/engines/m4/m4.cpp +++ b/engines/m4/m4.cpp @@ -182,7 +182,6 @@ Common::Error M4Engine::run() { } _rails = new Rails(); // needs to be initialized before _scene _scene = new Scene(this); - _actionsView = new ActionsView(this); _dialogs = new Dialogs(); _viewManager = new ViewManager(this); _inventory = new Inventory(this); @@ -306,8 +305,7 @@ Common::Error M4Engine::goMADS() { _scene->loadScene(106); // a more interesting scene } - _viewManager->addView(_scene); - _viewManager->addView(_actionsView); + _scene->show(); _font->setFont(FONT_MAIN_MADS); _font->setColors(2, 1, 3); diff --git a/engines/m4/m4.h b/engines/m4/m4.h index 6ed9444f51..c7bbfcdd87 100644 --- a/engines/m4/m4.h +++ b/engines/m4/m4.h @@ -186,7 +186,6 @@ public: Font *_font; Actor *_actor; Scene *_scene; - ActionsView *_actionsView; Dialogs *_dialogs; M4Surface *_screen; Inventory *_inventory; diff --git a/engines/m4/mads_menus.cpp b/engines/m4/mads_menus.cpp index 65ad96ef1e..a67b933860 100644 --- a/engines/m4/mads_menus.cpp +++ b/engines/m4/mads_menus.cpp @@ -307,12 +307,8 @@ void RexMainMenuView::handleAction(MadsGameAction action) { // Load a sample starting scene - note that, currently, calling loadScene automatically // removes this menu screen from being displayed vm->_mouse->cursorOn(); - vm->_viewManager->addView(vm->_scene); - vm->_viewManager->addView(vm->_actionsView); + vm->_scene->show(); vm->_scene->loadScene(101); - - // **DEBUG** - set the default object - vm->_scene->setSelectedObject(48); return; case SHOW_INTRO: diff --git a/engines/m4/scene.cpp b/engines/m4/scene.cpp index d4db5c811e..c555c9f546 100644 --- a/engines/m4/scene.cpp +++ b/engines/m4/scene.cpp @@ -44,9 +44,8 @@ Scene::Scene(M4Engine *vm): View(vm, Common::Rect(0, 0, vm->_screen->width(), vm _sceneResources.props = new HotSpotList(); _backgroundSurface = new M4Surface(); _codeSurface = new M4Surface(); - _madsInterfaceSurface = new M4Surface(); + _madsInterfaceSurface = new MadsInterfaceView(vm); _sceneSprites = NULL; - _objectSprites = NULL; _palData = NULL; _interfacePal = NULL; _inverseColorTable = NULL; @@ -65,12 +64,9 @@ Scene::~Scene() { delete _backgroundSurface; delete _codeSurface; - delete _madsInterfaceSurface; if (_sceneSprites) delete _sceneSprites; - if (_objectSprites) - delete _objectSprites; _vm->_palette->deleteAllRanges(); @@ -133,6 +129,8 @@ void Scene::loadScene(int sceneNumber) { _vm->_palette->addRange(_interfacePal); _madsInterfaceSurface->translate(_interfacePal); _backgroundSurface->copyFrom(_madsInterfaceSurface, Common::Rect(0, 0, 320, 44), 0, 200 - 44); + + _madsInterfaceSurface->initialise(); } } @@ -450,9 +448,10 @@ void Scene::playIntro() { } -static const int FRAME_SPEED = 8; -static const int INVENTORY_X = 160; -static const int INVENTORY_Y = 159; +void Scene::show() { + _vm->_viewManager->addView(this); + _vm->_viewManager->addView(_madsInterfaceSurface); +} void Scene::update() { // TODO: Needs a proper implementation @@ -473,19 +472,6 @@ void Scene::update() { } _madsInterfaceSurface->copyTo(this, 0, this->height() - _madsInterfaceSurface->height()); - - if (_objectSprites) { - // Display object sprite. Note that the frame number isn't used directly, because it would result - // in too fast an animation - M4Sprite *spr = _objectSprites->getFrame(_objectFrameNumber / FRAME_SPEED); - spr->copyTo(this, INVENTORY_X, INVENTORY_Y, 0); - - if (!_vm->_globals->invObjectsStill) { - // If objetcs are to animated, move to the next frame - if (++_objectFrameNumber >= (_objectSprites->getCount() * FRAME_SPEED)) - _objectFrameNumber = 0; - } - } } } @@ -698,31 +684,6 @@ void Scene::showMADSV2TextBox(char *text, int x, int y, char *faceName) { /*--------------------------------------------------------------------------*/ -void Scene::setSelectedObject(int objectNumber) { - VALIDATE_MADS; - - // Load inventory resource - if (_objectSprites) { - _vm->_palette->deleteRange(_objectPalData); - delete _objectSprites; - } - - char resName[80]; - sprintf(resName, "*OB%.3dI.SS", objectNumber); - - Common::SeekableReadStream *data = _vm->res()->get(resName); - _objectSprites = new SpriteAsset(_vm, data, data->size(), resName); - _vm->res()->toss(resName); - - // Slot it into available palette space - _objectPalData = _objectSprites->getRgbList(); - _vm->_palette->addRange(_objectPalData); - _objectSprites->translate(_objectPalData, true); - - _objectFrameNumber = 0; - _selectedObject = objectNumber; -} - /* * TODO: decide if this should be kept centralised like it is in the original, or separated for the different * visual elements @@ -795,16 +756,46 @@ void Scene::getInterfaceObjectRect(int xv, int yv, Common::Rect &bounds) { /** *-------------------------------------------------------------------------- - * Base class for interface elements - * + * MadsInterfaceView handles the user interface section at the bottom of + * game screens in MADS games *-------------------------------------------------------------------------- */ -InterfaceElement::InterfaceElement(M4Engine *vm, const Common::Rect &viewBounds, bool transparent): - View(vm, viewBounds, transparent) { +MadsInterfaceView::MadsInterfaceView(M4Engine *vm): View(vm, Common::Rect(0, MADS_SURFACE_HEIGHT, + vm->_screen->width(), vm->_screen->height())) { + _screenType = VIEWID_INTERFACE; + _highlightedElement = -1; + _topIndex = 0; + _selectedObject = -1; + + _objectSprites = NULL; + _objectPalData = NULL; + + /* Set up the rect list for screen elements */ + // Actions + for (int i = 0; i < 10; ++i) + _screenObjects.addRect((i / 5) * 32 + 1, (i % 5) * 8 + MADS_SURFACE_HEIGHT + 2, + ((i / 5) + 1) * 32 + 3, ((i % 5) + 1) * 8 + MADS_SURFACE_HEIGHT + 2); + + // Scroller elements (up arrow, scroller, down arrow) + _screenObjects.addRect(73, 160, 82, 167); + _screenObjects.addRect(73, 168, 82, 190); + _screenObjects.addRect(73, 191, 82, 198); + + // Inventory object names + for (int i = 0; i < 5; ++i) + _screenObjects.addRect(89, 158 + i * 8, 160, 166 + i * 8); + + // Full rectangle area for all vocab actions + for (int i = 0; i < 5; ++i) + _screenObjects.addRect(239, 158 + i * 8, 320, 166 + i * 8); } -void InterfaceElement::setFontMode(FontMode newMode) { +MadsInterfaceView::~MadsInterfaceView() { + delete _objectSprites; +} + +void MadsInterfaceView::setFontMode(InterfaceFontMode newMode) { switch (newMode) { case ITEM_NORMAL: _vm->_font->setColors(4, 4, 0xff); @@ -818,56 +809,160 @@ void InterfaceElement::setFontMode(FontMode newMode) { } } -/** - *-------------------------------------------------------------------------- - * ActionsView handles the display of the actions/verb list - * - *-------------------------------------------------------------------------- - */ +void MadsInterfaceView::initialise() { + // Build up the inventory list + _inventoryList.clear(); -ActionsView::ActionsView(M4Engine *vm): InterfaceElement(vm, Common::Rect(0, MADS_SURFACE_HEIGHT, 70, - vm->_screen->height())) { - _screenType = VIEWID_INTERFACE; - _highlightedAction = 0; + for (uint i = 0; i < _vm->_globals->getObjectsSize(); ++i) { + MadsObject *obj = _vm->_globals->getObject(i); + if (obj->roomNumber == PLAYER_INVENTORY) + _inventoryList.push_back(i); + } + + // If the inventory has at least one object, select it + if (_inventoryList.size() > 0) + setSelectedObject(_inventoryList[0]); } -void ActionsView::getActionRect(int actionId, Common::Rect &b) { - int idx = actionId - kVerbLook; +void MadsInterfaceView::setSelectedObject(int objectNumber) { + char resName[80]; - b.left = (idx / 5) * 32 + 2; - b.top = (idx % 5) * 8 + MADS_SURFACE_HEIGHT + 3; - b.right = ((idx / 5) + 1) * 32 + 3; - b.bottom = ((idx % 5) + 1) * 8 + MADS_SURFACE_HEIGHT + 4; + // Load inventory resource + if (_objectSprites) { + _vm->_palette->deleteRange(_objectPalData); + delete _objectSprites; + } + + // Check to make sure the object is in the inventory, and also visible on-screen + int idx = 0; + while (idx < (int)_inventoryList.size()) { + if (_inventoryList[idx] == objectNumber) { + // Found the object + if (idx < _topIndex) + _topIndex = idx; + else if (idx >= (_topIndex + 5)) + _topIndex = MAX(0, idx - 4); + break; + } + ++idx; + } + if (idx == (int)_inventoryList.size()) { + // Object wasn't found, so return + _selectedObject = -1; + return; + } + + _selectedObject = objectNumber; + sprintf(resName, "*OB%.3dI.SS", objectNumber); + + Common::SeekableReadStream *data = _vm->res()->get(resName); + _objectSprites = new SpriteAsset(_vm, data, data->size(), resName); + _vm->res()->toss(resName); + + // Slot it into available palette space + _objectPalData = _objectSprites->getRgbList(); + _vm->_palette->addRange(_objectPalData); + _objectSprites->translate(_objectPalData, true); + + _objectFrameNumber = 0; } -void ActionsView::onRefresh(RectList *rects, M4Surface *destSurface) { +static const int FRAME_SPEED = 8; +static const int INVENTORY_X = 160; +static const int INVENTORY_Y = 159; + + +void MadsInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) { _vm->_font->setFont(FONT_INTERFACE_MADS); + char buffer[100]; - int actionId = kVerbLook; + // Highlighting logic for action list + int actionIndex = 0; for (int x = 0; x < 2; ++x) { - for (int y = 0; y < 5; ++y, ++actionId) { - // Get the bounds for the next item - Common::Rect r; - getActionRect(actionId, r); - - // Determine the font colour depending on whether an item is selected. Note that the 'Look' item - // is always 'selected', even when another action is selected - setFontMode((_highlightedAction == actionId) ? ITEM_HIGHLIGHTED : - ((actionId == kVerbLook) ? ITEM_SELECTED : ITEM_NORMAL)); + for (int y = 0; y < 5; ++y, ++actionIndex) { + // Determine the font colour depending on whether an item is selected. Note that the first action, + // 'Look', is always 'selected', even when another action is clicked on + setFontMode((_highlightedElement == actionIndex) ? ITEM_HIGHLIGHTED : + ((actionIndex == 0) ? ITEM_SELECTED : ITEM_NORMAL)); // Get the verb action and capitalise it - const char *verbStr = _vm->_globals->getVocab(actionId); - char buffer[20]; + const char *verbStr = _vm->_globals->getVocab(kVerbLook + actionIndex); strcpy(buffer, verbStr); if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; // Display the verb + const Common::Rect r(_screenObjects[actionIndex]); + _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); + } + } + + // Check for highlighting of the scrollbar controls + if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_SCROLLER) || (_highlightedElement == SCROLL_DOWN)) { + // Highlight the control's borders + const Common::Rect r(_screenObjects[_highlightedElement]); + destSurface->frameRect(r, 5); + } + + // Draw the horizontal line in the scroller representing the current top selected + const Common::Rect scroller(_screenObjects[SCROLL_SCROLLER]); + int yP = (_inventoryList.size() == 0) ? 0 : (scroller.height() - 4) * _topIndex / _inventoryList.size(); + destSurface->setColor(4); + destSurface->hLine(scroller.left + 2, scroller.right - 3, scroller.top + 2 + yP); + + // List inventory items + for (uint i = 0; i < 5; ++i) { + if ((_topIndex + i) >= _inventoryList.size()) + break; + + const char *descStr = _vm->_globals->getVocab(_vm->_globals->getObject( + _inventoryList[_topIndex + i])->descId); + strcpy(buffer, descStr); + if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; + + const Common::Rect r(_screenObjects[INVLIST_START + i]); + + // Set the highlighting of the inventory item + if (_highlightedElement == (int)(INVLIST_START + i)) setFontMode(ITEM_HIGHLIGHTED); + else if (_selectedObject == _inventoryList[_topIndex + i]) setFontMode(ITEM_SELECTED); + else setFontMode(ITEM_NORMAL); + + // Write out it's description + _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); + } + + // Handle the display of any currently selected object + if (_objectSprites) { + // Display object sprite. Note that the frame number isn't used directly, because it would result + // in too fast an animation + M4Sprite *spr = _objectSprites->getFrame(_objectFrameNumber / FRAME_SPEED); + spr->copyTo(destSurface, INVENTORY_X, INVENTORY_Y, 0); + + if (!_vm->_globals->invObjectsStill) { + // If objetcs are to animated, move to the next frame + if (++_objectFrameNumber >= (_objectSprites->getCount() * FRAME_SPEED)) + _objectFrameNumber = 0; + } + + // List the vocab actions for the currently selected object + MadsObject *obj = _vm->_globals->getObject(_selectedObject); + int yIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1); + + for (int i = 0; i < obj->vocabCount; ++i) { + const Common::Rect r(_screenObjects[VOCAB_START + i]); + + // Get the vocab description and capitalise it + const char *descStr = _vm->_globals->getVocab(obj->vocabList[i].vocabId); + strcpy(buffer, descStr); + if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; + + // Set the highlighting and display the entry + setFontMode((i == yIndex) ? ITEM_HIGHLIGHTED : ITEM_NORMAL); _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); } } } -bool ActionsView::onEvent(M4EventType eventType, int param1, int x, int y, bool &captureEvents) { +bool MadsInterfaceView::onEvent(M4EventType eventType, int param1, int x, int y, bool &captureEvents) { if (eventType == MEVENT_MOVE) { // If the cursor isn't in "wait mode", don't do any processing if (_vm->_mouse->getCursorNum() == CURSOR_WAIT) @@ -876,17 +971,8 @@ bool ActionsView::onEvent(M4EventType eventType, int param1, int x, int y, bool // Ensure the cursor is the standard arrow _vm->_mouse->setCursorNum(CURSOR_ARROW); - // Check if any of the actions are currently highlighted - _highlightedAction = 0; - for (int i = kVerbLook; i <= kVerbThrow; ++i) { - Common::Rect r; - getActionRect(i, r); - - if (r.contains(x, y)) { - _highlightedAction = i; - break; - } - } + // Check if any interface element is currently highlighted + _highlightedElement = _screenObjects.find(Common::Point(x, y)); return true; } diff --git a/engines/m4/scene.h b/engines/m4/scene.h index 567660d5b2..5f33d510ff 100644 --- a/engines/m4/scene.h +++ b/engines/m4/scene.h @@ -72,6 +72,8 @@ struct SceneResources { int32 railNodeCount; // # of rails }; +class MadsInterfaceView; + class Scene: public View { public: Scene(M4Engine *vm); @@ -79,6 +81,7 @@ public: // TODO: perhaps move playIntro() someplace else? void playIntro(); + void show(); void loadScene(int sceneNumber); void loadSceneResources(int sceneNumber); void loadSceneHotSpotsMads(int sceneNumber); @@ -102,13 +105,11 @@ public: void onRefresh(RectList *rects, M4Surface *destSurface); bool onEvent(M4EventType eventType, int param1, int x, int y, bool &captureEvents); - void setSelectedObject(int objectNumber); - private: int _currentScene; M4Surface *_backgroundSurface; M4Surface *_codeSurface; - M4Surface *_madsInterfaceSurface; + MadsInterfaceView *_madsInterfaceSurface; byte *_inverseColorTable; RGBList *_palData; RGBList *_interfacePal; @@ -118,33 +119,34 @@ private: SpriteAsset *_walkerSprite; char _statusText[100]; - // Inventory related fields - int _selectedObject; - SpriteAsset *_objectSprites; - RGBList *_objectPalData; - int _objectFrameNumber; - void nextCommonCursor(); }; -enum FontMode {ITEM_NORMAL, ITEM_HIGHLIGHTED, ITEM_SELECTED}; +enum InterfaceFontMode {ITEM_NORMAL, ITEM_HIGHLIGHTED, ITEM_SELECTED}; -class InterfaceElement: public View { -protected: - void setFontMode(FontMode newMode); -public: - InterfaceElement(M4Engine *vm, const Common::Rect &viewBounds, bool transparent = true); - ~InterfaceElement() {}; -}; +enum InterfaceObjects {ACTIONS_START = 0, SCROLL_UP = 10, SCROLL_SCROLLER = 11, SCROLL_DOWN = 12, + INVLIST_START = 13, VOCAB_START = 18}; -class ActionsView: public InterfaceElement { +class MadsInterfaceView: public View { private: - int _highlightedAction; + Common::Array _inventoryList; + RectList _screenObjects; + int _highlightedElement; + int _topIndex; + + // Object display fields + int _selectedObject; + SpriteAsset *_objectSprites; + RGBList *_objectPalData; + int _objectFrameNumber; - void getActionRect(int actionId, Common::Rect &bounds); + void setFontMode(InterfaceFontMode newMode); public: - ActionsView(M4Engine *vm); - ~ActionsView() {}; + MadsInterfaceView(M4Engine *vm); + ~MadsInterfaceView(); + + void initialise(); + void setSelectedObject(int objectNumber); void onRefresh(RectList *rects, M4Surface *destSurface); bool onEvent(M4EventType eventType, int param1, int x, int y, bool &captureEvents); diff --git a/engines/m4/viewmgr.cpp b/engines/m4/viewmgr.cpp index 8bd56fab61..b87ed7c91f 100644 --- a/engines/m4/viewmgr.cpp +++ b/engines/m4/viewmgr.cpp @@ -58,6 +58,14 @@ void RectList::addRect(const Common::Rect &rect) { push_back(rect); } +int RectList::find(const Common::Point &pt) { + for (int idx = 0; idx < size(); ++idx) { + if (this->operator [](idx).contains(pt.x, pt.y)) + return idx; + } + return -1; +} + //-------------------------------------------------------------------------- HotkeyList::HotkeyList(View *owner) { diff --git a/engines/m4/viewmgr.h b/engines/m4/viewmgr.h index 38ed3635c4..61dcc49ab2 100644 --- a/engines/m4/viewmgr.h +++ b/engines/m4/viewmgr.h @@ -84,6 +84,7 @@ public: void addRect(const Common::Rect &rect); // Common::Rect& operator [](int idx) { return _rects[idx]; } + int find(const Common::Point &pt); }; struct Hotkey { -- cgit v1.2.3