1 /*
2 Copyright (C) 2009-2021 Parallel Realities
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19
20 #include "../headers.h"
21
22 #include "../collisions.h"
23 #include "../map.h"
24 #include "../system/error.h"
25 #include "../system/pak.h"
26 #include "animation.h"
27 #include "graphics.h"
28 #include "sprites.h"
29
30 static Animation animation[MAX_ANIMATIONS];
31 extern Entity *self;
32 extern Map map;
33
34 static int animationID = -1;
35
loadAnimationData(char * filename,int * spriteIndex,EntityAnimation * animationIndex)36 void loadAnimationData(char *filename, int *spriteIndex, EntityAnimation *animationIndex)
37 {
38 char *frameName, *line, *savePtr1, *savePtr2;
39 unsigned char *buffer;
40 int i, id;
41
42 savePtr1 = NULL;
43
44 buffer = loadFileFromPak(filename);
45
46 for (i=0;i<MAX_ANIMATION_TYPES;i++)
47 {
48 animationIndex[i].name[0] = '\0';
49 animationIndex[i].id = -1;
50 }
51
52 line = strtok_r((char *)buffer, "\n", &savePtr1);
53
54 id = 0;
55
56 while (line != NULL)
57 {
58 frameName = strtok_r(line, " ", &savePtr2);
59
60 if (frameName[0] == '#' || frameName[0] == '\n')
61 {
62 line = strtok_r(NULL, "\n", &savePtr1);
63
64 continue;
65 }
66
67 if (strcmpignorecase(frameName, "NAME") == 0)
68 {
69 if (animationID != -1)
70 {
71 if (animation[animationID].frameCount == 0)
72 {
73 showErrorAndExit("Animation %d from file %s was created with 0 frames", animationID, filename);
74 }
75 }
76
77 frameName = strtok_r(NULL, " ", &savePtr2);
78
79 animationID++;
80
81 if (animationID == MAX_ANIMATIONS)
82 {
83 showErrorAndExit("Ran out of space for animations");
84 }
85
86 STRNCPY(animationIndex[id].name, frameName, MAX_VALUE_LENGTH);
87
88 animationIndex[id].id = animationID;
89
90 id++;
91
92 if (id == MAX_ANIMATION_TYPES)
93 {
94 showErrorAndExit("Ran out of space for animation types");
95 }
96 }
97
98 else if (strcmpignorecase(frameName, "FRAMES") == 0)
99 {
100 frameName = strtok_r(NULL, " ", &savePtr2);
101
102 animation[animationID].frameCount = atoi(frameName);
103
104 /* Allocate space for the frame timer */
105
106 animation[animationID].frameTimer = malloc(animation[animationID].frameCount * sizeof(int));
107
108 if (animation[animationID].frameTimer == NULL)
109 {
110 showErrorAndExit("Ran out of memory when creating the animation for %s", filename);
111 }
112
113 /* Allocate space for the frame ID */
114
115 animation[animationID].frameID = malloc(animation[animationID].frameCount * sizeof(int));
116
117 if (animation[animationID].frameID == NULL)
118 {
119 showErrorAndExit("Ran out of memory when creating the animation for %s", filename);
120 }
121
122 /* Allocate space for the offsets */
123
124 animation[animationID].offsetX = malloc(animation[animationID].frameCount * sizeof(int));
125
126 if (animation[animationID].offsetX == NULL)
127 {
128 showErrorAndExit("Ran out of memory when creating the animation for %s", filename);
129 }
130
131 animation[animationID].offsetY = malloc(animation[animationID].frameCount * sizeof(int));
132
133 if (animation[animationID].offsetY == NULL)
134 {
135 showErrorAndExit("Ran out of memory when creating the animation for %s", filename);
136 }
137
138 /* Now load up each frame */
139
140 for (i=0;i<animation[animationID].frameCount;i++)
141 {
142 line = strtok_r(NULL, "\n", &savePtr1);
143
144 frameName = strtok_r(line, " ", &savePtr2);
145
146 animation[animationID].frameID[i] = atoi(frameName);
147
148 frameName = strtok_r(NULL, " ", &savePtr2);
149
150 animation[animationID].frameTimer[i] = atoi(frameName);
151
152 frameName = strtok_r(NULL, " ", &savePtr2);
153
154 animation[animationID].offsetX[i] = atoi(frameName);
155
156 frameName = strtok_r(NULL, "\0", &savePtr2);
157
158 animation[animationID].offsetY[i] = atoi(frameName);
159
160 /* Reassign the Animation frame to the appropriate Sprite index */
161
162 if (spriteIndex[animation[animationID].frameID[i]] == -1)
163 {
164 showErrorAndExit("Invalid sprite at animation index %d in file %s", animation[animationID].frameID[i], filename);
165 }
166
167 animation[animationID].frameID[i] = spriteIndex[animation[animationID].frameID[i]];
168 }
169 }
170
171 line = strtok_r(NULL, "\n", &savePtr1);
172 }
173
174 if (animation[animationID].frameCount == 0)
175 {
176 showErrorAndExit("Animation %d from file %s was created with 0 frames", animationID, filename);
177 }
178
179 free(buffer);
180 }
181
freeAnimation(Animation * anim)182 static void freeAnimation(Animation *anim)
183 {
184 if (anim->frameTimer != NULL)
185 {
186 free(anim->frameTimer);
187
188 anim->frameTimer = NULL;
189 }
190
191 if (anim->frameID != NULL)
192 {
193 free(anim->frameID);
194
195 anim->frameID = NULL;
196 }
197
198 if (anim->offsetX != NULL)
199 {
200 free(anim->offsetX);
201
202 anim->offsetX = NULL;
203 }
204
205 if (anim->offsetY != NULL)
206 {
207 free(anim->offsetY);
208
209 anim->offsetY = NULL;
210 }
211 }
212
freeAnimations()213 void freeAnimations()
214 {
215 int i;
216
217 for (i=0;i<MAX_ANIMATIONS;i++)
218 {
219 freeAnimation(&animation[i]);
220 }
221
222 animationID = -1;
223 }
224
drawLoopingAnimation(Entity * e,int x,int y,int w,int h,int center)225 void drawLoopingAnimation(Entity *e, int x, int y, int w, int h, int center)
226 {
227 Sprite *sprite;
228
229 e->frameTimer--;
230
231 if (e->frameTimer <= 0)
232 {
233 e->currentFrame++;
234
235 if (e->currentFrame >= animation[e->currentAnim].frameCount)
236 {
237 e->currentFrame = 0;
238 }
239
240 e->frameTimer = animation[e->currentAnim].frameTimer[e->currentFrame];
241
242 sprite = getSprite(animation[e->currentAnim].frameID[e->currentFrame]);
243
244 if (sprite->image == NULL)
245 {
246 showErrorAndExit("Image index %d is NULL!", animation[e->currentAnim].frameID[e->currentFrame]);
247 }
248
249 e->w = sprite->w;
250 e->h = sprite->h;
251
252 e->box = sprite->box;
253 }
254
255 else
256 {
257 sprite = getSprite(animation[e->currentAnim].frameID[e->currentFrame]);
258 }
259
260 if (center == 1)
261 {
262 drawImage(sprite->image, x + (w - sprite->w) / 2, y + (h - sprite->h) / 2, FALSE, e->alpha);
263 }
264
265 else
266 {
267 drawImage(sprite->image, x, y, FALSE, e->alpha);
268 }
269 }
270
drawLoopingAnimationToMap()271 int drawLoopingAnimationToMap()
272 {
273 int x, y, drawn;
274 int startX, startY;
275 Sprite *sprite;
276 void (*callback)(void);
277
278 drawn = FALSE;
279
280 startX = getMapStartX();
281 startY = getMapStartY();
282
283 self->frameTimer -= 1 * fabs(self->frameSpeed);
284
285 if (self->frameTimer <= 0)
286 {
287 self->currentFrame += self->frameSpeed > 0 ? 1 : -1;
288
289 if (self->currentFrame >= animation[self->currentAnim].frameCount)
290 {
291 self->currentFrame = self->animationCallback == NULL ? 0 : animation[self->currentAnim].frameCount - 1;
292
293 if (self->animationCallback != NULL)
294 {
295 callback = self->animationCallback;
296
297 self->animationCallback = NULL;
298
299 callback();
300
301 if (self->inUse == FALSE)
302 {
303 return drawn;
304 }
305 }
306 }
307
308 else if (self->currentFrame < 0)
309 {
310 self->currentFrame = self->animationCallback == NULL ? animation[self->currentAnim].frameCount - 1 : 0;
311
312 if (self->animationCallback != NULL)
313 {
314 callback = self->animationCallback;
315
316 self->animationCallback = NULL;
317
318 callback();
319
320 if (self->inUse == FALSE)
321 {
322 return drawn;
323 }
324 }
325 }
326
327 self->frameTimer = animation[self->currentAnim].frameTimer[self->currentFrame];
328
329 if (self->flags & FLASH)
330 {
331 sprite = getSprite(animation[self->currentAnim].frameID[self->currentFrame] + 1);
332 }
333
334 else
335 {
336 sprite = getSprite(animation[self->currentAnim].frameID[self->currentFrame]);
337 }
338
339 if (sprite->image == NULL)
340 {
341 showErrorAndExit("Image index %d is NULL!", animation[self->currentAnim].frameID[self->currentFrame]);
342 }
343
344 self->w = sprite->w;
345 self->h = sprite->h;
346
347 self->offsetX = animation[self->currentAnim].offsetX[self->currentFrame];
348 self->offsetY = animation[self->currentAnim].offsetY[self->currentFrame];
349
350 self->box = sprite->box;
351 }
352
353 else
354 {
355 if (self->flags & FLASH)
356 {
357 sprite = getSprite(animation[self->currentAnim].frameID[self->currentFrame] + 1);
358 }
359
360 else
361 {
362 sprite = getSprite(animation[self->currentAnim].frameID[self->currentFrame]);
363 }
364 }
365
366 if (self->alpha <= 0)
367 {
368 return FALSE;
369 }
370
371 if (self->face == LEFT)
372 {
373 if (self->parent == NULL || self->type == PROJECTILE)
374 {
375 x = self->x - startX;
376 y = self->y - startY;
377 }
378
379 else
380 {
381 x = self->x - startX + self->parent->w - self->w - self->offsetX;
382 y = self->y - startY + self->offsetY;
383 }
384 }
385
386 else
387 {
388 if (self->parent == NULL || self->type == PROJECTILE)
389 {
390 x = self->x - startX;
391 y = self->y - startY;
392 }
393
394 else
395 {
396 x = self->x - startX + self->offsetX;
397 y = self->y - startY + self->offsetY;
398 }
399 }
400
401 if (collision(x, y, sprite->w, sprite->h, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) == TRUE)
402 {
403 drawn = TRUE;
404
405 drawImage(sprite->image, x, y, (self->face == LEFT) ? TRUE : FALSE, self->alpha);
406
407 /*drawHitBox(x + self->w - self->box.w - self->box.x, y + self->box.y, self->box.w, self->box.h);*/
408 }
409
410 return drawn;
411 }
412
drawSpriteToMap()413 int drawSpriteToMap()
414 {
415 int x, y, drawn;
416 int startX, startY;
417 Sprite *sprite;
418
419 drawn = FALSE;
420
421 startX = getMapStartX();
422 startY = getMapStartY();
423
424 if (self->flags & FLASH)
425 {
426 sprite = getSprite(animation[self->currentAnim].frameID[self->currentFrame] + 1);
427 }
428
429 else
430 {
431 sprite = getSprite(animation[self->currentAnim].frameID[self->currentFrame]);
432 }
433
434 if (self->alpha <= 0)
435 {
436 return FALSE;
437 }
438
439 if (self->face == LEFT)
440 {
441 if (self->parent == NULL || self->type == PROJECTILE)
442 {
443 x = self->x - startX;
444 y = self->y - startY;
445 }
446
447 else
448 {
449 x = self->x - startX + self->parent->w - self->w - self->offsetX;
450 y = self->y - startY + self->offsetY;
451 }
452 }
453
454 else
455 {
456 if (self->parent == NULL || self->type == PROJECTILE)
457 {
458 x = self->x - startX;
459 y = self->y - startY;
460 }
461
462 else
463 {
464 x = self->x - startX + self->offsetX;
465 y = self->y - startY + self->offsetY;
466 }
467 }
468
469 if (collision(x, y, sprite->w, sprite->h, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) == TRUE)
470 {
471 drawn = TRUE;
472
473 drawImage(sprite->image, x, y, (self->face == LEFT) ? TRUE : FALSE, self->alpha);
474
475 /*drawHitBox(x + self->w - self->box.w - self->box.x, y + self->box.y, self->box.w, self->box.h);*/
476 }
477
478 return drawn;
479 }
480
drawSprite(Entity * e,int x,int y,int w,int h,int center)481 void drawSprite(Entity *e, int x, int y, int w, int h, int center)
482 {
483 Sprite *sprite;
484
485 sprite = getSprite(animation[e->currentAnim].frameID[e->currentFrame]);
486
487 if (center == 1)
488 {
489 drawImage(sprite->image, x + (w - sprite->w) / 2, y + (h - sprite->h) / 2, FALSE, e->alpha);
490 }
491
492 else
493 {
494 drawImage(sprite->image, x, y, FALSE, e->alpha);
495 }
496 }
497
drawLineDefToMap()498 int drawLineDefToMap()
499 {
500 drawBoxToMap(self->x, self->y, self->w, self->h, 255, 0, 0);
501
502 return TRUE;
503 }
504
drawPhaseDoorToMap()505 int drawPhaseDoorToMap()
506 {
507 drawBoxToMap(self->x, self->y, self->w, self->h, 0, 255, 0);
508
509 return TRUE;
510 }
511
setEntityAnimation(Entity * e,char * animationName)512 void setEntityAnimation(Entity *e, char *animationName)
513 {
514 int previousRightEdge, previousAnim, newRightEdge;
515 int previousBottom, newBottom, i;
516 Sprite *sprite;
517
518 if (e->inUse == FALSE)
519 {
520 return;
521 }
522
523 previousAnim = e->currentAnim;
524
525 if (e->currentAnim == -1)
526 {
527 previousRightEdge = -1;
528
529 previousBottom = -1;
530 }
531
532 else
533 {
534 previousRightEdge = e->x + e->w;
535
536 previousBottom = e->y + e->h;
537 }
538
539 if (e->currentAnim == -1 || strcmpignorecase(e->animationName, animationName))
540 {
541 e->currentAnim = -1;
542
543 for (i=0;i<MAX_ANIMATION_TYPES;i++)
544 {
545 if (strcmpignorecase(e->animation[i].name, animationName) == 0)
546 {
547 STRNCPY(e->animationName, e->animation[i].name, MAX_VALUE_LENGTH);
548
549 e->currentAnim = e->animation[i].id;
550
551 break;
552 }
553 }
554
555 if (e->currentAnim == -1)
556 {
557 showErrorAndExit("Animation %s not set for %s", animationName, e->name);
558 }
559
560 e->currentFrame = (e->frameSpeed >= 0 ? 0 : animation[e->currentAnim].frameCount - 1);
561 e->frameTimer = animation[e->currentAnim].frameTimer[e->frameSpeed >= 0 ? 0 : animation[e->currentAnim].frameCount - 1];
562
563 sprite = getSprite(animation[e->currentAnim].frameID[e->frameSpeed >= 0 ? 0 : animation[e->currentAnim].frameCount - 1]);
564
565 if (sprite->image == NULL)
566 {
567 showErrorAndExit("Image index %d for %s is NULL!", animation[e->currentAnim].frameID[0], e->name);
568 }
569
570 e->w = sprite->w;
571 e->h = sprite->h;
572
573 e->offsetX = animation[e->currentAnim].offsetX[e->currentFrame];
574 e->offsetY = animation[e->currentAnim].offsetY[e->currentFrame];
575
576 e->box = sprite->box;
577
578 /* Align the right and bottom edges to stop it looking bad */
579
580 if (previousAnim != -1)
581 {
582 if (e->face == LEFT)
583 {
584 newRightEdge = e->x + e->w;
585
586 e->x += (previousRightEdge - newRightEdge);
587 }
588
589 newBottom = e->y + e->h;
590
591 e->y += (previousBottom - newBottom);
592 }
593 }
594 }
595
hasEntityAnimation(Entity * e,char * animationName)596 int hasEntityAnimation(Entity *e, char *animationName)
597 {
598 int i;
599
600 for (i=0;i<MAX_ANIMATION_TYPES;i++)
601 {
602 if (strcmpignorecase(e->animation[i].name, animationName) == 0)
603 {
604 return TRUE;
605 }
606 }
607
608 return FALSE;
609 }
610
getAnimationTypeAtIndex(Entity * e)611 char *getAnimationTypeAtIndex(Entity *e)
612 {
613 int i;
614
615 for (i=0;i<MAX_ANIMATION_TYPES;i++)
616 {
617 if (e->currentAnim == e->animation[i].id)
618 {
619 return e->animation[i].name;
620 }
621 }
622
623 showErrorAndExit("Failed to find animation at index %d", e->currentAnim);
624
625 return NULL;
626 }
627
setFrameData(Entity * e)628 void setFrameData(Entity *e)
629 {
630 Sprite *sprite;
631
632 if (e->inUse == FALSE)
633 {
634 return;
635 }
636
637 sprite = getSprite(animation[e->currentAnim].frameID[e->currentFrame]);
638
639 e->w = sprite->w;
640 e->h = sprite->h;
641
642 e->offsetX = animation[e->currentAnim].offsetX[e->currentFrame];
643 e->offsetY = animation[e->currentAnim].offsetY[e->currentFrame];
644
645 e->box = sprite->box;
646 }
647
getCurrentSprite(Entity * e)648 Sprite *getCurrentSprite(Entity *e)
649 {
650 if (self->flags & FLASH)
651 {
652 return getSprite(animation[e->currentAnim].frameID[e->currentFrame] + 1);
653 }
654
655 return getSprite(animation[e->currentAnim].frameID[e->currentFrame]);
656 }
657
getFrameCount(Entity * e)658 int getFrameCount(Entity *e)
659 {
660 return animation[e->currentAnim].frameCount;
661 }
662
setEntityAnimationByID(Entity * e,int id)663 void setEntityAnimationByID(Entity *e, int id)
664 {
665 setEntityAnimation(e, e->animation[id].name);
666 }
667