Replaced more lisp::Lisp/lisp::Writer with Reader/Writer
[supertux.git] / src / object / particlesystem_interactive.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "object/particlesystem_interactive.hpp"
18
19 #include "math/aatriangle.hpp"
20 #include "math/random_generator.hpp"
21 #include "object/camera.hpp"
22 #include "object/rainsplash.hpp"
23 #include "object/tilemap.hpp"
24 #include "supertux/collision.hpp"
25 #include "supertux/globals.hpp"
26 #include "supertux/tile.hpp"
27 #include "util/reader.hpp"
28
29 //TODO: Find a way to make rain collide with objects like bonus blocks
30 //      Add an option to set rain strength
31 //      Fix rain being "respawned" over solid tiles
32 ParticleSystem_Interactive::ParticleSystem_Interactive() :
33   z_pos(),
34   particles(),
35   virtual_width(),
36   virtual_height()
37 {
38   virtual_width = SCREEN_WIDTH;
39   virtual_height = SCREEN_HEIGHT;
40   z_pos = 0;
41 }
42
43 ParticleSystem_Interactive::~ParticleSystem_Interactive()
44 {
45   std::vector<Particle*>::iterator i;
46   for(i = particles.begin(); i != particles.end(); ++i) {
47     delete *i;
48   }
49 }
50
51 void ParticleSystem_Interactive::draw(DrawingContext& context)
52 {
53   context.push_transform();
54
55   std::vector<Particle*>::iterator i;
56   for(i = particles.begin(); i != particles.end(); ++i) {
57     Particle* particle = *i;
58     context.draw_surface(particle->texture, particle->pos, z_pos);
59   }
60
61   context.pop_transform();
62 }
63
64 int
65 ParticleSystem_Interactive::collision(Particle* object, Vector movement)
66 {
67   using namespace collision;
68
69   // calculate rectangle where the object will move
70   float x1, x2;
71   float y1, y2;
72   x1 = object->pos.x;
73   x2 = x1 + 32 + movement.x;
74   y1 = object->pos.y;
75   y2 = y1 + 32 + movement.y;
76   bool water = false;
77
78   // test with all tiles in this rectangle
79   int starttilex = int(x1-1) / 32;
80   int starttiley = int(y1-1) / 32;
81   int max_x = int(x2+1);
82   int max_y = int(y2+1);
83
84   Rect dest = Rect(x1, y1, x2, y2);
85   dest.move(movement);
86   Constraints constraints;
87
88   for(std::list<TileMap*>::const_iterator i = Sector::current()->solid_tilemaps.begin(); i != Sector::current()->solid_tilemaps.end(); i++) {
89     TileMap* solids = *i;
90     for(int x = starttilex; x*32 < max_x; ++x) {
91       for(int y = starttiley; y*32 < max_y; ++y) {
92         const Tile* tile = solids->get_tile(x, y);
93         if(!tile)
94           continue;
95         // skip non-solid tiles, except water
96         if(! (tile->getAttributes() & (Tile::WATER | Tile::SOLID)))
97           continue;
98
99         if(tile->getAttributes() & Tile::SLOPE) { // slope tile
100           AATriangle triangle;
101           Vector p1(x*32, y*32);
102           Vector p2((x+1)*32, (y+1)*32);
103           triangle = AATriangle(p1, p2, tile->getData());
104
105           if(rectangle_aatriangle(&constraints, dest, triangle)) {
106             if(tile->getAttributes() & Tile::WATER)
107               water = true;
108           }
109         } else { // normal rectangular tile
110           Rect rect(x*32, y*32, (x+1)*32, (y+1)*32);
111           if(intersects(dest, rect)) {
112             if(tile->getAttributes() & Tile::WATER)
113               water = true;
114             set_rectangle_rectangle_constraints(&constraints, dest, rect);
115           }
116         }
117       }
118     }
119   }
120
121   // TODO don't use magic numbers here...
122
123   // did we collide at all?
124   if(!constraints.has_constraints())
125     return -1;
126
127   const CollisionHit& hit = constraints.hit;
128   if (water) {
129     return 0; //collision with water tile - don't draw splash
130   } else {
131     if (hit.right || hit.left) {
132       return 2; //collision from right
133     } else {
134       return 1; //collision from above
135     }
136   }
137
138   return 0;
139 }
140
141 RainParticleSystem::RainParticleSystem()
142 {
143   rainimages[0] = new Surface("images/objects/particles/rain0.png");
144   rainimages[1] = new Surface("images/objects/particles/rain1.png");
145
146   virtual_width = SCREEN_WIDTH * 2;
147
148   // create some random raindrops
149   size_t raindropcount = size_t(virtual_width/6.0);
150   for(size_t i=0; i<raindropcount; ++i) {
151     RainParticle* particle = new RainParticle;
152     particle->pos.x = systemRandom.rand(int(virtual_width));
153     particle->pos.y = systemRandom.rand(int(virtual_height));
154     int rainsize = systemRandom.rand(2);
155     particle->texture = rainimages[rainsize];
156     do {
157       particle->speed = (rainsize+1)*45 + systemRandom.randf(3.6);
158     } while(particle->speed < 1);
159     particle->speed *= 10; // gravity
160
161     particles.push_back(particle);
162   }
163 }
164
165 void
166 RainParticleSystem::parse(const Reader& reader)
167 {
168   reader.get("z-pos", z_pos);
169 }
170
171 RainParticleSystem::~RainParticleSystem()
172 {
173   for(int i=0;i<2;++i)
174     delete rainimages[i];
175 }
176
177 void RainParticleSystem::update(float elapsed_time)
178 {
179   std::vector<Particle*>::iterator i;
180   for(
181     i = particles.begin(); i != particles.end(); ++i) {
182     RainParticle* particle = (RainParticle*) *i;
183     float movement = particle->speed * elapsed_time;
184     float abs_x = Sector::current()->camera->get_translation().x;
185     float abs_y = Sector::current()->camera->get_translation().y;
186     particle->pos.y += movement;
187     particle->pos.x -= movement;
188     int col = collision(particle, Vector(-movement, movement));
189     if ((particle->pos.y > SCREEN_HEIGHT + abs_y) || (col >= 0)) {
190       //Create rainsplash
191       if ((particle->pos.y <= SCREEN_HEIGHT + abs_y) && (col >= 1)){
192         bool vertical = (col == 2);
193         int splash_x, splash_y;
194         if (!vertical) { //check if collision happened from above
195           splash_x = int(particle->pos.x);
196           splash_y = int(particle->pos.y) - (int(particle->pos.y) % 32) + 32;
197           Sector::current()->add_object(new RainSplash(Vector(splash_x, splash_y),vertical));
198         }
199         // Uncomment the following to display vertical splashes, too
200         /* else {
201            splash_x = int(particle->pos.x) - (int(particle->pos.x) % 32) + 32;
202            splash_y = int(particle->pos.y);
203            Sector::current()->add_object(new RainSplash(Vector(splash_x, splash_y),vertical));
204            } */
205       }
206       int new_x = systemRandom.rand(int(virtual_width)) + int(abs_x);
207       int new_y = 0;
208       //FIXME: Don't move particles over solid tiles
209       particle->pos.x = new_x;
210       particle->pos.y = new_y;
211     }
212   }
213 }
214
215 CometParticleSystem::CometParticleSystem()
216 {
217   cometimages[0] = new Surface("images/creatures/mr_bomb/exploding-left-0.png");
218   cometimages[1] = new Surface("images/creatures/mr_bomb/exploding-left-0.png");
219
220   virtual_width = SCREEN_WIDTH * 2;
221
222   // create some random comets
223   size_t cometcount = 2;
224   for(size_t i=0; i<cometcount; ++i) {
225     CometParticle* particle = new CometParticle;
226     particle->pos.x = systemRandom.rand(int(virtual_width));
227     particle->pos.y = systemRandom.rand(int(virtual_height));
228     int cometsize = systemRandom.rand(2);
229     particle->texture = cometimages[cometsize];
230     do {
231       particle->speed = (cometsize+1)*30 + systemRandom.randf(3.6);
232     } while(particle->speed < 1);
233     particle->speed *= 10; // gravity
234
235     particles.push_back(particle);
236   }
237 }
238
239 void
240 CometParticleSystem::parse(const Reader& reader)
241 {
242   reader.get("z-pos", z_pos);
243 }
244
245 CometParticleSystem::~CometParticleSystem()
246 {
247   for(int i=0;i<2;++i)
248     delete cometimages[i];
249 }
250
251 void CometParticleSystem::update(float elapsed_time)
252 {
253   (void) elapsed_time;
254 #if 0
255   std::vector<Particle*>::iterator i;
256   for(
257     i = particles.begin(); i != particles.end(); ++i) {
258     CometParticle* particle = (CometParticle*) *i;
259     float movement = particle->speed * elapsed_time;
260     float abs_x = Sector::current()->camera->get_translation().x;
261     float abs_y = Sector::current()->camera->get_translation().y;
262     particle->pos.y += movement;
263     particle->pos.x -= movement;
264     int col = collision(particle, Vector(-movement, movement));
265     if ((particle->pos.y > SCREEN_HEIGHT + abs_y) || (col >= 0)) {
266       if ((particle->pos.y <= SCREEN_HEIGHT + abs_y) && (col >= 1)) {
267         Sector::current()->add_object(new Bomb(particle->pos, LEFT));
268       }
269       int new_x = systemRandom.rand(int(virtual_width)) + int(abs_x);
270       int new_y = 0;
271       //FIXME: Don't move particles over solid tiles
272       particle->pos.x = new_x;
273       particle->pos.y = new_y;
274     }
275   }
276 #endif
277 }
278
279 /* EOF */