1 // $Id: collision.cpp 1026 2004-05-07 20:48:22Z tobgle $
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
rectcollision(const base_type & one,const base_type & two)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
rectcollision_offset(const base_type & one,const base_type & two,float off_x,float off_y)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
collision_object_map(const base_type & base)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
collision_func(const base_type & base,tiletestfunction function)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
test_goal_tile_function(Tile * tile)92 static void* test_goal_tile_function(Tile* tile)
93 {
94 if(tile && tile->goal)
95 return tile;
96 return 0;
97 }
98
collision_goal(const base_type & base)99 Tile* collision_goal(const base_type& base)
100 {
101 return (Tile*) collision_func(base, test_goal_tile_function);
102 }
103
collision_swept_object_map(base_type * old,base_type * current)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
gettile(float x,float y)243 Tile* gettile(float x, float y)
244 {
245 return TileManager::instance()->get(World::current()->get_level()->gettileid(x, y));
246 }
247
issolid(float x,float y)248 bool issolid(float x, float y)
249 {
250 Tile* tile = gettile(x,y);
251 return tile && tile->solid;
252 }
253
isbrick(float x,float y)254 bool isbrick(float x, float y)
255 {
256 Tile* tile = gettile(x,y);
257 return tile && tile->brick;
258 }
259
isice(float x,float y)260 bool isice(float x, float y)
261 {
262 Tile* tile = gettile(x,y);
263 return tile && tile->ice;
264 }
265
isfullbox(float x,float y)266 bool isfullbox(float x, float y)
267 {
268 Tile* tile = gettile(x,y);
269 return tile && tile->fullbox;
270 }
271
isdistro(float x,float y)272 bool isdistro(float x, float y)
273 {
274 Tile* tile = gettile(x,y);
275 return tile && tile->distro;
276 }
277
278 /* EOF */
279
280
281