Initial commit
This commit is contained in:
557
engines/tinsel/object.cpp
Normal file
557
engines/tinsel/object.cpp
Normal file
@@ -0,0 +1,557 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 {
|
||||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
// list of all objects
|
||||
static OBJECT *objectList = nullptr;
|
||||
|
||||
// pointer to free object list
|
||||
static OBJECT *pFreeObjects = nullptr;
|
||||
|
||||
#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 = _vm->_handle->GetImage(hImg);
|
||||
|
||||
// set ani X
|
||||
*pAniX = (int16) pImg->anioffX;
|
||||
|
||||
// set ani Y
|
||||
*pAniY = (int16) pImg->anioffY;
|
||||
|
||||
if (flags & DMA_FLIPH) {
|
||||
// we are flipped horizontally
|
||||
|
||||
// set ani X = -ani X + width - 1
|
||||
*pAniX = -*pAniX + pImg->imgWidth - 1;
|
||||
}
|
||||
|
||||
if (flags & DMA_FLIPV) {
|
||||
// we are flipped vertically
|
||||
|
||||
// set ani Y = -ani Y + height - 1
|
||||
*pAniY = -*pAniY + (pImg->imgHeight & ~C16_FLAG_MASK) - 1;
|
||||
}
|
||||
|
||||
delete pImg;
|
||||
} 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 = _vm->_handle->GetImage(pInitTbl->hObjImg); // handle to image
|
||||
|
||||
if (TinselVersion != 3) {
|
||||
if (pImg->hImgPal) {
|
||||
// allocate a palette for this object
|
||||
pPalQ = AllocPalette(pImg->hImgPal);
|
||||
|
||||
// make sure palette allocated
|
||||
assert(pPalQ != NULL);
|
||||
}
|
||||
|
||||
// assign palette to object
|
||||
pObj->pPal = pPalQ;
|
||||
} else {
|
||||
if ((pImg->colorFlags & 0x0C) == 0) { // bits 0b1100 are used to select blending mode
|
||||
pObj->flags = pObj->flags & ~DMA_GHOST;
|
||||
} else {
|
||||
assert((pObj->flags & DMA_WNZ) != 0);
|
||||
pObj->flags |= DMA_GHOST;
|
||||
}
|
||||
pObj->isRLE = pImg->isRLE;
|
||||
pObj->colorFlags = pImg->colorFlags;
|
||||
}
|
||||
|
||||
// set objects size
|
||||
pObj->width = pImg->imgWidth;
|
||||
pObj->height = pImg->imgHeight & ~C16_FLAG_MASK;
|
||||
pObj->flags &= ~C16_FLAG_MASK;
|
||||
pObj->flags |= pImg->imgHeight & C16_FLAG_MASK;
|
||||
|
||||
// set objects bitmap definition
|
||||
pObj->hBits = pImg->hImgBits;
|
||||
|
||||
delete pImg;
|
||||
|
||||
// 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 = _vm->_handle->GetImage(hNewImg);
|
||||
|
||||
// setup new shape
|
||||
pAniObj->width = pNewImg->imgWidth;
|
||||
pAniObj->height = pNewImg->imgHeight & ~C16_FLAG_MASK;
|
||||
newflags &= ~C16_FLAG_MASK;
|
||||
newflags |= pNewImg->imgHeight & C16_FLAG_MASK;
|
||||
|
||||
// set objects bitmap definition
|
||||
pAniObj->hBits = pNewImg->hImgBits;
|
||||
|
||||
delete pNewImg;
|
||||
} 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
|
||||
Reference in New Issue
Block a user