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 utilities to handle multi-part objects.
22  */
23 
24 #include "tinsel/multiobj.h"
25 #include "tinsel/handle.h"
26 #include "tinsel/object.h"
27 #include "tinsel/tinsel.h"
28 
29 namespace Tinsel {
30 
31 /**
32  * Initialize a multi-part object using a list of images to init
33  * each object piece. One object is created for each image in the list.
34  * All objects are given the same palette as the first image. A pointer
35  * to the first (master) object created is returned.
36  * @param pInitTbl			Pointer to multi-object initialisation table
37  */
MultiInitObject(const MULTI_INIT * pInitTbl)38 OBJECT *MultiInitObject(const MULTI_INIT *pInitTbl) {
39 	OBJ_INIT obj_init;	// object init table
40 	OBJECT *pFirst, *pObj;	// object pointers
41 	FRAME *pFrame;		// list of images for the multi-part object
42 
43 	if (FROM_32(pInitTbl->hMulFrame)) {
44 		// we have a frame handle
45 		pFrame = (FRAME *)LockMem(FROM_32(pInitTbl->hMulFrame));
46 
47 		obj_init.hObjImg  = READ_32(pFrame);	// first objects shape
48 	} else {	// this must be a animation list for a NULL object
49 		pFrame = NULL;
50 		obj_init.hObjImg = 0;	// first objects shape
51 	}
52 
53 	// init the object init table
54 	obj_init.objFlags = (int)FROM_32(pInitTbl->mulFlags);	// all objects have same flags
55 	obj_init.objID    = (int)FROM_32(pInitTbl->mulID);	// all objects have same ID
56 	obj_init.objX     = (int)FROM_32(pInitTbl->mulX);	// all objects have same X ani pos
57 	obj_init.objY     = (int)FROM_32(pInitTbl->mulY);	// all objects have same Y ani pos
58 	obj_init.objZ     = (int)FROM_32(pInitTbl->mulZ);	// all objects have same Z pos
59 
60 	// create and init the first object
61 	pObj = pFirst = InitObject(&obj_init);
62 
63 	if (pFrame) {
64 		// if we have any animation frames
65 
66 		pFrame++;
67 
68 		while (READ_32(pFrame) != 0) {
69 			// set next objects shape
70 			obj_init.hObjImg = READ_32(pFrame);
71 
72 			// create next object and link to previous
73 			pObj = pObj->pSlave = InitObject(&obj_init);
74 
75 			pFrame++;
76 		}
77 	}
78 
79 	// null end of list for final object
80 	pObj->pSlave = NULL;
81 
82 	// return master object
83 	return pFirst;
84 }
85 
86 /**
87  * Inserts the multi-part object onto the specified object list.
88  * @param pObjList			List to insert multi-part object onto
89 * @param  pInsObj			Head of multi-part object to insert
90 
91  */
92 
MultiInsertObject(OBJECT ** pObjList,OBJECT * pInsObj)93 void MultiInsertObject(OBJECT **pObjList, OBJECT *pInsObj) {
94 	// validate object pointer
95 	assert(isValidObject(pInsObj));
96 
97 	// for all the objects that make up this multi-part
98 	do {
99 		// add next part to the specified list
100 		InsertObject(pObjList, pInsObj);
101 
102 		// next obj in list
103 		pInsObj = pInsObj->pSlave;
104 	} while (pInsObj != NULL);
105 }
106 
107 /**
108  * Deletes all the pieces of a multi-part object from the
109  * specified object list.
110  * @param pObjList			List to delete multi-part object from
111  * @param pMultiObj			Multi-part object to be deleted
112  */
113 
MultiDeleteObject(OBJECT ** pObjList,OBJECT * pMultiObj)114 void MultiDeleteObject(OBJECT **pObjList, OBJECT *pMultiObj) {
115 	// validate object pointer
116 	assert(isValidObject(pMultiObj));
117 
118 	// for all the objects that make up this multi-part
119 	do {
120 		// delete object
121 		DelObject(pObjList, pMultiObj);
122 
123 		// next obj in list
124 		pMultiObj = pMultiObj->pSlave;
125 	} while (pMultiObj != NULL);
126 }
127 
128 /**
129  * Hides a multi-part object by giving each object a "NullImage"
130  * image pointer.
131  * @param pMultiObj			Multi-part object to be hidden
132  */
133 
MultiHideObject(OBJECT * pMultiObj)134 void MultiHideObject(OBJECT *pMultiObj) {
135 	// validate object pointer
136 	assert(isValidObject(pMultiObj));
137 
138 	// set master shape to null animation frame
139 	pMultiObj->hShape = 0;
140 
141 	// change all objects
142 	MultiReshape(pMultiObj);
143 }
144 
145 /**
146  * Horizontally flip a multi-part object.
147  * @param pFlipObj			Head of multi-part object to flip
148  */
149 
MultiHorizontalFlip(OBJECT * pFlipObj)150 void MultiHorizontalFlip(OBJECT *pFlipObj) {
151 	// validate object pointer
152 	assert(isValidObject(pFlipObj));
153 
154 	// for all the objects that make up this multi-part
155 	do {
156 		// horizontally flip the next part
157 		AnimateObjectFlags(pFlipObj, pFlipObj->flags ^ DMA_FLIPH,
158 			pFlipObj->hImg);
159 
160 		// next obj in list
161 		pFlipObj = pFlipObj->pSlave;
162 	} while (pFlipObj != NULL);
163 }
164 
165 /**
166  * Vertically flip a multi-part object.
167  * @param pFlipObj			Head of multi-part object to flip
168  */
169 
MultiVerticalFlip(OBJECT * pFlipObj)170 void MultiVerticalFlip(OBJECT *pFlipObj) {
171 	// validate object pointer
172 	assert(isValidObject(pFlipObj));
173 
174 	// for all the objects that make up this multi-part
175 	do {
176 		// vertically flip the next part
177 		AnimateObjectFlags(pFlipObj, pFlipObj->flags ^ DMA_FLIPV,
178 			pFlipObj->hImg);
179 
180 		// next obj in list
181 		pFlipObj = pFlipObj->pSlave;
182 	} while (pFlipObj != NULL);
183 }
184 
185 /**
186  * Adjusts the coordinates of a multi-part object.	The adjustments
187  * take into account the orientation of the object.
188  * @param pMultiObj			Multi-part object to be adjusted
189  * @param deltaX			X adjustment
190  * @param deltaY			Y adjustment
191  */
192 
MultiAdjustXY(OBJECT * pMultiObj,int deltaX,int deltaY)193 void MultiAdjustXY(OBJECT *pMultiObj, int deltaX, int deltaY) {
194 	// validate object pointer
195 	assert(isValidObject(pMultiObj));
196 
197 	if (deltaX == 0 && deltaY == 0)
198 		return;		// ignore no change
199 
200 	if (!TinselV2) {
201 		// *** This may be wrong!!!
202 		if (pMultiObj->flags & DMA_FLIPH) {
203 			// image is flipped horizontally - flip the x direction
204 			deltaX = -deltaX;
205 		}
206 
207 		if (pMultiObj->flags & DMA_FLIPV) {
208 			// image is flipped vertically - flip the y direction
209 			deltaY = -deltaY;
210 		}
211 	}
212 
213 	// for all the objects that make up this multi-part
214 	do {
215 		// signal a change in the object
216 		pMultiObj->flags |= DMA_CHANGED;
217 
218 		// adjust the x position
219 		pMultiObj->xPos += intToFrac(deltaX);
220 
221 		// adjust the y position
222 		pMultiObj->yPos += intToFrac(deltaY);
223 
224 		// next obj in list
225 		pMultiObj = pMultiObj->pSlave;
226 
227 	} while (pMultiObj != NULL);
228 }
229 
230 /**
231  * Moves all the pieces of a multi-part object by the specified
232  * amount. Does not take into account the objects orientation.
233  * @param pMultiObj			Multi-part object to be adjusted
234  * @param deltaX			X movement
235  * @param deltaY			Y movement
236  */
237 
MultiMoveRelXY(OBJECT * pMultiObj,int deltaX,int deltaY)238 void MultiMoveRelXY(OBJECT *pMultiObj, int deltaX, int deltaY) {
239 	// validate object pointer
240 	assert(isValidObject(pMultiObj));
241 
242 	if (deltaX == 0 && deltaY == 0)
243 		return;		// ignore no change
244 
245 	// for all the objects that make up this multi-part
246 	do {
247 		// signal a change in the object
248 		pMultiObj->flags |= DMA_CHANGED;
249 
250 		// adjust the x position
251 		pMultiObj->xPos += intToFrac(deltaX);
252 
253 		// adjust the y position
254 		pMultiObj->yPos += intToFrac(deltaY);
255 
256 		// next obj in list
257 		pMultiObj = pMultiObj->pSlave;
258 
259 	} while (pMultiObj != NULL);
260 }
261 
262 /**
263  * Sets the x & y anim position of all pieces of a multi-part object.
264  * @param pMultiObj			Multi-part object whose position is to be changed
265  * @param newAniX			New x animation position
266  * @param newAniY			New y animation position
267  */
268 
MultiSetAniXY(OBJECT * pMultiObj,int newAniX,int newAniY)269 void MultiSetAniXY(OBJECT *pMultiObj, int newAniX, int newAniY) {
270 	int curAniX, curAniY;	// objects current animation position
271 
272 	// validate object pointer
273 	assert(isValidObject(pMultiObj));
274 
275 	// get master objects current animation position
276 	GetAniPosition(pMultiObj, &curAniX, &curAniY);
277 
278 	// calc difference between current and new positions
279 	newAniX -= curAniX;
280 	newAniY -= curAniY;
281 
282 	// move all pieces by the difference
283 	MultiMoveRelXY(pMultiObj, newAniX, newAniY);
284 }
285 
286 /**
287  * Sets the x anim position of all pieces of a multi-part object.
288  * @param pMultiObj			Multi-part object whose x position is to be changed
289  * @param newAniX			New x animation position
290  */
291 
MultiSetAniX(OBJECT * pMultiObj,int newAniX)292 void MultiSetAniX(OBJECT *pMultiObj, int newAniX) {
293 	int curAniX, curAniY;	// objects current animation position
294 
295 	// validate object pointer
296 	assert(isValidObject(pMultiObj));
297 
298 	// get master objects current animation position
299 	GetAniPosition(pMultiObj, &curAniX, &curAniY);
300 
301 	// calc x difference between current and new positions
302 	newAniX -= curAniX;
303 	curAniY = 0;
304 
305 	// move all pieces by the difference
306 	MultiMoveRelXY(pMultiObj, newAniX, curAniY);
307 }
308 
309 /**
310  * Sets the y anim position of all pieces of a multi-part object.
311  * @param pMultiObj			Multi-part object whose x position is to be changed
312  * @param newAniX			New y animation position
313  */
314 
MultiSetAniY(OBJECT * pMultiObj,int newAniY)315 void MultiSetAniY(OBJECT *pMultiObj, int newAniY) {
316 	int curAniX, curAniY;	// objects current animation position
317 
318 	// validate object pointer
319 	assert(isValidObject(pMultiObj));
320 
321 	// get master objects current animation position
322 	GetAniPosition(pMultiObj, &curAniX, &curAniY);
323 
324 	// calc y difference between current and new positions
325 	curAniX = 0;
326 	newAniY -= curAniY;
327 
328 	// move all pieces by the difference
329 	MultiMoveRelXY(pMultiObj, curAniX, newAniY);
330 }
331 
332 /**
333  * Sets the Z position of all pieces of a multi-part object.
334  * @param pMultiObj		Multi-part object to be adjusted
335  * @param newZ			New Z order
336  */
337 
MultiSetZPosition(OBJECT * pMultiObj,int newZ)338 void MultiSetZPosition(OBJECT *pMultiObj, int newZ) {
339 	// validate object pointer
340 	assert(isValidObject(pMultiObj));
341 
342 	// for all the objects that make up this multi-part
343 	do {
344 		// signal a change in the object
345 		pMultiObj->flags |= DMA_CHANGED;
346 
347 		// set the new z position
348 		pMultiObj->zPos = newZ;
349 
350 		// next obj in list
351 		pMultiObj = pMultiObj->pSlave;
352 	} while (pMultiObj != NULL);
353 }
354 
355 /**
356  * Reshape a multi-part object.
357  * @param pMultiObj			Multi-part object to re-shape
358  */
359 
MultiReshape(OBJECT * pMultiObj)360 void MultiReshape(OBJECT *pMultiObj) {
361 	SCNHANDLE hFrame;
362 
363 	// validate object pointer
364 	assert(isValidObject(pMultiObj));
365 
366 	// get objects current anim frame
367 	hFrame = pMultiObj->hShape;
368 
369 	if (hFrame != 0 && hFrame != pMultiObj->hMirror) {
370 		// a valid shape frame which is different from previous
371 
372 		// get pointer to frame
373 		const FRAME *pFrame = (const FRAME *)LockMem(hFrame);
374 
375 		// update previous
376 		pMultiObj->hMirror = hFrame;
377 
378 		while (READ_32(pFrame) != 0 && pMultiObj != NULL) {
379 			// a normal image - update the current object with this image
380 			AnimateObject(pMultiObj, READ_32(pFrame));
381 
382 			// move to next image for this frame
383 			pFrame++;
384 
385 			// move to next part of object
386 			pMultiObj = pMultiObj->pSlave;
387 		}
388 
389 		// null the remaining object parts
390 		while (pMultiObj != NULL) {
391 			// set a null image for this object part
392 			AnimateObject(pMultiObj, 0);
393 
394 			// move to next part of object
395 			pMultiObj = pMultiObj->pSlave;
396 		}
397 	} else if (hFrame == 0) {
398 		// update previous
399 		pMultiObj->hMirror = hFrame;
400 
401 		// null all the object parts
402 		while (pMultiObj != NULL) {
403 			// set a null image for this object part
404 			AnimateObject(pMultiObj, 0);
405 
406 			// move to next part of object
407 			pMultiObj = pMultiObj->pSlave;
408 		}
409 	}
410 }
411 
412 /**
413  * Returns the left-most point of a multi-part object.
414  * @param pMulti			Multi-part object
415  */
416 
MultiLeftmost(OBJECT * pMulti)417 int MultiLeftmost(OBJECT *pMulti) {
418 	int left;
419 
420 	// validate object pointer
421 	assert(isValidObject(pMulti));
422 
423 	// init leftmost point to first object
424 	left = fracToInt(pMulti->xPos);
425 
426 	// for all the objects in this multi
427 	while ((pMulti = pMulti->pSlave) != NULL) {
428 		if (pMulti->hImg != 0) {
429 			// non null object part
430 
431 			if (fracToInt(pMulti->xPos) < left)
432 				// this object is further left
433 				left = fracToInt(pMulti->xPos);
434 		}
435 	}
436 
437 	// return left-most point
438 	return left;
439 }
440 
441 /**
442  * Returns the right-most point of a multi-part object.
443  * @param pMulti			Multi-part object
444  */
445 
MultiRightmost(OBJECT * pMulti)446 int MultiRightmost(OBJECT *pMulti) {
447 	int right;
448 
449 	// validate object pointer
450 	assert(isValidObject(pMulti));
451 
452 	// init right-most point to first object
453 	right = fracToInt(pMulti->xPos) + pMulti->width;
454 
455 	// for all the objects in this multi
456 	while ((pMulti = pMulti->pSlave) != NULL) {
457 		if (pMulti->hImg != 0) {
458 			// non null object part
459 
460 			if (fracToInt(pMulti->xPos) + pMulti->width > right)
461 				// this object is further right
462 				right = fracToInt(pMulti->xPos) + pMulti->width;
463 		}
464 	}
465 
466 	// return right-most point
467 	return right - 1;
468 }
469 
470 /**
471  * Returns the highest point of a multi-part object.
472  * @param pMulti			Multi-part object
473  */
474 
MultiHighest(OBJECT * pMulti)475 int MultiHighest(OBJECT *pMulti) {
476 	int highest;
477 
478 	// validate object pointer
479 	assert(isValidObject(pMulti));
480 
481 	// init highest point to first object
482 	highest = fracToInt(pMulti->yPos);
483 
484 	// for all the objects in this multi
485 	while ((pMulti = pMulti->pSlave) != NULL) {
486 		if (pMulti->hImg != 0) {
487 			// non null object part
488 
489 			if (fracToInt(pMulti->yPos) < highest)
490 				// this object is higher
491 				highest = fracToInt(pMulti->yPos);
492 		}
493 	}
494 
495 	// return highest point
496 	return highest;
497 }
498 
499 /**
500  * Returns the lowest point of a multi-part object.
501  * @param pMulti			Multi-part object
502  */
503 
MultiLowest(OBJECT * pMulti)504 int MultiLowest(OBJECT *pMulti) {
505 	int lowest;
506 
507 	// validate object pointer
508 	assert(isValidObject(pMulti));
509 
510 	// init lowest point to first object
511 	lowest = fracToInt(pMulti->yPos) + pMulti->height;
512 
513 	// for all the objects in this multi
514 	while ((pMulti = pMulti->pSlave) != NULL) {
515 		if (pMulti->hImg != 0) {
516 			// non null object part
517 
518 			if (fracToInt(pMulti->yPos) + pMulti->height > lowest)
519 				// this object is lower
520 				lowest = fracToInt(pMulti->yPos) + pMulti->height;
521 		}
522 	}
523 
524 	// return lowest point
525 	return lowest - 1;
526 }
527 
528 /**
529  * Returns TRUE if the object currently has an image.
530  * @param pMulti		Multi-part object
531  */
532 
MultiHasShape(POBJECT pMulti)533 bool MultiHasShape(POBJECT pMulti) {
534 	return (pMulti->hShape != 0);
535 }
536 
537 /**
538  * Bodge for text on movies. Makes sure it appears for it's lifetime.
539  * @param pMultiObj			Multi-part object to be adjusted
540  */
541 
MultiForceRedraw(POBJECT pMultiObj)542 void MultiForceRedraw(POBJECT pMultiObj) {
543 	// validate object pointer
544 	assert(isValidObject(pMultiObj));
545 
546 	// for all the objects that make up this multi-part
547 	do {
548 		// signal a change in the object
549 		pMultiObj->flags |= DMA_CHANGED;
550 
551 		// next obj in list
552 		pMultiObj = pMultiObj->pSlave;
553 	} while (pMultiObj != NULL);
554 }
555 
556 } // End of namespace Tinsel
557