/engines/tinsel/object.cpp
C++ | 541 lines | 223 code | 118 blank | 200 comment | 43 complexity | 22f33403c4736b454d2707a951993217 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, GPL-2.0
- /* 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 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"
- #include "tinsel/text.h"
- #include "tinsel/tinsel.h"
- #include "common/textconsole.h"
- #define OID_EFFECTS 0x2000 // generic special effects object id
- namespace Tinsel {
- // FIXME: Avoid non-const global vars
- // list of all objects
- static 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() {
- free(objectList);
- objectList= nullptr;
- }
- /**
- * Kills all objects and places them on the free list.
- */
- void KillAllObjects() {
- 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= nullptr;
- }
- #ifdef DEBUG
- /**
- * Shows the maximum number of objects used at once.
- */
- void ObjectStats() {
- debug("%i objects of %i used", maxObj, NUM_OBJECTS);
- }
- #endif
- /**
- * Allocate a object from the free list.
- */
- OBJECT *AllocObject() {
- 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
- pObj->reset();
- // 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;
- }
- bool isValidObject(OBJECT *obj) {
- return (obj >= objectList && obj <= objectList + NUM_OBJECTS - 1);
- }
- /**
- * 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= nullptr;
- }
- /**
- * 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 **pAnchor, *pObj; // object list traversal pointers
- // validate object pointer
- assert(isValidObject(pInsObj));
- for (pAnchor = pObjList, pObj = *pAnchor; pObj != NULL; pAnchor = &pObj->pNext, pObj = *pAnchor) {
- // 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 pAnchor and pObj
- pInsObj->pNext = pObj;
- *pAnchor = 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 **pAnchor, *pObj; // object list traversal pointers
- const Common::Rect rcScreen(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
- // validate object pointer
- assert(isValidObject(pDelObj));
- #ifdef DEBUG
- // one less object in use
- --numObj;
- assert(numObj >= 0);
- #endif
- for (pAnchor = pObjList, pObj = *pAnchor; pObj != NULL; pAnchor = &pObj->pNext, pObj = *pAnchor) {
- 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
- *pAnchor = 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
- // This can be triggered in Act 3 in DW1 while talking to the guard,
- // so this has been turned to a warning instead of an error
- warning("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;
- // 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_16(pImg->anioffX);
- // set ani Y
- *pAniY = (int16) FROM_16(pImg->anioffY);
- if (flags & DMA_FLIPH) {
- // we are flipped horizontally
- // set ani X = -ani X + width - 1
- *pAniX = -*pAniX + FROM_16(pImg->imgWidth) - 1;
- }
- if (flags & DMA_FLIPV) {
- // we are flipped vertically
- // set ani Y = -ani Y + height - 1
- *pAniY = -*pAniY + (FROM_16(pImg->imgHeight) & ~C16_FLAG_MASK) - 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(isValidObject(pObj));
- // 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);
- }
- /**
- * Initialize 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= nullptr; // palette queue pointer
- const IMAGE *pImg = (const IMAGE *)LockMem(pInitTbl->hObjImg); // handle to image
- if (pImg->hImgPal) {
- // allocate a palette for this object
- pPalQ = AllocPalette(FROM_32(pImg->hImgPal));
- // make sure palette allocated
- assert(pPalQ != NULL);
- }
- // assign palette to object
- pObj->pPal = pPalQ;
- // set objects size
- pObj->width = FROM_16(pImg->imgWidth);
- pObj->height = FROM_16(pImg->imgHeight) & ~C16_FLAG_MASK;
- pObj->flags &= ~C16_FLAG_MASK;
- pObj->flags |= FROM_16(pImg->imgHeight) & C16_FLAG_MASK;
- // set objects bitmap definition
- pObj->hBits = FROM_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(isValidObject(pAniObj));
- 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_16(pNewImg->imgWidth);
- pAniObj->height = FROM_16(pNewImg->imgHeight) & ~C16_FLAG_MASK;
- newflags &= ~C16_FLAG_MASK;
- newflags |= FROM_16(pNewImg->imgHeight) & C16_FLAG_MASK;
- // set objects bitmap definition
- pAniObj->hBits = FROM_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 color Which color offset from the above palette
- * @param width Width of rectangle
- * @param height Height of rectangle
- */
- OBJECT *RectangleObject(SCNHANDLE hPal, int color, int width, int height) {
- // template for initializing 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 color in the palette
- pRect->constant = color;
- // 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 initializing 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