renamed particlesystem_absolute to _interactive
[supertux.git] / src / object / particlesystem_interactive.cpp
1 //  $Id: particlesystem.cpp 2470 2005-05-11 14:49:28Z wansti $
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_interactive.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 #include "object/camera.h"
38 #include "badguy/bomb.h"
39
40 //TODO: Dynamically create splashes at collision spots
41 //      Find a way to make rain collide with objects like bonus blocks
42 //      Add an option to set rain strength
43 ParticleSystem_Interactive::ParticleSystem_Interactive()
44 {
45     virtual_width = SCREEN_WIDTH;
46     virtual_height = SCREEN_HEIGHT;
47     layer = LAYER_TILES;
48 }
49
50 ParticleSystem_Interactive::~ParticleSystem_Interactive()
51 {
52     std::vector<Particle*>::iterator i;
53     for(i = particles.begin(); i != particles.end(); ++i) {
54         delete *i;
55     }
56 }
57
58 void ParticleSystem_Interactive::draw(DrawingContext& context)
59 {
60   context.push_transform();
61   
62     std::vector<Particle*>::iterator i;
63     for(i = particles.begin(); i != particles.end(); ++i) {
64         Particle* particle = *i;
65         context.draw_surface(particle->texture, particle->pos, layer);
66     }
67
68     context.pop_transform();
69 }
70
71 bool
72 ParticleSystem_Interactive::collision(Particle* object, Vector movement)
73 {
74   TileMap* solids = Sector::current()->solids;
75   // calculate rectangle where the object will move
76   float x1, x2;
77   float y1, y2;
78   x1 = object->pos.x;
79   x2 = x1 + 32 + movement.x;
80   y1 = object->pos.y;
81   y2 = y1 + 32 + movement.y;
82   
83   // test with all tiles in this rectangle
84   int starttilex = int(x1-1) / 32;
85   int starttiley = int(y1-1) / 32;
86   int max_x = int(x2+1);
87   int max_y = int(y2+1);
88   
89   CollisionHit temphit, hit;
90   Rect dest = Rect(x1, y1, x2, y2);
91   dest.move(movement);
92   hit.time = -1; // represents an invalid value
93   for(int x = starttilex; x*32 < max_x; ++x) {
94     for(int y = starttiley; y*32 < max_y; ++y) {
95       const Tile* tile = solids->get_tile(x, y);
96       if(!tile)
97         continue;
98       // skip non-solid tiles
99       if(!(tile->getAttributes() & Tile::SOLID))
100         continue;
101
102       if(tile->getAttributes() & Tile::SLOPE) { // slope tile
103         AATriangle triangle;
104         Vector p1(x*32, y*32);
105         Vector p2((x+1)*32, (y+1)*32);
106         triangle = AATriangle(p1, p2, tile->getData());
107
108         if(Collision::rectangle_aatriangle(temphit, dest, movement,
109               triangle)) {
110           if(temphit.time > hit.time)
111             hit = temphit;
112         }
113       } else { // normal rectangular tile
114         Rect rect(x*32, y*32, (x+1)*32, (y+1)*32);
115         if(Collision::rectangle_rectangle(temphit, dest,
116               movement, rect)) {
117           if(temphit.time > hit.time)
118             hit = temphit;
119         }
120       }
121     }
122   }
123   
124   // did we collide at all?
125   if(hit.time < 0)
126     return false; else return true;
127 }
128
129 RainParticleSystem::RainParticleSystem()
130 {
131     rainimages[0] = new Surface(datadir+"/images/objects/particles/rain0.png", true);
132     rainimages[1] = new Surface(datadir+"/images/objects/particles/rain1.png", true);
133
134     virtual_width = SCREEN_WIDTH * 2;
135
136     // create some random raindrops
137     size_t raindropcount = size_t(virtual_width/6.0);
138     for(size_t i=0; i<raindropcount; ++i) {
139         RainParticle* particle = new RainParticle;
140         particle->pos.x = rand() % int(virtual_width);
141         particle->pos.y = rand() % int(virtual_height);
142         int rainsize = rand() % 2;
143         particle->texture = rainimages[rainsize];
144         do {
145             particle->speed = (rainsize+1)*45 + (float(rand()%10)*.4);
146         } while(particle->speed < 1);
147         particle->speed *= 10; // gravity
148
149         particles.push_back(particle);
150     }
151 }
152
153 void
154 RainParticleSystem::parse(const lisp::Lisp& reader)
155 {
156   reader.get("layer", layer);
157 }
158
159 void
160 RainParticleSystem::write(lisp::Writer& writer)
161 {
162   writer.start_list("particles-rain");
163   writer.write_int("layer", layer);
164   writer.end_list("particles-rain");
165 }
166
167 RainParticleSystem::~RainParticleSystem()
168 {
169   for(int i=0;i<2;++i)
170     delete rainimages[i];
171 }
172
173 void RainParticleSystem::update(float elapsed_time)
174 {
175     std::vector<Particle*>::iterator i;
176     for(
177         i = particles.begin(); i != particles.end(); ++i) {
178         RainParticle* particle = (RainParticle*) *i;
179         float movement = particle->speed * elapsed_time;
180         float abs_x = Sector::current()->camera->get_translation().x;
181         float abs_y = Sector::current()->camera->get_translation().y;
182         particle->pos.y += movement;
183         particle->pos.x -= movement;
184         if ((particle->pos.y > SCREEN_HEIGHT + abs_y) || (collision(particle, Vector(-movement, movement)))) {
185             int new_x = (rand() % int(virtual_width)) + int(abs_x);
186             int new_y = 0;
187             //FIXME: Don't move particles over solid tiles
188             particle->pos.x = new_x;
189             particle->pos.y = new_y;
190         }
191     }
192 }
193
194 CometParticleSystem::CometParticleSystem()
195 {
196     cometimages[0] = new Surface(datadir+"/images/creatures/mr_bomb/exploding-left-0.png", true);
197     cometimages[1] = new Surface(datadir+"/images/creatures/mr_bomb/exploding-left-0.png", true);
198
199     virtual_width = SCREEN_WIDTH * 2;
200
201     // create some random comets
202     size_t cometcount = 2;
203     for(size_t i=0; i<cometcount; ++i) {
204         CometParticle* particle = new CometParticle;
205         particle->pos.x = rand() % int(virtual_width);
206         particle->pos.y = rand() % int(virtual_height);
207         int cometsize = rand() % 2;
208         particle->texture = cometimages[cometsize];
209         do {
210             particle->speed = (cometsize+1)*30 + (float(rand()%10)*.4);
211         } while(particle->speed < 1);
212         particle->speed *= 10; // gravity
213
214         particles.push_back(particle);
215     }
216 }
217
218 void
219 CometParticleSystem::parse(const lisp::Lisp& reader)
220 {
221   reader.get("layer", layer);
222 }
223
224 void
225 CometParticleSystem::write(lisp::Writer& writer)
226 {
227   writer.start_list("particles-comets");
228   writer.write_int("layer", layer);
229   writer.end_list("particles-comets");
230 }
231
232 CometParticleSystem::~CometParticleSystem()
233 {
234   for(int i=0;i<2;++i)
235     delete cometimages[i];
236 }
237
238 void CometParticleSystem::update(float elapsed_time)
239 {
240     std::vector<Particle*>::iterator i;
241     for(
242         i = particles.begin(); i != particles.end(); ++i) {
243         CometParticle* particle = (CometParticle*) *i;
244         float movement = particle->speed * elapsed_time;
245         float abs_x = Sector::current()->camera->get_translation().x;
246         float abs_y = Sector::current()->camera->get_translation().y;
247         particle->pos.y += movement;
248         particle->pos.x -= movement;
249         if ((particle->pos.y > SCREEN_HEIGHT + abs_y) || (collision(particle, Vector(-movement, movement)))) {
250             if (particle->pos.y <= SCREEN_HEIGHT + abs_y) {
251               Sector::current()->add_object(new Bomb(particle->pos, LEFT));
252             }
253             int new_x = (rand() % int(virtual_width)) + int(abs_x);
254             int new_y = 0;
255             //FIXME: Don't move particles over solid tiles
256             particle->pos.x = new_x;
257             particle->pos.y = new_y;
258         }
259     }
260 }