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