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