void SpriteSlots::drawBackground() { Scene &scene = _vm->_game->_scene; // Initial draw loop for any active sprites in the background for (uint i = 0; i < size(); ++i) { SpriteSlot &spriteSlot = (*this)[i]; DirtyArea &dirtyArea = scene._dirtyAreas[i]; if (spriteSlot._flags >= IMG_STATIC) { // Foreground sprite, so we can ignore it dirtyArea._active = false; } else { dirtyArea._active = true; dirtyArea.setSpriteSlot(&spriteSlot); if (spriteSlot._flags == IMG_DELTA) { // Background object, so need to draw it assert(spriteSlot._frameNumber > 0); SpriteAsset *asset = scene._sprites[spriteSlot._spritesIndex]; MSprite *frame = asset->getFrame(spriteSlot._frameNumber - 1); Common::Point pt = spriteSlot._position; if (spriteSlot._scale != -1) { // Adjust the drawing position pt.x -= frame->w / 2; pt.y -= frame->h - 1; } if (spriteSlot._depth <= 1) { scene._backgroundSurface.transBlitFrom(*frame, pt, frame->getTransparencyIndex()); } else if (scene._depthStyle == 0) { scene._backgroundSurface.copyFrom(*frame, pt, spriteSlot._depth, &scene._depthSurface, -1, false, frame->getTransparencyIndex()); } else { scene._backgroundSurface.transBlitFrom(*frame, pt, frame->getTransparencyIndex()); } } } } // Mark any remaning sprite slot dirty areas as inactive for (uint i = size(); i < SPRITE_SLOTS_MAX_SIZE; ++i) scene._dirtyAreas[i]._active = false; // Flag any active text display for (uint i = 0; i < scene._textDisplay.size(); ++i) { TextDisplay &textDisplay = scene._textDisplay[i]; DirtyArea &dirtyArea = scene._dirtyAreas[i + SPRITE_SLOTS_MAX_SIZE]; if (textDisplay._expire >= 0 || !textDisplay._active) { dirtyArea._active = false; } else { dirtyArea._active = true; dirtyArea.setTextDisplay(&textDisplay); } } }
bool Console::cmdShowSprite(int argc, const char **argv) { View *view = _vm->_viewManager->getView(VIEWID_SCENE); if (view == NULL) DebugPrintf("The scene view isn't currently active\n"); else if (argc < 2) DebugPrintf("Usage: %s resource_name\n", argv[0]); else { char resourceName[20]; strncpy(resourceName, argv[1], 15); resourceName[15] = '\0'; if (!strchr(resourceName, '.')) strcat(resourceName, ".SS"); _vm->_viewManager->moveToFront(view); Common::SeekableReadStream *data = _vm->res()->get(resourceName); SpriteAsset *asset = new SpriteAsset(_vm, data, data->size(), resourceName); _vm->res()->toss(resourceName); RGBList *palData = new RGBList(asset->getColorCount(), asset->getPalette(), true); _vm->_palette->addRange(palData); // Get the scene background surface M4Surface *bg = _vm->_scene->getBackgroundSurface(); // Write the sprite onto the screen int x = 0, y = 0, yMax = 0; for (int index = 0; index < asset->getCount(); index++) { M4Sprite *spr = asset->getFrame(index); spr->translate(palData); // sprite pixel translation if ((x + spr->width() >= bg->width()) && (yMax != 0)) { x = 0; y += yMax; yMax = 0; } if (y >= bg->height()) break; // FIXME: We assume that the transparent color is the color of the top left pixel byte *transparentColor = (byte *)spr->pixels; spr->copyTo(bg, x, y, (int)*transparentColor); x += spr->width(); yMax = MAX(yMax, spr->height()); } view->restore(0, 0, view->width(), view->height()); return false; } return true; }
void DirtyArea::setUISlot(const UISlot *slot) { int type = slot->_flags; if (type <= IMG_UPDATE_ONLY) type += -IMG_UPDATE_ONLY; if (type >= 0x40) type &= ~0x40; MSurface &intSurface = _vm->_game->_scene._userInterface; switch (type) { case IMG_REFRESH: _bounds.left = 0; _bounds.top = 0; setArea(intSurface.w, intSurface.h, intSurface.w, intSurface.h); break; case IMG_OVERPRINT: _bounds.left = slot->_position.x; _bounds.top = slot->_position.y; _bounds.setWidth(slot->_width); _bounds.setHeight(slot->_height); setArea(slot->_width, slot->_height, intSurface.w, intSurface.h); break; default: { SpriteAsset *asset = _vm->_game->_scene._sprites[slot->_spritesIndex]; MSprite *frame = asset->getFrame(slot->_frameNumber - 1); int w = frame->w; int h = frame->h; if (slot->_segmentId == IMG_SPINNING_OBJECT) { _bounds.left = slot->_position.x; _bounds.top = slot->_position.y; } else { _bounds.left = slot->_position.x + w / 2; _bounds.top = slot->_position.y - h + 1; } setArea(w, h, intSurface.w, intSurface.h); break; } } }
void UISlots::draw(bool updateFlag, bool delFlag) { Scene &scene = _vm->_game->_scene; UserInterface &userInterface = scene._userInterface; DirtyArea *dirtyAreaPtr = nullptr; // Loop through setting up the dirty areas for (uint idx = 0; idx < size(); ++idx) { DirtyArea &dirtyArea = userInterface._dirtyAreas[idx]; UISlot &slot = (*this)[idx]; if (slot._flags >= IMG_STATIC) { dirtyArea._active = false; } else { dirtyArea.setUISlot(&slot); dirtyArea._textActive = true; if (slot._segmentId == IMG_SPINNING_OBJECT && slot._flags == IMG_FULL_UPDATE) { dirtyArea._active = false; dirtyAreaPtr = &dirtyArea; } } } userInterface._dirtyAreas.merge(1, userInterface._uiSlots.size()); if (dirtyAreaPtr) dirtyAreaPtr->_active = true; // Copy parts of the user interface background that need to be erased for (uint idx = 0; idx < size(); ++idx) { DirtyArea &dirtyArea = userInterface._dirtyAreas[idx]; UISlot &slot = (*this)[idx]; if (dirtyArea._active && dirtyArea._bounds.width() > 0 && dirtyArea._bounds.height() > 0 && slot._flags > -20) { if (slot._flags >= IMG_ERASE) { // Merge area userInterface.mergeFrom(&userInterface._surface, dirtyArea._bounds, Common::Point(dirtyArea._bounds.left, dirtyArea._bounds.top)); } else { // Copy area userInterface._surface.copyTo(&userInterface, dirtyArea._bounds, Common::Point(dirtyArea._bounds.left, dirtyArea._bounds.top)); } } } for (uint idx = 0; idx < size(); ++idx) { DirtyArea &dirtyArea = userInterface._dirtyAreas[idx]; UISlot &slot = (*this)[idx]; int slotType = slot._flags; if (slotType >= IMG_STATIC) { dirtyArea.setUISlot(&slot); if (!updateFlag) slotType &= ~0x40; dirtyArea._textActive = slotType > 0; slot._flags &= 0x40; } } userInterface._dirtyAreas.merge(1, userInterface._uiSlots.size()); for (uint idx = 0; idx < size(); ++idx) { DirtyArea *dirtyArea = &userInterface._dirtyAreas[idx]; UISlot &slot = (*this)[idx]; if (slot._flags >= IMG_STATIC && !(slot._flags & 0x40)) { if (!dirtyArea->_active) { do { dirtyArea = dirtyArea->_mergedArea; } while (!dirtyArea->_active); } if (dirtyArea->_textActive) { SpriteAsset *asset = scene._sprites[slot._spritesIndex]; // Get the frame details int frameNumber = ABS(slot._frameNumber); bool flipped = slot._frameNumber < 0; if (slot._segmentId == IMG_SPINNING_OBJECT) { MSprite *sprite = asset->getFrame(frameNumber - 1); sprite->copyTo(&userInterface, slot._position, sprite->getTransparencyIndex()); } else { MSprite *sprite = asset->getFrame(frameNumber - 1); if (flipped) { MSurface *spr = sprite->flipHorizontal(); userInterface.mergeFrom(spr, spr->getBounds(), slot._position, sprite->getTransparencyIndex()); delete spr; } else { userInterface.mergeFrom(sprite, sprite->getBounds(), slot._position, sprite->getTransparencyIndex()); } } } } } // Mark areas of the screen surface for updating if (updateFlag) { for (uint idx = 0; idx < size(); ++idx) { DirtyArea &dirtyArea = userInterface._dirtyAreas[idx]; if (dirtyArea._active && dirtyArea._textActive && dirtyArea._bounds.width() > 0 && dirtyArea._bounds.height() > 0) { // Flag area of screen as needing update Common::Rect r = dirtyArea._bounds; r.translate(0, scene._interfaceY); _vm->_screen.copyRectToScreen(r); } } } // Post-processing to remove slots no longer needed for (int idx = (int)size() - 1; idx >= 0; --idx) { UISlot &slot = (*this)[idx]; if (slot._flags < IMG_STATIC) { if (delFlag || updateFlag) remove_at(idx); else if (slot._flags > -20) slot._flags -= 20; } else { if (updateFlag) slot._flags &= ~0x40; else slot._flags |= 0x40; } } }
// TODO: calculate width and height, show text, show face if it exists // TODO: this has been tested with Dragonsphere only, there are some differences // in the sprites used in Phantom void MadsScene::showMADSV2TextBox(char *text, int x, int y, char *faceName) { int repeatX = 40; // FIXME: this is hardcoded int repeatY = 30; // FIXME: this is hardcoded int curX = x, curY = y; int topRightX = x; // TODO: this is probably not needed Common::SeekableReadStream *data = _vm->res()->get("box.ss"); SpriteAsset *boxSprites = new SpriteAsset(_vm, data, data->size(), "box.ss"); _vm->res()->toss("box.ss"); RGBList *palData = new RGBList(boxSprites->getColorCount(), boxSprites->getPalette(), true); _vm->_palette->addRange(palData); for (int i = 0; i < boxSprites->getCount(); i++) boxSprites->getFrame(i)->translate(palData); // sprite pixel translation // Top left corner boxSprites->getFrame(topLeft)->copyTo(_backgroundSurface, x, curY); curX += boxSprites->getFrame(topLeft)->width(); // Top line for (int i = 0; i < repeatX; i++) { boxSprites->getFrame(top)->copyTo(_backgroundSurface, curX, curY + 3); curX += boxSprites->getFrame(top)->width(); } // Top right corner boxSprites->getFrame(topRight)->copyTo(_backgroundSurface, curX, curY); topRightX = curX; // Top middle // FIXME: the transparent color for this is also the black border color boxSprites->getFrame(topMiddle)->copyTo(_backgroundSurface, x + (curX - x) / 2 - boxSprites->getFrame(topMiddle)->width() / 2, curY - 5, 167); curX = x; curY += boxSprites->getFrame(topLeft)->height(); // ----------------------------------------------------------------------------------------------- // Draw contents for (int i = 0; i < repeatY; i++) { for (int j = 0; j < repeatX; j++) { if (j == 0) { boxSprites->getFrame(left)->copyTo(_backgroundSurface, curX + 3, curY); curX += boxSprites->getFrame(left)->width(); } else if (j == repeatX - 1) { curX = topRightX - 2; boxSprites->getFrame(right)->copyTo(_backgroundSurface, curX + 3, curY + 1); } else { // TODO: the background of the contents follows a pattern which is not understood yet if (j % 2 == 0) { boxSprites->getFrame(filler1)->copyTo(_backgroundSurface, curX + 3, curY); curX += boxSprites->getFrame(filler1)->width(); } else { boxSprites->getFrame(filler2)->copyTo(_backgroundSurface, curX + 3, curY); curX += boxSprites->getFrame(filler2)->width(); } } } // for j curX = x; curY += boxSprites->getFrame(left)->height(); } // for i // ----------------------------------------------------------------------------------------------- curX = x; // Bottom left corner boxSprites->getFrame(bottomLeft)->copyTo(_backgroundSurface, curX, curY); curX += boxSprites->getFrame(bottomLeft)->width(); // Bottom line for (int i = 0; i < repeatX; i++) { boxSprites->getFrame(bottom)->copyTo(_backgroundSurface, curX, curY + 1); curX += boxSprites->getFrame(bottom)->width(); } // Bottom right corner boxSprites->getFrame(bottomRight)->copyTo(_backgroundSurface, curX, curY + 1); }
void Player::update() { Scene &scene = _vm->_game->_scene; if (_forceRefresh || (_visible != _priorVisible)) { int slotIndex = getSpriteSlot(); if (slotIndex >= 0) scene._spriteSlots[slotIndex]._flags = IMG_ERASE; int newDepth = 1; int yp = MIN(_playerPos.y, (int16)(MADS_SCENE_HEIGHT - 1)); for (int idx = 1; idx < DEPTH_BANDS_SIZE; ++idx) { if (scene._sceneInfo->_depthList[newDepth] >= yp) newDepth = idx + 1; } _currentDepth = newDepth; // Get the scale int newScale = getScale(_playerPos.y); _currentScale = MIN(newScale, 100); if (_visible) { // Player sprite needs to be rendered SpriteSlot slot; slot._flags = IMG_UPDATE; slot._seqIndex = PLAYER_SEQ_INDEX; slot._spritesIndex = _spritesStart + _spritesIdx; slot._frameNumber = _mirror ? -_frameNumber : _frameNumber; slot._position.x = _playerPos.x; slot._position.y = _playerPos.y + (_centerOfGravity * newScale) / 100; slot._depth = newDepth; slot._scale = newScale; if (slotIndex >= 0) { // Check if the existing player slot has the same details, and can be re-used SpriteSlot &s2 = scene._spriteSlots[slotIndex]; bool equal = (s2._seqIndex == slot._seqIndex) && (s2._spritesIndex == slot._spritesIndex) && (s2._frameNumber == slot._frameNumber) && (s2._position == slot._position) && (s2._depth == slot._depth) && (s2._scale == slot._scale); if (equal) // Undo the prior expiry of the player sprite s2._flags = IMG_STATIC; else slotIndex = -1; } if (slotIndex < 0) { // New slot needed, so allocate one and copy the slot data slotIndex = scene._spriteSlots.add(); scene._spriteSlots[slotIndex] = slot; } // If changing a scene, check to change the scene when the player // has moved off-screen if (_walkOffScreen) { SpriteAsset *asset = scene._sprites[slot._spritesIndex]; MSprite *frame = asset->getFrame(_frameNumber - 1); int xScale = frame->w * newScale / 200; int yScale = frame->h * newScale / 100; int playerX = slot._position.x; int playerY = slot._position.y; if ((playerX + xScale) < 0 || (playerX + xScale) >= MADS_SCREEN_WIDTH || playerY < 0 || (playerY + yScale) >= MADS_SCENE_HEIGHT) { scene._nextSceneId = _walkOffScreen; _walkOffScreen = 0; _walkAnywhere = false; } } } } _beenVisible |= _visible; _priorVisible = _visible; _forceRefresh = false; }