a616f84c39b8ed026dc6cbc52b5ec6aa2ceabce5
[supertux.git] / src / collision.c
1 //
2 // C Implementation: collision
3 //
4 // Description:
5 //
6 //
7 // Author: Tobias Glaesser <tobi.web@gmx.de>, (C) 2004
8 //
9 // Copyright: See COPYING file that comes with this distribution
10 //
11 //
12
13 #include "defines.h"
14 #include "collision.h"
15 #include "bitmask.h"
16 #include "scene.h"
17
18 int rectcollision(base_type* one, base_type* two)
19 {
20
21   if (one->x >= two->x - one->width + 1 &&
22       one->x <= two->x + two->width - 1  &&
23       one->y >= two->y - one->height + 1&&
24       one->y <= two->y + two->height - 1)
25     {
26       return YES;
27     }
28   else
29     {
30       return NO;
31     }
32 }
33
34 int rectcollision_offset(base_type* one, base_type* two, float off_x, float off_y)
35 {
36   if (one->x >= two->x - one->width +off_x + 1 &&
37       one->x <= two->x + two->width + off_x - 1 &&
38       one->y >= two->y - one->height + off_y + 1 &&
39       one->y <= two->y + two->height + off_y - 1)
40     {
41       return YES;
42     }
43   else
44     {
45       return NO;
46     }
47 }
48
49 int collision_object_map(base_type* pbase)
50 {
51   int v,h,i;
52
53   v = (int)pbase->height / 16;
54   h = (int)pbase->width / 16;
55
56   if(issolid(pbase->x + 1, pbase->y + 1) ||
57       issolid(pbase->x + pbase->width -1, pbase->y + 1) ||
58       issolid(pbase->x +1, pbase->y + pbase->height -1) ||
59       issolid(pbase->x + pbase->width -1, pbase->y + pbase->height - 1))
60     return YES;
61
62   for(i = 1; i < h; ++i)
63     {
64       if(issolid(pbase->x + i*16,pbase->y + 1))
65         return YES;
66     }
67
68   for(i = 1; i < h; ++i)
69     {
70       if(  issolid(pbase->x + i*16,pbase->y + pbase->height - 1))
71         return YES;
72     }
73
74   for(i = 1; i < v; ++i)
75     {
76       if(  issolid(pbase->x + 1, pbase->y + i*16))
77         return YES;
78     }
79   for(i = 1; i < v; ++i)
80     {
81       if(  issolid(pbase->x + pbase->width - 1, pbase->y + i*16))
82         return YES;
83     }
84
85   return NO;
86 }
87
88
89 int collision_swept_object_map(base_type* old, base_type* current)
90 {
91   int steps; /* Used to speed up the collision tests, by stepping every 16pixels in the path. */
92   int h; 
93   float i;
94   float lpath; /* Holds the longest path, which is either in X or Y direction. */
95   float xd,yd; /* Hold the smallest steps in X and Y directions. */
96   float temp, xt, yt; /* Temporary variable. */
97
98   lpath = 0;
99   xd = 0;
100   yd = 0;
101
102   if(old->x == current->x && old->y == current->y)
103     {
104       return 0;
105     }
106   else if(old->x == current->x && old->y != current->y)
107     {
108       lpath = current->y - old->y;
109       if(lpath < 0)
110         {
111           yd = -1;
112           lpath = -lpath;
113         }
114       else
115         {
116           yd = 1;
117         }
118
119       h = 1;
120       xd = 0;
121
122     }
123   else if(old->x != current->x && old->y == current->y)
124     {
125       lpath = current->x - old->x;
126       if(lpath < 0)
127         {
128           xd = -1;
129           lpath = -lpath;
130         }
131       else
132         {
133           xd = 1;
134         }
135       h = 2;
136       yd = 0;
137     }
138   else
139     {
140       lpath = current->x - old->x;
141       if(lpath < 0)
142         lpath = -lpath;
143       if(current->y - old->y > lpath || old->y - current->y > lpath)
144         lpath = current->y - old->y;
145       if(lpath < 0)
146         lpath = -lpath;
147       h = 3;
148       xd = (current->x - old->x) / lpath;
149       yd = (current->y - old->y) / lpath;
150     }
151
152   steps = (int)(lpath / (float)16);
153
154   old->x += xd;
155   old->y += yd;
156
157   for(i = 0; i <= lpath; old->x += xd, old->y += yd, ++i)
158     {
159       if(steps > 0)
160         {
161           old->y += yd*16.;
162           old->x += xd*16.;
163           steps--;
164         }
165
166       if(collision_object_map(old))
167         {
168
169           switch(h)
170             {
171             case 1:
172               current->y = old->y - yd;
173               while(collision_object_map(current))
174                 current->y -= yd;
175               break;
176             case 2:
177               current->x = old->x - xd;
178               while(collision_object_map(current))
179                 current->x -= xd;
180               break;
181             case 3:
182               xt = current->x;
183               yt = current->y;
184               current->x = old->x - xd;
185               current->y = old->y - yd;
186               while(collision_object_map(current))
187                 {
188                   current->x -= xd;
189                   current->y -= yd;
190                 }
191
192               temp = current->x;
193               current->x = xt;
194               if(!collision_object_map(current))
195                 break;
196
197               current->x = temp;
198               temp = current->y;
199               current->y = yt;
200
201               if(!collision_object_map(current))
202                 break;
203
204               current->y = temp;
205
206               break;
207             default:
208               break;
209             }
210           break;
211         }
212     }
213
214   *old = *current;
215
216 }
217
218 void collision_handler()
219 {
220   int i,j;
221
222   /* CO_BULLET & CO_BADGUY check */
223   for(i = 0; i < num_bullets; ++i)
224     {
225       if(bullets[i].base.alive)
226         {
227           for(j = 0; j < num_bad_guys; ++j)
228             {
229               if(bad_guys[j].dying == NO && bad_guys[j].base.alive)
230                 {
231                   if(rectcollision(&bullets[i].base,&bad_guys[j].base) == YES)
232                     {
233                       /* We have detected a collision and now call the collision functions of the collided objects. */
234                       bullet_collision(&bullets[i], CO_BADGUY);
235                       badguy_collision(&bad_guys[j], &bullets[i], CO_BULLET);
236                     }
237                 }
238             }
239         }
240     }
241
242   /* CO_BADGUY & CO_BADGUY check */
243   for(i = 0; i < num_bad_guys; ++i)
244     {
245       if(bad_guys[i].base.alive && bad_guys[i].dying == NO)
246         {
247           for(j = i+1; j < num_bad_guys; ++j)
248             {
249               if(j != i && bad_guys[j].base.alive && bad_guys[j].dying == NO)
250                 {
251                   if(rectcollision(&bad_guys[i].base,&bad_guys[j].base) == YES)
252                     {
253                       /* We have detected a collision and now call the collision functions of the collided objects. */
254                       badguy_collision(&bad_guys[j], &bad_guys[i], CO_BADGUY);
255                       badguy_collision(&bad_guys[i], &bad_guys[j], CO_BADGUY);
256                     }
257                 }
258             }
259         }
260     }
261
262
263     
264   /* CO_BADGUY & CO_PLAYER check */
265   for(i = 0; i < num_bad_guys; ++i)
266     {
267       if(bad_guys[i].base.alive)
268         {
269           if(bad_guys[i].dying == NO && rectcollision_offset(&bad_guys[i].base,&tux.base,0,0) == YES )
270             {
271               /* We have detected a collision and now call the collision functions of the collided objects. */
272               if (tux.previous_base.y < tux.base.y &&
273               tux.previous_base.y + tux.previous_base.height < bad_guys[i].base.y + bad_guys[i].base.height/2 &&
274               bad_guys[i].kind != BAD_MONEY && bad_guys[i].mode != HELD)
275                 {
276                   badguy_collision(&bad_guys[i], &tux, CO_PLAYER);
277                 }
278               else
279                 {
280                   player_collision(&tux, &bad_guys[i], CO_BADGUY);
281                 }
282             }
283         }
284     }
285
286   /* CO_UPGRADE & CO_PLAYER check */
287   for(i = 0; i < num_upgrades; ++i)
288     {
289       if(upgrades[i].base.alive)
290         {
291           if(rectcollision(&upgrades[i].base,&tux.base) == YES)
292             {
293               /* We have detected a collision and now call the collision functions of the collided objects. */
294               upgrade_collision(&upgrades[i], &tux, CO_PLAYER);
295             }
296
297         }
298     }
299
300 }
301
302