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