From 86dcc28342021933f04ff3542932fc8823ff2360 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Thu, 3 Jun 2010 09:34:32 +0000 Subject: Moved the MADS interface view into mads_scene.cpp and did a major cleanup of scene loading code svn-id: r49405 --- engines/m4/mads_scene.cpp | 552 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 514 insertions(+), 38 deletions(-) (limited to 'engines/m4/mads_scene.cpp') diff --git a/engines/m4/mads_scene.cpp b/engines/m4/mads_scene.cpp index 60e480e6fb..b69c8bd116 100644 --- a/engines/m4/mads_scene.cpp +++ b/engines/m4/mads_scene.cpp @@ -40,6 +40,13 @@ namespace M4 { +static const int INV_ANIM_FRAME_SPEED = 2; +static const int INVENTORY_X = 160; +static const int INVENTORY_Y = 159; +static const int SCROLLER_DELAY = 200; + +//-------------------------------------------------------------------------- + MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResources), MadsView(this) { _vm = vm; @@ -66,10 +73,17 @@ void MadsScene::loadScene2(const char *aaName) { _kernelMessages.clear(); // Load up the properties for the scene - _sceneResources.load(_currentScene); + _sceneResources.load(_currentScene, 0/*word_83546*/, NULL, _walkSurface, _backgroundSurface); // Load scene walk paths loadSceneCodes(_currentScene); + + // Initialise the scene animation + uint16 flags = 0x4100; + if (_madsVm->globals()->_config.textWindowStill) + flags |= 0x200; + + _sceneAnimation->load(aaName, flags, _interfaceSurface, NULL); } /** @@ -83,22 +97,7 @@ void MadsScene::loadSceneTemporary() { {0x00<<2, 0x10<<2, 0x16<<2, 0}}; _vm->_palette->setPalette(&sysColors[0], 4, 3); - _backgroundSurface->loadBackground(_currentScene, &_palData); - _vm->_palette->addRange(_palData); - _backgroundSurface->translate(_palData); - - if (_currentScene < 900) { - _interfaceSurface->madsloadInterface(0, &_interfacePal); - _vm->_palette->addRange(_interfacePal); - _interfaceSurface->translate(_interfacePal); - _backgroundSurface->copyFrom(_interfaceSurface, Common::Rect(0, 0, 320, 44), 0, 200 - 44); - - _interfaceSurface->initialise(); - } - - // Don't load other screen resources for system screens - if (_currentScene >= 900) - return; + _interfaceSurface->initialise(); loadSceneHotspots(_currentScene); @@ -185,15 +184,6 @@ void MadsScene::loadSceneCodes(int sceneNumber, int index) { sceneS = walkData.getItemStream(0); _walkSurface->loadCodesMads(sceneS); _vm->res()->toss(filename); - } else if (_vm->getGameType() == GType_RexNebular) { - // For Rex Nebular, the walk areas are part of the scene info - byte *destP = _walkSurface->getBasePtr(0, 0); - const byte *srcP = _sceneResources.walkData; - byte runLength; - while ((runLength = *srcP++) != 0) { - Common::set_to(destP, destP + runLength, *srcP++); - destP += runLength; - } } } @@ -614,45 +604,531 @@ void MadsAction::set() { /*--------------------------------------------------------------------------*/ -void MadsSceneResources::load(int sId) { - const char *sceneInfoStr = MADSResourceManager::getResourceName(RESPREFIX_RM, sId, ".DAT"); - Common::SeekableReadStream *rawStream = _vm->_resourceManager->get(sceneInfoStr); +void MadsSceneResources::load(int sceneId, const char *resName, int v0, M4Surface *depthSurface, M4Surface *surface) { + char buffer[80]; + const char *sceneName; + + // TODO: Initialise spriteSet / xp_list + + if (sceneId > 0) { + sceneName = MADSResourceManager::getResourceName(RESPREFIX_RM, sceneId, ".DAT"); + } else { + strcat(buffer, "*"); + strcat(buffer, resName); + sceneName = buffer; // TODO: Check whether this needs to be converted to 'HAG form' + } + + Common::SeekableReadStream *rawStream = _vm->_resourceManager->get(sceneName); MadsPack sceneInfo(rawStream); + // Chunk 0: // Basic scene info Common::SeekableReadStream *stream = sceneInfo.getItemStream(0); int resSceneId = stream->readUint16LE(); - assert(resSceneId == sId); - + assert(resSceneId == sceneId); artFileNum = stream->readUint16LE(); - field_4 = stream->readUint16LE(); + dialogStyle = stream->readUint16LE(); width = stream->readUint16LE(); height = stream->readUint16LE(); assert((width == 320) && (height == 156)); stream->skip(24); - objectCount = stream->readUint16LE(); + int objectCount = stream->readUint16LE(); stream->skip(40); + // Load in any scene objects for (int i = 0; i < objectCount; ++i) { - objects[i].load(stream); + MadsObject rec; + rec.load(stream); + objects.push_back(rec); + } + for (int i = 0; i < 20 - objectCount; ++i) + stream->skip(48); + + int setCount = stream->readUint16LE(); + stream->readUint16LE(); + for (int i = 0; i < setCount; ++i) { + char buffer[64]; + Common::String s(buffer, 64); + setNames.push_back(s); + } + + // Initialise a copy of the surfaces if they weren't provided + bool dsFlag = false, ssFlag = false; + int gfxSize = width * height; + if (!surface) { + surface = new M4Surface(width, height); + ssFlag = true; + } + int walkSize = gfxSize; + if (dialogStyle == 2) { + width >>= 2; + walkSize = width * height; + } + if (!depthSurface) { + depthSurface = new M4Surface(width, height); + dsFlag = true; } // For Rex Nebular, read in the scene's compressed walk surface information if (_vm->getGameType() == GType_RexNebular) { - delete walkData; - + assert(depthSurface); stream = sceneInfo.getItemStream(1); - walkData = (byte *)malloc(stream->size()); + byte *walkData = (byte *)malloc(stream->size()); stream->read(walkData, stream->size()); + + // For Rex Nebular, the walk areas are part of the scene info + byte *destP = depthSurface->getBasePtr(0, 0); + const byte *srcP = walkData; + byte runLength; + while ((runLength = *srcP++) != 0) { + Common::set_to(destP, destP + runLength, *srcP++); + destP += runLength; + } + + delete walkData; + delete stream; } - _vm->_resourceManager->toss(sceneInfoStr); + _vm->_resourceManager->toss(sceneName); + + // Load the surface artwork + surface->loadBackground(sceneId); + + // Final cleanup + if (ssFlag) + delete surface; + if (dsFlag) + delete depthSurface; } + /*--------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------- + * MadsInterfaceView handles the user interface section at the bottom of + * game screens in MADS games + *-------------------------------------------------------------------------- + */ + +MadsInterfaceView::MadsInterfaceView(MadsM4Engine *vm): GameInterfaceView(vm, + Common::Rect(0, MADS_SURFACE_HEIGHT, vm->_screen->width(), vm->_screen->height())) { + _screenType = VIEWID_INTERFACE; + _highlightedElement = -1; + _topIndex = 0; + _selectedObject = -1; + _cheatKeyCtr = 0; + + _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); +} + +MadsInterfaceView::~MadsInterfaceView() { + delete _objectSprites; +} + +void MadsInterfaceView::setFontMode(InterfaceFontMode newMode) { + switch (newMode) { + case ITEM_NORMAL: + _vm->_font->current()->setColours(4, 4, 0xff); + break; + case ITEM_HIGHLIGHTED: + _vm->_font->current()->setColours(5, 5, 0xff); + break; + case ITEM_SELECTED: + _vm->_font->current()->setColours(6, 6, 0xff); + break; + } +} + +void MadsInterfaceView::initialise() { + // Build up the inventory list + _inventoryList.clear(); + + for (uint i = 0; i < _madsVm->globals()->getObjectsSize(); ++i) { + MadsObject *obj = _madsVm->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 MadsInterfaceView::setSelectedObject(int objectNumber) { + char resName[80]; + + // 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 = _inventoryList.indexOf(objectNumber); + if (idx == -1) { + // Object wasn't found, so return + _selectedObject = -1; + return; + } + + // Found the object + if (idx < _topIndex) + _topIndex = idx; + else if (idx >= (_topIndex + 5)) + _topIndex = MAX(0, idx - 4); + + _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 MadsInterfaceView::addObjectToInventory(int objectNumber) { + if (_inventoryList.indexOf(objectNumber) == -1) { + _madsVm->globals()->getObject(objectNumber)->roomNumber = PLAYER_INVENTORY; + _inventoryList.push_back(objectNumber); + } + + setSelectedObject(objectNumber); +} + +void MadsInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) { + _vm->_font->setFont(FONT_INTERFACE_MADS); + char buffer[100]; + + // Check to see if any dialog is currently active + bool dialogVisible = _vm->_viewManager->getView(LAYER_DIALOG) != NULL; + + // Highlighting logic for action list + int actionIndex = 0; + for (int x = 0; x < 2; ++x) { + 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 = _madsVm->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->current()->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() < 2) ? 0 : (scroller.height() - 5) * _topIndex / (_inventoryList.size() - 1); + 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 = _madsVm->globals()->getVocab(_madsVm->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->current()->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 / INV_ANIM_FRAME_SPEED); + spr->copyTo(destSurface, INVENTORY_X, INVENTORY_Y, 0); + + if (!_madsVm->globals()->_config.invObjectsStill && !dialogVisible) { + // If objects need to be animated, move to the next frame + if (++_objectFrameNumber >= (_objectSprites->getCount() * INV_ANIM_FRAME_SPEED)) + _objectFrameNumber = 0; + } + + // List the vocab actions for the currently selected object + MadsObject *obj = _madsVm->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 = _madsVm->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->current()->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); + } + } +} + +bool MadsInterfaceView::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) { + MadsAction &act = _madsVm->scene()->getAction(); + + // If the mouse isn't being held down, then reset the repeated scroll timer + if (eventType != MEVENT_LEFT_HOLD) + _nextScrollerTicks = 0; + + // Handle various event types + switch (eventType) { + case MEVENT_MOVE: + // If the cursor isn't in "wait mode", don't do any processing + if (_vm->_mouse->getCursorNum() == CURSOR_WAIT) + return true; + + // Ensure the cursor is the standard arrow + _vm->_mouse->setCursorNum(CURSOR_ARROW); + + // Check if any interface element is currently highlighted + _highlightedElement = _screenObjects.find(Common::Point(x, y)); + + return true; + + case MEVENT_LEFT_CLICK: + // Left mouse click + { + // Check if an inventory object was selected + if ((_highlightedElement >= INVLIST_START) && (_highlightedElement < (INVLIST_START + 5))) { + // Ensure there is an inventory item listed in that cell + uint idx = _highlightedElement - INVLIST_START; + if ((_topIndex + idx) < _inventoryList.size()) { + // Set the selected object + setSelectedObject(_inventoryList[_topIndex + idx]); + } + } else if ((_highlightedElement >= ACTIONS_START) && (_highlightedElement < (ACTIONS_START + 10))) { + // A standard action was selected + int verbId = kVerbLook + (_highlightedElement - ACTIONS_START); + warning("Selected action #%d", verbId); + + } else if ((_highlightedElement >= VOCAB_START) && (_highlightedElement < (VOCAB_START + 5))) { + // A vocab action was selected + MadsObject *obj = _madsVm->globals()->getObject(_selectedObject); + int vocabIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1); + if (vocabIndex >= 0) { + act._actionMode = ACTMODE_OBJECT; + act._actionMode2 = ACTMODE2_2; + act._flags1 = obj->vocabList[1].flags1; + act._flags2 = obj->vocabList[1].flags2; + + act._currentHotspot = _selectedObject; + act._articleNumber = act._flags2; + } + } + } + return true; + + case MEVENT_LEFT_HOLD: + // Left mouse hold + // Handle the scroller - the up/down buttons allow for multiple actions whilst the mouse is held down + if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_DOWN)) { + if ((_nextScrollerTicks == 0) || (g_system->getMillis() >= _nextScrollerTicks)) { + // Handle scroll up/down action + _nextScrollerTicks = g_system->getMillis() + SCROLLER_DELAY; + + if ((_highlightedElement == SCROLL_UP) && (_topIndex > 0)) + --_topIndex; + if ((_highlightedElement == SCROLL_DOWN) && (_topIndex < (int)(_inventoryList.size() - 1))) + ++_topIndex; + } + } + return true; + + case MEVENT_LEFT_DRAG: + // Left mouse drag + // Handle the the the scroller area that can be dragged to adjust the top displayed index + if (_highlightedElement == SCROLL_SCROLLER) { + // Calculate the new top index based on the Y position + const Common::Rect r(_screenObjects[SCROLL_SCROLLER]); + _topIndex = CLIP((int)(_inventoryList.size() - 1) * (y - r.top - 2) / (r.height() - 5), + 0, (int)_inventoryList.size() - 1); + } + return true; + + case KEVENT_KEY: + if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX) + handleCheatKey(param1); + handleKeypress(param1); + return true; + + default: + break; + } + + return false; +} + +bool MadsInterfaceView::handleCheatKey(int32 keycode) { + switch (keycode) { + case Common::KEYCODE_SPACE: + // TODO: Move player to current destination + return true; + + case Common::KEYCODE_t | (Common::KEYCODE_LALT): + case Common::KEYCODE_t | (Common::KEYCODE_RALT): + { + // Teleport to room + //Scene *sceneView = (Scene *)vm->_viewManager->getView(VIEWID_SCENE); + + + return true; + } + + default: + break; + } + + return false; +} + +const char *CHEAT_SEQUENCE = "widepipe"; + +bool MadsInterfaceView::handleKeypress(int32 keycode) { + int flags = keycode >> 24; + int kc = keycode & 0xffff; + + // Capitalise the letter if necessary + if (_cheatKeyCtr < CHEAT_SEQUENCE_MAX) { + if ((flags & Common::KBD_CTRL) && (kc == CHEAT_SEQUENCE[_cheatKeyCtr])) { + ++_cheatKeyCtr; + if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX) + Dialog::display(_vm, 22, cheatingEnabledDesc); + return true; + } else { + _cheatKeyCtr = 0; + } + } + + // Handle the various keys + if ((keycode == Common::KEYCODE_ESCAPE) || (keycode == Common::KEYCODE_F1)) { + // Game menu + _madsVm->globals()->dialogType = DIALOG_GAME_MENU; + leaveScene(); + return false; + } else if (flags & Common::KBD_CTRL) { + // Handling of the different control key combinations + switch (kc) { + case Common::KEYCODE_i: + // Mouse to inventory + warning("TODO: Mouse to inventory"); + break; + + case Common::KEYCODE_k: + // Toggle hotspots + warning("TODO: Toggle hotspots"); + break; + + case Common::KEYCODE_p: + // Player stats + warning("TODO: Player stats"); + break; + + case Common::KEYCODE_q: + // Quit game + break; + + case Common::KEYCODE_s: + // Activate sound + warning("TODO: Activate sound"); + break; + + case Common::KEYCODE_u: + // Rotate player + warning("TODO: Rotate player"); + break; + + case Common::KEYCODE_v: { + // Release version + Dialog *dlg = new Dialog(_vm, GameReleaseInfoStr, GameReleaseTitleStr); + _vm->_viewManager->addView(dlg); + _vm->_viewManager->moveToFront(dlg); + return false; + } + + default: + break; + } + } else if ((flags & Common::KBD_ALT) && (kc == Common::KEYCODE_q)) { + // Quit Game + + } else { + // Standard keypresses + switch (kc) { + case Common::KEYCODE_F2: + // Save game + _madsVm->globals()->dialogType = DIALOG_SAVE; + leaveScene(); + break; + case Common::KEYCODE_F3: + // Restore game + _madsVm->globals()->dialogType = DIALOG_RESTORE; + leaveScene(); + break; + } + } +//DIALOG_OPTIONS + return false; +} + +void MadsInterfaceView::leaveScene() { + // Close the scene + View *view = _madsVm->_viewManager->getView(VIEWID_SCENE); + _madsVm->_viewManager->deleteView(view); +} + } // End of namespace M4 -- cgit v1.2.3