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 "audio/audio.h"
23 #include "collisions.h"
24 #include "custom_actions.h"
25 #include "entity.h"
26 #include "event/global_trigger.h"
27 #include "event/script.h"
28 #include "event/trigger.h"
29 #include "game.h"
30 #include "geometry.h"
31 #include "graphics/animation.h"
32 #include "graphics/decoration.h"
33 #include "hud.h"
34 #include "item/item.h"
35 #include "player.h"
36 #include "system/error.h"
37 #include "system/properties.h"
38 #include "system/random.h"
39 #include "system/resources.h"
40 
41 extern Entity player;
42 extern Entity *self;
43 extern Game game;
44 
45 static int drawLayerIndex[MAX_LAYERS];
46 static Entity *drawLayer[MAX_LAYERS][MAX_ENTITIES];
47 static EntityList *entities;
48 
49 static void scriptEntityMoveToTarget(void);
50 static void entityMoveToTarget(void);
51 static void scriptDoNothing(void);
52 static void duplicateWait(void);
53 static int isReferenced(Entity *);
54 
freeEntities()55 void freeEntities()
56 {
57 	EntityList *p, *q;
58 
59 	if (entities != NULL)
60 	{
61 		for (p=entities->next;p!=NULL;p=q)
62 		{
63 			if (p->entity != NULL)
64 			{
65 				free(p->entity);
66 			}
67 
68 			q = p->next;
69 
70 			free(p);
71 		}
72 
73 		free(entities);
74 	}
75 
76 	entities = malloc(sizeof(EntityList));
77 
78 	if (entities == NULL)
79 	{
80 		showErrorAndExit("Failed to allocate a whole %d bytes for Entity List", (int)sizeof(EntityList));
81 	}
82 
83 	entities->next = NULL;
84 
85 	clearDrawLayers();
86 }
87 
getFreeEntity()88 Entity *getFreeEntity()
89 {
90 	Entity *e;
91 
92 	e = malloc(sizeof(Entity));
93 
94 	if (e == NULL)
95 	{
96 		showErrorAndExit("Failed to allocate %d bytes for an Entity", (int)sizeof(Entity));
97 	}
98 
99 	memset(e, 0, sizeof(Entity));
100 
101 	e->inUse = TRUE;
102 
103 	e->active = TRUE;
104 
105 	e->frameSpeed = 1;
106 
107 	e->weight = 1;
108 
109 	e->originalWeight = 1;
110 
111 	e->fallout = NULL;
112 
113 	e->currentAnim = -1;
114 
115 	e->layer = MID_GROUND_LAYER;
116 
117 	e->alpha = 255;
118 
119 	addEntityToList(entities, e);
120 
121 	return e;
122 }
123 
doEntities()124 void doEntities()
125 {
126 	int i;
127 	EntityList *el;
128 
129 	/* Loop through the entities and perform their action */
130 
131 	for (el=entities->next;el!=NULL;el=el->next)
132 	{
133 		self = el->entity;
134 
135 		if (self->inUse == TRUE)
136 		{
137 			self->flags &= ~(HELPLESS|INVULNERABLE|FLASH|ATTRACTED);
138 
139 			for (i=0;i<MAX_CUSTOM_ACTIONS;i++)
140 			{
141 				if (self->customAction[i].thinkTime > 0)
142 				{
143 					doCustomAction(&self->customAction[i]);
144 				}
145 			}
146 
147 			if (!(self->flags & TELEPORTING))
148 			{
149 				if (!(self->flags & (FLY|GRABBED)))
150 				{
151 					switch (self->environment)
152 					{
153 						case WATER:
154 						case SLIME:
155 							self->dirY += GRAVITY_SPEED * 0.25 * self->weight;
156 
157 							if (self->flags & FLOATS)
158 							{
159 								if (self->dirX != 0)
160 								{
161 									self->endY++;
162 
163 									self->dirY = cos(DEG_TO_RAD(self->endY)) / 20;
164 								}
165 							}
166 
167 							if (self->dirY >= MAX_WATER_SPEED)
168 							{
169 								self->dirY = MAX_WATER_SPEED;
170 							}
171 						break;
172 
173 						default:
174 							self->dirY += GRAVITY_SPEED * self->weight;
175 
176 							if (self->dirY >= MAX_AIR_SPEED)
177 							{
178 								self->dirY = MAX_AIR_SPEED;
179 							}
180 
181 							else if (self->dirY > 0 && self->dirY < 1)
182 							{
183 								self->dirY = 1;
184 							}
185 						break;
186 					}
187 				}
188 
189 				if (self->flags & GRABBED)
190 				{
191 					self->dirY = 0;
192 				}
193 
194 				if (self->standingOn != NULL)
195 				{
196 					if (self->standingOn->dirX != 0)
197 					{
198 						self->dirX = self->standingOn->dirX;
199 					}
200 
201 					if (self->standingOn->dirY > 0)
202 					{
203 						self->dirY = self->standingOn->dirY + 1;
204 					}
205 				}
206 
207 				if (!(self->flags & HELPLESS))
208 				{
209 					if (self->action == NULL)
210 					{
211 						showErrorAndExit("%s has no action function", self->name);
212 					}
213 
214 					self->action();
215 				}
216 
217 				else
218 				{
219 					checkToMap(self);
220 				}
221 
222 				if (self->standingOn != NULL)
223 				{
224 					self->flags |= WAS_STANDING_ON;
225 				}
226 
227 				else
228 				{
229 					self->flags &= ~WAS_STANDING_ON;
230 				}
231 
232 				self->standingOn = NULL;
233 
234 				if (self->flags & SPAWNED_IN)
235 				{
236 					self->spawnTime--;
237 				}
238 
239 				if (self->inUse == TRUE)
240 				{
241 					addToGrid(self);
242 				}
243 			}
244 
245 			else
246 			{
247 				doTeleport();
248 			}
249 
250 			addToDrawLayer(self, self->layer);
251 		}
252 	}
253 }
254 
drawEntities(int depth)255 void drawEntities(int depth)
256 {
257 	int i, drawn;
258 	EntityList *el;
259 
260 	/* Draw standard entities */
261 
262 	if (depth == -1)
263 	{
264 		for (el=entities->next;el!=NULL;el=el->next)
265 		{
266 			self = el->entity;
267 
268 			if (self->inUse == TRUE)
269 			{
270 				if (self->draw == NULL)
271 				{
272 					showErrorAndExit("%s has no draw function", self->name);
273 				}
274 
275 				self->draw();
276 			}
277 		}
278 	}
279 
280 	else
281 	{
282 		for (i=0;i<MAX_ENTITIES;i++)
283 		{
284 			self = drawLayer[depth][i];
285 
286 			if (self == NULL)
287 			{
288 				break;
289 			}
290 
291 			if (self->inUse == TRUE && !(self->flags & NO_DRAW) && self->layer == depth)
292 			{
293 				if (self->draw == NULL)
294 				{
295 					showErrorAndExit("%s has no draw function", self->name);
296 				}
297 
298 				drawn = self->draw();
299 
300 				/* Live for 2 minutes whilst on the screen */
301 
302 				if (drawn == TRUE && (self->flags & SPAWNED_IN))
303 				{
304 					self->spawnTime = SPAWNED_IN_TIME;
305 				}
306 
307 				else if (drawn == FALSE && (self->flags & SPAWNED_IN) && self->spawnTime <= 0 && (self->spawnTime % 60 == 0))
308 				{
309 					/* Teleport expired enemies beneath the map */
310 
311 					if (self->health != 0)
312 					{
313 						self->y = (MAX_MAP_Y + 1) * TILE_SIZE;
314 
315 						self->action = &entityDieNoDrop;
316 					}
317 				}
318 			}
319 		}
320 	}
321 }
322 
removeEntity()323 void removeEntity()
324 {
325 	self->thinkTime--;
326 
327 	if (self->thinkTime <= 0)
328 	{
329 		self->inUse = FALSE;
330 	}
331 }
332 
removeAllSpawnedIn()333 void removeAllSpawnedIn()
334 {
335 	Entity *e;
336 	EntityList *el;
337 
338 	for (el=entities->next;el!=NULL;el=el->next)
339 	{
340 		e = el->entity;
341 
342 		if (e->inUse == TRUE && (e->flags & SPAWNED_IN))
343 		{
344 			/* Teleport expired enemies beneath the map */
345 
346 			if (e->health != 0)
347 			{
348 				e->y = (MAX_MAP_Y + 1) * TILE_SIZE;
349 
350 				e->action = &entityDieNoDrop;
351 			}
352 		}
353 	}
354 }
355 
removeUnreferencedEntities()356 void removeUnreferencedEntities()
357 {
358 	int removeCount;
359 	EntityList *el, *prev, *el2;
360 
361 	removeCount = 0;
362 
363 	prev = entities;
364 
365 	for (el=entities->next;el!=NULL;el=el2)
366 	{
367 		el2 = el->next;
368 
369 		if (el->entity->inUse == FALSE && isReferenced(el->entity) == FALSE)
370 		{
371 			prev->next = el2;
372 
373 			removeCount++;
374 
375 			free(el->entity);
376 
377 			el->entity = NULL;
378 
379 			free(el);
380 
381 			el = NULL;
382 		}
383 
384 		else
385 		{
386 			prev = prev->next;
387 		}
388 	}
389 
390 	#if DEV == 1
391 	if (removeCount != 0)
392 	{
393 		printf("Removed %d entities taking up %d bytes\n", removeCount, (int)sizeof(Entity) * removeCount);
394 	}
395 	#endif
396 }
397 
disableSpawners(int disable)398 void disableSpawners(int disable)
399 {
400 	Entity *e;
401 	EntityList *el;
402 
403 	for (el=entities->next;el!=NULL;el=el->next)
404 	{
405 		e = el->entity;
406 
407 		if (e->inUse == TRUE && e->type == SPAWNER)
408 		{
409 			if (disable == TRUE)
410 			{
411 				e->flags |= HELPLESS;
412 			}
413 
414 			else
415 			{
416 				e->flags &= ~HELPLESS;
417 			}
418 		}
419 	}
420 }
421 
doNothing()422 void doNothing()
423 {
424 	self->thinkTime--;
425 
426 	if (self->thinkTime < 0)
427 	{
428 		self->thinkTime = 0;
429 	}
430 
431 	if (self->flags & PUSHABLE)
432 	{
433 		self->frameSpeed = 0;
434 	}
435 
436 	if (!(self->flags & GRABBED))
437 	{
438 		if (self->flags & PUSHABLE)
439 		{
440 			self->dirX = self->standingOn != NULL ? self->standingOn->dirX : 0;
441 		}
442 
443 		else
444 		{
445 			self->dirX = self->standingOn != NULL ? self->standingOn->dirX : (self->flags & ON_GROUND ? 0 : self->dirX);
446 		}
447 
448 		if (self->standingOn != NULL)
449 		{
450 			self->dirX = self->standingOn->dirX;
451 
452 			if (self->standingOn->dirY > 0)
453 			{
454 				self->dirY = self->standingOn->dirY + 1;
455 			}
456 
457 			else if (self->standingOn->dirY == 0 && self->standingOn->dirX == 0)
458 			{
459 				self->dirY = 0;
460 			}
461 
462 			self->flags |= ON_GROUND;
463 		}
464 	}
465 
466 	else if (self->dirX != 0)
467 	{
468 		self->frameSpeed = self->dirX < 0 ? -1 : 1;
469 	}
470 
471 	checkToMap(self);
472 
473 	if (self->environment == WATER && (self->flags & FLOATS))
474 	{
475 		self->action = &floatLeftToRight;
476 
477 		self->endX = self->dirX = 1.0;
478 
479 		self->thinkTime = 0;
480 	}
481 }
482 
moveLeftToRight()483 void moveLeftToRight()
484 {
485 	if (self->dirX == 0)
486 	{
487 		self->x += self->face == LEFT ? self->box.x : -self->box.x;
488 
489 		self->face = self->face == RIGHT ? LEFT : RIGHT;
490 	}
491 
492 	if (self->standingOn == NULL || self->standingOn->dirX == 0)
493 	{
494 		self->dirX = (self->face == RIGHT ? self->speed : -self->speed);
495 	}
496 
497 	else
498 	{
499 		self->dirX += (self->face == RIGHT ? self->speed : -self->speed);
500 	}
501 
502 	if (isAtEdge(self) == TRUE)
503 	{
504 		self->dirX = 0;
505 	}
506 
507 	checkToMap(self);
508 
509 	if (self->dirX == 0)
510 	{
511 		self->dirX = (self->face == RIGHT ? -self->speed : self->speed);
512 
513 		self->face = (self->face == RIGHT ? LEFT : RIGHT);
514 	}
515 }
516 
flyLeftToRight()517 void flyLeftToRight()
518 {
519 	if (self->dirX == 0)
520 	{
521 		self->x += self->face == LEFT ? self->box.x : -self->box.x;
522 
523 		self->dirX = (self->face == RIGHT ? -self->speed : self->speed);
524 
525 		self->face = (self->face == RIGHT ? LEFT : RIGHT);
526 	}
527 
528 	self->thinkTime += 5;
529 
530 	self->dirY += cos(DEG_TO_RAD(self->thinkTime)) / 3;
531 
532 	checkToMap(self);
533 }
534 
floatLeftToRight()535 void floatLeftToRight()
536 {
537 	if (self->thinkTime > 0)
538 	{
539 		self->thinkTime--;
540 
541 		if (self->thinkTime == 0)
542 		{
543 			self->dirX = self->endX;
544 		}
545 	}
546 
547 	else
548 	{
549 		self->dirX = self->endX;
550 
551 		checkToMap(self);
552 
553 		if (self->dirX == 0)
554 		{
555 			self->endX *= -1;
556 
557 			self->thinkTime = 120;
558 		}
559 	}
560 }
561 
syncBoulderFrameSpeed()562 void syncBoulderFrameSpeed()
563 {
564 	float distancePerRevolution = self->w;
565 
566 	distancePerRevolution *= PI;
567 
568 	distancePerRevolution /= getFrameCount(self) + 1;
569 
570 	self->frameSpeed = fabs(self->dirX) / distancePerRevolution;
571 }
572 
entityDie()573 void entityDie()
574 {
575 	self->damage = 0;
576 
577 	self->health = 0;
578 
579 	if (!(self->flags & INVULNERABLE))
580 	{
581 		self->flags &= ~FLY;
582 
583 		self->weight = fabs(self->originalWeight);
584 
585 		self->flags |= DO_NOT_PERSIST;
586 
587 		self->thinkTime = 60;
588 
589 		setCustomAction(self, &invulnerable, 240, 0, 0);
590 
591 		self->frameSpeed = 0;
592 
593 		self->action = &standardDie;
594 
595 		self->damage = 0;
596 	}
597 }
598 
standardDie()599 void standardDie()
600 {
601 	if (self->flags & ON_GROUND)
602 	{
603 		self->dirX = self->standingOn != NULL ? self->standingOn->dirX : 0;
604 	}
605 
606 	self->thinkTime--;
607 
608 	if (self->thinkTime <= 0)
609 	{
610 		fireTrigger(self->objectiveName);
611 
612 		fireGlobalTrigger(self->objectiveName);
613 
614 		self->inUse = FALSE;
615 
616 		dropRandomItem(self->x + self->w / 2, self->y);
617 	}
618 
619 	checkToMap(self);
620 }
621 
entityDieNoDrop()622 void entityDieNoDrop()
623 {
624 	self->damage = 0;
625 
626 	self->health = 0;
627 
628 	if (!(self->flags & INVULNERABLE))
629 	{
630 		self->flags &= ~FLY;
631 
632 		self->weight = fabs(self->originalWeight);
633 
634 		self->flags |= DO_NOT_PERSIST;
635 
636 		self->thinkTime = 60;
637 
638 		setCustomAction(self, &invulnerable, 240, 0, 0);
639 
640 		self->frameSpeed = 0;
641 
642 		self->action = &noItemDie;
643 
644 		fireTrigger(self->objectiveName);
645 
646 		fireGlobalTrigger(self->objectiveName);
647 	}
648 }
649 
entityDieVanish()650 void entityDieVanish()
651 {
652 	fireTrigger(self->objectiveName);
653 
654 	fireGlobalTrigger(self->objectiveName);
655 
656 	self->inUse = FALSE;
657 }
658 
noItemDie()659 void noItemDie()
660 {
661 	if (self->flags & ON_GROUND)
662 	{
663 		self->dirX = self->standingOn != NULL ? self->standingOn->dirX : 0;
664 	}
665 
666 	self->thinkTime--;
667 
668 	if (self->thinkTime <= 0)
669 	{
670 		fireTrigger(self->objectiveName);
671 
672 		fireGlobalTrigger(self->objectiveName);
673 
674 		self->inUse = FALSE;
675 	}
676 
677 	checkToMap(self);
678 }
679 
entityTakeDamageFlinch(Entity * other,int damage)680 void entityTakeDamageFlinch(Entity *other, int damage)
681 {
682 	Entity *temp;
683 
684 	if (self->flags & INVULNERABLE)
685 	{
686 		return;
687 	}
688 
689 	if (damage != 0)
690 	{
691 		self->health -= damage;
692 
693 		if (self->health > 0)
694 		{
695 			setCustomAction(self, &helpless, 10, 0, 0);
696 			setCustomAction(self, &invulnerable, HIT_INVULNERABLE_TIME, 0, 0);
697 
698 			if (self->pain != NULL)
699 			{
700 				self->pain();
701 			}
702 
703 			self->dirX = other->face == RIGHT ? 6 : -6;
704 		}
705 
706 		else
707 		{
708 			self->damage = 0;
709 
710 			if (other->type == WEAPON || other->type == PROJECTILE)
711 			{
712 				increaseKillCount();
713 			}
714 
715 			self->die();
716 		}
717 
718 		if (other->type == PROJECTILE)
719 		{
720 			temp = self;
721 
722 			self = other;
723 
724 			self->die();
725 
726 			self = temp;
727 		}
728 	}
729 }
730 
entityTakeDamageNoFlinch(Entity * other,int damage)731 void entityTakeDamageNoFlinch(Entity *other, int damage)
732 {
733 	Entity *temp;
734 
735 	if (self->flags & INVULNERABLE)
736 	{
737 		return;
738 	}
739 
740 	if (damage != 0)
741 	{
742 		self->health -= damage;
743 
744 		if (self->health > 0)
745 		{
746 			setCustomAction(self, &flashWhite, 6, 0, 0);
747 
748 			/* Don't make an enemy invulnerable from a projectile hit, allows multiple hits */
749 
750 			if (other->type != PROJECTILE)
751 			{
752 				setCustomAction(self, &invulnerableNoFlash, HIT_INVULNERABLE_TIME, 0, 0);
753 			}
754 
755 			if (self->pain != NULL)
756 			{
757 				self->pain();
758 			}
759 		}
760 
761 		else
762 		{
763 			self->damage = 0;
764 
765 			if (other->type == WEAPON || other->type == PROJECTILE)
766 			{
767 				increaseKillCount();
768 			}
769 
770 			self->die();
771 		}
772 
773 		if (other->type == PROJECTILE)
774 		{
775 			temp = self;
776 
777 			self = other;
778 
779 			self->die();
780 
781 			self = temp;
782 		}
783 	}
784 }
785 
enemyPain()786 void enemyPain()
787 {
788 	int i = prand() % 3;
789 
790 	switch (i)
791 	{
792 		case 0:
793 			playSoundToMap("sound/common/splat1", -1, self->x, self->y, 0);
794 		break;
795 
796 		case 1:
797 			playSoundToMap("sound/common/splat2", -1, self->x, self->y, 0);
798 		break;
799 
800 		default:
801 			playSoundToMap("sound/common/splat3", -1, self->x, self->y, 0);
802 		break;
803 	}
804 }
805 
entityTouch(Entity * other)806 void entityTouch(Entity *other)
807 {
808 	Entity *temp;
809 
810 	if (other->type == PLAYER && self->parent != other && self->damage != 0)
811 	{
812 		temp = self;
813 
814 		self = other;
815 
816 		self->takeDamage(temp, temp->damage);
817 
818 		self = temp;
819 	}
820 
821 	else if ((other->type == EXPLOSION || (other->type == WEAPON && (other->flags & ATTACKING))) && other->damage != 0)
822 	{
823 		if (self->takeDamage != NULL && !(self->flags & INVULNERABLE))
824 		{
825 			self->takeDamage(other, other->damage);
826 		}
827 	}
828 
829 	else if (other->type == PROJECTILE && other->parent != self && self->health > 0)
830 	{
831 		if (self->takeDamage != NULL && !(self->flags & INVULNERABLE))
832 		{
833 			self->takeDamage(other, other->damage);
834 		}
835 	}
836 }
837 
pushEntity(Entity * other)838 void pushEntity(Entity *other)
839 {
840 	int pushable, collided;
841 	int x1, x2, y1, y2;
842 	Entity *temp;
843 	float dirX;
844 	static int depth = 0;
845 	long wasOnGround;
846 
847 	if (other->touch == NULL || (other->type == WEAPON && (other->flags & ATTACKING)) || (other->flags & PLAYER_TOUCH_ONLY))
848 	{
849 		return;
850 	}
851 
852 	if (other->flags & OBSTACLE)
853 	{
854 		return;
855 	}
856 
857 	if (other->type == PROJECTILE)
858 	{
859 		temp = self;
860 
861 		self = other;
862 
863 		self->die();
864 
865 		self = temp;
866 
867 		return;
868 	}
869 
870 	other->x -= other->dirX;
871 	other->y -= other->dirY;
872 	/*
873 	if (other->type == PLAYER && other->dirX > 0)
874 	{
875 		other->x = ceil(other->x);
876 	}
877 
878 	else if (other->type == PLAYER && other->dirX < 0)
879 	{
880 		other->x = floor(other->x);
881 	}
882 	*/
883 	x1 = getLeftEdge(self);
884 
885 	y1 = self->y + self->box.y;
886 
887 	x2 = getLeftEdge(other);
888 
889 	y2 = other->y + other->box.y;
890 
891 	pushable = (self->flags & PUSHABLE);
892 
893 	if ((self->flags & OBSTACLE) || depth == 1)
894 	{
895 		pushable = 0;
896 	}
897 
898 	collided = FALSE;
899 
900 	/* Test the vertical movement */
901 
902 	if (other->dirY > 0)
903 	{
904 		/* Trying to move down */
905 
906 		if (collision(x1, y1, self->box.w, self->box.h, x2, y2 + ceil(other->dirY), other->box.w, other->box.h) == TRUE)
907 		{
908 			if (self->dirY < 0)
909 			{
910 				other->y -= self->dirY;
911 
912 				other->dirY = self->dirY;
913 
914 				dirX = other->dirX;
915 
916 				other->dirX = 0;
917 
918 				checkToMap(other);
919 
920 				if (other->dirY == 0)
921 				{
922 					self->y = other->y + other->h;
923 
924 					self->dirY = 0;
925 				}
926 
927 				other->dirX = dirX;
928 			}
929 
930 			if (self->type != AUTO_DOOR || (self->type == AUTO_DOOR && (int)self->startY == (int)self->endY))
931 			{
932 				/* Place the entity as close as possible */
933 
934 				other->y = self->y + self->box.y;
935 				other->y -= other->box.h + other->box.y;
936 
937 				other->standingOn = self;
938 				other->dirY = 0;
939 				other->flags |= ON_GROUND;
940 
941 				collided = TRUE;
942 			}
943 
944 			else
945 			{
946 				self->y = other->y - other->h;
947 			}
948 		}
949 	}
950 
951 	else if (other->dirY < 0 && !(self->flags & ON_GROUND))
952 	{
953 		/* Trying to move up */
954 
955 		if (collision(x1, y1, self->box.w, self->box.h, x2, y2 + floor(other->dirY), other->box.w, other->box.h) == TRUE)
956 		{
957 			/* Place the entity as close as possible */
958 
959 			other->y = self->y + self->box.y;
960 			other->y += self->box.h;
961 
962 			other->dirY = 0;
963 
964 			collided = TRUE;
965 		}
966 	}
967 
968 	else if (self->type == AUTO_DOOR && other->dirY == 0 && self->dirY > 0)
969 	{
970 		if (collision(x1, y1, self->box.w, self->box.h, x2, y2 + floor(other->dirY), other->box.w, other->box.h) == TRUE)
971 		{
972 			self->y = other->y - self->h;
973 
974 			collided = TRUE;
975 		}
976 	}
977 
978 	/* Test the horizontal movement */
979 
980 	if (other->dirX > 0 && collided == FALSE)
981 	{
982 		/* Trying to move right */
983 
984 		if (collision(x1, y1, self->box.w, self->box.h, x2 + ceil(other->dirX), y2, other->box.w, other->box.h) == TRUE)
985 		{
986 			if (pushable != 0)
987 			{
988 				self->y -= self->dirY;
989 
990 				/*self->dirX += ceil(other->dirX);*/
991 
992 				self->dirX += other->dirX;
993 
994 				wasOnGround = (self->flags & ON_GROUND);
995 
996 				checkToMap(self);
997 
998 				if (wasOnGround != 0)
999 				{
1000 					self->flags |= ON_GROUND;
1001 				}
1002 
1003 				self->y -= self->dirY;
1004 
1005 				depth++;
1006 
1007 				if (checkEntityToEntity(self) != NULL)
1008 				{
1009 					self->x -= other->dirX;
1010 
1011 					self->dirX = 0;
1012 				}
1013 
1014 				depth--;
1015 
1016 				if (self->dirX == 0)
1017 				{
1018 					pushable = 0;
1019 				}
1020 
1021 				else
1022 				{
1023 					self->frameSpeed = 1;
1024 				}
1025 
1026 				self->y += self->dirY;
1027 			}
1028 
1029 			if (pushable == 0)
1030 			{
1031 				/* Place the entity as close as possible */
1032 
1033 				other->x = getLeftEdge(self) - other->w;
1034 
1035 				if (other->face == RIGHT)
1036 				{
1037 					other->x += other->w - other->box.x - other->box.w;
1038 				}
1039 
1040 				else
1041 				{
1042 					other->x += other->w - other->box.w;
1043 				}
1044 
1045 				other->dirX = (other->flags & BOUNCES) ? -other->dirX : 0;
1046 
1047 				if ((other->flags & GRABBING) && other->target != NULL)
1048 				{
1049 					other->target->x -= other->target->dirX;
1050 					other->target->dirX = 0;
1051 				}
1052 			}
1053 
1054 			if ((other->flags & GRABBING) && other->target == NULL && (self->flags & PUSHABLE))
1055 			{
1056 				other->target = self;
1057 
1058 				self->flags |= GRABBED;
1059 			}
1060 
1061 			collided = TRUE;
1062 		}
1063 	}
1064 
1065 	else if (other->dirX < 0 && collided == FALSE)
1066 	{
1067 		/* Trying to move left */
1068 
1069 		if (collision(x1, y1, self->box.w, self->box.h, x2 + floor(other->dirX), y2, other->box.w, other->box.h) == TRUE)
1070 		{
1071 			if (pushable != 0)
1072 			{
1073 				self->y -= self->dirY;
1074 
1075 				/*self->dirX += floor(other->dirX);*/
1076 
1077 				self->dirX += other->dirX;
1078 
1079 				wasOnGround = (self->flags & ON_GROUND);
1080 
1081 				checkToMap(self);
1082 
1083 				if (wasOnGround != 0)
1084 				{
1085 					self->flags |= ON_GROUND;
1086 				}
1087 
1088 				self->y -= self->dirY;
1089 
1090 				depth++;
1091 
1092 				if (checkEntityToEntity(self) != NULL)
1093 				{
1094 					self->x -= other->dirX;
1095 
1096 					self->dirX = 0;
1097 				}
1098 
1099 				depth--;
1100 
1101 				if (self->dirX == 0)
1102 				{
1103 					pushable = 0;
1104 				}
1105 
1106 				else
1107 				{
1108 					self->frameSpeed = -1;
1109 				}
1110 
1111 				self->y += self->dirY;
1112 			}
1113 
1114 			if (pushable == 0)
1115 			{
1116 				/* Place the entity as close as possible */
1117 
1118 				other->x = getRightEdge(self);
1119 
1120 				if (other->face == RIGHT)
1121 				{
1122 					other->x -= other->box.x;
1123 				}
1124 
1125 				else
1126 				{
1127 					other->x -= other->w - (other->box.w + other->box.x);
1128 				}
1129 
1130 				other->dirX = (other->flags & BOUNCES) ? -other->dirX : 0;
1131 
1132 				if ((other->flags & GRABBING) && other->target != NULL)
1133 				{
1134 					other->target->x -= other->target->dirX;
1135 					other->target->dirX = 0;
1136 				}
1137 			}
1138 
1139 			if ((other->flags & GRABBING) && other->target == NULL && (self->flags & PUSHABLE))
1140 			{
1141 				other->target = self;
1142 
1143 				self->flags |= GRABBED;
1144 			}
1145 
1146 			collided = TRUE;
1147 		}
1148 	}
1149 
1150 	other->x += other->dirX;
1151 	other->y += other->dirY;
1152 }
1153 
addEntity(Entity e,int x,int y)1154 Entity *addEntity(Entity e, int x, int y)
1155 {
1156 	Entity *ent;
1157 
1158 	ent = getFreeEntity();
1159 
1160 	memcpy(ent, &e, sizeof(Entity));
1161 
1162 	ent->currentFrame = 0;
1163 
1164 	ent->inUse = TRUE;
1165 
1166 	ent->x = x;
1167 
1168 	ent->y = y;
1169 
1170 	return ent;
1171 
1172 }
1173 
getEntityByName(char * name)1174 Entity *getEntityByName(char *name)
1175 {
1176 	EntityList *el;
1177 
1178 	for (el=entities->next;el!=NULL;el=el->next)
1179 	{
1180 		if (el->entity->inUse == TRUE && strcmpignorecase(el->entity->name, name) == 0)
1181 		{
1182 			return el->entity;
1183 		}
1184 	}
1185 
1186 	return NULL;
1187 }
1188 
getEntityByObjectiveName(char * name)1189 Entity *getEntityByObjectiveName(char *name)
1190 {
1191 	EntityList *el;
1192 
1193 	for (el=entities->next;el!=NULL;el=el->next)
1194 	{
1195 		if (el->entity->inUse == TRUE && strcmpignorecase(el->entity->objectiveName, name) == 0)
1196 		{
1197 			return el->entity;
1198 		}
1199 	}
1200 
1201 	return NULL;
1202 }
1203 
getEntityByRequiredName(char * name)1204 Entity *getEntityByRequiredName(char *name)
1205 {
1206 	EntityList *el;
1207 
1208 	for (el=entities->next;el!=NULL;el=el->next)
1209 	{
1210 		if (el->entity->inUse == TRUE && strcmpignorecase(el->entity->requires, name) == 0)
1211 		{
1212 			return el->entity;
1213 		}
1214 	}
1215 
1216 	return NULL;
1217 }
1218 
getEntitiesByObjectiveName(char * name)1219 EntityList *getEntitiesByObjectiveName(char *name)
1220 {
1221 	EntityList *list, *el;
1222 
1223 	list = malloc(sizeof(EntityList));
1224 
1225 	if (list == NULL)
1226 	{
1227 		showErrorAndExit("Failed to allocate a whole %d bytes for Entity List", (int)sizeof(EntityList));
1228 	}
1229 
1230 	list->next = NULL;
1231 
1232 	for (el=entities->next;el!=NULL;el=el->next)
1233 	{
1234 		if (el->entity->inUse == TRUE && strcmpignorecase(el->entity->objectiveName, name) == 0)
1235 		{
1236 			addEntityToList(list, el->entity);
1237 		}
1238 	}
1239 
1240 	return list;
1241 }
1242 
getEntitiesByRequiredName(char * name)1243 EntityList *getEntitiesByRequiredName(char *name)
1244 {
1245 	EntityList *list, *el;
1246 
1247 	list = malloc(sizeof(EntityList));
1248 
1249 	if (list == NULL)
1250 	{
1251 		showErrorAndExit("Failed to allocate a whole %d bytes for Entity List", (int)sizeof(EntityList));
1252 	}
1253 
1254 	list->next = NULL;
1255 
1256 	for (el=entities->next;el!=NULL;el=el->next)
1257 	{
1258 		if (el->entity->inUse == TRUE && strcmpignorecase(el->entity->requires, name) == 0)
1259 		{
1260 			addEntityToList(list, el->entity);
1261 		}
1262 	}
1263 
1264 	return list;
1265 }
1266 
getEntitiesByName(char * name)1267 EntityList *getEntitiesByName(char *name)
1268 {
1269 	EntityList *list, *el;
1270 
1271 	list = malloc(sizeof(EntityList));
1272 
1273 	if (list == NULL)
1274 	{
1275 		showErrorAndExit("Failed to allocate a whole %d bytes for Entity List", (int)sizeof(EntityList));
1276 	}
1277 
1278 	list->next = NULL;
1279 
1280 	for (el=entities->next;el!=NULL;el=el->next)
1281 	{
1282 		if (el->entity->inUse == TRUE && strcmpignorecase(el->entity->name, name) == 0)
1283 		{
1284 			addEntityToList(list, el->entity);
1285 		}
1286 	}
1287 
1288 	return list;
1289 }
1290 
freeEntityList(EntityList * list)1291 void freeEntityList(EntityList *list)
1292 {
1293 	EntityList *p, *q;
1294 
1295 	if (list == NULL)
1296 	{
1297 		return;
1298 	}
1299 
1300 	for (p=list->next;p!=NULL;p=q)
1301 	{
1302 		q = p->next;
1303 
1304 		free(p);
1305 	}
1306 
1307 	free(list);
1308 }
1309 
getEntityByStartXY(int x,int y)1310 Entity *getEntityByStartXY(int x, int y)
1311 {
1312 	EntityList *el;
1313 
1314 	for (el=entities->next;el!=NULL;el=el->next)
1315 	{
1316 		if (el->entity->inUse == TRUE && el->entity->startX == x && el->entity->startY == y)
1317 		{
1318 			return el->entity;
1319 		}
1320 	}
1321 
1322 	return NULL;
1323 }
1324 
getEntityByXY(int x,int y)1325 Entity *getEntityByXY(int x, int y)
1326 {
1327 	EntityList *el;
1328 
1329 	for (el=entities->next;el!=NULL;el=el->next)
1330 	{
1331 		if (el->entity->inUse == TRUE && el->entity->x == x && el->entity->y == y)
1332 		{
1333 			return el->entity;
1334 		}
1335 	}
1336 
1337 	return NULL;
1338 }
1339 
activateEntitiesWithRequiredName(char * name,int active)1340 void activateEntitiesWithRequiredName(char *name, int active)
1341 {
1342 	EntityList *el;
1343 
1344 	if (name == NULL || strlen(name) == 0)
1345 	{
1346 		showErrorAndExit("Activate Required Entities : Name is blank!");
1347 	}
1348 
1349 	for (el=entities->next;el!=NULL;el=el->next)
1350 	{
1351 		if (el->entity->inUse == TRUE && strcmpignorecase(el->entity->requires, name) == 0)
1352 		{
1353 			el->entity->active = active;
1354 		}
1355 	}
1356 }
1357 
activateEntitiesWithObjectiveName(char * name,int active)1358 void activateEntitiesWithObjectiveName(char *name, int active)
1359 {
1360 	EntityList *el;
1361 
1362 	if (name == NULL || strlen(name) == 0)
1363 	{
1364 		showErrorAndExit("Activate Objective Entities : Name is blank!");
1365 	}
1366 
1367 	for (el=entities->next;el!=NULL;el=el->next)
1368 	{
1369 		if (el->entity->inUse == TRUE && strcmpignorecase(el->entity->objectiveName, name) == 0)
1370 		{
1371 			el->entity->active = active;
1372 		}
1373 	}
1374 }
1375 
activateEntitiesValueWithObjectiveName(char * name,int value)1376 void activateEntitiesValueWithObjectiveName(char *name, int value)
1377 {
1378 	EntityList *el;
1379 	Entity *temp;
1380 
1381 	for (el=entities->next;el!=NULL;el=el->next)
1382 	{
1383 		if (el->entity->inUse == TRUE && el->entity->activate != NULL && strcmpignorecase(el->entity->objectiveName, name) == 0)
1384 		{
1385 			temp = self;
1386 
1387 			self = el->entity;
1388 
1389 			if (self->type == MANUAL_LIFT && self->active == FALSE)
1390 			{
1391 				setInfoBoxMessage(90, 255, 255, 255, _("This lift is not active"));
1392 			}
1393 
1394 			else
1395 			{
1396 				self->activate(value - self->health);
1397 			}
1398 
1399 			self = temp;
1400 		}
1401 	}
1402 }
1403 
interactWithEntity(int x,int y,int w,int h)1404 void interactWithEntity(int x, int y, int w, int h)
1405 {
1406 	EntityList *el;
1407 	Entity *e;
1408 
1409 	for (el=entities->next;el!=NULL;el=el->next)
1410 	{
1411 		if (el->entity->inUse == TRUE && el->entity->activate != NULL)
1412 		{
1413 			if (collision(x, y, w, h, el->entity->x + el->entity->box.x, el->entity->y + el->entity->box.y, el->entity->box.w, el->entity->box.h) == 1)
1414 			{
1415 				e = self;
1416 
1417 				self = el->entity;
1418 
1419 				self->activate(0);
1420 
1421 				self = e;
1422 			}
1423 		}
1424 	}
1425 }
1426 
initLineDefs()1427 void initLineDefs()
1428 {
1429 	EntityList *el;
1430 
1431 	for (el=entities->next;el!=NULL;el=el->next)
1432 	{
1433 		if (el->entity->inUse == TRUE
1434 			&& (el->entity->type == LINE_DEF || el->entity->type == SCRIPT_LINE_DEF || strcmpignorecase(el->entity->name, "item/phase_door") == 0))
1435 		{
1436 			self = el->entity;
1437 
1438 			self->flags &= ~NO_DRAW;
1439 
1440 			self->action();
1441 		}
1442 	}
1443 }
1444 
changeDirection(Entity * other)1445 void changeDirection(Entity *other)
1446 {
1447 	self->dirX = 0;
1448 }
1449 
writeEntitiesToFile(FILE * fp)1450 void writeEntitiesToFile(FILE *fp)
1451 {
1452 	int count;
1453 	EntityList *el;
1454 
1455 	count = 0;
1456 
1457 	for (el=entities->next;el!=NULL;el=el->next)
1458 	{
1459 		self = el->entity;
1460 
1461 		if (self->inUse == TRUE && self->type != PROJECTILE && !(self->flags & DO_NOT_PERSIST))
1462 		{
1463 			fprintf(fp, "{\n");
1464 			fprintf(fp, "TYPE %s\n", getEntityTypeByID(self->type));
1465 			fprintf(fp, "NAME %s\n", self->name);
1466 			fprintf(fp, "X %d\n", (self->flags & TELEPORTING) ? (int)self->targetX : (int)self->x);
1467 			fprintf(fp, "Y %d\n", (self->flags & TELEPORTING) ? (int)self->targetY : (int)self->y);
1468 			fprintf(fp, "START_X %d\n", (int)self->startX);
1469 			fprintf(fp, "START_Y %d\n", (int)self->startY);
1470 			fprintf(fp, "END_X %d\n", (int)self->endX);
1471 			fprintf(fp, "END_Y %d\n", (int)self->endY);
1472 			fprintf(fp, "DIR_X %0.2f\n", (self->flags & TELEPORTING) ? 0 : self->dirX);
1473 			fprintf(fp, "DIR_Y %0.2f\n", (self->flags & TELEPORTING) ? 0 : self->dirY);
1474 			fprintf(fp, "MAX_THINKTIME %d\n", self->maxThinkTime);
1475 			fprintf(fp, "THINKTIME %d\n", self->thinkTime);
1476 			fprintf(fp, "MENTAL %d\n", self->mental);
1477 			fprintf(fp, "HEALTH %d\n", self->health);
1478 			fprintf(fp, "DAMAGE %d\n", self->damage);
1479 			fprintf(fp, "SPAWNTIME %d\n", self->spawnTime);
1480 			fprintf(fp, "SPEED %0.2f\n", self->speed); /* Save the original speed, not the current speed */
1481 			fprintf(fp, "WEIGHT %0.2f\n", self->originalWeight); /* Save the original weight, not the current weight */
1482 			fprintf(fp, "OBJECTIVE_NAME %s\n", self->objectiveName);
1483 			fprintf(fp, "REQUIRES %s\n", self->requires);
1484 			fprintf(fp, "ACTIVE %s\n", self->active == TRUE ? "TRUE" : "FALSE");
1485 			fprintf(fp, "FACE %s\n", self->face == RIGHT ? "RIGHT" : "LEFT");
1486 			fprintf(fp, "}\n\n");
1487 
1488 			count++;
1489 		}
1490 	}
1491 
1492 	printf("Total Entities in use: %d\n", count);
1493 }
1494 
addEntityFromScript(char * line)1495 void addEntityFromScript(char *line)
1496 {
1497 	char entityType[MAX_VALUE_LENGTH], entityName[MAX_VALUE_LENGTH], objectiveName[MAX_VALUE_LENGTH];
1498 	int x, y;
1499 	Entity *e;
1500 
1501 	sscanf(line, "%s %s \"%[^\"]\" %d %d", entityType, entityName, objectiveName, &x, &y);
1502 
1503 	e = addEntityFromResource(entityType, entityName, x, y);
1504 
1505 	e->startX = x;
1506 	e->startY = y;
1507 	e->endX = x;
1508 	e->endY = y;
1509 
1510 	if (strcmpignorecase(objectiveName, " ") != 0)
1511 	{
1512 		STRNCPY(e->objectiveName, objectiveName, sizeof(e->objectiveName));
1513 	}
1514 }
1515 
entityWalkTo(Entity * e,char * coords)1516 void entityWalkTo(Entity *e, char *coords)
1517 {
1518 	int x, y, read;
1519 	char wait[10], anim[20];
1520 
1521 	read = sscanf(coords, "%d %d %s %s", &x, &y, wait, anim);
1522 
1523 	e->targetX = x;
1524 	e->targetY = y;
1525 
1526 	if (!(e->flags & FLY))
1527 	{
1528 		e->targetY = e->y;
1529 	}
1530 
1531 	if (strcmpignorecase(wait, "WAIT") == 0)
1532 	{
1533 		e->action = &scriptEntityMoveToTarget;
1534 
1535 		setScriptCounter(1);
1536 	}
1537 
1538 	else
1539 	{
1540 		e->action = &entityMoveToTarget;
1541 	}
1542 
1543 	e->face = (e->x < e->targetX) ? RIGHT : LEFT;
1544 
1545 	if (read == 4)
1546 	{
1547 		setEntityAnimation(e, anim);
1548 	}
1549 
1550 	else
1551 	{
1552 		setEntityAnimation(e, "WALK");
1553 	}
1554 
1555 	if (e->type == PLAYER)
1556 	{
1557 		syncWeaponShieldToPlayer();
1558 	}
1559 }
1560 
entityWalkToEntity(Entity * e,char * coords)1561 void entityWalkToEntity(Entity *e, char *coords)
1562 {
1563 	int read;
1564 	char wait[10], anim[20], entityName[MAX_VALUE_LENGTH];
1565 	Entity *e2;
1566 
1567 	read = sscanf(coords, "%s %s %s", entityName, wait, anim);
1568 
1569 	e2 = getEntityByObjectiveName(entityName);
1570 
1571 	if (e2 == NULL)
1572 	{
1573 		showErrorAndExit("Could not find Entity %s to walk to", entityName);
1574 	}
1575 
1576 	e->targetX = e2->x;
1577 	e->targetY = e2->y;
1578 
1579 	if (!(e->flags & FLY))
1580 	{
1581 		e->targetY = e->y;
1582 	}
1583 
1584 	if (strcmpignorecase(wait, "WAIT") == 0)
1585 	{
1586 		e->action = &scriptEntityMoveToTarget;
1587 
1588 		setScriptCounter(1);
1589 	}
1590 
1591 	else
1592 	{
1593 		e->action = &entityMoveToTarget;
1594 	}
1595 
1596 	e->face = (e->x < e->targetX) ? RIGHT : LEFT;
1597 
1598 	if (read == 3)
1599 	{
1600 		setEntityAnimation(e, anim);
1601 	}
1602 
1603 	else
1604 	{
1605 		setEntityAnimation(e, "WALK");
1606 	}
1607 
1608 	if (e->type == PLAYER)
1609 	{
1610 		syncWeaponShieldToPlayer();
1611 	}
1612 }
1613 
entityWalkToRelative(Entity * e,char * coords)1614 void entityWalkToRelative(Entity *e, char *coords)
1615 {
1616 	int x, y, read;
1617 	char wait[10], anim[20];
1618 
1619 	read = sscanf(coords, "%d %d %s %s", &x, &y, wait, anim);
1620 
1621 	e->targetX = e->x + x;
1622 	e->targetY = e->y + y;
1623 
1624 	if (!(e->flags & FLY))
1625 	{
1626 		e->targetY = e->y;
1627 	}
1628 
1629 	if (strcmpignorecase(wait, "WAIT") == 0)
1630 	{
1631 		e->action = &scriptEntityMoveToTarget;
1632 
1633 		setScriptCounter(1);
1634 	}
1635 
1636 	else
1637 	{
1638 		e->action = &entityMoveToTarget;
1639 	}
1640 
1641 	e->face = (e->x < e->targetX) ? RIGHT : LEFT;
1642 
1643 	if (read == 4)
1644 	{
1645 		setEntityAnimation(e, anim);
1646 	}
1647 
1648 	else
1649 	{
1650 		setEntityAnimation(e, "WALK");
1651 	}
1652 
1653 	if (e->type == PLAYER)
1654 	{
1655 		syncWeaponShieldToPlayer();
1656 	}
1657 }
1658 
scriptEntityMoveToTarget()1659 static void scriptEntityMoveToTarget()
1660 {
1661 	if (self->speed == 0)
1662 	{
1663 		showErrorAndExit("%s has a speed of 0 and will not move!", self->objectiveName);
1664 	}
1665 
1666 	if (abs(self->x - self->targetX) > self->speed)
1667 	{
1668 		self->dirX = (self->x < self->targetX ? self->speed : -self->speed);
1669 	}
1670 
1671 	else
1672 	{
1673 		self->x = self->targetX;
1674 	}
1675 
1676 	if (!(self->flags & FLY))
1677 	{
1678 		self->targetY = self->y;
1679 	}
1680 
1681 	if (abs(self->y - self->targetY) > self->speed)
1682 	{
1683 		self->dirY = (self->y < self->targetY ? self->speed : -self->speed);
1684 	}
1685 
1686 	else
1687 	{
1688 		self->y = self->targetY;
1689 	}
1690 
1691 	if (self->x == self->targetX && self->y == self->targetY)
1692 	{
1693 		setEntityAnimation(self, "STAND");
1694 
1695 		if (self->type == PLAYER)
1696 		{
1697 			self->action = &playerWaitForDialog;
1698 
1699 			syncWeaponShieldToPlayer();
1700 		}
1701 
1702 		else
1703 		{
1704 			self->dirX = 0;
1705 			self->dirY = 0;
1706 
1707 			self->action = &scriptDoNothing;
1708 		}
1709 
1710 		setScriptCounter(-1);
1711 	}
1712 
1713 	else
1714 	{
1715 		checkToMap(self);
1716 	}
1717 }
1718 
scriptDoNothing()1719 static void scriptDoNothing()
1720 {
1721 	if (!(self->flags & FLY))
1722 	{
1723 		if (self->standingOn != NULL)
1724 		{
1725 			self->dirX = self->standingOn->dirX;
1726 
1727 			if (self->standingOn->dirY > 0)
1728 			{
1729 				self->dirY = self->standingOn->dirY + 1;
1730 			}
1731 
1732 			self->flags |= ON_GROUND;
1733 		}
1734 	}
1735 
1736 	checkToMap(self);
1737 }
1738 
entityMoveToTarget()1739 static void entityMoveToTarget()
1740 {
1741 	if (self->speed == 0)
1742 	{
1743 		showErrorAndExit("%s has a speed of 0 and will not move!", self->objectiveName);
1744 	}
1745 
1746 	if (abs(self->x - self->targetX) > self->speed)
1747 	{
1748 		self->dirX = (self->x < self->targetX ? self->speed : -self->speed);
1749 	}
1750 
1751 	else
1752 	{
1753 		self->x = self->targetX;
1754 
1755 		self->dirX = 0;
1756 	}
1757 
1758 	if (!(self->flags & FLY))
1759 	{
1760 		self->targetY = self->y;
1761 	}
1762 
1763 	if (abs(self->y - self->targetY) > self->speed)
1764 	{
1765 		self->dirY = (self->y < self->targetY ? self->speed : -self->speed);
1766 	}
1767 
1768 	else
1769 	{
1770 		self->y = self->targetY;
1771 
1772 		self->dirY = 0;
1773 	}
1774 
1775 	if (self->x == self->targetX && self->y == self->targetY)
1776 	{
1777 		setEntityAnimation(self, "STAND");
1778 
1779 		if (self->type == PLAYER)
1780 		{
1781 			self->action = &playerWaitForDialog;
1782 
1783 			syncWeaponShieldToPlayer();
1784 		}
1785 	}
1786 
1787 	checkToMap(self);
1788 }
1789 
rotateAroundStartPoint()1790 void rotateAroundStartPoint()
1791 {
1792 	float x, y, radians;
1793 
1794 	x = self->endX;
1795 	y = self->endY;
1796 
1797 	self->thinkTime += self->speed;
1798 
1799 	if (self->thinkTime >= 360)
1800 	{
1801 		self->thinkTime = 0;
1802 	}
1803 
1804 	radians = DEG_TO_RAD(self->thinkTime);
1805 
1806 	self->x = (x * cos(radians) - y * sin(radians));
1807 	self->y = (x * sin(radians) + y * cos(radians));
1808 
1809 	self->x += self->startX - (self->w / 2);
1810 	self->y += self->startY - (self->h / 2);
1811 }
1812 
countSiblings(Entity * sibling,int * total)1813 int countSiblings(Entity *sibling, int *total)
1814 {
1815 	int remaining = 0;
1816 	EntityList *el;
1817 
1818 	*total = 0;
1819 
1820 	for (el=entities->next;el!=NULL;el=el->next)
1821 	{
1822 		if (el->entity->inUse == TRUE && sibling != el->entity && sibling->type == el->entity->type
1823 			&& strcmpignorecase(sibling->objectiveName, el->entity->objectiveName) == 0)
1824 		{
1825 			if (el->entity->active == FALSE)
1826 			{
1827 				remaining++;
1828 			}
1829 
1830 			(*total)++;
1831 		}
1832 	}
1833 
1834 	return remaining;
1835 }
1836 
doTeleport()1837 void doTeleport()
1838 {
1839 	int i;
1840 	float speed;
1841 	Entity *e;
1842 
1843 	if (abs(self->x - self->targetX) < TELEPORT_SPEED && abs(self->y - self->targetY) < TELEPORT_SPEED)
1844 	{
1845 		self->flags &= ~(NO_DRAW|HELPLESS|TELEPORTING);
1846 
1847 		self->x = self->targetX;
1848 		self->y = self->targetY;
1849 
1850 		addParticleExplosion(self->x + self->w / 2, self->y + self->h / 2);
1851 
1852 		self->dirY = self->dirX = 0;
1853 
1854 		self->standingOn = NULL;
1855 
1856 		if (!(self->flags & NO_END_TELEPORT_SOUND))
1857 		{
1858 			playSoundToMap("sound/common/teleport", -1, self->x, self->y, 0);
1859 		}
1860 	}
1861 
1862 	else
1863 	{
1864 		self->flags |= NO_DRAW|HELPLESS|INVULNERABLE;
1865 
1866 		speed = getDistance(self->x, self->y, self->targetX, self->targetY) / 20;
1867 
1868 		speed = speed < TELEPORT_SPEED ? TELEPORT_SPEED : (speed > 30 ? 30 : speed);
1869 
1870 		normalize(&self->dirX, &self->dirY);
1871 
1872 		self->dirX *= speed;
1873 		self->dirY *= speed;
1874 
1875 		self->x += self->dirX;
1876 		self->y += self->dirY;
1877 
1878 		for (i=0;i<5;i++)
1879 		{
1880 			e = addBasicDecoration(self->x, self->y, "decoration/particle");
1881 
1882 			if (e != NULL)
1883 			{
1884 				e->x += prand() % self->w;
1885 				e->y += prand() % self->h;
1886 
1887 				e->thinkTime = 5 + prand() % 30;
1888 
1889 				setEntityAnimationByID(e, prand() % 5);
1890 			}
1891 		}
1892 	}
1893 }
1894 
getLeftEdge(Entity * e)1895 int getLeftEdge(Entity *e)
1896 {
1897 	return e->face == RIGHT ? e->x + e->box.x : e->x + e->w - e->box.w - e->box.x;
1898 }
1899 
getRightEdge(Entity * e)1900 int getRightEdge(Entity *e)
1901 {
1902 	return e->face == RIGHT ? e->x + e->box.x + e->box.w : e->x + e->w - e->box.x;
1903 }
1904 
addEntityToList(EntityList * head,Entity * e)1905 void addEntityToList(EntityList *head, Entity *e)
1906 {
1907 	EntityList *listHead, *list;
1908 
1909 	listHead = head;
1910 
1911 	while (listHead->next != NULL)
1912 	{
1913 		listHead = listHead->next;
1914 	}
1915 
1916 	list = malloc(sizeof(EntityList));
1917 
1918 	if (list == NULL)
1919 	{
1920 		showErrorAndExit("Failed to allocate a whole %d bytes for Entity List", (int)sizeof(EntityList));
1921 	}
1922 
1923 	list->entity = e;
1924 	list->next = NULL;
1925 
1926 	listHead->next = list;
1927 }
1928 
killEntity(char * name)1929 void killEntity(char *name)
1930 {
1931 	Entity *e = getEntityByObjectiveName(name);
1932 
1933 	if (e != NULL)
1934 	{
1935 		e->inUse = FALSE;
1936 	}
1937 }
1938 
atTarget()1939 int atTarget()
1940 {
1941 	if (fabs(self->dirY) > 0 && fabs(self->dirY) < 0.01)
1942 	{
1943 		if (fabs(self->targetX - self->x) <= fabs(self->dirX))
1944 		{
1945 			self->x = self->targetX;
1946 			self->y = self->targetY;
1947 
1948 			self->dirX = 0;
1949 			self->dirY = 0;
1950 
1951 			return TRUE;
1952 		}
1953 	}
1954 
1955 	else if (fabs(self->dirX) > 0 && fabs(self->dirX) < 0.01)
1956 	{
1957 		if (fabs(self->targetY - self->y) <= fabs(self->dirY))
1958 		{
1959 			self->x = self->targetX;
1960 			self->y = self->targetY;
1961 
1962 			self->dirX = 0;
1963 			self->dirY = 0;
1964 
1965 			return TRUE;
1966 		}
1967 	}
1968 
1969 	else if (fabs(self->targetX - self->x) <= fabs(self->dirX) && fabs(self->targetY - self->y) <= fabs(self->dirY))
1970 	{
1971 		self->x = self->targetX;
1972 		self->y = self->targetY;
1973 
1974 		self->dirX = 0;
1975 		self->dirY = 0;
1976 
1977 		return TRUE;
1978 	}
1979 
1980 	return FALSE;
1981 }
1982 
faceTarget()1983 void faceTarget()
1984 {
1985 	self->face = self->target->x < self->x ? LEFT : RIGHT;
1986 }
1987 
addToDrawLayer(Entity * e,int layer)1988 void addToDrawLayer(Entity *e, int layer)
1989 {
1990 	if (e->inUse == FALSE)
1991 	{
1992 		return;
1993 	}
1994 
1995 	drawLayer[layer][drawLayerIndex[layer]] = e;
1996 
1997 	drawLayerIndex[layer]++;
1998 }
1999 
clearDrawLayers()2000 void clearDrawLayers()
2001 {
2002 	int i;
2003 
2004 	for (i=0;i<MAX_LAYERS;i++)
2005 	{
2006 		drawLayerIndex[i] = 0;
2007 
2008 		memset(drawLayer[i], 0, sizeof(Entity *) * MAX_ENTITIES);
2009 	}
2010 }
2011 
teleportEntityFromScript(Entity * e,char * line)2012 void teleportEntityFromScript(Entity *e, char *line)
2013 {
2014 	sscanf(line, "%d %d", &e->targetX, &e->targetY);
2015 
2016 	calculatePath(e->x, e->y, e->targetX, e->targetY, &e->dirX, &e->dirY);
2017 
2018 	e->flags |= (NO_DRAW|HELPLESS|TELEPORTING);
2019 
2020 	playSoundToMap("sound/common/teleport", -1, e->x, e->y, 0);
2021 }
2022 
landedOnGround(long wasOnGround)2023 int landedOnGround(long wasOnGround)
2024 {
2025 	if (((self->standingOn != NULL) && !(self->flags & WAS_STANDING_ON))
2026 		|| (wasOnGround == 0 && (self->flags & ON_GROUND)))
2027 	{
2028 		return TRUE;
2029 	}
2030 
2031 	return FALSE;
2032 }
2033 
creditsMove()2034 void creditsMove()
2035 {
2036 	self->dirX = self->speed;
2037 
2038 	checkToMap(self);
2039 
2040 	if (self->dirX == 0)
2041 	{
2042 		self->inUse = FALSE;
2043 	}
2044 }
2045 
addDuplicateImage(Entity * e)2046 void addDuplicateImage(Entity *e)
2047 {
2048 	char shadowAnim[MAX_VALUE_LENGTH];
2049 	Entity *duplicate;
2050 
2051 	duplicate = getFreeEntity();
2052 
2053 	if (duplicate == NULL)
2054 	{
2055 		return;
2056 	}
2057 
2058 	loadProperties(e->name, duplicate);
2059 
2060 	duplicate->x = e->x;
2061 	duplicate->y = e->y;
2062 
2063 	duplicate->thinkTime = 15;
2064 
2065 	duplicate->draw = &drawLoopingAnimationToMap;
2066 
2067 	duplicate->action = &duplicateWait;
2068 
2069 	duplicate->creditsAction = &duplicateWait;
2070 
2071 	SNPRINTF(shadowAnim, MAX_VALUE_LENGTH, "%s_SHADOW", getAnimationTypeAtIndex(e));
2072 
2073 	setEntityAnimation(duplicate, shadowAnim);
2074 
2075 	duplicate->currentFrame = e->currentFrame;
2076 
2077 	duplicate->frameSpeed = 0;
2078 
2079 	duplicate->face = e->face;
2080 
2081 	duplicate->layer = BACKGROUND_LAYER;
2082 
2083 	duplicate->flags |= DO_NOT_PERSIST;
2084 
2085 	duplicate->head = e;
2086 }
2087 
duplicateWait()2088 static void duplicateWait()
2089 {
2090 	self->thinkTime--;
2091 
2092 	if (self->thinkTime <= 0)
2093 	{
2094 		self->inUse = FALSE;
2095 	}
2096 }
2097 
getEntities()2098 EntityList *getEntities()
2099 {
2100 	return entities;
2101 }
2102 
isReferenced(Entity * e)2103 static int isReferenced(Entity *e)
2104 {
2105 	EntityList *el;
2106 
2107 	for (el=entities->next;el!=NULL;el=el->next)
2108 	{
2109 		if (el->entity->head == e || el->entity->target == e || el->entity->standingOn == e)
2110 		{
2111 			return TRUE;
2112 		}
2113 	}
2114 
2115 	if (player.head == e || player.target == e || player.standingOn == e)
2116 	{
2117 		return TRUE;
2118 	}
2119 
2120 	return FALSE;
2121 }
2122