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