- moved some more stuff into the world class
[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 #include "world.h"
18 #include "tile.h"
19
20 bool rectcollision(base_type* one, base_type* two)
21 {
22   return (one->x >= two->x - one->width + 1  &&
23           one->x <= two->x + two->width - 1  &&
24           one->y >= two->y - one->height + 1 &&
25           one->y <= two->y + two->height - 1);
26 }
27
28 bool rectcollision_offset(base_type* one, base_type* two, float off_x, float off_y)
29 {
30   return (one->x >= two->x - one->width +off_x + 1 &&
31           one->x <= two->x + two->width + off_x - 1 &&
32           one->y >= two->y - one->height + off_y + 1 &&
33           one->y <= two->y + two->height + off_y - 1);
34 }
35
36 bool collision_object_map(base_type* pbase)
37 {
38   int v = (int)pbase->height / 16;
39   int 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(int i = 1; i < h; ++i)
48     {
49       if(issolid(pbase->x + i*16,pbase->y + 1))
50         return true;
51     }
52
53   for(int 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(int i = 1; i < v; ++i)
60     {
61       if(  issolid(pbase->x + 1, pbase->y + i*16))
62         return true;
63     }
64   for(int 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 lpath; /* Holds the longest path, which is either in X or Y direction. */
79   float xd,yd; /* Hold the smallest steps in X and Y directions. */
80   float temp, xt, yt; /* Temporary variable. */
81
82   lpath = 0;
83   xd = 0;
84   yd = 0;
85
86   if(old->x == current->x && old->y == current->y)
87     {
88       return;
89     }
90   else if(old->x == current->x && old->y != current->y)
91     {
92       lpath = current->y - old->y;
93       if(lpath < 0)
94         {
95           yd = -1;
96           lpath = -lpath;
97         }
98       else
99         {
100           yd = 1;
101         }
102
103       h = 1;
104       xd = 0;
105     }
106   else if(old->x != current->x && old->y == current->y)
107     {
108       lpath = current->x - old->x;
109       if(lpath < 0)
110         {
111           xd = -1;
112           lpath = -lpath;
113         }
114       else
115         {
116           xd = 1;
117         }
118       h = 2;
119       yd = 0;
120     }
121   else
122     {
123       lpath = current->x - old->x;
124       if(lpath < 0)
125         lpath = -lpath;
126       if(current->y - old->y > lpath || old->y - current->y > lpath)
127         lpath = current->y - old->y;
128       if(lpath < 0)
129         lpath = -lpath;
130       h = 3;
131       xd = (current->x - old->x) / lpath;
132       yd = (current->y - old->y) / lpath;
133     }
134
135   steps = (int)(lpath / (float)16);
136
137   old->x += xd;
138   old->y += yd;
139
140   for(float i = 0; i <= lpath; old->x += xd, old->y += yd, ++i)
141     {
142       if(steps > 0)
143         {
144           old->y += yd*16.;
145           old->x += xd*16.;
146           steps--;
147         }
148
149       if(collision_object_map(old))
150         {
151           switch(h)
152             {
153             case 1:
154               current->y = old->y - yd;
155               while(collision_object_map(current))
156                 current->y -= yd;
157               break;
158             case 2:
159               current->x = old->x - xd;
160               while(collision_object_map(current))
161                 current->x -= xd;
162               break;
163             case 3:
164               xt = current->x;
165               yt = current->y;
166               current->x = old->x - xd;
167               current->y = old->y - yd;
168               while(collision_object_map(current))
169                 {
170                   current->x -= xd;
171                   current->y -= yd;
172                 }
173
174               temp = current->x;
175               current->x = xt;
176               if(!collision_object_map(current))
177                 break;
178               current->x = temp;
179               temp = current->y;
180               current->y = yt;
181
182               if(!collision_object_map(current))
183                 {
184                   break;
185                 }
186               else
187                 {
188                   current->y = temp;
189                   while(!collision_object_map(current))
190                     current->y += yd;
191                   current->y -= yd;
192                   break;
193                 }
194
195               break;
196             default:
197               break;
198             }
199           break;
200         }
201     }
202
203   *old = *current;
204 }
205
206 void collision_handler()
207 {
208   // CO_BULLET & CO_BADGUY check
209   for(unsigned int i = 0; i < world.bullets.size(); ++i)
210     {
211       for(unsigned int j = 0; j < world.bad_guys.size(); ++j)
212         {
213           if(world.bad_guys[j].dying != DYING_NOT)
214             continue;
215           if(rectcollision(&world.bullets[i].base, &world.bad_guys[j].base))
216             {
217               // We have detected a collision and now call the
218               // collision functions of the collided objects.
219               // collide with bad_guy first, since bullet_collision will
220               // delete the bullet
221               world.bad_guys[j].collision(0, CO_BULLET);
222               bullet_collision(&world.bullets[i], CO_BADGUY);
223               break; // bullet is invalid now, so break
224             }
225         }
226     }
227
228   /* CO_BADGUY & CO_BADGUY check */
229   for(unsigned int i = 0; i < world.bad_guys.size(); ++i)
230     {
231       if(world.bad_guys[i].dying != DYING_NOT)
232         continue;
233       
234       for(unsigned int j = i+1; j < world.bad_guys.size(); ++j)
235         {
236           if(j == i || world.bad_guys[j].dying != DYING_NOT)
237             continue;
238
239           if(rectcollision(&world.bad_guys[i].base, &world.bad_guys[j].base))
240             {
241               // We have detected a collision and now call the
242               // collision functions of the collided objects.
243               world.bad_guys[j].collision(&world.bad_guys[i], CO_BADGUY);
244               world.bad_guys[i].collision(&world.bad_guys[j], CO_BADGUY);
245             }
246         }
247     }
248
249   if(tux.dying != DYING_NOT) return;
250     
251   // CO_BADGUY & CO_PLAYER check 
252   for(unsigned int i = 0; i < world.bad_guys.size(); ++i)
253     {
254       if(world.bad_guys[i].dying != DYING_NOT)
255         continue;
256       
257       if(rectcollision_offset(&world.bad_guys[i].base,&tux.base,0,0))
258         {
259           // We have detected a collision and now call the collision
260           // functions of the collided objects.
261           if (tux.previous_base.y < tux.base.y &&
262               tux.previous_base.y + tux.previous_base.height 
263               < world.bad_guys[i].base.y + world.bad_guys[i].base.height/2)
264             {
265               world.bad_guys[i].collision(&tux, CO_PLAYER, COLLISION_SQUISH);
266             }
267           else
268             {
269               tux.collision(&world.bad_guys[i], CO_BADGUY);
270             }
271         }
272     }
273
274   // CO_UPGRADE & CO_PLAYER check
275   for(unsigned int i = 0; i < world.upgrades.size(); ++i)
276     {
277       if(rectcollision(&world.upgrades[i].base, &tux.base))
278         {
279           // We have detected a collision and now call the collision
280           // functions of the collided objects.
281           upgrade_collision(&world.upgrades[i], &tux, CO_PLAYER);
282         }
283     }
284 }
285
286
287 Tile* gettile(float x, float y)
288 {
289   return TileManager::instance()->get(World::current()->get_level()->gettileid(x, y));
290 }
291
292 bool issolid(float x, float y)
293 {
294   Tile* tile = gettile(x,y);
295   return tile && tile->solid;
296 }
297
298 bool isbrick(float x, float y)
299 {
300   Tile* tile = gettile(x,y);
301   return tile && tile->brick;
302 }
303
304 bool isice(float x, float y)
305 {
306   Tile* tile = gettile(x,y);
307   return tile && tile->ice;
308 }
309
310 bool isfullbox(float x, float y)
311 {
312   Tile* tile = gettile(x,y);
313   return tile && tile->fullbox;
314 }
315
316 bool isdistro(float x, float y)
317 {
318   Tile* tile = gettile(x,y);
319   return tile && tile->distro;
320 }
321
322 /* EOF */
323
324