- added unisolid patch from Upsilon
[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 "defines.h"
22 #include "collision.h"
23 #include "bitmask.h"
24 #include "scene.h"
25 #include "world.h"
26 #include "level.h"
27 #include "tile.h"
28
29 bool rectcollision(const base_type& one, const base_type& two)
30 {
31   return (one.x >= two.x - one.width + 1  &&
32           one.x <= two.x + two.width - 1  &&
33           one.y >= two.y - one.height + 1 &&
34           one.y <= two.y + two.height - 1);
35 }
36
37 bool rectcollision_offset(const base_type& one, const base_type& two, float off_x, float off_y)
38 {
39   return (one.x >= two.x - one.width  + off_x + 1 &&
40           one.x <= two.x + two.width  + off_x - 1 &&
41           one.y >= two.y - one.height + off_y + 1 &&
42           one.y <= two.y + two.height + off_y - 1);
43 }
44
45 bool collision_object_map(const base_type& base)
46 {
47   if(!World::current())
48   return false;
49   
50   const Level& level = *World::current()->get_level();
51   TileManager& tilemanager = *TileManager::instance();
52
53   // we make the collision rectangle 1 pixel smaller
54   int starttilex = int(base.x+1) / 32;
55   int starttiley = int(base.y+1) / 32;
56   int max_x = int(base.x + base.width);
57   int max_y = int(base.y + base.height);
58
59   for(int x = starttilex; x*32 < max_x; ++x) {
60     for(int y = starttiley; y*32 < max_y; ++y) {
61       Tile* tile = tilemanager.get(level.get_tile_at(x, y));
62       if(tile && tile->solid)
63         return true;
64     }
65   }
66
67   return false;
68 }
69
70 void* collision_func(const base_type& base, tiletestfunction function)
71 {
72   const Level& level = *World::current()->get_level();
73   TileManager& tilemanager = *TileManager::instance();
74   
75   int starttilex = int(base.x) / 32;
76   int starttiley = int(base.y) / 32;
77   int max_x = int(base.x + base.width);
78   int max_y = int(base.y + base.height);
79
80   for(int x = starttilex; x*32 < max_x; ++x) {
81     for(int y = starttiley; y*32 < max_y; ++y) {
82       Tile* tile = tilemanager.get(level.get_tile_at(x, y));
83       void* result = function(tile);
84       if(result != 0)
85         return result;
86     }
87   }
88
89   return 0;
90 }
91
92 static void* test_goal_tile_function(Tile* tile)
93 {
94   if(tile && tile->goal)
95     return tile;
96   return 0;
97 }
98
99 Tile* collision_goal(const base_type& base)
100 {
101   return (Tile*) collision_func(base, test_goal_tile_function);
102 }
103
104 void collision_swept_object_map(base_type* old, base_type* current)
105 {
106   int steps; /* Used to speed up the collision tests, by stepping every 16pixels in the path. */
107   int h;
108   float lpath; /* Holds the longest path, which is either in X or Y direction. */
109   float xd,yd; /* Hold the smallest steps in X and Y directions. */
110   float temp, xt, yt; /* Temporary variable. */
111
112   lpath = 0;
113   xd = 0;
114   yd = 0;
115
116   if(old->x == current->x && old->y == current->y)
117     {
118       return;
119     }
120   else if(old->x == current->x && old->y != current->y)
121     {
122       lpath = current->y - old->y;
123       if(lpath < 0)
124         {
125           yd = -1;
126           lpath = -lpath;
127         }
128       else
129         {
130           yd = 1;
131         }
132
133       h = 1;
134       xd = 0;
135     }
136   else if(old->x != current->x && old->y == current->y)
137     {
138       lpath = current->x - old->x;
139       if(lpath < 0)
140         {
141           xd = -1;
142           lpath = -lpath;
143         }
144       else
145         {
146           xd = 1;
147         }
148       h = 2;
149       yd = 0;
150     }
151   else
152     {
153       lpath = current->x - old->x;
154       if(lpath < 0)
155         lpath = -lpath;
156       if(current->y - old->y > lpath || old->y - current->y > lpath)
157         lpath = current->y - old->y;
158       if(lpath < 0)
159         lpath = -lpath;
160       h = 3;
161       xd = (current->x - old->x) / lpath;
162       yd = (current->y - old->y) / lpath;
163     }
164
165   steps = (int)(lpath / (float)16);
166
167   float orig_x = old->x;
168   float orig_y = old->y;
169   old->x += xd;
170   old->y += yd;
171
172   for(float i = 0; i <= lpath; old->x += xd, old->y += yd, ++i)
173     {
174       if(steps > 0)
175         {
176           old->y += yd*16.;
177           old->x += xd*16.;
178           steps--;
179         }
180
181       if(collision_object_map(*old))
182         {
183           switch(h)
184             {
185             case 1:
186               current->y = old->y - yd;
187               while(collision_object_map(*current))
188                 current->y -= yd;
189               break;
190             case 2:
191               current->x = old->x - xd;
192               while(collision_object_map(*current))
193                 current->x -= xd;
194               break;
195             case 3:
196               xt = current->x;
197               yt = current->y;
198               current->x = old->x - xd;
199               current->y = old->y - yd;
200               while(collision_object_map(*current))
201                 {
202                   current->x -= xd;
203                   current->y -= yd;
204                 }
205
206               temp = current->x;
207               current->x = xt;
208               if(!collision_object_map(*current))
209                 break;
210               current->x = temp;
211               temp = current->y;
212               current->y = yt;
213
214               if(!collision_object_map(*current))
215                 {
216                   break;
217                 }
218               else
219                 {
220                   current->y = temp;
221                   while(!collision_object_map(*current))
222                     current->y += yd;
223                   current->y -= yd;
224                   break;
225                 }
226
227               break;
228             default:
229               break;
230             }
231           break;
232         }
233     }
234
235   if((xd > 0 && current->x < orig_x) || (xd < 0 && current->x > orig_x))
236     current->x = orig_x;
237   if((yd > 0 && current->y < orig_y) || (yd < 0 && current->y > orig_y))
238     current->y = orig_y;
239
240   *old = *current;
241 }
242
243 Tile* gettile(float x, float y)
244 {
245   return TileManager::instance()->get(World::current()->get_level()->gettileid(x, y));
246 }
247
248 bool issolid(float x, float y)
249 {
250   Tile* tile = gettile(x,y);
251   return tile && tile->solid;
252 }
253
254 bool isunisolid(float x, float y)
255 {
256     Tile* tile = gettile(x,y);
257     return tile && tile->unisolid;
258 }
259
260
261 bool isbrick(float x, float y)
262 {
263   Tile* tile = gettile(x,y);
264   return tile && tile->brick;
265 }
266
267 bool isice(float x, float y)
268 {
269   Tile* tile = gettile(x,y);
270   return tile && tile->ice;
271 }
272
273 bool isspike(float x, float y)
274 {
275   Tile* tile = gettile(x,y);
276   return tile && tile->spike;
277 }
278
279 bool isfullbox(float x, float y)
280 {
281   Tile* tile = gettile(x,y);
282   return tile && tile->fullbox;
283 }
284
285 bool isdistro(float x, float y)
286 {
287   Tile* tile = gettile(x,y);
288   return tile && tile->distro;
289 }
290
291 /* EOF */
292
293