Massive copyright update. I'm sorry if I'm crediting Matze for something he didn...
[supertux.git] / src / object / particlesystem_interactive.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 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
20 #include <config.h>
21
22 #include <iostream>
23 #include <cmath>
24
25 #include "particlesystem_interactive.hpp"
26 #include "video/drawing_context.hpp"
27 #include "lisp/parser.hpp"
28 #include "lisp/lisp.hpp"
29 #include "lisp/writer.hpp"
30 #include "resources.hpp"
31 #include "main.hpp"
32
33 #include "tile.hpp"
34 #include "tilemap.hpp"
35 #include "math/aatriangle.hpp"
36 #include "collision.hpp"
37 #include "collision_hit.hpp"
38 #include "object/camera.hpp"
39 #include "object/rainsplash.hpp"
40 #include "badguy/bomb.hpp"
41
42 //TODO: Find a way to make rain collide with objects like bonus blocks
43 //      Add an option to set rain strength
44 //      Fix rain being "respawned" over solid tiles
45 ParticleSystem_Interactive::ParticleSystem_Interactive()
46 {
47     virtual_width = SCREEN_WIDTH;
48     virtual_height = SCREEN_HEIGHT;
49     layer = LAYER_TILES;
50 }
51
52 ParticleSystem_Interactive::~ParticleSystem_Interactive()
53 {
54     std::vector<Particle*>::iterator i;
55     for(i = particles.begin(); i != particles.end(); ++i) {
56         delete *i;
57     }
58 }
59
60 void ParticleSystem_Interactive::draw(DrawingContext& context)
61 {
62   context.push_transform();
63   
64     std::vector<Particle*>::iterator i;
65     for(i = particles.begin(); i != particles.end(); ++i) {
66         Particle* particle = *i;
67         context.draw_surface(particle->texture, particle->pos, layer);
68     }
69
70     context.pop_transform();
71 }
72
73 int
74 ParticleSystem_Interactive::collision(Particle* object, Vector movement)
75 {
76   TileMap* solids = Sector::current()->solids;
77   // calculate rectangle where the object will move
78   float x1, x2;
79   float y1, y2;
80   x1 = object->pos.x;
81   x2 = x1 + 32 + movement.x;
82   y1 = object->pos.y;
83   y2 = y1 + 32 + movement.y;
84   bool water = false;
85   
86   // test with all tiles in this rectangle
87   int starttilex = int(x1-1) / 32;
88   int starttiley = int(y1-1) / 32;
89   int max_x = int(x2+1);
90   int max_y = int(y2+1);
91   
92   CollisionHit temphit, hit;
93   Rect dest = Rect(x1, y1, x2, y2);
94   dest.move(movement);
95   hit.time = -1; // represents an invalid value
96   for(int x = starttilex; x*32 < max_x; ++x) {
97     for(int y = starttiley; y*32 < max_y; ++y) {
98       const Tile* tile = solids->get_tile(x, y);
99       if(!tile)
100         continue;
101       // skip non-solid tiles, except water
102       if (tile->getAttributes() & Tile::WATER)
103         water = true;
104       if(!water && !(tile->getAttributes() & Tile::SOLID))
105         continue;
106
107       if(tile->getAttributes() & Tile::SLOPE) { // slope tile
108         AATriangle triangle;
109         Vector p1(x*32, y*32);
110         Vector p2((x+1)*32, (y+1)*32);
111         triangle = AATriangle(p1, p2, tile->getData());
112
113         if(Collision::rectangle_aatriangle(temphit, dest, movement,
114               triangle)) {
115           if(temphit.time > hit.time)
116             hit = temphit;
117         }
118       } else { // normal rectangular tile
119         Rect rect(x*32, y*32, (x+1)*32, (y+1)*32);
120         if(Collision::rectangle_rectangle(temphit, dest,
121               movement, rect)) {
122           if(temphit.time > hit.time)
123             hit = temphit;
124         }
125       }
126     }
127   }
128   
129   // did we collide at all?
130   if(hit.time < 0) {
131     return -1; //no collision
132   }
133   else {
134     if (water)
135       return 0; //collision with water tile - don't draw splash
136     else {
137       if ((hit.normal.x == 1) && (hit.normal.y == 0))
138         return 2; //collision from right
139       else return 1; //collision from above
140     }
141   }
142 }
143
144 RainParticleSystem::RainParticleSystem()
145 {
146     rainimages[0] = new Surface("images/objects/particles/rain0.png");
147     rainimages[1] = new Surface("images/objects/particles/rain1.png");
148
149     virtual_width = SCREEN_WIDTH * 2;
150
151     // create some random raindrops
152     size_t raindropcount = size_t(virtual_width/6.0);
153     for(size_t i=0; i<raindropcount; ++i) {
154         RainParticle* particle = new RainParticle;
155         particle->pos.x = rand() % int(virtual_width);
156         particle->pos.y = rand() % int(virtual_height);
157         int rainsize = rand() % 2;
158         particle->texture = rainimages[rainsize];
159         do {
160             particle->speed = (rainsize+1)*45 + (float(rand()%10)*.4);
161         } while(particle->speed < 1);
162         particle->speed *= 10; // gravity
163
164         particles.push_back(particle);
165     }
166 }
167
168 void
169 RainParticleSystem::parse(const lisp::Lisp& reader)
170 {
171   reader.get("layer", layer);
172 }
173
174 void
175 RainParticleSystem::write(lisp::Writer& writer)
176 {
177   writer.start_list("particles-rain");
178   writer.write_int("layer", layer);
179   writer.end_list("particles-rain");
180 }
181
182 RainParticleSystem::~RainParticleSystem()
183 {
184   for(int i=0;i<2;++i)
185     delete rainimages[i];
186 }
187
188 void RainParticleSystem::update(float elapsed_time)
189 {
190     std::vector<Particle*>::iterator i;
191     for(
192         i = particles.begin(); i != particles.end(); ++i) {
193         RainParticle* particle = (RainParticle*) *i;
194         float movement = particle->speed * elapsed_time;
195         float abs_x = Sector::current()->camera->get_translation().x;
196         float abs_y = Sector::current()->camera->get_translation().y;
197         particle->pos.y += movement;
198         particle->pos.x -= movement;
199         int col = collision(particle, Vector(-movement, movement));
200         if ((particle->pos.y > SCREEN_HEIGHT + abs_y) || (col >= 0)) {
201             //Create rainsplash
202             if ((particle->pos.y <= SCREEN_HEIGHT + abs_y) && (col >= 1)){
203               bool vertical = (col == 2);
204               int splash_x, splash_y;
205               if (!vertical) { //check if collision happened from above
206                 splash_x = int(particle->pos.x);
207                 splash_y = int(particle->pos.y) - (int(particle->pos.y) % 32) + 32;
208                 Sector::current()->add_object(new RainSplash(Vector(splash_x, splash_y),vertical));
209               }
210               // Uncomment the following to display vertical splashes, too
211               /* else {
212                 splash_x = int(particle->pos.x) - (int(particle->pos.x) % 32) + 32;
213                 splash_y = int(particle->pos.y);
214                 Sector::current()->add_object(new RainSplash(Vector(splash_x, splash_y),vertical));
215               } */
216             }
217             int new_x = (rand() % int(virtual_width)) + int(abs_x);
218             int new_y = 0;
219             //FIXME: Don't move particles over solid tiles
220             particle->pos.x = new_x;
221             particle->pos.y = new_y;
222         }
223     }
224 }
225
226 CometParticleSystem::CometParticleSystem()
227 {
228     cometimages[0] = new Surface("images/creatures/mr_bomb/exploding-left-0.png");
229     cometimages[1] = new Surface("images/creatures/mr_bomb/exploding-left-0.png");
230
231     virtual_width = SCREEN_WIDTH * 2;
232
233     // create some random comets
234     size_t cometcount = 2;
235     for(size_t i=0; i<cometcount; ++i) {
236         CometParticle* particle = new CometParticle;
237         particle->pos.x = rand() % int(virtual_width);
238         particle->pos.y = rand() % int(virtual_height);
239         int cometsize = rand() % 2;
240         particle->texture = cometimages[cometsize];
241         do {
242             particle->speed = (cometsize+1)*30 + (float(rand()%10)*.4);
243         } while(particle->speed < 1);
244         particle->speed *= 10; // gravity
245
246         particles.push_back(particle);
247     }
248 }
249
250 void
251 CometParticleSystem::parse(const lisp::Lisp& reader)
252 {
253   reader.get("layer", layer);
254 }
255
256 void
257 CometParticleSystem::write(lisp::Writer& writer)
258 {
259   writer.start_list("particles-comets");
260   writer.write_int("layer", layer);
261   writer.end_list("particles-comets");
262 }
263
264 CometParticleSystem::~CometParticleSystem()
265 {
266   for(int i=0;i<2;++i)
267     delete cometimages[i];
268 }
269
270 void CometParticleSystem::update(float elapsed_time)
271 {
272     std::vector<Particle*>::iterator i;
273     for(
274         i = particles.begin(); i != particles.end(); ++i) {
275         CometParticle* particle = (CometParticle*) *i;
276         float movement = particle->speed * elapsed_time;
277         float abs_x = Sector::current()->camera->get_translation().x;
278         float abs_y = Sector::current()->camera->get_translation().y;
279         particle->pos.y += movement;
280         particle->pos.x -= movement;
281         int col = collision(particle, Vector(-movement, movement));
282         if ((particle->pos.y > SCREEN_HEIGHT + abs_y) || (col >= 0)) {
283             if ((particle->pos.y <= SCREEN_HEIGHT + abs_y) && (col >= 1)) {
284               Sector::current()->add_object(new Bomb(particle->pos, LEFT));
285             }
286             int new_x = (rand() % int(virtual_width)) + int(abs_x);
287             int new_y = 0;
288             //FIXME: Don't move particles over solid tiles
289             particle->pos.x = new_x;
290             particle->pos.y = new_y;
291         }
292     }
293 }