1 /*
2  * file sprite.c - handling sprites
3  *
4  * $Id: sprite.c,v 1.9 2006/02/09 21:21:25 fzago Exp $
5  *
6  * Program XBLAST
7  * (C) by Oliver Vogel (e-mail: m.vogel@ndh.net)
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published
11  * by the Free Software Foundation; either version 2; or (at your option)
12  * any later version
13  *
14  * This program is distributed in the hope that it will be entertaining,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILTY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17  * Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.
21  * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 
24 #include "xblast.h"
25 
26 /*
27  * local types
28  */
29 
30 /* function pointers for region and drawing */
31 
32 /*
33  * local variables
34  */
35 AnySprite *spriteFirst = NULL;
36 AnySprite *spriteLast = NULL;
37 
38 /*
39  * mark sprite positon to redraw
40  */
41 void
MarkMazeSprite(const Sprite * spr)42 MarkMazeSprite (const Sprite * spr)
43 {
44 	const BMRectangle *r = (*spr->any.rect) (spr);
45 	assert (r != NULL);
46 	MarkMaze (r->x / BLOCK_WIDTH, r->y / BLOCK_HEIGHT, (r->x + r->w - 1) / BLOCK_WIDTH,
47 			  (r->y + r->h - 1) / BLOCK_HEIGHT);
48 }								/* MarkMazeSprite */
49 
50 /*
51  * local function: create sprite
52  */
53 static Sprite *
CreateSprite(SpriteType type,DrawFunc draw,RectFunc rect,int x,int y,int ysort,int anime,int mode)54 CreateSprite (SpriteType type, DrawFunc draw, RectFunc rect, int x, int y, int ysort, int anime,
55 			  int mode)
56 {
57 	AnySprite *ptr;
58 	AnySprite *other;
59 	/* alloc memory */
60 	if (NULL == (ptr = (AnySprite *) malloc (sizeof (Sprite)))) {
61 		return NULL;
62 	}
63 
64 	/* init values */
65 	ptr->type = type;
66 	ptr->dirty = XBTrue;
67 	ptr->draw = draw;
68 	ptr->rect = rect;
69 	ptr->x = x;
70 	ptr->y = y;
71 	ptr->ysort = ysort;
72 	ptr->anime = anime;
73 	ptr->mode = mode;
74 	ptr->next = NULL;
75 	ptr->prev = NULL;
76 
77 	/* store sprite in list */
78 	if (NULL == spriteFirst) {
79 		spriteFirst = spriteLast = ptr;
80 	}
81 	else {
82 		for (other = spriteFirst; other != NULL; other = other->next) {
83 			/* list is sorted by inccreasing (y+ysort) value */
84 			if ((other->ysort + other->y) > (ptr->ysort + ptr->y)) {
85 				if (NULL == other->prev) {
86 					/* start of list */
87 					spriteFirst = ptr;
88 					ptr->prev = NULL;
89 				}
90 				else {
91 					other->prev->next = ptr;
92 					ptr->prev = other->prev;
93 				}
94 				ptr->next = other;
95 				other->prev = ptr;
96 				goto Finish;
97 			}
98 		}
99 		spriteLast->next = ptr;
100 		ptr->prev = spriteLast;
101 		spriteLast = ptr;
102 	}
103   Finish:
104 	return (Sprite *) ptr;
105 }								/* CreateSprite */
106 
107 /*
108  * rectangle function for player sprite
109  */
110 static const BMRectangle *
RectPlayerSprite(const Sprite * ptr)111 RectPlayerSprite (const Sprite * ptr)
112 {
113 	static BMRectangle result;
114 
115 	assert (ptr != NULL);
116 	result.x = imgRectSprite[ptr->any.anime].x + ptr->any.x;
117 	result.y = imgRectSprite[ptr->any.anime].y + ptr->any.y;
118 	result.w = imgRectSprite[ptr->any.anime].w;
119 	result.h = imgRectSprite[ptr->any.anime].h;
120 
121 	return &result;
122 }								/* RectPlayerSprite */
123 
124 /*
125  * public function: CreatePlayerSprite
126  */
127 Sprite *
CreatePlayerSprite(int player,int x,int y,unsigned anime,int mode)128 CreatePlayerSprite (int player, int x, int y, unsigned anime, int mode)
129 {
130 	Sprite *ptr;
131 
132 	ptr =
133 		CreateSprite (STPlayer, GUI_DrawPlayerSprite, RectPlayerSprite, x, y, BLOCK_HEIGHT, anime,
134 					  mode);
135 	if (NULL == ptr) {
136 		return NULL;
137 	}
138 	ptr->player.player = player;
139 	/* mark maze to redrawn */
140 	if (ptr->any.mode == SPM_MAPPED) {
141 		MarkMazeSprite ((Sprite *) ptr);
142 	}
143 	return ptr;
144 }								/* CreatePlayerSprite */
145 
146 static const BMRectangle *
RectBombSprite(const Sprite * ptr)147 RectBombSprite (const Sprite * ptr)
148 {
149 	static BMRectangle result;
150 
151 	assert (ptr != NULL);
152 	result.x = ptr->any.x;
153 	result.y = ptr->any.y;
154 	result.w = BLOCK_WIDTH;
155 	result.h = BLOCK_HEIGHT;
156 
157 	return &result;
158 }								/* GUI_RectBombSprite */
159 
160 /*
161  * public function: CreateBombSprite
162  */
163 Sprite *
CreateBombSprite(int bomb,int x,int y,unsigned anime,int mode)164 CreateBombSprite (int bomb, int x, int y, unsigned anime, int mode)
165 {
166 	Sprite *ptr;
167 
168 	ptr = CreateSprite (STBomb, GUI_DrawBombSprite, RectBombSprite, x, y, BASE_Y / 2, anime, mode);
169 	if (NULL == ptr) {
170 		return NULL;
171 	}
172 	ptr->bomb.bomb = bomb;
173 	/* mark maze to redrawn */
174 	if (ptr->any.mode == SPM_MAPPED) {
175 		MarkMazeSprite ((Sprite *) ptr);
176 	}
177 	return ptr;
178 }								/* CreateBombSprite */
179 
180 /*
181  *
182  */
183 static const BMRectangle *
RectTextSprite(const Sprite * ptr)184 RectTextSprite (const Sprite * ptr)
185 {
186 	static BMRectangle result;
187 
188 	assert (ptr != NULL);
189 	result.x = ptr->any.x;
190 	result.y = ptr->any.y;
191 	result.w = ptr->text.w;
192 	result.h = ptr->text.h;
193 
194 	return &result;
195 }								/* GUI_RectTextSprite */
196 
197 /*
198  *
199  */
200 Sprite *
CreateTextSprite(const char * text,int x,int y,int w,int h,unsigned anime,int mode)201 CreateTextSprite (const char *text, int x, int y, int w, int h, unsigned anime, int mode)
202 {
203 	Sprite *ptr;
204 
205 	ptr = CreateSprite (STText, GUI_DrawTextSprite, RectTextSprite, x, y, 0, anime, mode);
206 	if (NULL == ptr) {
207 		return NULL;
208 	}
209 	ptr->any.anime = anime;
210 	ptr->text.text = text;
211 	ptr->text.w = w;
212 	ptr->text.h = h;
213 	/* mark maze to redrawn */
214 	if (ptr->any.mode == SPM_MAPPED) {
215 		MarkMazeSprite ((Sprite *) ptr);
216 	}
217 	return ptr;
218 }								/* CreateTextSprite */
219 
220 /*
221  *
222  */
223 static const BMRectangle *
RectIconSprite(const Sprite * spr)224 RectIconSprite (const Sprite * spr)
225 {
226 	static BMRectangle result;
227 
228 	assert (spr != NULL);
229 	assert (spr->any.anime < MAX_ICON_SPRITES);
230 
231 	result.x = imgRectIcon[spr->any.anime].x + spr->any.x;
232 	result.y = imgRectIcon[spr->any.anime].y + spr->any.y;
233 	result.w = imgRectIcon[spr->any.anime].w;
234 	result.h = imgRectIcon[spr->any.anime].h;
235 
236 	return &result;
237 }								/* RectColorSprite */
238 
239 /*
240  *
241  */
242 Sprite *
CreateIconSprite(int x,int y,unsigned anime,int mode)243 CreateIconSprite (int x, int y, unsigned anime, int mode)
244 {
245 	Sprite *ptr;
246 
247 	ptr = CreateSprite (STIcon, GUI_DrawIconSprite, RectIconSprite, x, y, 0, anime, mode);
248 	if (NULL == ptr) {
249 		return NULL;
250 	}
251 	/* mark maze to redrawn */
252 	if (ptr->any.mode == SPM_MAPPED) {
253 		MarkMazeSprite ((Sprite *) ptr);
254 	}
255 	return ptr;
256 }								/* CreateIconSprite */
257 
258 /*
259  * public function delete_sprite
260  */
261 void
DeleteSprite(Sprite * spr)262 DeleteSprite (Sprite * spr)
263 {
264 
265 	/* check for non-dirty sprites to be redrawn */
266 	if (NULL == spr->any.prev) {
267 		spriteFirst = spr->any.next;
268 	}
269 	else {
270 		spr->any.prev->next = spr->any.next;
271 	}
272 	if (NULL == spr->any.next) {
273 		spriteLast = spr->any.prev;
274 	}
275 	else {
276 		spr->any.next->prev = spr->any.prev;
277 	}
278 	if (spr->any.mode & SPM_MAPPED) {
279 		/* mark tiles on position */
280 		MarkMazeSprite (spr);
281 	}
282 	free (spr);
283 }								/* DeleteSprite */
284 
285 /*
286  * local function swap_sprite_prev
287  */
288 static void
SwapSpritePrev(AnySprite * ptr)289 SwapSpritePrev (AnySprite * ptr)
290 {
291 	AnySprite *prev;
292 
293 	if (NULL == (prev = ptr->prev)) {
294 		return;
295 	}
296 
297 	/* references from outside */
298 	if (prev->prev != NULL) {
299 		prev->prev->next = ptr;
300 	}
301 	else {
302 		spriteFirst = ptr;
303 	}
304 	if (ptr->next != NULL) {
305 		ptr->next->prev = prev;
306 	}
307 	else {
308 		spriteLast = prev;
309 	}
310 	/* internal refs */
311 	prev->next = ptr->next;
312 	ptr->prev = prev->prev;
313 
314 	prev->prev = ptr;
315 	ptr->next = prev;
316 }								/* SwapSpritePrev */
317 
318 /*
319  * public function: move_sprite
320  */
321 void
MoveSprite(Sprite * sprite,int x,int y)322 MoveSprite (Sprite * sprite, int x, int y)
323 {
324 	AnySprite *spr = (AnySprite *) sprite;
325 	AnySprite *ptr;
326 
327 	if ((spr->y == y) && (spr->x == x)) {
328 		return;
329 	}
330 
331 	if (spr->mode & SPM_MAPPED) {
332 		/* mark sprite as dirty */
333 		spr->dirty = XBTrue;
334 		/* mark tiles on old position */
335 		MarkMazeSprite ((Sprite *) spr);
336 	}
337 
338 	spr->x = x;
339 	if (spr->y < y) {
340 		spr->y = y;
341 		/* sprite has move downwards */
342 		for (ptr = spr->next;
343 			 (ptr != NULL) && ((ptr->y + ptr->ysort) < (spr->y + spr->ysort)); ptr = spr->next) {
344 			SwapSpritePrev (ptr);
345 		}
346 	}
347 	else if (spr->y > y) {
348 		spr->y = y;
349 		/* sprite has moved upwards */
350 		for (ptr = spr->prev;
351 			 (ptr != NULL) && ((ptr->y + ptr->ysort) > (spr->y + spr->ysort)); ptr = spr->prev) {
352 			SwapSpritePrev (spr);
353 		}
354 	}
355 
356 	/* mark tiles on new position */
357 	if (spr->mode & SPM_MAPPED) {
358 		MarkMazeSprite ((Sprite *) spr);
359 	}
360 }								/* MoveSprite */
361 
362 /*
363  * public function: set_sprite_mode
364  */
365 void
SetSpriteMode(Sprite * sprite,int mode)366 SetSpriteMode (Sprite * sprite, int mode)
367 {
368 	if (mode == sprite->any.mode) {
369 		return;
370 	}
371 	MarkMazeSprite ((Sprite *) sprite);
372 	sprite->any.mode = mode;
373 	sprite->any.dirty = XBTrue;
374 }								/* SetSpriteMode */
375 
376 /*
377  *
378  */
379 void
SetSpriteText(Sprite * sprite,const char * text)380 SetSpriteText (Sprite * sprite, const char *text)
381 {
382 	assert (sprite != NULL);
383 	assert (sprite->type = STText);
384 
385 	MarkMazeSprite ((Sprite *) sprite);
386 	sprite->text.text = text;
387 	sprite->any.dirty = XBTrue;
388 }								/* SetSpriteText */
389 
390 /*
391  *
392  */
393 void
SetSpriteAnime(Sprite * sprite,unsigned anime)394 SetSpriteAnime (Sprite * sprite, unsigned anime)
395 {
396 	if (anime == sprite->any.anime) {
397 		return;
398 	}
399 	if (sprite->any.mode & SPM_MAPPED) {
400 		MarkMazeSprite ((Sprite *) sprite);
401 		sprite->any.dirty = XBTrue;
402 	}
403 	sprite->any.anime = anime;
404 	if (sprite->any.mode & SPM_MAPPED) {
405 		MarkMazeSprite ((Sprite *) sprite);
406 	}
407 }								/* SetSpriteAnime */
408 
409 /*
410  * set color for an icon/xolor sprite
411  */
412 void
SetSpriteColor(Sprite * sprite,XBColor color)413 SetSpriteColor (Sprite * sprite, XBColor color)
414 {
415 	assert (sprite != NULL);
416 	assert (sprite->type == STIcon);
417 
418 	GUI_LoadIconSprite (sprite->any.anime, color);
419 	MarkMazeSprite (sprite);
420 }								/* SetSpriteColor */
421 
422 /*
423  * local function: sprite_intersect
424  */
425 static void
SpriteIntersect(AnySprite * a,AnySprite * b)426 SpriteIntersect (AnySprite * a, AnySprite * b)
427 {
428 	BMRectangle rect_a, rect_b;
429 	int left, right, top, bottom;
430 
431 	if ((a->mode & SPM_MAPPED) && (b->mode & SPM_MAPPED)) {
432 		rect_a = *(*a->rect) ((Sprite *) a);
433 		rect_b = *(*b->rect) ((Sprite *) b);
434 
435 		left = MAX (rect_a.x, rect_b.x);
436 		right = MIN (rect_a.x + rect_a.w, rect_b.x + rect_b.w);
437 
438 		if (left < right) {
439 			top = MAX (rect_a.y, rect_b.y);
440 			bottom = MIN (rect_a.y + rect_a.h, rect_b.y + rect_b.h);
441 			if (top < bottom) {
442 				a->dirty = XBTrue;
443 				b->dirty = XBTrue;
444 				MarkMazeRect (left, top, right - left, bottom - top);
445 			}
446 		}
447 	}
448 }								/* SpriteIntersect */
449 
450 /*
451  *
452  */
453 void
ShuffleAllSprites(void)454 ShuffleAllSprites (void)
455 {
456 	AnySprite *ptr;
457 
458 	/* switch sprites on same (y+ysort) */
459 	if (spriteFirst != NULL) {
460 		ptr = spriteFirst;
461 		while (ptr->next != NULL) {
462 			if ((ptr->y + ptr->ysort) == (ptr->next->y + ptr->next->ysort)) {
463 				if ((!ptr->dirty) && (!ptr->next->dirty)) {
464 					SpriteIntersect (ptr, ptr->next);
465 				}
466 				SwapSpritePrev (ptr->next);
467 			}
468 			else {
469 				ptr = ptr->next;
470 			}
471 		}
472 	}
473 }								/* ShuffleAllSprites */
474 
475 void
DeleteAllBombSprites(void)476 DeleteAllBombSprites (void)
477 {
478 	AnySprite *ptr, *tmp;
479 
480 	/* check for non-dirty sprites to be redrawn */
481 	for (ptr = spriteFirst; ptr != NULL;) {
482 		if (ptr->draw == GUI_DrawBombSprite) {
483 			tmp = ptr->next;
484 			DeleteSprite ((Sprite *) ptr);
485 			ptr = tmp;
486 		}
487 		else {
488 			ptr = ptr->next;
489 		}
490 	}
491 }
492 
493 /*
494  *
495  */
496 void
MarkAllSprites(void)497 MarkAllSprites (void)
498 {
499 	AnySprite *ptr;
500 
501 	/* check for non-dirty sprites to be redrawn */
502 	for (ptr = spriteFirst; ptr != NULL; ptr = ptr->next) {
503 		if (!ptr->dirty) {
504 			ptr->dirty = SpriteMarked ((Sprite *) ptr);
505 		}
506 	}
507 }								/* MarkAllSprites */
508 
509 /*
510  * public function draw all sprites
511  */
512 void
DrawAllSprites(void)513 DrawAllSprites (void)
514 {
515 	AnySprite *ptr;
516 
517 	for (ptr = spriteFirst; ptr != NULL; ptr = ptr->next) {
518 		if (ptr->type == STBomb) {
519 		}
520 		if ((ptr->mode & SPM_MAPPED) && (ptr->dirty)) {
521 			(*ptr->draw) ((Sprite *) ptr);
522 		}
523 		ptr->dirty = XBFalse;
524 	}
525 }								/* DrawAllSprites */
526 
527 /*
528  * get current region of sprite
529  */
530 const BMRectangle *
SpriteRectangle(const Sprite * sprite)531 SpriteRectangle (const Sprite * sprite)
532 {
533 	assert (sprite != NULL);
534 	assert (sprite->any.rect != NULL);
535 	return (*sprite->any.rect) (sprite);
536 }								/* SpriteRectangle */
537 
538 /*
539  * get sorite animation phase
540  */
541 int
SpriteAnime(const Sprite * sprite)542 SpriteAnime (const Sprite * sprite)
543 {
544 	assert (sprite != NULL);
545 	return sprite->any.anime;
546 }								/* SpriteAnime */
547 
548 /*
549  * get bomb sprite bomb type (mini/normal)
550  */
551 int
SpriteBomb(const Sprite * sprite)552 SpriteBomb (const Sprite * sprite)
553 {
554 	assert (sprite != NULL);
555 	assert (sprite->type == STBomb);
556 	return sprite->bomb.bomb;
557 }								/* SpriteBomb */
558 
559 /*
560  * get player sprite id
561  */
562 int
SpritePlayer(const Sprite * sprite)563 SpritePlayer (const Sprite * sprite)
564 {
565 	assert (sprite != NULL);
566 	assert (sprite->type == STPlayer);
567 	return sprite->player.player;
568 }								/* SpritePlayer */
569 
570 /*
571  * get text sprite text to draw
572  */
573 const char *
SpriteText(const Sprite * sprite)574 SpriteText (const Sprite * sprite)
575 {
576 	assert (sprite != NULL);
577 	assert (sprite->type == STText);
578 	return sprite->text.text;
579 }								/* SpriteText */
580 
581 /*
582  * check if sprite is masked
583  */
584 XBBool
SpriteIsMasked(const Sprite * sprite)585 SpriteIsMasked (const Sprite * sprite)
586 {
587 	assert (sprite != NULL);
588 	return (sprite->any.mode & SPM_MASKED) ? XBTrue : XBFalse;
589 }								/* SpriteIsMasked */
590 
591 /*
592  * end of sprite.c
593  */
594