/* 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. * * This file contains utilities to handle multi-part objects. */ #include "tinsel/multiobj.h" #include "tinsel/handle.h" #include "tinsel/object.h" #include "tinsel/tinsel.h" namespace Tinsel { /** * Initialize a multi-part object using a list of images to init * each object piece. One object is created for each image in the list. * All objects are given the same palette as the first image. A pointer * to the first (master) object created is returned. * @param pInitTbl Pointer to multi-object initialisation table */ OBJECT *MultiInitObject(const MULTI_INIT *pInitTbl) { OBJ_INIT obj_init; // object init table OBJECT *pFirst, *pObj; // object pointers FRAME *pFrame; // list of images for the multi-part object if (FROM_32(pInitTbl->hMulFrame)) { // we have a frame handle pFrame = (FRAME *)LockMem(FROM_32(pInitTbl->hMulFrame)); obj_init.hObjImg = READ_32(pFrame); // first objects shape } else { // this must be a animation list for a NULL object pFrame = NULL; obj_init.hObjImg = 0; // first objects shape } // init the object init table obj_init.objFlags = (int)FROM_32(pInitTbl->mulFlags); // all objects have same flags obj_init.objID = (int)FROM_32(pInitTbl->mulID); // all objects have same ID obj_init.objX = (int)FROM_32(pInitTbl->mulX); // all objects have same X ani pos obj_init.objY = (int)FROM_32(pInitTbl->mulY); // all objects have same Y ani pos obj_init.objZ = (int)FROM_32(pInitTbl->mulZ); // all objects have same Z pos // create and init the first object pObj = pFirst = InitObject(&obj_init); if (pFrame) { // if we have any animation frames pFrame++; while (READ_32(pFrame) != 0) { // set next objects shape obj_init.hObjImg = READ_32(pFrame); // create next object and link to previous pObj = pObj->pSlave = InitObject(&obj_init); pFrame++; } } // null end of list for final object pObj->pSlave = NULL; // return master object return pFirst; } /** * Inserts the multi-part object onto the specified object list. * @param pObjList List to insert multi-part object onto * @param pInsObj Head of multi-part object to insert */ void MultiInsertObject(OBJECT **pObjList, OBJECT *pInsObj) { // validate object pointer assert(isValidObject(pInsObj)); // for all the objects that make up this multi-part do { // add next part to the specified list InsertObject(pObjList, pInsObj); // next obj in list pInsObj = pInsObj->pSlave; } while (pInsObj != NULL); } /** * Deletes all the pieces of a multi-part object from the * specified object list. * @param pObjList List to delete multi-part object from * @param pMultiObj Multi-part object to be deleted */ void MultiDeleteObject(OBJECT **pObjList, OBJECT *pMultiObj) { // validate object pointer assert(isValidObject(pMultiObj)); // for all the objects that make up this multi-part do { // delete object DelObject(pObjList, pMultiObj); // next obj in list pMultiObj = pMultiObj->pSlave; } while (pMultiObj != NULL); } /** * Hides a multi-part object by giving each object a "NullImage" * image pointer. * @param pMultiObj Multi-part object to be hidden */ void MultiHideObject(OBJECT *pMultiObj) { // validate object pointer assert(isValidObject(pMultiObj)); // set master shape to null animation frame pMultiObj->hShape = 0; // change all objects MultiReshape(pMultiObj); } /** * Horizontally flip a multi-part object. * @param pFlipObj Head of multi-part object to flip */ void MultiHorizontalFlip(OBJECT *pFlipObj) { // validate object pointer assert(isValidObject(pFlipObj)); // for all the objects that make up this multi-part do { // horizontally flip the next part AnimateObjectFlags(pFlipObj, pFlipObj->flags ^ DMA_FLIPH, pFlipObj->hImg); // next obj in list pFlipObj = pFlipObj->pSlave; } while (pFlipObj != NULL); } /** * Vertically flip a multi-part object. * @param pFlipObj Head of multi-part object to flip */ void MultiVerticalFlip(OBJECT *pFlipObj) { // validate object pointer assert(isValidObject(pFlipObj)); // for all the objects that make up this multi-part do { // vertically flip the next part AnimateObjectFlags(pFlipObj, pFlipObj->flags ^ DMA_FLIPV, pFlipObj->hImg); // next obj in list pFlipObj = pFlipObj->pSlave; } while (pFlipObj != NULL); } /** * Adjusts the coordinates of a multi-part object. The adjustments * take into account the orientation of the object. * @param pMultiObj Multi-part object to be adjusted * @param deltaX X adjustment * @param deltaY Y adjustment */ void MultiAdjustXY(OBJECT *pMultiObj, int deltaX, int deltaY) { // validate object pointer assert(isValidObject(pMultiObj)); if (deltaX == 0 && deltaY == 0) return; // ignore no change if (!TinselV2) { // *** This may be wrong!!! if (pMultiObj->flags & DMA_FLIPH) { // image is flipped horizontally - flip the x direction deltaX = -deltaX; } if (pMultiObj->flags & DMA_FLIPV) { // image is flipped vertically - flip the y direction deltaY = -deltaY; } } // for all the objects that make up this multi-part do { // signal a change in the object pMultiObj->flags |= DMA_CHANGED; // adjust the x position pMultiObj->xPos += intToFrac(deltaX); // adjust the y position pMultiObj->yPos += intToFrac(deltaY); // next obj in list pMultiObj = pMultiObj->pSlave; } while (pMultiObj != NULL); } /** * Moves all the pieces of a multi-part object by the specified * amount. Does not take into account the objects orientation. * @param pMultiObj Multi-part object to be adjusted * @param deltaX X movement * @param deltaY Y movement */ void MultiMoveRelXY(OBJECT *pMultiObj, int deltaX, int deltaY) { // validate object pointer assert(isValidObject(pMultiObj)); if (deltaX == 0 && deltaY == 0) return; // ignore no change // for all the objects that make up this multi-part do { // signal a change in the object pMultiObj->flags |= DMA_CHANGED; // adjust the x position pMultiObj->xPos += intToFrac(deltaX); // adjust the y position pMultiObj->yPos += intToFrac(deltaY); // next obj in list pMultiObj = pMultiObj->pSlave; } while (pMultiObj != NULL); } /** * Sets the x & y anim position of all pieces of a multi-part object. * @param pMultiObj Multi-part object whose position is to be changed * @param newAniX New x animation position * @param newAniY New y animation position */ void MultiSetAniXY(OBJECT *pMultiObj, int newAniX, int newAniY) { int curAniX, curAniY; // objects current animation position // validate object pointer assert(isValidObject(pMultiObj)); // get master objects current animation position GetAniPosition(pMultiObj, &curAniX, &curAniY); // calc difference between current and new positions newAniX -= curAniX; newAniY -= curAniY; // move all pieces by the difference MultiMoveRelXY(pMultiObj, newAniX, newAniY); } /** * Sets the x anim position of all pieces of a multi-part object. * @param pMultiObj Multi-part object whose x position is to be changed * @param newAniX New x animation position */ void MultiSetAniX(OBJECT *pMultiObj, int newAniX) { int curAniX, curAniY; // objects current animation position // validate object pointer assert(isValidObject(pMultiObj)); // get master objects current animation position GetAniPosition(pMultiObj, &curAniX, &curAniY); // calc x difference between current and new positions newAniX -= curAniX; curAniY = 0; // move all pieces by the difference MultiMoveRelXY(pMultiObj, newAniX, curAniY); } /** * Sets the y anim position of all pieces of a multi-part object. * @param pMultiObj Multi-part object whose x position is to be changed * @param newAniX New y animation position */ void MultiSetAniY(OBJECT *pMultiObj, int newAniY) { int curAniX, curAniY; // objects current animation position // validate object pointer assert(isValidObject(pMultiObj)); // get master objects current animation position GetAniPosition(pMultiObj, &curAniX, &curAniY); // calc y difference between current and new positions curAniX = 0; newAniY -= curAniY; // move all pieces by the difference MultiMoveRelXY(pMultiObj, curAniX, newAniY); } /** * Sets the Z position of all pieces of a multi-part object. * @param pMultiObj Multi-part object to be adjusted * @param newZ New Z order */ void MultiSetZPosition(OBJECT *pMultiObj, int newZ) { // validate object pointer assert(isValidObject(pMultiObj)); // for all the objects that make up this multi-part do { // signal a change in the object pMultiObj->flags |= DMA_CHANGED; // set the new z position pMultiObj->zPos = newZ; // next obj in list pMultiObj = pMultiObj->pSlave; } while (pMultiObj != NULL); } /** * Reshape a multi-part object. * @param pMultiObj Multi-part object to re-shape */ void MultiReshape(OBJECT *pMultiObj) { SCNHANDLE hFrame; // validate object pointer assert(isValidObject(pMultiObj)); // get objects current anim frame hFrame = pMultiObj->hShape; if (hFrame != 0 && hFrame != pMultiObj->hMirror) { // a valid shape frame which is different from previous // get pointer to frame const FRAME *pFrame = (const FRAME *)LockMem(hFrame); // update previous pMultiObj->hMirror = hFrame; while (READ_32(pFrame) != 0 && pMultiObj != NULL) { // a normal image - update the current object with this image AnimateObject(pMultiObj, READ_32(pFrame)); // move to next image for this frame pFrame++; // move to next part of object pMultiObj = pMultiObj->pSlave; } // null the remaining object parts while (pMultiObj != NULL) { // set a null image for this object part AnimateObject(pMultiObj, 0); // move to next part of object pMultiObj = pMultiObj->pSlave; } } else if (hFrame == 0) { // update previous pMultiObj->hMirror = hFrame; // null all the object parts while (pMultiObj != NULL) { // set a null image for this object part AnimateObject(pMultiObj, 0); // move to next part of object pMultiObj = pMultiObj->pSlave; } } } /** * Returns the left-most point of a multi-part object. * @param pMulti Multi-part object */ int MultiLeftmost(OBJECT *pMulti) { int left; // validate object pointer assert(isValidObject(pMulti)); // init leftmost point to first object left = fracToInt(pMulti->xPos); // for all the objects in this multi while ((pMulti = pMulti->pSlave) != NULL) { if (pMulti->hImg != 0) { // non null object part if (fracToInt(pMulti->xPos) < left) // this object is further left left = fracToInt(pMulti->xPos); } } // return left-most point return left; } /** * Returns the right-most point of a multi-part object. * @param pMulti Multi-part object */ int MultiRightmost(OBJECT *pMulti) { int right; // validate object pointer assert(isValidObject(pMulti)); // init right-most point to first object right = fracToInt(pMulti->xPos) + pMulti->width; // for all the objects in this multi while ((pMulti = pMulti->pSlave) != NULL) { if (pMulti->hImg != 0) { // non null object part if (fracToInt(pMulti->xPos) + pMulti->width > right) // this object is further right right = fracToInt(pMulti->xPos) + pMulti->width; } } // return right-most point return right - 1; } /** * Returns the highest point of a multi-part object. * @param pMulti Multi-part object */ int MultiHighest(OBJECT *pMulti) { int highest; // validate object pointer assert(isValidObject(pMulti)); // init highest point to first object highest = fracToInt(pMulti->yPos); // for all the objects in this multi while ((pMulti = pMulti->pSlave) != NULL) { if (pMulti->hImg != 0) { // non null object part if (fracToInt(pMulti->yPos) < highest) // this object is higher highest = fracToInt(pMulti->yPos); } } // return highest point return highest; } /** * Returns the lowest point of a multi-part object. * @param pMulti Multi-part object */ int MultiLowest(OBJECT *pMulti) { int lowest; // validate object pointer assert(isValidObject(pMulti)); // init lowest point to first object lowest = fracToInt(pMulti->yPos) + pMulti->height; // for all the objects in this multi while ((pMulti = pMulti->pSlave) != NULL) { if (pMulti->hImg != 0) { // non null object part if (fracToInt(pMulti->yPos) + pMulti->height > lowest) // this object is lower lowest = fracToInt(pMulti->yPos) + pMulti->height; } } // return lowest point return lowest - 1; } /** * Returns TRUE if the object currently has an image. * @param pMulti Multi-part object */ bool MultiHasShape(POBJECT pMulti) { return (pMulti->hShape != 0); } /** * Bodge for text on movies. Makes sure it appears for it's lifetime. * @param pMultiObj Multi-part object to be adjusted */ void MultiForceRedraw(POBJECT pMultiObj) { // validate object pointer assert(isValidObject(pMultiObj)); // for all the objects that make up this multi-part do { // signal a change in the object pMultiObj->flags |= DMA_CHANGED; // next obj in list pMultiObj = pMultiObj->pSlave; } while (pMultiObj != NULL); } } // End of namespace Tinsel