- moved some collision code into the world class, since it only acts on world data
[supertux.git] / src / collision.cpp
1 //
2 // C Implementation: collision
3 //
4 // Description:
5 //
6 //
7 // Author: Tobias Glaesser <tobi.web@gmx.de>, (C) 2004
8 //
9 // Copyright: See COPYING file that comes with this distribution
10 //
11 //
12
13 #include "defines.h"
14 #include "collision.h"
15 #include "bitmask.h"
16 #include "scene.h"
17 #include "world.h"
18 #include "tile.h"
19
20 bool rectcollision(base_type* one, base_type* two)
21 {
22   return (one->x >= two->x - one->width + 1  &&
23           one->x <= two->x + two->width - 1  &&
24           one->y >= two->y - one->height + 1 &&
25           one->y <= two->y + two->height - 1);
26 }
27
28 bool rectcollision_offset(base_type* one, base_type* two, float off_x, float off_y)
29 {
30   return (one->x >= two->x - one->width +off_x + 1 &&
31           one->x <= two->x + two->width + off_x - 1 &&
32           one->y >= two->y - one->height + off_y + 1 &&
33           one->y <= two->y + two->height + off_y - 1);
34 }
35
36 bool collision_object_map(base_type* pbase)
37 {
38   int v = (int)pbase->height / 16;
39   int h = (int)pbase->width / 16;
40
41   if(issolid(pbase->x + 1, pbase->y + 1) ||
42      issolid(pbase->x + pbase->width -1, pbase->y + 1) ||
43      issolid(pbase->x +1, pbase->y + pbase->height -1) ||
44      issolid(pbase->x + pbase->width -1, pbase->y + pbase->height - 1))
45     return true;
46
47   for(int i = 1; i < h; ++i)
48     {
49       if(issolid(pbase->x + i*16,pbase->y + 1))
50         return true;
51     }
52
53   for(int i = 1; i < h; ++i)
54     {
55       if(  issolid(pbase->x + i*16,pbase->y + pbase->height - 1))
56         return true;
57     }
58
59   for(int i = 1; i < v; ++i)
60     {
61       if(  issolid(pbase->x + 1, pbase->y + i*16))
62         return true;
63     }
64   for(int i = 1; i < v; ++i)
65     {
66       if(  issolid(pbase->x + pbase->width - 1, pbase->y + i*16))
67         return true;
68     }
69
70   return false;
71 }
72
73
74 void collision_swept_object_map(base_type* old, base_type* current)
75 {
76   int steps; /* Used to speed up the collision tests, by stepping every 16pixels in the path. */
77   int h;
78   float lpath; /* Holds the longest path, which is either in X or Y direction. */
79   float xd,yd; /* Hold the smallest steps in X and Y directions. */
80   float temp, xt, yt; /* Temporary variable. */
81
82   lpath = 0;
83   xd = 0;
84   yd = 0;
85
86   if(old->x == current->x && old->y == current->y)
87     {
88       return;
89     }
90   else if(old->x == current->x && old->y != current->y)
91     {
92       lpath = current->y - old->y;
93       if(lpath < 0)
94         {
95           yd = -1;
96           lpath = -lpath;
97         }
98       else
99         {
100           yd = 1;
101         }
102
103       h = 1;
104       xd = 0;
105     }
106   else if(old->x != current->x && old->y == current->y)
107     {
108       lpath = current->x - old->x;
109       if(lpath < 0)
110         {
111           xd = -1;
112           lpath = -lpath;
113         }
114       else
115         {
116           xd = 1;
117         }
118       h = 2;
119       yd = 0;
120     }
121   else
122     {
123       lpath = current->x - old->x;
124       if(lpath < 0)
125         lpath = -lpath;
126       if(current->y - old->y > lpath || old->y - current->y > lpath)
127         lpath = current->y - old->y;
128       if(lpath < 0)
129         lpath = -lpath;
130       h = 3;
131       xd = (current->x - old->x) / lpath;
132       yd = (current->y - old->y) / lpath;
133     }
134
135   steps = (int)(lpath / (float)16);
136
137   old->x += xd;
138   old->y += yd;
139
140   for(float i = 0; i <= lpath; old->x += xd, old->y += yd, ++i)
141     {
142       if(steps > 0)
143         {
144           old->y += yd*16.;
145           old->x += xd*16.;
146           steps--;
147         }
148
149       if(collision_object_map(old))
150         {
151           switch(h)
152             {
153             case 1:
154               current->y = old->y - yd;
155               while(collision_object_map(current))
156                 current->y -= yd;
157               break;
158             case 2:
159               current->x = old->x - xd;
160               while(collision_object_map(current))
161                 current->x -= xd;
162               break;
163             case 3:
164               xt = current->x;
165               yt = current->y;
166               current->x = old->x - xd;
167               current->y = old->y - yd;
168               while(collision_object_map(current))
169                 {
170                   current->x -= xd;
171                   current->y -= yd;
172                 }
173
174               temp = current->x;
175               current->x = xt;
176               if(!collision_object_map(current))
177                 break;
178               current->x = temp;
179               temp = current->y;
180               current->y = yt;
181
182               if(!collision_object_map(current))
183                 {
184                   break;
185                 }
186               else
187                 {
188                   current->y = temp;
189                   while(!collision_object_map(current))
190                     current->y += yd;
191                   current->y -= yd;
192                   break;
193                 }
194
195               break;
196             default:
197               break;
198             }
199           break;
200         }
201     }
202
203   *old = *current;
204 }
205
206
207 Tile* gettile(float x, float y)
208 {
209   return TileManager::instance()->get(World::current()->get_level()->gettileid(x, y));
210 }
211
212 bool issolid(float x, float y)
213 {
214   Tile* tile = gettile(x,y);
215   return tile && tile->solid;
216 }
217
218 bool isbrick(float x, float y)
219 {
220   Tile* tile = gettile(x,y);
221   return tile && tile->brick;
222 }
223
224 bool isice(float x, float y)
225 {
226   Tile* tile = gettile(x,y);
227   return tile && tile->ice;
228 }
229
230 bool isfullbox(float x, float y)
231 {
232   Tile* tile = gettile(x,y);
233   return tile && tile->fullbox;
234 }
235
236 bool isdistro(float x, float y)
237 {
238   Tile* tile = gettile(x,y);
239   return tile && tile->distro;
240 }
241
242 /* EOF */
243
244