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