diff options
Diffstat (limited to 'engines/tinsel/object.cpp')
-rw-r--r-- | engines/tinsel/object.cpp | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/engines/tinsel/object.cpp b/engines/tinsel/object.cpp new file mode 100644 index 0000000000..709fa4fad9 --- /dev/null +++ b/engines/tinsel/object.cpp @@ -0,0 +1,530 @@ +/* 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. + * + * $URL$ + * $Id$ + * + * This file contains the Object Manager code. + */ + +#include "tinsel/object.h" +#include "tinsel/background.h" +#include "tinsel/cliprect.h" // object clip rect defs +#include "tinsel/graphics.h" // low level interface +#include "tinsel/handle.h" + +#define OID_EFFECTS 0x2000 // generic special effects object id + +namespace Tinsel { + +/** screen clipping rectangle */ +static const Common::Rect rcScreen(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + +// list of all objects +OBJECT *objectList = 0; + +// pointer to free object list +static OBJECT *pFreeObjects = 0; + +#ifdef DEBUG +// diagnostic object counters +static int numObj = 0; +static int maxObj = 0; +#endif + +void FreeObjectList(void) { + if (objectList) { + free(objectList); + objectList = NULL; + } +} + +/** + * Kills all objects and places them on the free list. + */ + +void KillAllObjects(void) { + int i; + +#ifdef DEBUG + // clear number of objects in use + numObj = 0; +#endif + + if (objectList == NULL) { + // first time - allocate memory for object list + objectList = (OBJECT *)calloc(NUM_OBJECTS, sizeof(OBJECT)); + + // make sure memory allocated + if (objectList == NULL) { + error("Cannot allocate memory for object data"); + } + } + + // place first object on free list + pFreeObjects = objectList; + + // link all other objects after first + for (i = 1; i < NUM_OBJECTS; i++) { + objectList[i - 1].pNext = objectList + i; + } + + // null the last object + objectList[NUM_OBJECTS - 1].pNext = NULL; +} + + +#ifdef DEBUG +/** + * Shows the maximum number of objects used at once. + */ + +void ObjectStats(void) { + printf("%i objects of %i used.\n", maxObj, NUM_OBJECTS); +} +#endif + +/** + * Allocate a object from the free list. + */ +OBJECT *AllocObject(void) { + OBJECT *pObj = pFreeObjects; // get a free object + + // check for no free objects + assert(pObj != NULL); + + // a free object exists + + // get link to next free object + pFreeObjects = pObj->pNext; + + // clear out object + memset(pObj, 0, sizeof(OBJECT)); + + // set default drawing mode and set changed bit + pObj->flags = DMA_WNZ | DMA_CHANGED; + +#ifdef DEBUG + // one more object in use + if (++numObj > maxObj) + maxObj = numObj; +#endif + + // return new object + return pObj; +} + +/** + * Copy one object to another. + * @param pDest Destination object + * @param pSrc Source object + */ +void CopyObject(OBJECT *pDest, OBJECT *pSrc) { + // save previous dimensions etc. + Common::Rect rcSave = pDest->rcPrev; + + // make a copy + memcpy(pDest, pSrc, sizeof(OBJECT)); + + // restore previous dimensions etc. + pDest->rcPrev = rcSave; + + // set changed flag in destination + pDest->flags |= DMA_CHANGED; + + // null the links + pDest->pNext = pDest->pSlave = NULL; +} + +/** + * Inserts an object onto the specified object list. The object + * lists are sorted in Z Y order. + * @param pObjList List to insert object onto + * @param pInsObj Object to insert + */ + +void InsertObject(OBJECT *pObjList, OBJECT *pInsObj) { + OBJECT *pPrev, *pObj; // object list traversal pointers + + // validate object pointer + assert(pInsObj >= objectList && pInsObj <= objectList + NUM_OBJECTS - 1); + + for (pPrev = pObjList, pObj = pObjList->pNext; pObj != NULL; pPrev = pObj, pObj = pObj->pNext) { + // check Z order + if (pInsObj->zPos < pObj->zPos) { + // object Z is lower than list Z - insert here + break; + } else if (pInsObj->zPos == pObj->zPos) { + // Z values are the same - sort on Y + if (fracToDouble(pInsObj->yPos) <= fracToDouble(pObj->yPos)) { + // object Y is lower than or same as list Y - insert here + break; + } + } + } + + // insert obj between pPrev and pObj + pInsObj->pNext = pObj; + pPrev->pNext = pInsObj; +} + + +/** + * Deletes an object from the specified object list and places it + * on the free list. + * @param pObjList List to delete object from + * @param pDelObj Object to delete + */ +void DelObject(OBJECT *pObjList, OBJECT *pDelObj) { + OBJECT *pPrev, *pObj; // object list traversal pointers + + // validate object pointer + assert(pDelObj >= objectList && pDelObj <= objectList + NUM_OBJECTS - 1); + +#ifdef DEBUG + // one less object in use + --numObj; + assert(numObj >= 0); +#endif + + for (pPrev = pObjList, pObj = pObjList->pNext; pObj != NULL; pPrev = pObj, pObj = pObj->pNext) { + if (pObj == pDelObj) { + // found object to delete + + if (IntersectRectangle(pDelObj->rcPrev, pDelObj->rcPrev, rcScreen)) { + // allocate a clipping rect for objects previous pos + AddClipRect(pDelObj->rcPrev); + } + + // make PREV next = OBJ next - removes OBJ from list + pPrev->pNext = pObj->pNext; + + // place free list in OBJ next + pObj->pNext = pFreeObjects; + + // add OBJ to top of free list + pFreeObjects = pObj; + + // delete objects palette + if (pObj->pPal) + FreePalette(pObj->pPal); + + // quit + return; + } + } + + // if we get to here - object has not been found on the list + error("DelObject(): formally 'assert(0)!'"); +} + + +/** + * Sort the specified object list in Z Y order. + * @param pObjList List to sort + */ +void SortObjectList(OBJECT *pObjList) { + OBJECT *pPrev, *pObj; // object list traversal pointers + OBJECT head; // temporary head of list - because pObjList is not usually a OBJECT + + // put at head of list + head.pNext = pObjList->pNext; + + // set head of list dummy OBJ Z Y values to lowest possible + head.yPos = intToFrac(MIN_INT16); + head.zPos = MIN_INT; + + for (pPrev = &head, pObj = head.pNext; pObj != NULL; pPrev = pObj, pObj = pObj->pNext) { + // check Z order + if (pObj->zPos < pPrev->zPos) { + // object Z is lower than previous Z + + // remove object from list + pPrev->pNext = pObj->pNext; + + // re-insert object on list + InsertObject(pObjList, pObj); + + // back to beginning of list + pPrev = &head; + pObj = head.pNext; + } else if (pObj->zPos == pPrev->zPos) { + // Z values are the same - sort on Y + if (fracToDouble(pObj->yPos) < fracToDouble(pPrev->yPos)) { + // object Y is lower than previous Y + + // remove object from list + pPrev->pNext = pObj->pNext; + + // re-insert object on list + InsertObject(pObjList, pObj); + + // back to beginning of list + pPrev = &head; + pObj = head.pNext; + } + } + } +} + +/** + * Returns the animation offsets of a image, dependent on the + * images orientation flags. + * @param hImg Iimage to get animation offset of + * @param flags Images current flags + * @param pAniX Gets set to new X animation offset + * @param pAniY Gets set to new Y animation offset + */ +void GetAniOffset(SCNHANDLE hImg, int flags, int *pAniX, int *pAniY) { + if (hImg) { + const IMAGE *pImg = (const IMAGE *)LockMem(hImg); + + // set ani X + *pAniX = (int16) FROM_LE_16(pImg->anioffX); + + // set ani Y + *pAniY = (int16) FROM_LE_16(pImg->anioffY); + + if (flags & DMA_FLIPH) { + // we are flipped horizontally + + // set ani X = -ani X + width - 1 + *pAniX = -*pAniX + FROM_LE_16(pImg->imgWidth) - 1; + } + + if (flags & DMA_FLIPV) { + // we are flipped vertically + + // set ani Y = -ani Y + height - 1 + *pAniY = -*pAniY + FROM_LE_16(pImg->imgHeight) - 1; + } + } else + // null image + *pAniX = *pAniY = 0; +} + + +/** + * Returns the x,y position of an objects animation point. + * @param pObj Pointer to object + * @param pPosX Gets set to objects X animation position + * @param pPosY Gets set to objects Y animation position + */ +void GetAniPosition(OBJECT *pObj, int *pPosX, int *pPosY) { + // validate object pointer + assert(pObj >= objectList && pObj <= objectList + NUM_OBJECTS - 1); + + // get the animation offset of the object + GetAniOffset(pObj->hImg, pObj->flags, pPosX, pPosY); + + // from animation offset and objects position - determine objects animation point + *pPosX += fracToInt(pObj->xPos); + *pPosY += fracToInt(pObj->yPos); +} + +/** + * Initialise a object using a OBJ_INIT structure to supply parameters. + * @param pInitTbl Pointer to object initialisation table + */ +OBJECT *InitObject(const OBJ_INIT *pInitTbl) { + // allocate a new object + OBJECT *pObj = AllocObject(); + + // make sure object created + assert(pObj != NULL); + + // set objects shape + pObj->hImg = pInitTbl->hObjImg; + + // set objects ID + pObj->oid = pInitTbl->objID; + + // set objects flags + pObj->flags = DMA_CHANGED | pInitTbl->objFlags; + + // set objects Z position + pObj->zPos = pInitTbl->objZ; + + // get pointer to image + if (pInitTbl->hObjImg) { + int aniX, aniY; // objects animation offsets + PALQ *pPalQ; // palette queue pointer + const IMAGE *pImg = (const IMAGE *)LockMem(pInitTbl->hObjImg); // handle to image + + // allocate a palette for this object + pPalQ = AllocPalette(FROM_LE_32(pImg->hImgPal)); + + // make sure palette allocated + assert(pPalQ != NULL); + + // assign palette to object + pObj->pPal = pPalQ; + + // set objects size + pObj->width = FROM_LE_16(pImg->imgWidth); + pObj->height = FROM_LE_16(pImg->imgHeight); + + // set objects bitmap definition + pObj->hBits = FROM_LE_32(pImg->hImgBits); + + // get animation offset of object + GetAniOffset(pObj->hImg, pInitTbl->objFlags, &aniX, &aniY); + + // set objects X position - subtract ani offset + pObj->xPos = intToFrac(pInitTbl->objX - aniX); + + // set objects Y position - subtract ani offset + pObj->yPos = intToFrac(pInitTbl->objY - aniY); + } else { // no image handle - null image + + // set objects X position + pObj->xPos = intToFrac(pInitTbl->objX); + + // set objects Y position + pObj->yPos = intToFrac(pInitTbl->objY); + } + + // return new object + return pObj; +} + +/** + * Give a object a new image and new orientation flags. + * @param pAniObj Object to be updated + * @param newflags Objects new flags + * @param hNewImg Objects new image + */ +void AnimateObjectFlags(OBJECT *pAniObj, int newflags, SCNHANDLE hNewImg) { + // validate object pointer + assert(pAniObj >= objectList && pAniObj <= objectList + NUM_OBJECTS - 1); + + if (pAniObj->hImg != hNewImg + || (pAniObj->flags & DMA_HARDFLAGS) != (newflags & DMA_HARDFLAGS)) { + // something has changed + + int oldAniX, oldAniY; // objects old animation offsets + int newAniX, newAniY; // objects new animation offsets + + // get objects old animation offsets + GetAniOffset(pAniObj->hImg, pAniObj->flags, &oldAniX, &oldAniY); + + // get objects new animation offsets + GetAniOffset(hNewImg, newflags, &newAniX, &newAniY); + + if (hNewImg) { + // get pointer to image + const IMAGE *pNewImg = (IMAGE *)LockMem(hNewImg); + + // setup new shape + pAniObj->width = FROM_LE_16(pNewImg->imgWidth); + pAniObj->height = FROM_LE_16(pNewImg->imgHeight); + + // set objects bitmap definition + pAniObj->hBits = FROM_LE_32(pNewImg->hImgBits); + } else { // null image + pAniObj->width = 0; + pAniObj->height = 0; + pAniObj->hBits = 0; + } + + // set objects flags and signal a change + pAniObj->flags = newflags | DMA_CHANGED; + + // set objects image + pAniObj->hImg = hNewImg; + + // adjust objects position - subtract new from old for difference + pAniObj->xPos += intToFrac(oldAniX - newAniX); + pAniObj->yPos += intToFrac(oldAniY - newAniY); + } +} + +/** + * Give an object a new image. + * @param pAniObj Object to animate + * @param hNewImg Objects new image + */ +void AnimateObject(OBJECT *pAniObj, SCNHANDLE hNewImg) { + // dont change the objects flags + AnimateObjectFlags(pAniObj, pAniObj->flags, hNewImg); +} + +/** + * Creates a rectangle object of the given dimensions and returns + * a pointer to the object. + * @param hPal Palette for the rectangle object + * @param colour Which colour offset from the above palette + * @param width Width of rectangle + * @param height Height of rectangle + */ +OBJECT *RectangleObject(SCNHANDLE hPal, int colour, int width, int height) { + // template for initialising the rectangle object + static const OBJ_INIT rectObj = {0, DMA_CONST, OID_EFFECTS, 0, 0, 0}; + PALQ *pPalQ; // palette queue pointer + + // allocate and init a new object + OBJECT *pRect = InitObject(&rectObj); + + // allocate a palette for this object + pPalQ = AllocPalette(hPal); + + // make sure palette allocated + assert(pPalQ != NULL); + + // assign palette to object + pRect->pPal = pPalQ; + + // set colour in the palette + pRect->constant = colour; + + // set rectangle width + pRect->width = width; + + // set rectangle height + pRect->height = height; + + // return pointer to rectangle object + return pRect; +} + +/** + * Creates a translucent rectangle object of the given dimensions + * and returns a pointer to the object. + * @param width Width of rectangle + * @param height Height of rectangle + */ +OBJECT *TranslucentObject(int width, int height) { + // template for initialising the rectangle object + static const OBJ_INIT rectObj = {0, DMA_TRANS, OID_EFFECTS, 0, 0, 0}; + + // allocate and init a new object + OBJECT *pRect = InitObject(&rectObj); + + // set rectangle width + pRect->width = width; + + // set rectangle height + pRect->height = height; + + // return pointer to rectangle object + return pRect; +} + +} // end of namespace Tinsel |