- changed way badguy removal is handled (ie. with a flag now)
[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
83 void collision_swept_object_map(base_type* old, base_type* current)
84 {
85   int steps; /* Used to speed up the collision tests, by stepping every 16pixels in the path. */
86   int h;
87   float lpath; /* Holds the longest path, which is either in X or Y direction. */
88   float xd,yd; /* Hold the smallest steps in X and Y directions. */
89   float temp, xt, yt; /* Temporary variable. */
90
91   lpath = 0;
92   xd = 0;
93   yd = 0;
94
95   if(old->x == current->x && old->y == current->y)
96     {
97       return;
98     }
99   else if(old->x == current->x && old->y != current->y)
100     {
101       lpath = current->y - old->y;
102       if(lpath < 0)
103         {
104           yd = -1;
105           lpath = -lpath;
106         }
107       else
108         {
109           yd = 1;
110         }
111
112       h = 1;
113       xd = 0;
114     }
115   else if(old->x != current->x && old->y == current->y)
116     {
117       lpath = current->x - old->x;
118       if(lpath < 0)
119         {
120           xd = -1;
121           lpath = -lpath;
122         }
123       else
124         {
125           xd = 1;
126         }
127       h = 2;
128       yd = 0;
129     }
130   else
131     {
132       lpath = current->x - old->x;
133       if(lpath < 0)
134         lpath = -lpath;
135       if(current->y - old->y > lpath || old->y - current->y > lpath)
136         lpath = current->y - old->y;
137       if(lpath < 0)
138         lpath = -lpath;
139       h = 3;
140       xd = (current->x - old->x) / lpath;
141       yd = (current->y - old->y) / lpath;
142     }
143
144   steps = (int)(lpath / (float)16);
145
146   old->x += xd;
147   old->y += yd;
148
149   for(float i = 0; i <= lpath; old->x += xd, old->y += yd, ++i)
150     {
151       if(steps > 0)
152         {
153           old->y += yd*16.;
154           old->x += xd*16.;
155           steps--;
156         }
157
158       if(collision_object_map(*old))
159         {
160           switch(h)
161             {
162             case 1:
163               current->y = old->y - yd;
164               while(collision_object_map(*current))
165                 current->y -= yd;
166               break;
167             case 2:
168               current->x = old->x - xd;
169               while(collision_object_map(*current))
170                 current->x -= xd;
171               break;
172             case 3:
173               xt = current->x;
174               yt = current->y;
175               current->x = old->x - xd;
176               current->y = old->y - yd;
177               while(collision_object_map(*current))
178                 {
179                   current->x -= xd;
180                   current->y -= yd;
181                 }
182
183               temp = current->x;
184               current->x = xt;
185               if(!collision_object_map(*current))
186                 break;
187               current->x = temp;
188               temp = current->y;
189               current->y = yt;
190
191               if(!collision_object_map(*current))
192                 {
193                   break;
194                 }
195               else
196                 {
197                   current->y = temp;
198                   while(!collision_object_map(*current))
199                     current->y += yd;
200                   current->y -= yd;
201                   break;
202                 }
203
204               break;
205             default:
206               break;
207             }
208           break;
209         }
210     }
211
212   *old = *current;
213 }
214
215
216 Tile* gettile(float x, float y)
217 {
218   return TileManager::instance()->get(World::current()->get_level()->gettileid(x, y));
219 }
220
221 bool issolid(float x, float y)
222 {
223   Tile* tile = gettile(x,y);
224   return tile && tile->solid;
225 }
226
227 bool isbrick(float x, float y)
228 {
229   Tile* tile = gettile(x,y);
230   return tile && tile->brick;
231 }
232
233 bool isice(float x, float y)
234 {
235   Tile* tile = gettile(x,y);
236   return tile && tile->ice;
237 }
238
239 bool isfullbox(float x, float y)
240 {
241   Tile* tile = gettile(x,y);
242   return tile && tile->fullbox;
243 }
244
245 bool isdistro(float x, float y)
246 {
247   Tile* tile = gettile(x,y);
248   return tile && tile->distro;
249 }
250
251 /* EOF */
252
253