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