added collision for raindrops (wip)
[supertux.git] / src / object / particlesystem.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2004 Matthias Braun <matze@braunis.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  02111-1307, USA.
19 #include <config.h>
20
21 #include <iostream>
22 #include <cmath>
23
24 #include "particlesystem.h"
25 #include "video/drawing_context.h"
26 #include "lisp/parser.h"
27 #include "lisp/lisp.h"
28 #include "lisp/writer.h"
29 #include "resources.h"
30 #include "main.h"
31
32 #include "tile.h"
33 #include "tilemap.h"
34 #include "math/aatriangle.h"
35 #include "collision.h"
36 #include "collision_hit.h"
37
38
39 ParticleSystem::ParticleSystem()
40 {
41     virtual_width = SCREEN_WIDTH;
42     virtual_height = SCREEN_HEIGHT;
43     layer = LAYER_BACKGROUND1;
44 }
45
46 ParticleSystem::~ParticleSystem()
47 {
48     std::vector<Particle*>::iterator i;
49     for(i = particles.begin(); i != particles.end(); ++i) {
50         delete *i;
51     }
52 }
53
54 void ParticleSystem::draw(DrawingContext& context)
55 {
56   float scrollx = context.get_translation().x;
57   float scrolly = context.get_translation().y;
58
59   context.push_transform();
60   context.set_translation(Vector(0,0));
61   
62     std::vector<Particle*>::iterator i;
63     for(i = particles.begin(); i != particles.end(); ++i) {
64         Particle* particle = *i;
65
66         // remap x,y coordinates onto screencoordinates
67         Vector pos;
68         pos.x = fmodf(particle->pos.x - scrollx, virtual_width);
69         if(pos.x < 0) pos.x += virtual_width;
70         pos.y = fmodf(particle->pos.y - scrolly, virtual_height);
71         if(pos.y < 0) pos.y += virtual_height;
72
73         if(pos.x > SCREEN_WIDTH) pos.x -= virtual_width;
74         if(pos.y > SCREEN_HEIGHT) pos.y -= virtual_height;
75         context.draw_surface(particle->texture, pos, layer);
76     }
77
78     context.pop_transform();
79 }
80
81 SnowParticleSystem::SnowParticleSystem()
82 {
83     snowimages[0] = new Surface(datadir+"/images/objects/particles/snow0.png", true);
84     snowimages[1] = new Surface(datadir+"/images/objects/particles/snow1.png", true);
85     snowimages[2] = new Surface(datadir+"/images/objects/particles/snow2.png", true);
86
87     virtual_width = SCREEN_WIDTH * 2;
88
89     // create some random snowflakes
90     size_t snowflakecount = size_t(virtual_width/10.0);
91     for(size_t i=0; i<snowflakecount; ++i) {
92         SnowParticle* particle = new SnowParticle;
93         particle->pos.x = rand() % int(virtual_width);
94         particle->pos.y = rand() % SCREEN_HEIGHT;
95         int snowsize = rand() % 3;
96         particle->texture = snowimages[snowsize];
97         do {
98             particle->speed = snowsize*.2 + (float(rand()%10)*.4);
99         } while(particle->speed < 1);
100         particle->speed *= 10; // gravity
101
102         particles.push_back(particle);
103     }
104 }
105
106 void
107 SnowParticleSystem::parse(const lisp::Lisp& reader)
108 {
109   reader.get("layer", layer);
110 }
111
112 void
113 SnowParticleSystem::write(lisp::Writer& writer)
114 {
115   writer.start_list("particles-snow");
116   writer.write_int("layer", layer);
117   writer.end_list("particles-snow");
118 }
119
120 SnowParticleSystem::~SnowParticleSystem()
121 {
122   for(int i=0;i<3;++i)
123     delete snowimages[i];
124 }
125
126 void SnowParticleSystem::update(float elapsed_time)
127 {
128     std::vector<Particle*>::iterator i;
129     for(i = particles.begin(); i != particles.end(); ++i) {
130         SnowParticle* particle = (SnowParticle*) *i;
131         particle->pos.y += particle->speed * elapsed_time;
132         if(particle->pos.y > SCREEN_HEIGHT) {
133             particle->pos.y = fmodf(particle->pos.y , virtual_height);
134             particle->pos.x = rand() % int(virtual_width);
135         }
136     }
137 }
138
139 RainParticleSystem::RainParticleSystem()
140 {
141     rainimages[0] = new Surface(datadir+"/images/objects/particles/rain0.png", true);
142     rainimages[1] = new Surface(datadir+"/images/objects/particles/rain1.png", true);
143
144     virtual_width = SCREEN_WIDTH * 2;
145
146     // create some random raindrops
147     size_t raindropcount = size_t(virtual_width/8.0);
148     for(size_t i=0; i<raindropcount; ++i) {
149         RainParticle* particle = new RainParticle;
150         particle->pos.x = rand() % int(virtual_width);
151         particle->pos.y = rand() % int(virtual_height);
152         int rainsize = rand() % 2;
153         particle->texture = rainimages[rainsize];
154         do {
155             particle->speed = (rainsize+1)*45 + (float(rand()%10)*.4);
156         } while(particle->speed < 1);
157         particle->speed *= 10; // gravity
158
159         particles.push_back(particle);
160     }
161 }
162
163 void
164 RainParticleSystem::parse(const lisp::Lisp& reader)
165 {
166   reader.get("layer", layer);
167 }
168
169 void
170 RainParticleSystem::write(lisp::Writer& writer)
171 {
172   writer.start_list("particles-rain");
173   writer.write_int("layer", layer);
174   writer.end_list("particles-rain");
175 }
176
177 RainParticleSystem::~RainParticleSystem()
178 {
179   for(int i=0;i<2;++i)
180     delete rainimages[i];
181 }
182
183 void RainParticleSystem::update(float elapsed_time)
184 {
185     std::vector<Particle*>::iterator i;
186     for(
187         i = particles.begin(); i != particles.end(); ++i) {
188         RainParticle* particle = (RainParticle*) *i;
189         float movement = particle->speed * elapsed_time;
190         particle->pos.y += movement;
191         particle->pos.x -= movement;
192         if ((particle->pos.y > SCREEN_HEIGHT) || (collision(particle, Vector(-movement, movement)))) {
193             particle->pos.y = 0;
194             particle->pos.x = rand() % int(virtual_width);
195         }
196     }
197 }
198
199 bool
200 RainParticleSystem::collision(RainParticle* object, Vector movement)
201 {
202   TileMap* solids = Sector::current()->solids;
203   // calculate rectangle where the object will move
204   float x1, x2;
205   
206   /*if(object->get_movement().x >= 0) {
207     x1 = object->get_pos().x;
208     x2 = object->get_bbox().p2.x + object->get_movement().x;
209   } else {
210     x1 = object->get_pos().x + object->get_movement().x;
211     x2 = object->get_bbox().p2.x;
212   }*/
213   float y1, y2;
214   /*
215   if(object->get_movement().y >= 0) {
216     y1 = object->get_pos().y;
217     y2 = object->get_bbox().p2.y + object->get_movement().y;
218   } else {
219     y1 = object->get_pos().y + object->get_movement().y;
220     y2 = object->get_bbox().p2.y;
221   }*/
222   x1 = object->pos.x;
223   x2 = x1 + 32 + movement.x;
224   y1 = object->pos.y;
225   y2 = y1 + 32 + movement.y;
226   
227   // test with all tiles in this rectangle
228   int starttilex = int(x1-1) / 32;
229   int starttiley = int(y1-1) / 32;
230   int max_x = int(x2+1);
231   int max_y = int(y2+1);
232
233   CollisionHit temphit, hit;
234   Rect dest = Rect(x1, y1, x2, y2);
235   dest.move(movement);
236   hit.time = -1; // represents an invalid value
237   for(int x = starttilex; x*32 < max_x; ++x) {
238     for(int y = starttiley; y*32 < max_y; ++y) {
239       const Tile* tile = solids->get_tile(x, y);
240       if(!tile)
241         continue;
242       // skip non-solid tiles
243       if(!(tile->getAttributes() & Tile::SOLID))
244         continue;
245
246       if(tile->getAttributes() & Tile::SLOPE) { // slope tile
247         AATriangle triangle;
248         Vector p1(x*32, y*32);
249         Vector p2((x+1)*32, (y+1)*32);
250         triangle = AATriangle(p1, p2, tile->getData());
251
252         if(Collision::rectangle_aatriangle(temphit, dest, movement,
253               triangle)) {
254           if(temphit.time > hit.time)
255             hit = temphit;
256         }
257       } else { // normal rectangular tile
258         Rect rect(x*32, y*32, (x+1)*32, (y+1)*32);
259         if(Collision::rectangle_rectangle(temphit, dest,
260               movement, rect)) {
261           if(temphit.time > hit.time)
262             hit = temphit;
263         }
264       }
265     }
266   }
267   
268   // did we collide at all?
269   if(hit.time < 0)
270     return false; else return true;
271 }
272
273 CloudParticleSystem::CloudParticleSystem()
274 {
275     cloudimage = new Surface(datadir + "/images/objects/particles/cloud.png", true);
276
277     virtual_width = 2000.0;
278
279     // create some random clouds
280     for(size_t i=0; i<15; ++i) {
281         CloudParticle* particle = new CloudParticle;
282         particle->pos.x = rand() % int(virtual_width);
283         particle->pos.y = rand() % int(virtual_height);
284         particle->texture = cloudimage;
285         particle->speed = -float(25 + rand() % 30);
286
287         particles.push_back(particle);
288     }
289 }
290
291 void
292 CloudParticleSystem::parse(const lisp::Lisp& reader)
293 {
294   reader.get("layer", layer);
295 }
296
297 void
298 CloudParticleSystem::write(lisp::Writer& writer)
299 {
300   writer.start_list("particles-clouds");
301   writer.write_int("layer", layer);
302   writer.end_list("particles-clouds");
303 }
304
305 CloudParticleSystem::~CloudParticleSystem()
306 {
307   delete cloudimage;
308 }
309
310 void CloudParticleSystem::update(float elapsed_time)
311 {
312     std::vector<Particle*>::iterator i;
313     for(i = particles.begin(); i != particles.end(); ++i) {
314         CloudParticle* particle = (CloudParticle*) *i;
315         particle->pos.x += particle->speed * elapsed_time;
316     }
317 }