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 "entity.h"
25 #include "map.h"
26
27 extern Entity *self;
28 extern Entity player, playerShield, playerWeapon;
29
30 static Grid grid[GRID_MAX_Y][GRID_MAX_X];
31
32 static void addToList(int, int, Entity *);
33
initCollisionGrid()34 void initCollisionGrid()
35 {
36 int x, y;
37
38 for (y=0;y<GRID_MAX_Y;y++)
39 {
40 for (x=0;x<GRID_MAX_X;x++)
41 {
42 grid[y][x].count = 0;
43 grid[y][x].listHead.next = NULL;
44 }
45 }
46 }
47
addToGrid(Entity * e)48 void addToGrid(Entity *e)
49 {
50 int left, right, top, bottom, x, y;
51
52 if (e == &playerWeapon)
53 {
54 if (e->face == LEFT)
55 {
56 left = (e->x - e->w + e->offsetX - 1) / TILE_SIZE / GRID_SIZE;
57 right = (e->x + e->offsetX - 1) / TILE_SIZE / GRID_SIZE;
58 }
59
60 else
61 {
62 left = (e->x + e->offsetX) / TILE_SIZE / GRID_SIZE;
63 right = (e->x + e->offsetX + e->w) / TILE_SIZE / GRID_SIZE;
64 }
65
66 top = e->y / TILE_SIZE / GRID_SIZE;
67 bottom = (e->y + e->h) / TILE_SIZE / GRID_SIZE;
68 }
69
70 else
71 {
72 left = getLeftEdge(e) / TILE_SIZE / GRID_SIZE;
73 right = getRightEdge(e) / TILE_SIZE / GRID_SIZE;
74
75 top = (e->y + e->box.y) / TILE_SIZE / GRID_SIZE;
76 bottom = (e->y + e->box.y + e->box.h) / TILE_SIZE / GRID_SIZE;
77 }
78
79 for (x=left;x<=right;x++)
80 {
81 for (y=top;y<=bottom;y++)
82 {
83 addToList(y, x, e);
84 }
85 }
86 }
87
addToList(int y,int x,Entity * e)88 static void addToList(int y, int x, Entity *e)
89 {
90 EntityList *listHead, *list;
91
92 if (x < 0 || x >= GRID_MAX_X || y < 0 || y >= GRID_MAX_Y)
93 {
94 return;
95 }
96
97 listHead = &grid[y][x].listHead;
98
99 while (listHead->next != NULL)
100 {
101 listHead = listHead->next;
102 }
103
104 list = malloc(sizeof(EntityList));
105
106 list->entity = e;
107 list->next = NULL;
108
109 listHead->next = list;
110
111 grid[y][x].count++;
112 }
113
freeCollisionGrid()114 void freeCollisionGrid()
115 {
116 int x, y;
117 EntityList *p, *q;
118
119 for (y=0;y<GRID_MAX_Y;y++)
120 {
121 for (x=0;x<GRID_MAX_X;x++)
122 {
123 for (p=grid[y][x].listHead.next;p!=NULL;p=q)
124 {
125 q = p->next;
126
127 free(p);
128 }
129
130 grid[y][x].count = 0;
131 grid[y][x].listHead.next = NULL;
132 }
133 }
134 }
135
doCollisions()136 void doCollisions()
137 {
138 int i, j, x1, y1, x2, y2, w1, h1, w2, h2;
139 Entity *e1, *e2, *temp;
140 EntityList *list1, *list2;
141
142 for (i=0;i<GRID_MAX_Y;i++)
143 {
144 for (j=0;j<GRID_MAX_X;j++)
145 {
146 for (list1=grid[i][j].listHead.next;list1!=NULL;list1=list1->next)
147 {
148 e1 = list1->entity;
149
150 if (e1->flags & TELEPORTING)
151 {
152 continue;
153 }
154
155 if (e1->inUse == TRUE && e1->touch != NULL)
156 {
157 for (list2=grid[i][j].listHead.next;list2!=NULL;list2=list2->next)
158 {
159 e2 = list2->entity;
160
161 if ((e1->type != PLAYER && (e2->flags & PLAYER_TOUCH_ONLY)) ||
162 ((e1->flags & PLAYER_TOUCH_ONLY) && e2->type != PLAYER))
163 {
164 continue;
165 }
166
167 if ((e1->type == CONVEYOR_BELT && e2->type == CONVEYOR_BELT) ||
168 (e1->type == WEAK_WALL && e2->type == WEAK_WALL) ||
169 (e1->type == ANTI_GRAVITY && e2->type == ANTI_GRAVITY))
170 {
171 continue;
172 }
173
174 if (e2->flags & TELEPORTING)
175 {
176 continue;
177 }
178
179 if (e1 != e2 && e2->inUse == TRUE && e2->touch != NULL)
180 {
181 if (e1->type == PROJECTILE)
182 {
183 if (e2->type == PROJECTILE || (e1->parent != NULL && (e1->parent->type == ENEMY || e1->parent->type == SPAWNER) && e2->type == ENEMY))
184 {
185 continue;
186 }
187 }
188
189 if ((e1 == &player && e2 == &playerWeapon) || (e1 == &playerWeapon && e2 == &player))
190 {
191 continue;
192 }
193
194 x1 = getLeftEdge(e1);
195
196 y1 = e1->y + e1->box.y;
197
198 w1 = e1->box.w;
199 h1 = e1->box.h;
200
201 x2 = getLeftEdge(e2);
202
203 y2 = e2->y + e2->box.y;
204
205 w2 = e2->box.w;
206 h2 = e2->box.h;
207
208 if (e1 == &playerWeapon)
209 {
210 if (e1->face == LEFT)
211 {
212 x1 = e1->parent->x - e1->w + e1->offsetX - 1;
213 }
214
215 else
216 {
217 x1 = e1->x + e1->offsetX;
218 }
219
220
221 y1 = e1->parent->y + e1->offsetY;
222 }
223
224 else if (e2 == &playerWeapon)
225 {
226 if (e2->face == LEFT)
227 {
228 x2 = e2->parent->x - e2->w + e2->offsetX - 1;
229 }
230
231 else
232 {
233 x2 = e2->x + e2->offsetX;
234 }
235
236
237 y2 = e2->parent->y + e2->offsetY;
238 }
239
240 if (collision(x1, y1, w1, h1, x2, y2, w2, h2) == TRUE)
241 {
242 temp = self;
243
244 self = e2;
245
246 self->touch(e1);
247
248 self = temp;
249 }
250 }
251
252 if (e1->inUse == FALSE)
253 {
254 break;
255 }
256 }
257 }
258 }
259 }
260 }
261 }
262
checkEntityToEntity(Entity * e)263 Entity *checkEntityToEntity(Entity *e)
264 {
265 int i, j, x1, y1, x2, y2, w1, h1, w2, h2;
266 Entity *e1, *e2;
267 EntityList *list1, *list2;
268
269 for (i=0;i<GRID_MAX_Y;i++)
270 {
271 for (j=0;j<GRID_MAX_X;j++)
272 {
273 for (list1=grid[i][j].listHead.next;list1!=NULL;list1=list1->next)
274 {
275 e1 = list1->entity;
276
277 if (e1 != e)
278 {
279 continue;
280 }
281
282 if (e1->inUse == TRUE)
283 {
284 for (list2=grid[i][j].listHead.next;list2!=NULL;list2=list2->next)
285 {
286 e2 = list2->entity;
287
288 if (e1 != e2 && e2->inUse == TRUE && e2->touch != NULL && e2->weight > 0)
289 {
290 if (e1->type == ENEMY && e2->type == ENEMY)
291 {
292 continue;
293 }
294
295 if (e1->type == ANTI_GRAVITY || e2->type == ANTI_GRAVITY)
296 {
297 continue;
298 }
299
300 if ((e1->type != PLAYER && (e2->flags & PLAYER_TOUCH_ONLY)) ||
301 ((e1->flags & PLAYER_TOUCH_ONLY) && e2->type != PLAYER))
302 {
303 continue;
304 }
305
306 if (e1->type == PROJECTILE)
307 {
308 if (e2->type == PROJECTILE || (e1->parent != NULL && e1->parent->type == ENEMY && e2->type == ENEMY))
309 {
310 continue;
311 }
312 }
313
314 if ((e1 == &player && e2 == &playerWeapon) || (e1 == &playerWeapon && e2 == &player))
315 {
316 continue;
317 }
318
319 x1 = getLeftEdge(e1);
320
321 y1 = e1->y + e1->box.y;
322
323 w1 = e1->box.w;
324 h1 = e1->box.h;
325
326 x2 = getLeftEdge(e2);
327
328 y2 = e2->y + e2->box.y;
329
330 w2 = e2->box.w;
331 h2 = e2->box.h;
332
333 if (e1 == &playerWeapon)
334 {
335 x1 = e1->x + e1->box.x;
336 y1 = e1->y + e1->box.y;
337
338 if (e1->face == LEFT)
339 {
340 x1 += e1->parent->w - e1->w - e1->offsetX;
341 }
342
343 else
344 {
345 x1 += e1->offsetX;
346 }
347
348
349 y1 += e1->offsetY;
350 }
351
352 if (collision(x1, y1, w1, h1, x2, y2, w2, h2) == TRUE)
353 {
354 return e2;
355 }
356 }
357 }
358 }
359 }
360 }
361 }
362
363 return NULL;
364 }
365
isSpaceEmpty(Entity * e)366 Entity *isSpaceEmpty(Entity *e)
367 {
368 EntityList *el, *entities;
369
370 entities = getEntities();
371
372 if (player.inUse == TRUE && collision(e->x, e->y, e->w, e->h, player.x, player.y, player.w, player.h) == 1)
373 {
374 return &player;
375 }
376
377 for (el=entities->next;el!=NULL;el=el->next)
378 {
379 if (el->entity->inUse == TRUE && e != el->entity
380 && collision(e->x, e->y, e->w, e->h, el->entity->x, el->entity->y, el->entity->w, el->entity->h) == 1)
381 {
382 return el->entity;
383 }
384 }
385
386 return NULL;
387 }
388
isNearObstacle(Entity * e)389 int isNearObstacle(Entity *e)
390 {
391 EntityList *el, *entities;
392
393 entities = getEntities();
394
395 for (el=entities->next;el!=NULL;el=el->next)
396 {
397 if (el->entity->inUse == TRUE && (el->entity->flags & (OBSTACLE|PUSHABLE))
398 && collision(e->x, e->y, e->w, e->h, el->entity->x, el->entity->y, el->entity->w, el->entity->h) == 1)
399 {
400 return TRUE;
401 }
402 }
403
404 return FALSE;
405 }
406
checkToMap(Entity * e)407 void checkToMap(Entity *e)
408 {
409 int i, x1, x2, y1, y2, previousEnvironment;
410 int topLeft, topRight, bottomLeft, bottomRight, previousY2, previous;
411 int tempX, tempY, wasOnGround;
412
413 wasOnGround = (e->flags & ON_GROUND);
414
415 /* Remove the entity from the ground */
416
417 e->flags &= ~ON_GROUND;
418
419 /* Set environment to air */
420
421 previousEnvironment = e->environment;
422
423 e->environment = AIR;
424
425 /* Test the horizontal movement first */
426
427 i = e->h > TILE_SIZE ? TILE_SIZE : e->h;
428
429 for (;;)
430 {
431 x1 = (e->x + e->dirX) / TILE_SIZE;
432 x2 = (e->x + e->dirX + e->w - 1) / TILE_SIZE;
433
434 y1 = (e->y) / TILE_SIZE;
435 y2 = (e->y + i - 1) / TILE_SIZE;
436
437 if (x1 >= 0 && x2 < MAX_MAP_X && y1 >= 0 && y2 < MAX_MAP_Y)
438 {
439 topLeft = mapTileAt(x1, y1);
440 topRight = mapTileAt(x2, y1);
441 bottomLeft = mapTileAt(x1, y2);
442 bottomRight = mapTileAt(x2, y2);
443
444 if (e->dirX > 0)
445 {
446 /* Trying to move right */
447
448 previous = (e->x + e->w - 1) / TILE_SIZE;
449
450 previous = mapTileAt(previous, y2);
451
452 if (previous >= SLOPE_UP_START && previous <= SLOPE_UP_END)
453 {
454 if (i == e->h)
455 {
456 if (bottomRight >= SLOPE_UP_START && bottomRight <= SLOPE_UP_END)
457 {
458 e->y -= e->dirX;
459
460 e->dirY = 0;
461
462 e->flags |= ON_GROUND;
463 }
464
465 else if (bottomRight != BLANK_TILE && bottomRight < BACKGROUND_TILE_START)
466 {
467 e->y = (int)((e->y + e->h - 1) / TILE_SIZE);
468
469 e->y *= TILE_SIZE;
470
471 e->y -= e->h + 0.5;
472
473 e->dirY = 0;
474
475 e->flags |= ON_GROUND;
476
477 previous = mapTileAt(x2, (e->y + e->h - 1) / TILE_SIZE);
478
479 if (previous >= SLOPE_UP_START && previous <= SLOPE_UP_END)
480 {
481 e->y -= e->dirX;
482
483 e->dirY = 0;
484
485 e->flags |= ON_GROUND;
486 }
487 }
488 }
489 }
490
491 else if (bottomRight >= SLOPE_UP_START && bottomRight <= SLOPE_UP_END)
492 {
493 if (i == e->h)
494 {
495 if (!(e->flags & FLY))
496 {
497 if ((e->flags & BOUNCES) && e->dirY > 4)
498 {
499 e->dirY = -e->dirY * 2 / 3;
500 }
501
502 else
503 {
504 e->dirY = 0;
505
506 e->flags |= ON_GROUND;
507 }
508 }
509
510 else
511 {
512 e->x = x2 * TILE_SIZE;
513
514 e->x -= e->w;
515
516 e->dirX = 0;
517 }
518
519 if (e->type == PROJECTILE)
520 {
521 e->die();
522
523 return;
524 }
525 }
526 }
527
528 else if ((previous >= SLOPE_UP_START && previous <= SLOPE_UP_END) &&
529 !(bottomRight >= SLOPE_UP_START && bottomRight <= SLOPE_UP_END))
530 {
531 }
532
533 else if (bottomLeft >= SLOPE_DOWN_START && bottomLeft <= SLOPE_DOWN_END)
534 {
535 e->flags |= ON_GROUND;
536 }
537
538 else if ((topRight >= JUMP_THROUGH_TILE_START && topRight <= JUMP_THROUGH_TILE_END) ||
539 ((bottomRight >= JUMP_THROUGH_TILE_START && bottomRight <= JUMP_THROUGH_TILE_END)))
540 {
541
542 }
543
544 else if ((topRight != BLANK_TILE && topRight < BACKGROUND_TILE_START) || (bottomRight != BLANK_TILE && bottomRight < BACKGROUND_TILE_START))
545 {
546 /* Place the player as close to the solid tile as possible */
547
548 e->x = x2 * TILE_SIZE;
549
550 e->x -= e->w;
551
552 e->dirX = (e->flags & BOUNCES) ? -e->dirX : 0;
553
554 if ((e->flags & GRABBING) && e->target != NULL)
555 {
556 e->target->dirX = 0;
557 }
558
559 if (e->type == PROJECTILE)
560 {
561 e->die();
562
563 return;
564 }
565 }
566 }
567
568 else if (e->dirX < 0)
569 {
570 /* Trying to move left */
571
572 previous = (e->x) / TILE_SIZE;
573
574 previous = mapTileAt(previous, y2);
575
576 if (previous >= SLOPE_DOWN_START && previous <= SLOPE_DOWN_END)
577 {
578 if (i == e->h)
579 {
580 if (bottomLeft >= SLOPE_DOWN_START && bottomLeft <= SLOPE_DOWN_END)
581 {
582 e->y += e->dirX;
583
584 e->dirY = 0;
585
586 e->flags |= ON_GROUND;
587 }
588
589 else if (bottomLeft != BLANK_TILE && bottomLeft < BACKGROUND_TILE_START)
590 {
591 e->y = (int)((e->y + e->h - 1) / TILE_SIZE);
592
593 e->y *= TILE_SIZE;
594
595 e->y -= e->h + 0.5;
596
597 e->dirY = 0;
598
599 e->flags |= ON_GROUND;
600
601 previous = mapTileAt(x1, (e->y + e->h - 1) / TILE_SIZE);
602
603 if (previous >= SLOPE_DOWN_START && previous <= SLOPE_DOWN_END)
604 {
605 e->y += e->dirX;
606
607 e->dirY = 0;
608
609 e->flags |= ON_GROUND;
610 }
611 }
612 }
613 }
614
615 else if (bottomLeft >= SLOPE_DOWN_START && bottomLeft <= SLOPE_DOWN_END)
616 {
617 if (i == e->h)
618 {
619 if (!(e->flags & FLY))
620 {
621 if ((e->flags & BOUNCES) && e->dirY > 4)
622 {
623 e->dirY = -e->dirY * 2 / 3;
624 }
625
626 else
627 {
628 e->dirY = 0;
629
630 e->flags |= ON_GROUND;
631 }
632 }
633
634 else
635 {
636 e->x = (x1 + 1) * TILE_SIZE;
637
638 e->dirX = 0;
639 }
640
641 if (e->type == PROJECTILE)
642 {
643 e->die();
644
645 return;
646 }
647 }
648 }
649
650 else if ((previous >= SLOPE_DOWN_START && previous <= SLOPE_DOWN_END) &&
651 !(bottomLeft >= SLOPE_DOWN_START && bottomLeft <= SLOPE_DOWN_END))
652 {
653 }
654
655 else if (bottomRight >= SLOPE_UP_START && bottomRight <= SLOPE_UP_END)
656 {
657 e->flags |= ON_GROUND;
658 }
659
660 else if ((topLeft >= JUMP_THROUGH_TILE_START && topLeft <= JUMP_THROUGH_TILE_END) ||
661 ((bottomLeft >= JUMP_THROUGH_TILE_START && bottomLeft <= JUMP_THROUGH_TILE_END)))
662 {
663
664 }
665
666 else if ((topLeft != BLANK_TILE && topLeft < BACKGROUND_TILE_START) || (bottomLeft != BLANK_TILE && bottomLeft < BACKGROUND_TILE_START))
667 {
668 /* Place the player as close to the solid tile as possible */
669
670 e->x = (x1 + 1) * TILE_SIZE;
671
672 e->dirX = (e->flags & BOUNCES) ? -e->dirX : 0;
673
674 if ((e->flags & GRABBING) && e->target != NULL)
675 {
676 e->target->dirX = 0;
677 }
678
679 if (e->type == PROJECTILE)
680 {
681 e->die();
682
683 return;
684 }
685 }
686 }
687 }
688
689 /* Exit this loop if we have tested all of the body */
690
691 if (i == e->h || e->h <= TILE_SIZE)
692 {
693 break;
694 }
695
696 /* Test the next block */
697
698 i += TILE_SIZE;
699
700 if (i > e->h)
701 {
702 i = e->h;
703 }
704 }
705
706 /* Now test the vertical movement */
707
708 i = e->w > TILE_SIZE ? TILE_SIZE : e->w;
709
710 for (;;)
711 {
712 x1 = (e->x) / TILE_SIZE;
713 x2 = (e->x + i - 1) / TILE_SIZE;
714
715 y1 = (e->y + e->dirY) / TILE_SIZE;
716 y2 = (e->y + e->dirY + e->h - 1) / TILE_SIZE;
717
718 if (x1 >= 0 && x2 < MAX_MAP_X && y1 >= 0 && y2 < MAX_MAP_Y)
719 {
720 topLeft = mapTileAt(x1, y1);
721 topRight = mapTileAt(x2, y1);
722 bottomLeft = mapTileAt(x1, y2);
723 bottomRight = mapTileAt(x2, y2);
724
725 if (e->dirY > 0)
726 {
727 /* Trying to move down */
728
729 if (bottomRight >= SLOPE_UP_START && bottomRight <= SLOPE_UP_END)
730 {
731 if (i == e->w)
732 {
733 tempX = (int)(e->x + i - 1) % TILE_SIZE;
734 tempY = (int)(e->y + e->dirY + e->h - 1) % TILE_SIZE;
735
736 tempX = TILE_SIZE - tempX;
737
738 if (tempY > tempX || wasOnGround != 0)
739 {
740 e->y = y2 * TILE_SIZE;
741 e->y -= e->h;
742 e->y += tempX + 1;
743
744 if ((e->flags & BOUNCES) && e->dirY > 4)
745 {
746 e->dirY = -e->dirY * 2 / 3;
747 }
748
749 else
750 {
751 e->dirY = 0;
752
753 e->flags |= ON_GROUND;
754 }
755 }
756 }
757
758 if (e->type == PROJECTILE)
759 {
760 e->die();
761
762 return;
763 }
764 }
765
766 else if (bottomLeft >= SLOPE_DOWN_START && bottomLeft <= SLOPE_DOWN_END)
767 {
768 if (i == (e->w > TILE_SIZE ? TILE_SIZE : e->w))
769 {
770 tempX = (int)(e->x) % TILE_SIZE;
771 tempY = (int)(e->y + e->dirY + e->h - 1) % TILE_SIZE;
772
773 if (tempY > tempX || wasOnGround != 0)
774 {
775 e->y = y2 * TILE_SIZE;
776 e->y -= e->h;
777 e->y += tempX + 1;
778
779 if ((e->flags & BOUNCES) && e->dirY > 4)
780 {
781 e->dirY = -e->dirY * 2 / 3;
782 }
783
784 else
785 {
786 e->dirY = 0;
787
788 e->flags |= ON_GROUND;
789 }
790 }
791 }
792
793 if (e->type == PROJECTILE)
794 {
795 e->die();
796
797 return;
798 }
799 }
800
801 else if ((bottomLeft >= JUMP_THROUGH_TILE_START && bottomLeft <= JUMP_THROUGH_TILE_END) ||
802 ((bottomRight >= JUMP_THROUGH_TILE_START && bottomRight <= JUMP_THROUGH_TILE_END)))
803 {
804 previousY2 = y2;
805
806 x1 = (e->x) / TILE_SIZE;
807 x2 = (e->x + i - 1) / TILE_SIZE;
808
809 y2 = (e->y + e->h - 1) / TILE_SIZE;
810
811 bottomLeft = mapTileAt(x1, y2);
812 bottomRight = mapTileAt(x2, y2);
813
814 if (!((bottomLeft >= JUMP_THROUGH_TILE_START && bottomLeft <= JUMP_THROUGH_TILE_END) ||
815 ((bottomRight >= JUMP_THROUGH_TILE_START && bottomRight <= JUMP_THROUGH_TILE_END))))
816 {
817 y2 = previousY2;
818
819 /* Place the player as close to the solid tile as possible */
820
821 e->y = y2 * TILE_SIZE;
822 e->y -= e->h;
823
824 if ((e->flags & BOUNCES) && e->dirY > 4)
825 {
826 e->dirY = -e->dirY * 2 / 3;
827 }
828
829 else
830 {
831 e->dirY = 0;
832
833 e->flags |= ON_GROUND;
834 }
835 }
836
837 if (e->type == PROJECTILE)
838 {
839 e->die();
840
841 return;
842 }
843 }
844
845 else if ((bottomLeft != BLANK_TILE && bottomLeft < BACKGROUND_TILE_START) || (bottomRight != BLANK_TILE && bottomRight < BACKGROUND_TILE_START))
846 {
847 /* Place the player as close to the solid tile as possible */
848
849 e->y = y2 * TILE_SIZE;
850 e->y -= e->h;
851
852 if ((e->flags & BOUNCES) && e->dirY > 4)
853 {
854 e->dirY = -e->dirY * 2 / 3;
855 }
856
857 else
858 {
859 e->dirY = 0;
860
861 e->flags |= ON_GROUND;
862 }
863
864 if (e->type == PROJECTILE)
865 {
866 e->die();
867
868 return;
869 }
870 }
871 }
872
873 else if (e->dirY < 0)
874 {
875 /* Trying to move up */
876
877 if ((topLeft >= JUMP_THROUGH_TILE_START && topLeft <= JUMP_THROUGH_TILE_END) ||
878 ((topRight >= JUMP_THROUGH_TILE_START && topRight <= JUMP_THROUGH_TILE_END)))
879 {
880
881 }
882
883 else if ((topLeft != BLANK_TILE && topLeft < BACKGROUND_TILE_START) || (topRight != BLANK_TILE && topRight < BACKGROUND_TILE_START))
884 {
885 /* Place the player as close to the solid tile as possible */
886
887 e->y = (y1 + 1) * TILE_SIZE;
888
889 e->dirY = (e->flags & BOUNCES) ? -e->dirY : 0;
890
891 if (e->type == PROJECTILE)
892 {
893 e->die();
894
895 return;
896 }
897 }
898 }
899 }
900
901 if (i == e->w || e->w <= TILE_SIZE)
902 {
903 break;
904 }
905
906 i += TILE_SIZE;
907
908 if (i > e->w)
909 {
910 i = e->w;
911 }
912 }
913
914 /* Now apply the movement */
915
916 e->x += e->dirX;
917 e->y += e->dirY;
918
919 x1 = (e->type == PLAYER || (e->flags & LIMIT_TO_SCREEN)) ? getPlayerMinX() : getMapMinX();
920 x2 = (e->type == PLAYER || (e->flags & LIMIT_TO_SCREEN)) ? getPlayerMaxX() : getMapMaxX();
921
922 y1 = (e->flags & LIMIT_TO_SCREEN) ? getMapStartY() : getMapMinY() - 300;
923
924 if (e->x < x1)
925 {
926 e->x = x1;
927
928 e->dirX = 0;
929
930 if ((e->flags & GRABBING) && e->target != NULL)
931 {
932 e->target->dirX = 0;
933 }
934
935 if (e->type == PROJECTILE)
936 {
937 e->die();
938 }
939
940 }
941
942 else if (e->x + e->w >= x2)
943 {
944 e->x = x2 - e->w - 1;
945
946 e->dirX = 0;
947
948 if ((e->flags & GRABBING) && e->target != NULL)
949 {
950 e->target->dirX = 0;
951 }
952
953 if (e->type == PROJECTILE)
954 {
955 e->die();
956 }
957 }
958
959 if (e->y > getMapMaxY() && e->y - e->dirY <= getMapMaxY())
960 {
961 e->flags &= ~(HELPLESS|INVULNERABLE);
962
963 if (e->fallout == NULL)
964 {
965 printf("%s has no fallout defined. Removing\n", e->name);
966
967 e->inUse = FALSE;
968
969 return;
970 }
971
972 else
973 {
974 e->fallout();
975 }
976 }
977
978 else if (e->y < y1)
979 {
980 /* Way too high... */
981
982 e->y = y1;
983
984 e->dirY = 0;
985 }
986
987 x1 = (e->x) / TILE_SIZE;
988 x2 = (e->x + e->w - 1) / TILE_SIZE;
989
990 y1 = (e->y) / TILE_SIZE;
991 y2 = (e->y + e->h - 1) / TILE_SIZE;
992
993 topLeft = mapTileAt(x1, y1);
994 topRight = mapTileAt(x2, y1);
995 bottomLeft = mapTileAt(x1, y2);
996 bottomRight = mapTileAt(x2, y2);
997
998 if ((topLeft >= LAVA_TILE_START && topLeft <= LAVA_TILE_END) ||
999 (bottomLeft >= LAVA_TILE_START && bottomLeft <= LAVA_TILE_END) ||
1000 (topRight >= LAVA_TILE_START && topRight <= LAVA_TILE_END) ||
1001 (bottomRight >= LAVA_TILE_START && bottomRight <= LAVA_TILE_END))
1002 {
1003 e->environment = LAVA;
1004
1005 if (previousEnvironment != LAVA && e->fallout != NULL)
1006 {
1007 /* Fire based entities won't die */
1008
1009 if (e->element != FIRE)
1010 {
1011 if (e->type != TEMP_ITEM)
1012 {
1013 playSoundToMap("sound/common/lava", -1, e->x, e->y, 0);
1014 }
1015
1016 e->flags &= ~(HELPLESS|INVULNERABLE);
1017
1018 e->fallout();
1019 }
1020 }
1021 }
1022
1023 else if ((topLeft >= SLIME_TILE_START && topLeft <= SLIME_TILE_BLEND) ||
1024 (bottomLeft >= SLIME_TILE_START && bottomLeft <= SLIME_TILE_BLEND) ||
1025 (topRight >= SLIME_TILE_START && topRight <= SLIME_TILE_BLEND) ||
1026 (bottomRight >= SLIME_TILE_START && bottomRight <= SLIME_TILE_BLEND))
1027 {
1028 e->environment = SLIME;
1029
1030 if (previousEnvironment != SLIME && e->fallout != NULL)
1031 {
1032 /* Slime based entities won't die */
1033
1034 if (e->element != SLIME)
1035 {
1036 if (e->type != TEMP_ITEM)
1037 {
1038 playSoundToMap("sound/common/slime", -1, e->x, e->y, 0);
1039 }
1040
1041 e->flags &= ~(HELPLESS|INVULNERABLE);
1042
1043 e->fallout();
1044 }
1045 }
1046 }
1047
1048 else
1049 {
1050 y2 = (e->y + (e->h / 2)) / TILE_SIZE;
1051
1052 bottomLeft = mapTileAt(x1, y2);
1053 bottomRight = mapTileAt(x2, y2);
1054
1055 if ((topLeft >= WATER_TILE_START && topLeft <= WATER_TILE_END) &&
1056 (bottomLeft >= WATER_TILE_START && bottomLeft <= WATER_TILE_END) &&
1057 (topRight >= WATER_TILE_START && topRight <= WATER_TILE_END) &&
1058 (bottomRight >= WATER_TILE_START && bottomRight <= WATER_TILE_END))
1059 {
1060 e->environment = WATER;
1061
1062 if (previousEnvironment != WATER && e->fallout != NULL)
1063 {
1064 if (e->type != TEMP_ITEM)
1065 {
1066 playSoundToMap("sound/common/splash", -1, e->x, e->y, 0);
1067 }
1068
1069 if (!(e->flags & FLOATS))
1070 {
1071 e->flags &= ~(HELPLESS|INVULNERABLE);
1072
1073 e->fallout();
1074 }
1075 }
1076 }
1077 }
1078 }
1079
isAtEdge(Entity * e)1080 int isAtEdge(Entity *e)
1081 {
1082 int i, tile;
1083 int x = e->face == LEFT ? floor(e->x) : ceil(e->x) + e->w;
1084 int y = e->y + e->h - 1;
1085 EntityList *el, *entities;
1086
1087 entities = getEntities();
1088
1089 x /= TILE_SIZE;
1090 y /= TILE_SIZE;
1091
1092 y++;
1093
1094 tile = mapTileAt(x, y);
1095
1096 /* Return immediately if the tile isn't blank */
1097
1098 if (!(e->flags & ON_GROUND) || (tile != BLANK_TILE && tile < BACKGROUND_TILE_START))
1099 {
1100 return FALSE;
1101 }
1102
1103 if (e->w > TILE_SIZE)
1104 {
1105 if (e->dirX > 0)
1106 {
1107 for (i=0;;)
1108 {
1109 x = e->x + i;
1110
1111 x /= TILE_SIZE;
1112
1113 tile = mapTileAt(x, y);
1114
1115 if (tile >= SLOPE_DOWN_START && tile <= SLOPE_DOWN_END)
1116 {
1117 return FALSE;
1118 }
1119
1120 if (i == e->w)
1121 {
1122 break;
1123 }
1124
1125 i += TILE_SIZE;
1126
1127 if (i > e->w)
1128 {
1129 i = e->w;
1130 }
1131 }
1132 }
1133
1134 else
1135 {
1136 for (i=e->w;;)
1137 {
1138 x = e->x + i;
1139
1140 x /= TILE_SIZE;
1141
1142 tile = mapTileAt(x, y);
1143
1144 if (tile >= SLOPE_UP_START && tile <= SLOPE_UP_END)
1145 {
1146 return FALSE;
1147 }
1148
1149 if (i == 0)
1150 {
1151 break;
1152 }
1153
1154 i -= TILE_SIZE;
1155
1156 if (i < 0)
1157 {
1158 i = 0;
1159 }
1160 }
1161 }
1162 }
1163
1164 x = e->face == LEFT ? floor(e->x) : ceil(e->x);
1165
1166 if (e->face == RIGHT)
1167 {
1168 x += e->w;
1169 }
1170
1171 /* There might still be Entities that can be walked on */
1172
1173 for (el=entities->next;el!=NULL;el=el->next)
1174 {
1175 if (e != el->entity && el->entity->inUse == TRUE && el->entity->touch != NULL
1176 && ((el->entity->flags & (PUSHABLE|OBSTACLE)) || (el->entity->type == WEAK_WALL)
1177 || (el->entity->type == PRESSURE_PLATE) || (el->entity->type == ANTI_GRAVITY)))
1178 {
1179 if (collision(x, e->y, 1, e->h + 10, el->entity->x, el->entity->y, el->entity->w, el->entity->h) == TRUE)
1180 {
1181 return FALSE;
1182 }
1183 }
1184 }
1185
1186 return TRUE;
1187 }
1188
isAtCeilingEdge(Entity * e)1189 int isAtCeilingEdge(Entity *e)
1190 {
1191 int i, tile;
1192 int x = e->x + (e->dirX > 0 ? e->w : 0);
1193 int y = e->y;
1194
1195 x /= TILE_SIZE;
1196 y /= TILE_SIZE;
1197
1198 y--;
1199
1200 tile = mapTileAt(x, y);
1201
1202 /* Return immediately if the tile isn't blank */
1203
1204 if (tile != BLANK_TILE && tile < BACKGROUND_TILE_START)
1205 {
1206 return FALSE;
1207 }
1208
1209 if (e->w > TILE_SIZE)
1210 {
1211 if (e->dirX > 0)
1212 {
1213 for (i=0;;)
1214 {
1215 x = e->x + i;
1216
1217 x /= TILE_SIZE;
1218
1219 tile = mapTileAt(x, y);
1220
1221 if (tile >= SLOPE_DOWN_START && tile <= SLOPE_DOWN_END)
1222 {
1223 return FALSE;
1224 }
1225
1226 if (i == e->w)
1227 {
1228 break;
1229 }
1230
1231 i += TILE_SIZE;
1232
1233 if (i > e->w)
1234 {
1235 i = e->w;
1236 }
1237 }
1238 }
1239
1240 else
1241 {
1242 for (i=e->w;;)
1243 {
1244 x = e->x + i;
1245
1246 x /= TILE_SIZE;
1247
1248 tile = mapTileAt(x, y);
1249
1250 if (tile >= SLOPE_UP_START && tile <= SLOPE_UP_END)
1251 {
1252 return FALSE;
1253 }
1254
1255 if (i == 0)
1256 {
1257 break;
1258 }
1259
1260 i -= TILE_SIZE;
1261
1262 if (i < 0)
1263 {
1264 i = 0;
1265 }
1266 }
1267 }
1268 }
1269
1270 return TRUE;
1271 }
1272
onSingleTile(Entity * e)1273 int onSingleTile(Entity *e)
1274 {
1275 int leftTile, rightTile, midTile, wallLeft, wallRight;
1276 int x = e->x + e->w / 2;
1277 int y = e->y + e->h - 1;
1278
1279 x /= TILE_SIZE;
1280 y /= TILE_SIZE;
1281
1282 y++;
1283
1284 midTile = mapTileAt(x, y);
1285
1286 leftTile = mapTileAt(x - 1, y);
1287
1288 rightTile = mapTileAt(x + 1, y);
1289
1290 wallLeft = mapTileAt(x - 1, y - 1);
1291
1292 wallRight = mapTileAt(x + 1, y - 1);
1293
1294 /* On a tile with nothing either side */
1295 if ((midTile != BLANK_TILE && midTile < BACKGROUND_TILE_START) && leftTile == BLANK_TILE && rightTile == BLANK_TILE)
1296 {
1297 return TRUE;
1298 }
1299
1300 /* On a tile with nothing on the left and a wall on the right */
1301 if ((midTile != BLANK_TILE && midTile < BACKGROUND_TILE_START) && leftTile == BLANK_TILE && (wallRight != BLANK_TILE && wallRight < BACKGROUND_TILE_START))
1302 {
1303 return TRUE;
1304 }
1305
1306 /* On a tile with nothing on the right and a wall on the left */
1307 if ((midTile != BLANK_TILE && midTile < BACKGROUND_TILE_START) && rightTile == BLANK_TILE && (wallLeft != BLANK_TILE && wallLeft < BACKGROUND_TILE_START))
1308 {
1309 return TRUE;
1310 }
1311
1312 return FALSE;
1313 }
1314
isValidOnMap(Entity * e)1315 int isValidOnMap(Entity *e)
1316 {
1317 int i, x1, x2, y1, y2;
1318
1319 if (e->x < getMapMinX() || e->x + e->w > getMapMaxX() || e->h < 0 || e->y + e->h > getMapMaxY())
1320 {
1321 return FALSE;
1322 }
1323
1324 i = e->w > TILE_SIZE ? TILE_SIZE : e->w;
1325
1326 for (;;)
1327 {
1328 x1 = (e->x) / TILE_SIZE;
1329 x2 = (e->x + i - 1) / TILE_SIZE;
1330
1331 y1 = (e->y) / TILE_SIZE;
1332 y2 = (e->y + e->h - 1) / TILE_SIZE;
1333
1334 if ((mapTileAt(x1, y1) < BACKGROUND_TILE_START && mapTileAt(x1, y1) > BLANK_TILE) ||
1335 (mapTileAt(x2, y1) < BACKGROUND_TILE_START && mapTileAt(x2, y1) > BLANK_TILE) ||
1336 (mapTileAt(x1, y2) < BACKGROUND_TILE_START && mapTileAt(x1, y2) > BLANK_TILE) ||
1337 (mapTileAt(x2, y2) < BACKGROUND_TILE_START && mapTileAt(x2, y2) > BLANK_TILE))
1338 {
1339 return FALSE;
1340 }
1341
1342 if (i == e->w)
1343 {
1344 break;
1345 }
1346
1347 i += e->w;
1348
1349 if (i > e->w)
1350 {
1351 i = e->w;
1352 }
1353 }
1354
1355 i = e->h > TILE_SIZE ? TILE_SIZE : e->h;
1356
1357 for (;;)
1358 {
1359 x1 = (e->x) / TILE_SIZE;
1360 x2 = (e->x + e->w - 1) / TILE_SIZE;
1361
1362 y1 = (e->y) / TILE_SIZE;
1363 y2 = (e->y + i - 1) / TILE_SIZE;
1364
1365 if ((mapTileAt(x1, y1) < BACKGROUND_TILE_START && mapTileAt(x1, y1) > BLANK_TILE) ||
1366 (mapTileAt(x2, y1) < BACKGROUND_TILE_START && mapTileAt(x2, y1) > BLANK_TILE) ||
1367 (mapTileAt(x1, y2) < BACKGROUND_TILE_START && mapTileAt(x1, y2) > BLANK_TILE) ||
1368 (mapTileAt(x2, y2) < BACKGROUND_TILE_START && mapTileAt(x2, y2) > BLANK_TILE))
1369 {
1370 return FALSE;
1371 }
1372
1373 if (i == e->h)
1374 {
1375 break;
1376 }
1377
1378 i += e->h;
1379
1380 if (i > e->h)
1381 {
1382 i = e->h;
1383 }
1384 }
1385
1386 return TRUE;
1387 }
1388
getMapFloor(int x,int y)1389 int getMapFloor(int x, int y)
1390 {
1391 int maxY = getMapMaxY() / TILE_SIZE;
1392 int tileID;
1393
1394 x /= TILE_SIZE;
1395 y /= TILE_SIZE;
1396
1397 tileID = mapTileAt(x, y);
1398
1399 while (tileID == BLANK_TILE || (tileID >= BACKGROUND_TILE_START && tileID <= BACKGROUND_TILE_END))
1400 {
1401 y++;
1402
1403 if (y >= maxY)
1404 {
1405 break;
1406 }
1407
1408 tileID = mapTileAt(x, y);
1409 }
1410
1411 y *= TILE_SIZE;
1412
1413 return y;
1414 }
1415
getMapCeiling(int x,int y)1416 int getMapCeiling(int x, int y)
1417 {
1418 int tileID;
1419
1420 x /= TILE_SIZE;
1421 y /= TILE_SIZE;
1422
1423 tileID = mapTileAt(x, y);
1424
1425 while (tileID == BLANK_TILE || (tileID >= BACKGROUND_TILE_START && tileID <= BACKGROUND_TILE_END))
1426 {
1427 y--;
1428
1429 if (y < 0)
1430 {
1431 break;
1432 }
1433
1434 tileID = mapTileAt(x, y);
1435 }
1436
1437 y = y * TILE_SIZE + TILE_SIZE;
1438
1439 return y;
1440 }
1441
getMapLeft(int x,int y)1442 int getMapLeft(int x, int y)
1443 {
1444 int tileID;
1445
1446 x /= TILE_SIZE;
1447 y /= TILE_SIZE;
1448
1449 tileID = mapTileAt(x, y);
1450
1451 while (tileID == BLANK_TILE || (tileID >= BACKGROUND_TILE_START && tileID <= BACKGROUND_TILE_END))
1452 {
1453 x--;
1454
1455 if (x <= 0)
1456 {
1457 break;
1458 }
1459
1460 tileID = mapTileAt(x, y);
1461 }
1462
1463 x *= TILE_SIZE;
1464
1465 return x;
1466 }
1467
getMapRight(int x,int y)1468 int getMapRight(int x, int y)
1469 {
1470 int maxX = getMapMaxX() / TILE_SIZE;
1471 int tileID;
1472
1473 x /= TILE_SIZE;
1474 y /= TILE_SIZE;
1475
1476 tileID = mapTileAt(x, y);
1477
1478 while (tileID == BLANK_TILE || (tileID >= BACKGROUND_TILE_START && tileID <= BACKGROUND_TILE_END))
1479 {
1480 x++;
1481
1482 if (x >= maxX)
1483 {
1484 break;
1485 }
1486
1487 tileID = mapTileAt(x, y);
1488 }
1489
1490 x *= TILE_SIZE;
1491
1492 return x;
1493 }
1494
getWaterTop(int x,int y)1495 int getWaterTop(int x, int y)
1496 {
1497 int tileID;
1498
1499 x /= TILE_SIZE;
1500 y /= TILE_SIZE;
1501
1502 tileID = mapTileAt(x, y);
1503
1504 while (tileID == WATER_TILE_START)
1505 {
1506 y--;
1507
1508 if (y < 0)
1509 {
1510 break;
1511 }
1512
1513 tileID = mapTileAt(x, y);
1514 }
1515
1516 y++;
1517
1518 y *= TILE_SIZE;
1519
1520 return y;
1521 }
1522
1523 /* Very standard 2D collision detection routine */
1524
collision(int x0,int y0,int w0,int h0,int x2,int y2,int w1,int h1)1525 int collision(int x0, int y0, int w0, int h0, int x2, int y2, int w1, int h1)
1526 {
1527 int x1 = x0 + w0 - 1;
1528 int y1 = y0 + h0 - 1;
1529
1530 int x3 = x2 + w1 - 1;
1531 int y3 = y2 + h1 - 1;
1532
1533 return !(x1<x2 || x3<x0 || y1<y2 || y3<y0);
1534 }
1535