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