Fade out and pause music on death and resume on restart of level, fixes #1064
[supertux.git] / src / object / snow_particle_system.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/snow_particle_system.hpp"
18
19 #include <math.h>
20
21 #include "math/random_generator.hpp"
22 #include "supertux/globals.hpp"
23 #include "video/drawing_context.hpp"
24
25 // TODO: tweak values
26 namespace SNOW {
27 static const float SPIN_SPEED = 60.0f;
28 static const float WIND_SPEED = 30.0f; // max speed of wind will be randf(WIND_SPEED) * randf(STATE_LENGTH)
29 static const float STATE_LENGTH = 5.0f;
30 static const float DECAY_RATIO = 0.2f; // ratio of attack speed to decay speed
31 static const float EPSILON = 0.5f; //velocity changes by up to this much each tick
32 static const float WOBBLE_DECAY = 0.99f; //wobble decays exponentially by this much each tick
33 static const float WOBBLE_FACTOR = 4 * .005f; //wobble approaches drift_speed by this much each tick
34 }
35
36 SnowParticleSystem::SnowParticleSystem() :
37   state(RELEASING),
38   timer(),
39   gust_onset(0),
40   gust_current_velocity(0)
41 {
42   snowimages[0] = Surface::create("images/objects/particles/snow2.png");
43   snowimages[1] = Surface::create("images/objects/particles/snow1.png");
44   snowimages[2] = Surface::create("images/objects/particles/snow0.png");
45
46   virtual_width = SCREEN_WIDTH * 2;
47
48   timer.start(.01);
49
50   // create some random snowflakes
51   size_t snowflakecount = size_t(virtual_width/10.0);
52   for(size_t i=0; i<snowflakecount; ++i) {
53     SnowParticle* particle = new SnowParticle;
54     int snowsize = graphicsRandom.rand(3);
55
56     particle->pos.x = graphicsRandom.randf(virtual_width);
57     particle->pos.y = graphicsRandom.randf(SCREEN_HEIGHT);
58     particle->anchorx = particle->pos.x + (graphicsRandom.randf(-0.5, 0.5) * 16);
59     // drift will change with wind gusts
60     particle->drift_speed = graphicsRandom.randf(-0.5, 0.5) * 0.3;
61     particle->wobble = 0.0;
62
63     particle->texture = snowimages[snowsize];
64     particle->flake_size = powf(snowsize+3,4); // since it ranges from 0 to 2
65
66     particle->speed = 2 * (1 + (2 - snowsize)/2 + graphicsRandom.randf(1.8)) * 10; // gravity
67
68     // Spinning
69     particle->angle = graphicsRandom.randf(360.0);
70     particle->spin_speed = graphicsRandom.randf(-SNOW::SPIN_SPEED,SNOW::SPIN_SPEED);
71
72     particles.push_back(particle);
73   }
74 }
75
76 void
77 SnowParticleSystem::parse(const Reader& reader)
78 {
79   z_pos = reader_get_layer (reader, /* default = */ LAYER_BACKGROUND1);
80 }
81
82 SnowParticleSystem::~SnowParticleSystem()
83 {
84 }
85
86 void SnowParticleSystem::update(float elapsed_time)
87 {
88   // Simple ADSR wind gusts
89
90   if (timer.check()) {
91     // Change state
92     state = (State) ((state + 1) % MAX_STATE);
93
94     if(state == RESTING) {
95       // stop wind
96       gust_current_velocity = 0;
97       // new wind strength
98       gust_onset   = graphicsRandom.randf(-SNOW::WIND_SPEED, SNOW::WIND_SPEED);
99     }
100     timer.start(graphicsRandom.randf(SNOW::STATE_LENGTH));
101   }
102
103   // Update velocities
104   switch(state) {
105     case ATTACKING:
106       gust_current_velocity += gust_onset * elapsed_time;
107       break;
108     case DECAYING:
109       gust_current_velocity -= gust_onset * elapsed_time * SNOW::DECAY_RATIO;
110       break;
111     case RELEASING:
112       // uses current time/velocity instead of constants
113       gust_current_velocity -= gust_current_velocity * elapsed_time / timer.get_timeleft();
114       break;
115     case SUSTAINING:
116     case RESTING:
117       //do nothing
118       break;
119     default:
120       assert(false);
121   }
122
123   std::vector<Particle*>::iterator i;
124
125   for(i = particles.begin(); i != particles.end(); ++i) {
126     SnowParticle* particle = (SnowParticle*) *i;
127     float anchor_delta;
128
129     // Falling
130     particle->pos.y += particle->speed * elapsed_time;
131     // Drifting (speed approaches wind at a rate dependent on flake size)
132     particle->drift_speed += (gust_current_velocity - particle->drift_speed) / particle->flake_size + graphicsRandom.randf(-SNOW::EPSILON,SNOW::EPSILON);
133     particle->anchorx += particle->drift_speed * elapsed_time;
134     // Wobbling (particle approaches anchorx)
135     particle->pos.x += particle->wobble * elapsed_time;
136     anchor_delta = (particle->anchorx - particle->pos.x);
137     particle->wobble += (SNOW::WOBBLE_FACTOR * anchor_delta) + graphicsRandom.randf(-SNOW::EPSILON, SNOW::EPSILON);
138     particle->wobble *= SNOW::WOBBLE_DECAY;
139     // Spinning
140     particle->angle += particle->spin_speed * elapsed_time;
141     particle->angle = fmodf(particle->angle, 360.0);
142   }
143 }
144
145 /* EOF */