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