1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 * This file contains the Object Manager code.
22 */
23
24 #include "tinsel/object.h"
25 #include "tinsel/background.h"
26 #include "tinsel/cliprect.h" // object clip rect defs
27 #include "tinsel/graphics.h" // low level interface
28 #include "tinsel/handle.h"
29 #include "tinsel/text.h"
30 #include "tinsel/tinsel.h"
31
32 #include "common/textconsole.h"
33
34 #define OID_EFFECTS 0x2000 // generic special effects object id
35
36 namespace Tinsel {
37
38 // FIXME: Avoid non-const global vars
39
40 // list of all objects
41 static OBJECT *objectList = 0;
42
43 // pointer to free object list
44 static OBJECT *pFreeObjects = 0;
45
46 #ifdef DEBUG
47 // diagnostic object counters
48 static int numObj = 0;
49 static int maxObj = 0;
50 #endif
51
FreeObjectList()52 void FreeObjectList() {
53 free(objectList);
54 objectList = NULL;
55 }
56
57 /**
58 * Kills all objects and places them on the free list.
59 */
60
KillAllObjects()61 void KillAllObjects() {
62 int i;
63
64 #ifdef DEBUG
65 // clear number of objects in use
66 numObj = 0;
67 #endif
68
69 if (objectList == NULL) {
70 // first time - allocate memory for object list
71 objectList = (OBJECT *)calloc(NUM_OBJECTS, sizeof(OBJECT));
72
73 // make sure memory allocated
74 if (objectList == NULL) {
75 error("Cannot allocate memory for object data");
76 }
77 }
78
79 // place first object on free list
80 pFreeObjects = objectList;
81
82 // link all other objects after first
83 for (i = 1; i < NUM_OBJECTS; i++) {
84 objectList[i - 1].pNext = objectList + i;
85 }
86
87 // null the last object
88 objectList[NUM_OBJECTS - 1].pNext = NULL;
89 }
90
91
92 #ifdef DEBUG
93 /**
94 * Shows the maximum number of objects used at once.
95 */
96
ObjectStats()97 void ObjectStats() {
98 debug("%i objects of %i used", maxObj, NUM_OBJECTS);
99 }
100 #endif
101
102 /**
103 * Allocate a object from the free list.
104 */
AllocObject()105 OBJECT *AllocObject() {
106 OBJECT *pObj = pFreeObjects; // get a free object
107
108 // check for no free objects
109 assert(pObj != NULL);
110
111 // a free object exists
112
113 // get link to next free object
114 pFreeObjects = pObj->pNext;
115
116 // clear out object
117 pObj->reset();
118
119 // set default drawing mode and set changed bit
120 pObj->flags = DMA_WNZ | DMA_CHANGED;
121
122 #ifdef DEBUG
123 // one more object in use
124 if (++numObj > maxObj)
125 maxObj = numObj;
126 #endif
127
128 // return new object
129 return pObj;
130 }
131
isValidObject(OBJECT * obj)132 bool isValidObject(OBJECT *obj) {
133 return (obj >= objectList && obj <= objectList + NUM_OBJECTS - 1);
134 }
135
136 /**
137 * Copy one object to another.
138 * @param pDest Destination object
139 * @param pSrc Source object
140 */
CopyObject(OBJECT * pDest,OBJECT * pSrc)141 void CopyObject(OBJECT *pDest, OBJECT *pSrc) {
142 // save previous dimensions etc.
143 Common::Rect rcSave = pDest->rcPrev;
144
145 // make a copy
146 memcpy(pDest, pSrc, sizeof(OBJECT));
147
148 // restore previous dimensions etc.
149 pDest->rcPrev = rcSave;
150
151 // set changed flag in destination
152 pDest->flags |= DMA_CHANGED;
153
154 // null the links
155 pDest->pNext = pDest->pSlave = NULL;
156 }
157
158 /**
159 * Inserts an object onto the specified object list. The object
160 * lists are sorted in Z Y order.
161 * @param pObjList List to insert object onto
162 * @param pInsObj Object to insert
163 */
164
InsertObject(OBJECT ** pObjList,OBJECT * pInsObj)165 void InsertObject(OBJECT **pObjList, OBJECT *pInsObj) {
166 OBJECT **pAnchor, *pObj; // object list traversal pointers
167
168 // validate object pointer
169 assert(isValidObject(pInsObj));
170
171 for (pAnchor = pObjList, pObj = *pAnchor; pObj != NULL; pAnchor = &pObj->pNext, pObj = *pAnchor) {
172 // check Z order
173 if (pInsObj->zPos < pObj->zPos) {
174 // object Z is lower than list Z - insert here
175 break;
176 } else if (pInsObj->zPos == pObj->zPos) {
177 // Z values are the same - sort on Y
178 if (fracToDouble(pInsObj->yPos) <= fracToDouble(pObj->yPos)) {
179 // object Y is lower than or same as list Y - insert here
180 break;
181 }
182 }
183 }
184
185 // insert obj between pAnchor and pObj
186 pInsObj->pNext = pObj;
187 *pAnchor = pInsObj;
188 }
189
190
191 /**
192 * Deletes an object from the specified object list and places it
193 * on the free list.
194 * @param pObjList List to delete object from
195 * @param pDelObj Object to delete
196 */
DelObject(OBJECT ** pObjList,OBJECT * pDelObj)197 void DelObject(OBJECT **pObjList, OBJECT *pDelObj) {
198 OBJECT **pAnchor, *pObj; // object list traversal pointers
199 const Common::Rect rcScreen(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
200
201 // validate object pointer
202 assert(isValidObject(pDelObj));
203
204 #ifdef DEBUG
205 // one less object in use
206 --numObj;
207 assert(numObj >= 0);
208 #endif
209
210 for (pAnchor = pObjList, pObj = *pAnchor; pObj != NULL; pAnchor = &pObj->pNext, pObj = *pAnchor) {
211 if (pObj == pDelObj) {
212 // found object to delete
213
214 if (IntersectRectangle(pDelObj->rcPrev, pDelObj->rcPrev, rcScreen)) {
215 // allocate a clipping rect for objects previous pos
216 AddClipRect(pDelObj->rcPrev);
217 }
218
219 // make PREV next = OBJ next - removes OBJ from list
220 *pAnchor = pObj->pNext;
221
222 // place free list in OBJ next
223 pObj->pNext = pFreeObjects;
224
225 // add OBJ to top of free list
226 pFreeObjects = pObj;
227
228 // delete objects palette
229 if (pObj->pPal)
230 FreePalette(pObj->pPal);
231
232 // quit
233 return;
234 }
235 }
236
237 // if we get to here - object has not been found on the list
238 // This can be triggered in Act 3 in DW1 while talking to the guard,
239 // so this has been turned to a warning instead of an error
240 warning("DelObject(): formally 'assert(0)!'");
241 }
242
243
244 /**
245 * Sort the specified object list in Z Y order.
246 * @param pObjList List to sort
247 */
SortObjectList(OBJECT ** pObjList)248 void SortObjectList(OBJECT **pObjList) {
249 OBJECT *pPrev, *pObj; // object list traversal pointers
250 OBJECT head; // temporary head of list - because pObjList is not usually a OBJECT
251
252 // put at head of list
253 head.pNext = *pObjList;
254
255 // set head of list dummy OBJ Z Y values to lowest possible
256 head.yPos = intToFrac(MIN_INT16);
257 head.zPos = MIN_INT;
258
259 for (pPrev = &head, pObj = head.pNext; pObj != NULL; pPrev = pObj, pObj = pObj->pNext) {
260 // check Z order
261 if (pObj->zPos < pPrev->zPos) {
262 // object Z is lower than previous Z
263
264 // remove object from list
265 pPrev->pNext = pObj->pNext;
266
267 // re-insert object on list
268 InsertObject(pObjList, pObj);
269
270 // back to beginning of list
271 pPrev = &head;
272 pObj = head.pNext;
273 } else if (pObj->zPos == pPrev->zPos) {
274 // Z values are the same - sort on Y
275 if (fracToDouble(pObj->yPos) < fracToDouble(pPrev->yPos)) {
276 // object Y is lower than previous Y
277
278 // remove object from list
279 pPrev->pNext = pObj->pNext;
280
281 // re-insert object on list
282 InsertObject(pObjList, pObj);
283
284 // back to beginning of list
285 pPrev = &head;
286 pObj = head.pNext;
287 }
288 }
289 }
290 }
291
292 /**
293 * Returns the animation offsets of a image, dependent on the
294 * images orientation flags.
295 * @param hImg Iimage to get animation offset of
296 * @param flags Images current flags
297 * @param pAniX Gets set to new X animation offset
298 * @param pAniY Gets set to new Y animation offset
299 */
GetAniOffset(SCNHANDLE hImg,int flags,int * pAniX,int * pAniY)300 void GetAniOffset(SCNHANDLE hImg, int flags, int *pAniX, int *pAniY) {
301 if (hImg) {
302 const IMAGE *pImg = (const IMAGE *)LockMem(hImg);
303
304 // set ani X
305 *pAniX = (int16) FROM_16(pImg->anioffX);
306
307 // set ani Y
308 *pAniY = (int16) FROM_16(pImg->anioffY);
309
310 if (flags & DMA_FLIPH) {
311 // we are flipped horizontally
312
313 // set ani X = -ani X + width - 1
314 *pAniX = -*pAniX + FROM_16(pImg->imgWidth) - 1;
315 }
316
317 if (flags & DMA_FLIPV) {
318 // we are flipped vertically
319
320 // set ani Y = -ani Y + height - 1
321 *pAniY = -*pAniY + (FROM_16(pImg->imgHeight) & ~C16_FLAG_MASK) - 1;
322 }
323 } else
324 // null image
325 *pAniX = *pAniY = 0;
326 }
327
328
329 /**
330 * Returns the x,y position of an objects animation point.
331 * @param pObj Pointer to object
332 * @param pPosX Gets set to objects X animation position
333 * @param pPosY Gets set to objects Y animation position
334 */
GetAniPosition(OBJECT * pObj,int * pPosX,int * pPosY)335 void GetAniPosition(OBJECT *pObj, int *pPosX, int *pPosY) {
336 // validate object pointer
337 assert(isValidObject(pObj));
338
339 // get the animation offset of the object
340 GetAniOffset(pObj->hImg, pObj->flags, pPosX, pPosY);
341
342 // from animation offset and objects position - determine objects animation point
343 *pPosX += fracToInt(pObj->xPos);
344 *pPosY += fracToInt(pObj->yPos);
345 }
346
347 /**
348 * Initialize a object using a OBJ_INIT structure to supply parameters.
349 * @param pInitTbl Pointer to object initialisation table
350 */
InitObject(const OBJ_INIT * pInitTbl)351 OBJECT *InitObject(const OBJ_INIT *pInitTbl) {
352 // allocate a new object
353 OBJECT *pObj = AllocObject();
354
355 // make sure object created
356 assert(pObj != NULL);
357
358 // set objects shape
359 pObj->hImg = pInitTbl->hObjImg;
360
361 // set objects ID
362 pObj->oid = pInitTbl->objID;
363
364 // set objects flags
365 pObj->flags = DMA_CHANGED | pInitTbl->objFlags;
366
367 // set objects Z position
368 pObj->zPos = pInitTbl->objZ;
369
370 // get pointer to image
371 if (pInitTbl->hObjImg) {
372 int aniX, aniY; // objects animation offsets
373 PALQ *pPalQ = NULL; // palette queue pointer
374 const IMAGE *pImg = (const IMAGE *)LockMem(pInitTbl->hObjImg); // handle to image
375
376 if (pImg->hImgPal) {
377 // allocate a palette for this object
378 pPalQ = AllocPalette(FROM_32(pImg->hImgPal));
379
380 // make sure palette allocated
381 assert(pPalQ != NULL);
382 }
383
384 // assign palette to object
385 pObj->pPal = pPalQ;
386
387 // set objects size
388 pObj->width = FROM_16(pImg->imgWidth);
389 pObj->height = FROM_16(pImg->imgHeight) & ~C16_FLAG_MASK;
390 pObj->flags &= ~C16_FLAG_MASK;
391 pObj->flags |= FROM_16(pImg->imgHeight) & C16_FLAG_MASK;
392
393 // set objects bitmap definition
394 pObj->hBits = FROM_32(pImg->hImgBits);
395
396 // get animation offset of object
397 GetAniOffset(pObj->hImg, pInitTbl->objFlags, &aniX, &aniY);
398
399 // set objects X position - subtract ani offset
400 pObj->xPos = intToFrac(pInitTbl->objX - aniX);
401
402 // set objects Y position - subtract ani offset
403 pObj->yPos = intToFrac(pInitTbl->objY - aniY);
404 } else { // no image handle - null image
405
406 // set objects X position
407 pObj->xPos = intToFrac(pInitTbl->objX);
408
409 // set objects Y position
410 pObj->yPos = intToFrac(pInitTbl->objY);
411 }
412
413 // return new object
414 return pObj;
415 }
416
417 /**
418 * Give a object a new image and new orientation flags.
419 * @param pAniObj Object to be updated
420 * @param newflags Objects new flags
421 * @param hNewImg Objects new image
422 */
AnimateObjectFlags(OBJECT * pAniObj,int newflags,SCNHANDLE hNewImg)423 void AnimateObjectFlags(OBJECT *pAniObj, int newflags, SCNHANDLE hNewImg) {
424 // validate object pointer
425 assert(isValidObject(pAniObj));
426
427 if (pAniObj->hImg != hNewImg
428 || (pAniObj->flags & DMA_HARDFLAGS) != (newflags & DMA_HARDFLAGS)) {
429 // something has changed
430
431 int oldAniX, oldAniY; // objects old animation offsets
432 int newAniX, newAniY; // objects new animation offsets
433
434 // get objects old animation offsets
435 GetAniOffset(pAniObj->hImg, pAniObj->flags, &oldAniX, &oldAniY);
436
437 // get objects new animation offsets
438 GetAniOffset(hNewImg, newflags, &newAniX, &newAniY);
439
440 if (hNewImg) {
441 // get pointer to image
442 const IMAGE *pNewImg = (IMAGE *)LockMem(hNewImg);
443
444 // setup new shape
445 pAniObj->width = FROM_16(pNewImg->imgWidth);
446 pAniObj->height = FROM_16(pNewImg->imgHeight) & ~C16_FLAG_MASK;
447 newflags &= ~C16_FLAG_MASK;
448 newflags |= FROM_16(pNewImg->imgHeight) & C16_FLAG_MASK;
449
450 // set objects bitmap definition
451 pAniObj->hBits = FROM_32(pNewImg->hImgBits);
452 } else { // null image
453 pAniObj->width = 0;
454 pAniObj->height = 0;
455 pAniObj->hBits = 0;
456 }
457
458 // set objects flags and signal a change
459 pAniObj->flags = newflags | DMA_CHANGED;
460
461 // set objects image
462 pAniObj->hImg = hNewImg;
463
464 // adjust objects position - subtract new from old for difference
465 pAniObj->xPos += intToFrac(oldAniX - newAniX);
466 pAniObj->yPos += intToFrac(oldAniY - newAniY);
467 }
468 }
469
470 /**
471 * Give an object a new image.
472 * @param pAniObj Object to animate
473 * @param hNewImg Objects new image
474 */
AnimateObject(OBJECT * pAniObj,SCNHANDLE hNewImg)475 void AnimateObject(OBJECT *pAniObj, SCNHANDLE hNewImg) {
476 // dont change the objects flags
477 AnimateObjectFlags(pAniObj, pAniObj->flags, hNewImg);
478 }
479
480 /**
481 * Creates a rectangle object of the given dimensions and returns
482 * a pointer to the object.
483 * @param hPal Palette for the rectangle object
484 * @param color Which color offset from the above palette
485 * @param width Width of rectangle
486 * @param height Height of rectangle
487 */
RectangleObject(SCNHANDLE hPal,int color,int width,int height)488 OBJECT *RectangleObject(SCNHANDLE hPal, int color, int width, int height) {
489 // template for initializing the rectangle object
490 static const OBJ_INIT rectObj = {0, DMA_CONST, OID_EFFECTS, 0, 0, 0};
491 PALQ *pPalQ; // palette queue pointer
492
493 // allocate and init a new object
494 OBJECT *pRect = InitObject(&rectObj);
495
496 // allocate a palette for this object
497 pPalQ = AllocPalette(hPal);
498
499 // make sure palette allocated
500 assert(pPalQ != NULL);
501
502 // assign palette to object
503 pRect->pPal = pPalQ;
504
505 // set color in the palette
506 pRect->constant = color;
507
508 // set rectangle width
509 pRect->width = width;
510
511 // set rectangle height
512 pRect->height = height;
513
514 // return pointer to rectangle object
515 return pRect;
516 }
517
518 /**
519 * Creates a translucent rectangle object of the given dimensions
520 * and returns a pointer to the object.
521 * @param width Width of rectangle
522 * @param height Height of rectangle
523 */
TranslucentObject(int width,int height)524 OBJECT *TranslucentObject(int width, int height) {
525 // template for initializing the rectangle object
526 static const OBJ_INIT rectObj = {0, DMA_TRANS, OID_EFFECTS, 0, 0, 0};
527
528 // allocate and init a new object
529 OBJECT *pRect = InitObject(&rectObj);
530
531 // set rectangle width
532 pRect->width = width;
533
534 // set rectangle height
535 pRect->height = height;
536
537 // return pointer to rectangle object
538 return pRect;
539 }
540
541 } // End of namespace Tinsel
542