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