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