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