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