7b93e824647e426dc2002d448cb48292496b3f67
[supertux.git] / src / collision.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 // 
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 //  02111-1307, USA.
20
21 #include <config.h>
22
23 #include <cmath>
24 #include "defines.h"
25 #include "collision.h"
26 #include "bitmask.h"
27 #include "scene.h"
28 #include "sector.h"
29 #include "tilemap.h"
30 #include "tile.h"
31
32 #if 0
33 bool rectcollision(const base_type& one, const base_type& two)
34 {
35   return (one.x >= two.x - one.width + 1  &&
36           one.x <= two.x + two.width - 1  &&
37           one.y >= two.y - one.height + 1 &&
38           one.y <= two.y + two.height - 1);
39 }
40
41 bool rectcollision_offset(const base_type& one, const base_type& two, float off_x, float off_y)
42 {
43   return (one.x >= two.x - one.width  + off_x + 1 &&
44           one.x <= two.x + two.width  + off_x - 1 &&
45           one.y >= two.y - one.height + off_y + 1 &&
46           one.y <= two.y + two.height + off_y - 1);
47 }
48
49 bool collision_object_map(const Rectangle& rect)
50 {
51   base_type base;
52   base.x = rect.p1.x;
53   base.y = rect.p1.y;
54   base.width = rect.get_width();    
55   base.height = rect.get_height();
56   return collision_object_map(base);
57 }
58
59 bool collision_object_map(const base_type& base)
60 {
61   const TileMap& tilemap = *Sector::current()->solids;
62
63   // we make the collision rectangle 1 pixel smaller
64   int starttilex = int(base.x+1) / 32;
65   int starttiley = int(base.y+1) / 32;
66   int max_x = int(base.x + base.width);
67   int max_y = int(base.y + base.height);
68
69   for(int x = starttilex; x*32 < max_x; ++x) {
70     for(int y = starttiley; y*32 < max_y; ++y) {
71       const Tile* tile = tilemap.get_tile(x, y);
72       if(tile && tile->attributes & Tile::SOLID)
73         return true;
74     }
75   }
76
77   return false;
78 }
79
80 void* collision_func(const base_type& base, tiletestfunction function)
81 {
82   const TileMap& tilemap = *Sector::current()->solids;
83   
84   int starttilex = int(base.x) / 32;
85   int starttiley = int(base.y) / 32;
86   int max_x = int(base.x + base.width);
87   int max_y = int(base.y + base.height);
88
89   for(int x = starttilex; x*32 < max_x; ++x) {
90     for(int y = starttiley; y*32 < max_y; ++y) {
91       const Tile* tile = tilemap.get_tile(x, y);
92       void* result = function(tile);
93       if(result != 0)
94         return result;
95     }
96   }
97
98   return 0;
99 }
100
101 static void* test_goal_tile_function(const Tile* tile)
102 {
103   if(tile && (tile->attributes & Tile::GOAL))
104     return const_cast<void*> ((const void*) tile); // evil cast...
105   return 0;
106 }
107
108 const Tile* collision_goal(const Rectangle& rect)
109 {
110   // too lazy to rewrite for now, so we transform to base_type...
111   base_type base;
112   base.x = rect.p1.x;
113   base.y = rect.p1.y;
114   base.width = rect.get_width();
115   base.height = rect.get_height();
116   return (const Tile*) collision_func(base, test_goal_tile_function);
117 }
118
119 void collision_swept_object_map(base_type* old, base_type* current)
120 {
121   int steps; /* Used to speed up the collision tests, by stepping every 16pixels in the path. */
122   int h;
123   float lpath; /* Holds the longest path, which is either in X or Y direction. */
124   float xd,yd; /* Hold the smallest steps in X and Y directions. */
125   float temp, xt, yt; /* Temporary variable. */
126
127   lpath = 0;
128   xd = 0;
129   yd = 0;
130
131   if(old->x == current->x && old->y == current->y)
132     {
133       return;
134     }
135   else if(old->x == current->x && old->y != current->y)
136     {
137       lpath = current->y - old->y;
138       if(lpath < 0)
139         {
140           yd = -1;
141           lpath = -lpath;
142         }
143       else
144         {
145           yd = 1;
146         }
147
148       h = 1;
149       xd = 0;
150     }
151   else if(old->x != current->x && old->y == current->y)
152     {
153       lpath = current->x - old->x;
154       if(lpath < 0)
155         {
156           xd = -1;
157           lpath = -lpath;
158         }
159       else
160         {
161           xd = 1;
162         }
163       h = 2;
164       yd = 0;
165     }
166   else
167     {
168       lpath = current->x - old->x;
169       if(lpath < 0)
170         lpath = -lpath;
171       if(current->y - old->y > lpath || old->y - current->y > lpath)
172         lpath = current->y - old->y;
173       if(lpath < 0)
174         lpath = -lpath;
175       h = 3;
176       xd = (current->x - old->x) / lpath;
177       yd = (current->y - old->y) / lpath;
178     }
179
180   steps = (int)(lpath / (float)16);
181
182   float orig_x = old->x;
183   float orig_y = old->y;
184   old->x += xd;
185   old->y += yd;
186
187   for(float i = 0; i <= lpath; old->x += xd, old->y += yd, ++i)
188     {
189       if(steps > 0)
190         {
191           old->y += yd*16.;
192           old->x += xd*16.;
193           steps--;
194         }
195
196       if(collision_object_map(*old))
197         {
198           switch(h)
199             {
200             case 1:
201               current->y = old->y - yd;
202               while(collision_object_map(*current))
203                 current->y -= yd;
204               break;
205             case 2:
206               current->x = old->x - xd;
207               while(collision_object_map(*current))
208                 current->x -= xd;
209               break;
210             case 3:
211               xt = current->x;
212               yt = current->y;
213               current->x = old->x - xd;
214               current->y = old->y - yd;
215               while(collision_object_map(*current))
216                 {
217                   current->x -= xd;
218                   current->y -= yd;
219                 }
220
221               temp = current->x;
222               current->x = xt;
223               if(!collision_object_map(*current))
224                 break;
225               current->x = temp;
226               temp = current->y;
227               current->y = yt;
228
229               if(!collision_object_map(*current))
230                 {
231                   break;
232                 }
233               else
234                 {
235                   current->y = temp;
236                   while(!collision_object_map(*current))
237                     current->y += yd;
238                   current->y -= yd;
239                   break;
240                 }
241
242               break;
243             default:
244               break;
245             }
246           break;
247         }
248     }
249
250   if((xd > 0 && current->x < orig_x) || (xd < 0 && current->x > orig_x))
251     current->x = orig_x;
252   if((yd > 0 && current->y < orig_y) || (yd < 0 && current->y > orig_y))
253     current->y = orig_y;
254
255   *old = *current;
256 }
257
258 const Tile* gettile(float x, float y)
259 {
260   const TileMap& tilemap = *Sector::current()->solids;
261   return tilemap.get_tile_at(Vector(x, y));
262 }
263
264 bool issolid(float x, float y)
265 {
266   const Tile* tile = gettile(x,y);
267   return tile && (tile->attributes & Tile::SOLID);
268 }
269
270 bool isbrick(float x, float y)
271 {
272   const Tile* tile = gettile(x,y);
273   return tile && (tile->attributes & Tile::BRICK);
274 }
275
276 bool isice(float x, float y)
277 {
278   const Tile* tile = gettile(x,y);
279   return tile && (tile->attributes & Tile::ICE);
280 }
281
282 bool isspike(float x, float y)
283 {
284   const Tile* tile = gettile(x,y);
285   return tile && (tile->attributes & Tile::SPIKE);
286 }
287
288 bool isfullbox(float x, float y)
289 {
290   const Tile* tile = gettile(x,y);
291   return tile && (tile->attributes & Tile::FULLBOX);
292 }
293
294 bool iscoin(float x, float y)
295 {
296   const Tile* tile = gettile(x,y);
297   return tile && (tile->attributes & Tile::COIN);
298 }
299
300 #endif
301