1 #include <iostream>
2 #include <deque>
3 #include <boost/cstdint.hpp>
4 #include <math.h>
5 
6 #include "asserts.hpp"
7 #include "color_utils.hpp"
8 #include "entity.hpp"
9 #include "foreach.hpp"
10 #include "formula.hpp"
11 #include "frame.hpp"
12 #include "particle_system.hpp"
13 #include "preferences.hpp"
14 #include "string_utils.hpp"
15 #include "texture.hpp"
16 #include "variant_utils.hpp"
17 #include "weather_particle_system.hpp"
18 #include "water_particle_system.hpp"
19 #include "raster.hpp"
20 
21 namespace {
22 
23 class particle_animation {
24 public:
particle_animation(variant node)25 	explicit particle_animation(variant node) :
26 	  id_(node["id"].as_string()),
27 	  texture_(graphics::texture::get(node["image"].as_string())),
28 	  duration_(node["duration"].as_int()),
29 	  reverse_frame_(node["reverse"].as_bool()),
30 	  loops_(node["loops"].as_bool(false))
31 	{
32 		rect base_area(node.has_key("rect") ? rect(node["rect"]) :
33 	           rect(node["x"].as_int(),
34 	                node["y"].as_int(),
35 	                node["w"].as_int(),
36 	                node["h"].as_int()));
37 		width_  = base_area.w()*node["scale"].as_int(2);
38 		height_ = base_area.h()*node["scale"].as_int(2);
39 		int nframes = node["frames"].as_int(1);
40 		if(nframes < 1) {
41 			nframes = 1;
42 		}
43 
44 		const int nframes_per_row = node["frames_per_row"].as_int(-1);
45 		const int pad = node["pad"].as_int();
46 
47 		frame frame_obj(node);
48 
49 		int row = 0, col = 0;
50 		for(int n = 0; n != nframes; ++n) {
51 			const frame::frame_info& info = frame_obj.frame_layout()[n];
52 			const rect& area = info.area;
53 
54 			frame_area a;
55 			a.u1 = GLfloat(area.x())/GLfloat(texture_.width());
56 			a.u2 = GLfloat(area.x2())/GLfloat(texture_.width());
57 			a.v1 = GLfloat(area.y())/GLfloat(texture_.height());
58 			a.v2 = GLfloat(area.y2())/GLfloat(texture_.height());
59 
60 			a.x_adjust = info.x_adjust*2;
61 			a.y_adjust = info.y_adjust*2;
62 			a.x2_adjust = info.x2_adjust*2;
63 			a.y2_adjust = info.y2_adjust*2;
64 
65 			frames_.push_back(a);
66 
67 			++col;
68 			if(col == nframes_per_row) {
69 				col = 0;
70 				++row;
71 			}
72 		}
73 	}
74 
75 	struct frame_area {
76 		GLfloat u1, v1, u2, v2;
77 		int x_adjust, y_adjust, x2_adjust, y2_adjust;
78 	};
79 
get_frame(int t) const80 	const frame_area& get_frame(int t) const {
81 		int index = t/duration_;
82 		if(index < 0) {
83 			index = 0;
84 		} else if(index >= frames_.size()) {
85 			if(loops_ && !reverse_frame_) {
86 				index = index%frames_.size();
87 			} else if (loops_ && reverse_frame_){
88 				index = (running_in_reverse(index)? frames_.size()- 1 - index%frames_.size(): index%frames_.size());
89 			} else {
90 				index = frames_.size() - 1;
91 			}
92 		}
93 
94 		return frames_[index];
95 	}
running_in_reverse(int current_frame) const96 	bool running_in_reverse(int current_frame) const
97 	{
98 		return current_frame % (2* frames_.size()) >= frames_.size();
99 	}
100 
101 
set_texture() const102 	void set_texture() const {
103 		texture_.set_as_current_texture();
104 	}
105 
width() const106 	int width() const { return width_; }
height() const107 	int height() const { return height_; }
108 private:
109 	std::string id_;
110 	graphics::texture texture_;
111 
112 	std::vector<frame_area> frames_;
113 	int duration_;
114 	int reverse_frame_;
115 	int width_, height_;
116 	bool loops_;
117 };
118 
119 struct simple_particle_system_info {
simple_particle_system_info__anon54335c4e0111::simple_particle_system_info120 	simple_particle_system_info(variant node)
121 	  : spawn_rate_(node["spawn_rate"].as_int(1)),
122 	    spawn_rate_random_(node["spawn_rate_random"].as_int()),
123 	    system_time_to_live_(node["system_time_to_live"].as_int(-1)),
124 	    time_to_live_(node["time_to_live"].as_int(50)),
125 	    min_x_(node["min_x"].as_int(0)),
126 	    max_x_(node["max_x"].as_int(0)),
127 	    min_y_(node["min_y"].as_int(0)),
128 	    max_y_(node["max_y"].as_int(0)),
129 		velocity_x_(node["velocity_x"].as_int(0)),
130 		velocity_y_(node["velocity_y"].as_int(0)),
131 		velocity_x_rand_(node["velocity_x_random"].as_int(0)),
132 		velocity_y_rand_(node["velocity_y_random"].as_int(0)),
133 		velocity_magnitude_(node["velocity_magnitude"].as_int(0)),
134 		velocity_magnitude_rand_(node["velocity_magnitude_random"].as_int(0)),
135 		velocity_rotate_(node["velocity_rotate"].as_int(0)),
136 		velocity_rotate_rand_(node["velocity_rotate_random"].as_int(0)),
137 		accel_x_(node["accel_x"].as_int(0)),
138 		accel_y_(node["accel_y"].as_int(0)),
139 		pre_pump_cycles_(node["pre_pump_cycles"].as_int(0)),
140 		delta_r_(node["delta_r"].as_int(0)),
141 		delta_g_(node["delta_g"].as_int(0)),
142 		delta_b_(node["delta_b"].as_int(0)),
143 		delta_a_(node["delta_a"].as_int(0)),
144 		random_schedule_(false)
145 
146 	{
147 		if(node.has_key("velocity_x_schedule")) {
148 			velocity_x_schedule_ = node["velocity_x_schedule"].as_list_int();
149 		}
150 
151 		if(node.has_key("velocity_y_schedule")) {
152 			velocity_y_schedule_ = node["velocity_y_schedule"].as_list_int();
153 		}
154 
155 		random_schedule_ = node["random_schedule"].as_bool(velocity_x_schedule_.empty() == false || velocity_y_schedule_.empty() == false);
156 	}
157 	int spawn_rate_, spawn_rate_random_;
158 	int system_time_to_live_;
159 	int time_to_live_;
160 	int min_x_, max_x_, min_y_, max_y_;
161 	int velocity_x_, velocity_y_;
162 	int velocity_x_rand_, velocity_y_rand_;
163 	int velocity_magnitude_, velocity_magnitude_rand_;
164 	int velocity_rotate_, velocity_rotate_rand_;
165 	int accel_x_, accel_y_;
166 
167 	int pre_pump_cycles_;  //# of cycles to pre-emptively simulate so the particle system appears to have been running for a while, rather than visibly starting to emit particles just when the player walks onscreen
168 
169 	int delta_r_, delta_g_, delta_b_, delta_a_;
170 
171 	std::vector<int> velocity_x_schedule_, velocity_y_schedule_;
172 
173 	bool random_schedule_;
174 };
175 
176 class simple_particle_system_factory : public particle_system_factory {
177 public:
178 	explicit simple_particle_system_factory(variant node);
~simple_particle_system_factory()179 	~simple_particle_system_factory() {}
180 
181 	particle_system_ptr create(const entity& e) const;
182 
183 	std::vector<particle_animation> frames_;
184 
185 	simple_particle_system_info info_;
186 };
187 
simple_particle_system_factory(variant node)188 simple_particle_system_factory::simple_particle_system_factory(variant node)
189   : info_(node)
190 {
191 	foreach(variant frame_node, node["animation"].as_list()) {
192 		frames_.push_back(particle_animation(frame_node));
193 	}
194 }
195 
196 class simple_particle_system : public particle_system
197 {
198 public:
199 	simple_particle_system(const entity& e, const simple_particle_system_factory& factory);
~simple_particle_system()200 	~simple_particle_system() {}
201 
is_destroyed() const202 	bool is_destroyed() const { return info_.system_time_to_live_ == 0 || info_.spawn_rate_ < 0 && particles_.empty(); }
should_save() const203 	bool should_save() const { return info_.spawn_rate_ >= 0; }
204 	void process(const entity& e);
205 	void draw(const rect& area, const entity& e) const;
206 
207 private:
208 	void prepump(const entity& e);
209 
get_value(const std::string & key) const210 	variant get_value(const std::string& key) const {
211 		if(key == "spawn_rate") {
212 			return variant(info_.spawn_rate_);
213 		} else if(key == "spawn_rate_random") {
214 			return variant(info_.spawn_rate_random_);
215 		} else if(key == "system_time_to_live") {
216 			return variant(info_.system_time_to_live_);
217 		} else if(key == "time_to_live") {
218 			return variant(info_.time_to_live_);
219 		} else if(key == "min_x") {
220 			return variant(info_.min_x_);
221 		} else if(key == "max_x") {
222 			return variant(info_.max_x_);
223 		} else if(key == "min_y") {
224 			return variant(info_.min_y_);
225 		} else if(key == "max_y") {
226 			return variant(info_.max_y_);
227 		} else if(key == "velocity_x") {
228 			return variant(info_.velocity_x_);
229 		} else if(key == "velocity_y") {
230 			return variant(info_.velocity_y_);
231 		} else if(key == "velocity_x_random") {
232 			return variant(info_.velocity_x_rand_);
233 		} else if(key == "velocity_y_random") {
234 			return variant(info_.velocity_y_rand_);
235 		} else if(key == "velocity_magnitude") {
236 			return variant(info_.velocity_magnitude_);
237 		} else if(key == "velocity_magnitude_random") {
238 			return variant(info_.velocity_magnitude_rand_);
239 		} else if(key == "velocity_rotate") {
240 			return variant(info_.velocity_rotate_);
241 		} else if(key == "velocity_rotate_random") {
242 			return variant(info_.velocity_rotate_rand_);
243 		} else if(key == "velocity_rotate") {
244 			return variant(info_.velocity_rotate_);
245 		} else if(key == "velocity_rotate_random") {
246 			return variant(info_.velocity_rotate_rand_);
247 		} else if(key == "accel_x") {
248 			return variant(info_.accel_x_);
249 		} else if(key == "accel_y") {
250 			return variant(info_.accel_y_);
251 		} else if(key == "pre_pump_cycles") {
252 			return variant(info_.pre_pump_cycles_);
253 		} else if(key == "delta_r") {
254 			return variant(info_.delta_r_);
255 		} else if(key == "delta_g") {
256 			return variant(info_.delta_g_);
257 		} else if(key == "delta_b") {
258 			return variant(info_.delta_b_);
259 		} else if(key == "delta_a") {
260 			return variant(info_.delta_a_);
261 		} else {
262 			return variant();
263 		}
264 	}
265 
set_value(const std::string & key,const variant & value)266 	void set_value(const std::string& key, const variant& value) {
267 		if(key == "spawn_rate") {
268 			info_.spawn_rate_ = value.as_int();
269 		} else if(key == "spawn_rate_random") {
270 			info_.spawn_rate_random_ = value.as_int();
271 		} else if(key == "system_time_to_live") {
272 			info_.system_time_to_live_ = value.as_int();
273 		} else if(key == "time_to_live") {
274 			info_.time_to_live_ = value.as_int();
275 		} else if(key == "min_x") {
276 			info_.min_x_ = value.as_int();
277 		} else if(key == "max_x") {
278 			info_.max_x_ = value.as_int();
279 		} else if(key == "min_y") {
280 			info_.min_y_ = value.as_int();
281 		} else if(key == "max_y") {
282 			info_.max_y_ = value.as_int();
283 		} else if(key == "velocity_x") {
284 			info_.velocity_x_ = value.as_int();
285 		} else if(key == "velocity_y") {
286 			info_.velocity_y_ = value.as_int();
287 		} else if(key == "velocity_x_random") {
288 			info_.velocity_x_rand_ = value.as_int();
289 		} else if(key == "velocity_y_random") {
290 			info_.velocity_y_rand_ = value.as_int();
291 		} else if(key == "velocity_magnitude") {
292 			info_.velocity_magnitude_ = value.as_int();
293 		} else if(key == "velocity_magnitude_random") {
294 			info_.velocity_magnitude_rand_ = value.as_int();
295 		} else if(key == "velocity_rotate") {
296 			info_.velocity_rotate_ = value.as_int();
297 		} else if(key == "velocity_rotate_random") {
298 			info_.velocity_rotate_rand_ = value.as_int();
299 		} else if(key == "velocity_rotate") {
300 			info_.velocity_rotate_ = value.as_int();
301 		} else if(key == "velocity_rotate_random") {
302 			info_.velocity_rotate_rand_ = value.as_int();
303 		} else if(key == "accel_x") {
304 			info_.accel_x_ = value.as_int();
305 		} else if(key == "accel_y") {
306 			info_.accel_y_ = value.as_int();
307 		} else if(key == "pre_pump_cycles") {
308 			info_.pre_pump_cycles_ = value.as_int();
309 		} else if(key == "delta_r") {
310 			info_.delta_r_ = value.as_int();
311 		} else if(key == "delta_g") {
312 			info_.delta_g_ = value.as_int();
313 		} else if(key == "delta_b") {
314 			info_.delta_b_ = value.as_int();
315 		} else if(key == "delta_a") {
316 			info_.delta_a_ = value.as_int();
317 		}
318 
319 	}
320 
321 	const simple_particle_system_factory& factory_;
322 	simple_particle_system_info info_;
323 
324 	int cycle_;
325 
326 	struct particle {
327 		GLfloat pos[2];
328 		GLfloat velocity[2];
329 		const particle_animation* anim;
330 		int random;
331 	};
332 
333 	struct generation {
334 		int members;
335 		int created_at;
336 	};
337 
338 	std::deque<particle> particles_;
339 	std::deque<generation> generations_;
340 
341 	int spawn_buildup_;
342 };
343 
simple_particle_system(const entity & e,const simple_particle_system_factory & factory)344 simple_particle_system::simple_particle_system(const entity& e, const simple_particle_system_factory& factory)
345   : factory_(factory), info_(factory.info_), cycle_(0), spawn_buildup_(0)
346 {
347 }
348 
prepump(const entity & e)349 void simple_particle_system::prepump(const entity& e)
350 {
351 	//cosmetic thing for very slow-moving particles:
352 	//it looks weird when you walk into a scene, with, say, a column of smoke that's presumably been rising for quite some time,
353 	//but it only begins rising the moment you arrive.  To overcome this, we can optionally have particle systems pre-simulate their particles
354 	//for the short period of time (often as low as 4 seconds) needed to eliminate that implementation artifact
355 	for( int i = 0; i < info_.pre_pump_cycles_; ++i)
356 	{
357 		process(e);
358 	}
359 
360 }
361 
process(const entity & e)362 void simple_particle_system::process(const entity& e)
363 {
364 	--info_.system_time_to_live_;
365 	++cycle_;
366 
367 	if(cycle_ == 1) {
368 		prepump(e);
369 	}
370 
371 	while(!generations_.empty() && cycle_ - generations_.front().created_at == info_.time_to_live_) {
372 		particles_.erase(particles_.begin(), particles_.begin() + generations_.front().members);
373 		generations_.pop_front();
374 	}
375 
376 	std::deque<particle>::iterator p = particles_.begin();
377 	foreach(generation& gen, generations_) {
378 		for(int n = 0; n != gen.members; ++n) {
379 			p->pos[0] += p->velocity[0];
380 			p->pos[1] += p->velocity[1];
381 			if(e.face_right()) {
382 				p->velocity[0] += info_.accel_x_/1000.0;
383 			} else {
384 				p->velocity[0] -= info_.accel_x_/1000.0;
385 			}
386 			p->velocity[1] += info_.accel_y_/1000.0;
387 			++p;
388 		}
389 	}
390 
391 	if(info_.velocity_x_schedule_.empty() == false) {
392 		std::deque<particle>::iterator p = particles_.begin();
393 		foreach(generation& gen, generations_) {
394 
395 			for(int n = 0; n != gen.members; ++n) {
396 				const int ncycle = p->random + cycle_ - gen.created_at - 1;
397 				p->velocity[0] += info_.velocity_x_schedule_[ncycle%info_.velocity_x_schedule_.size()];
398 				if(cycle_ - gen.created_at > 1) {
399 					p->velocity[0] -= info_.velocity_x_schedule_[(ncycle-1)%info_.velocity_x_schedule_.size()];
400 				}
401 
402 				++p;
403 			}
404 		}
405 	}
406 
407 	if(info_.velocity_y_schedule_.empty() == false) {
408 		std::deque<particle>::iterator p = particles_.begin();
409 		foreach(generation& gen, generations_) {
410 			for(int n = 0; n != gen.members; ++n) {
411 				const int ncycle = p->random + cycle_ - gen.created_at - 1;
412 				p->velocity[1] += info_.velocity_y_schedule_[ncycle%info_.velocity_y_schedule_.size()];
413 				if(cycle_ - gen.created_at > 1) {
414 					p->velocity[1] -= info_.velocity_y_schedule_[(ncycle-1)%info_.velocity_y_schedule_.size()];
415 				}
416 
417 				++p;
418 			}
419 		}
420 	}
421 
422 	int nspawn = info_.spawn_rate_;
423 	if(info_.spawn_rate_random_ > 0) {
424 		nspawn += rand()%info_.spawn_rate_random_;
425 	}
426 
427 	if(nspawn > 0) {
428 		nspawn += spawn_buildup_;
429 	}
430 
431 	spawn_buildup_ = nspawn%1000;
432 	nspawn /= 1000;
433 
434 	if(nspawn == 0) {
435 		return;
436 	}
437 
438 	generation new_gen;
439 	new_gen.members = nspawn;
440 	new_gen.created_at = cycle_;
441 
442 	generations_.push_back(new_gen);
443 
444 	while(nspawn-- > 0) {
445 		particle p;
446 		p.pos[0] = e.face_right() ? (e.x() + info_.min_x_) : (e.x() + e.current_frame().width() - info_.max_x_);
447 		p.pos[1] = e.y() + info_.min_y_;
448 		p.velocity[0] = info_.velocity_x_/1000.0;
449 		p.velocity[1] = info_.velocity_y_/1000.0;
450 
451 		if(info_.velocity_x_rand_ > 0) {
452 			p.velocity[0] += (rand()%info_.velocity_x_rand_)/1000.0;
453 		}
454 
455 		if(info_.velocity_y_rand_ > 0) {
456 			p.velocity[1] += (rand()%info_.velocity_y_rand_)/1000.0;
457 		}
458 
459 		int velocity_magnitude = info_.velocity_magnitude_;
460 		if(info_.velocity_magnitude_rand_ > 0) {
461 			velocity_magnitude += rand()%info_.velocity_magnitude_rand_;
462 		}
463 
464 		if(velocity_magnitude) {
465 			int rotate_velocity = info_.velocity_rotate_;
466 			if(info_.velocity_rotate_rand_) {
467 				rotate_velocity += rand()%info_.velocity_rotate_rand_;
468 			}
469 
470 			const GLfloat rotate_radians = (GLfloat(rotate_velocity)/360.0)*3.14*2.0;
471 			const GLfloat magnitude = velocity_magnitude/1000.0;
472 			p.velocity[0] += sin(rotate_radians)*magnitude;
473 			p.velocity[1] += cos(rotate_radians)*magnitude;
474 		}
475 
476 		ASSERT_GT(factory_.frames_.size(), 0);
477 		p.anim = &factory_.frames_[rand()%factory_.frames_.size()];
478 
479 		const int diff_x = info_.max_x_ - info_.min_x_;
480 		if(diff_x > 0) {
481 			p.pos[0] += (rand()%(diff_x*1000))/1000.0;
482 		}
483 
484 		const int diff_y = info_.max_y_ - info_.min_y_;
485 		if(diff_y > 0) {
486 			p.pos[1] += (rand()%(diff_y*1000))/1000.0;
487 		}
488 
489 		if(!e.face_right()) {
490 			p.velocity[0] = -p.velocity[0];
491 		}
492 
493 		if(info_.random_schedule_) {
494 			p.random = rand();
495 		} else {
496 			p.random = 0;
497 		}
498 
499 		particles_.push_back(p);
500 	}
501 }
502 
draw(const rect & area,const entity & e) const503 void simple_particle_system::draw(const rect& area, const entity& e) const
504 {
505 	if(particles_.empty()) {
506 		return;
507 	}
508 
509 	std::deque<particle>::const_iterator p = particles_.begin();
510 
511 	//all particles must have the same texture, so just set it once.
512 	p->anim->set_texture();
513 	std::vector<GLfloat>& varray = graphics::global_vertex_array();
514 	std::vector<GLfloat>& tcarray = graphics::global_texcoords_array();
515 	std::vector<GLbyte>& carray = graphics::global_vertex_color_array();
516 
517 	const int facing = e.face_right() ? 1 : -1;
518 
519 
520 	carray.clear();
521 	varray.clear();
522 	tcarray.clear();
523 	foreach(const generation& gen, generations_) {
524 		for(int n = 0; n != gen.members; ++n) {
525 			const particle_animation* anim = p->anim;
526 			const particle_animation::frame_area& f = anim->get_frame(cycle_ - gen.created_at);
527 
528 			if(info_.delta_a_){
529 				//Spare the bandwidth if we're opaque
530 				const int alpha_level = std::max(256 - info_.delta_a_*(cycle_ - gen.created_at), 0);
531 				const int red = 255;
532 				const int green = 255;
533 				const int blue = 255;
534 				for( int i = 0; i < 6; ++i){
535 					carray.push_back(red); carray.push_back(green); carray.push_back(blue); carray.push_back(alpha_level);
536 				}
537 
538 			}
539 			//draw the first point twice, to allow drawing all particles
540 			//in one drawing operation.
541 
542 			tcarray.push_back(graphics::texture::get_coord_x(f.u1));
543 			tcarray.push_back(graphics::texture::get_coord_y(f.v1));
544 			varray.push_back(p->pos[0] + f.x_adjust*facing);
545 			varray.push_back(p->pos[1] + f.y_adjust);
546 			tcarray.push_back(graphics::texture::get_coord_x(f.u1));
547 			tcarray.push_back(graphics::texture::get_coord_y(f.v1));
548 			varray.push_back(p->pos[0] + f.x_adjust*facing);
549 			varray.push_back(p->pos[1] + f.y_adjust);
550 
551 			tcarray.push_back(graphics::texture::get_coord_x(f.u2));
552 			tcarray.push_back(graphics::texture::get_coord_y(f.v1));
553 			varray.push_back(p->pos[0] + (anim->width() - f.x2_adjust)*facing);
554 			varray.push_back(p->pos[1] + f.y_adjust);
555 			tcarray.push_back(graphics::texture::get_coord_x(f.u1));
556 			tcarray.push_back(graphics::texture::get_coord_y(f.v2));
557 			varray.push_back(p->pos[0] + f.x_adjust*facing);
558 			varray.push_back(p->pos[1] + anim->height() - f.y2_adjust);
559 
560 			//draw the last point twice.
561 			tcarray.push_back(graphics::texture::get_coord_x(f.u2));
562 			tcarray.push_back(graphics::texture::get_coord_y(f.v2));
563 			varray.push_back(p->pos[0] + (anim->width() - f.x2_adjust)*facing);
564 			varray.push_back(p->pos[1] + anim->height() - f.y2_adjust);
565 			tcarray.push_back(graphics::texture::get_coord_x(f.u2));
566 			tcarray.push_back(graphics::texture::get_coord_y(f.v2));
567 			varray.push_back(p->pos[0] + (anim->width() - f.x2_adjust)*facing);
568 			varray.push_back(p->pos[1] + anim->height() - f.y2_adjust);
569 			++p;
570 		}
571 	}
572 
573 #if defined(USE_GLES2)
574 	if(info_.delta_a_) {
575 		gles2::manager gles2_manager(gles2::get_texcol_shader());
576 		gles2::active_shader()->shader()->color_array(4, GL_UNSIGNED_BYTE, GL_TRUE, 0, &carray.front());
577 		gles2::active_shader()->shader()->vertex_array(2, GL_FLOAT, GL_FALSE, 0, &varray.front());
578 		gles2::active_shader()->shader()->texture_array(2, GL_FLOAT, GL_FALSE, 0, &tcarray.front());
579 		glDrawArrays(GL_TRIANGLE_STRIP, 0, varray.size()/2);
580 	} else {
581 		gles2::active_shader()->prepare_draw();
582 		gles2::active_shader()->shader()->vertex_array(2, GL_FLOAT, GL_FALSE, 0, &varray.front());
583 		gles2::active_shader()->shader()->texture_array(2, GL_FLOAT, GL_FALSE, 0, &tcarray.front());
584 		glDrawArrays(GL_TRIANGLE_STRIP, 0, varray.size()/2);
585 	}
586 #else
587 	if(info_.delta_a_){
588 		glEnableClientState(GL_COLOR_ARRAY);
589 		glColorPointer(4, GL_UNSIGNED_BYTE, 0, &carray.front());
590 	}
591 
592 	glVertexPointer(2, GL_FLOAT, 0, &varray.front());
593 	glTexCoordPointer(2, GL_FLOAT, 0, &tcarray.front());
594 	glDrawArrays(GL_TRIANGLE_STRIP, 0, varray.size()/2);
595 
596 	if(info_.delta_a_){
597 		glDisableClientState(GL_COLOR_ARRAY);
598 	}
599 #endif
600 	glColor4f(1.0, 1.0, 1.0, 1.0);
601 }
602 
create(const entity & e) const603 particle_system_ptr simple_particle_system_factory::create(const entity& e) const
604 {
605 	return particle_system_ptr(new simple_particle_system(e, *this));
606 }
607 
608 struct point_particle_info
609 {
point_particle_info__anon54335c4e0111::point_particle_info610 	explicit point_particle_info(variant node)
611 	  : generation_rate_millis(node["generation_rate_millis"].as_int()),
612 	    pos_x(node["pos_x"].as_int()*1024),
613 	    pos_y(node["pos_y"].as_int()*1024),
614 	    pos_x_rand(node["pos_x_rand"].as_int()*1024),
615 	    pos_y_rand(node["pos_y_rand"].as_int()*1024),
616 	    velocity_x(node["velocity_x"].as_int()),
617 	    velocity_y(node["velocity_y"].as_int()),
618 		accel_x(node["accel_x"].as_int()),
619 		accel_y(node["accel_y"].as_int()),
620 	    velocity_x_rand(node["velocity_x_rand"].as_int()),
621 	    velocity_y_rand(node["velocity_y_rand"].as_int()),
622 		dot_size(node["dot_size"].as_int(1)*(preferences::double_scale() ? 2 : 1)),
623 		dot_rounded(node["dot_rounded"].as_bool(false)),
624 	    time_to_live(node["time_to_live"].as_int()),
625 	    time_to_live_max(node["time_to_live_rand"].as_int() + time_to_live) {
626 
627 		if(node.has_key("colors")) {
628 			const std::vector<variant> colors_vec = node["colors"].as_list();
629 			foreach(const variant& col, colors_vec) {
630 				unsigned int val = strtoul(col.as_string().c_str(), NULL, 16);
631 				unsigned char* c = reinterpret_cast<unsigned char*>(&val);
632 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
633 				std::reverse(c, c+4);
634 #endif
635 				colors.push_back(val);
636 			}
637 		}
638 
639 		if(node.has_key("colors_expression")) {
640 			const variant v = game_logic::formula(node["colors_expression"]).execute();
641 			for(int n = 0; n != v.num_elements(); ++n) {
642 				const variant u = v[n];
643 				ASSERT_LOG(u.num_elements() == 4, "UNEXPECTED colors_expression: " << u.to_debug_string());
644 				const unsigned int r = u[0].as_int();
645 				const unsigned int g = u[1].as_int();
646 				const unsigned int b = u[2].as_int();
647 				const unsigned int a = u[3].as_int();
648 				unsigned int val = (r << 24) + (g << 16) + (b << 8) + a;
649 
650 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
651 				unsigned char* c = reinterpret_cast<unsigned char*>(&val);
652 				std::reverse(c, c+4);
653 #endif
654 				colors.push_back(val);
655 			}
656 		}
657 
658 		std::reverse(colors.begin(), colors.end());
659 
660 		ttl_divisor = time_to_live_max/(colors.size()-1);
661 
662 		rgba[0] = node["red"].as_int();
663 		rgba[1] = node["green"].as_int();
664 		rgba[2] = node["blue"].as_int();
665 		rgba[3] = node["alpha"].as_int(255);
666 		rgba_rand[0] = node["red_rand"].as_int();
667 		rgba_rand[1] = node["green_rand"].as_int();
668 		rgba_rand[2] = node["blue_rand"].as_int();
669 		rgba_rand[3] = node["alpha_rand"].as_int();
670 		rgba_delta[0] = node["red_delta"].as_int();
671 		rgba_delta[1] = node["green_delta"].as_int();
672 		rgba_delta[2] = node["blue_delta"].as_int();
673 		rgba_delta[3] = node["alpha_delta"].as_int();
674 	}
675 
676 	int generation_rate_millis;
677 	int pos_x, pos_y, pos_x_rand, pos_y_rand;
678 	int velocity_x, velocity_y, velocity_x_rand, velocity_y_rand;
679 	int accel_x, accel_y;
680 	int time_to_live, time_to_live_max;
681 	unsigned char rgba[4];
682 	unsigned char rgba_rand[4];
683 	char rgba_delta[4];
684 	int dot_size;
685 	bool dot_rounded;
686 
687 	std::vector<unsigned int> colors;
688 	int ttl_divisor;
689 };
690 
691 class point_particle_system : public particle_system
692 {
693 public:
point_particle_system(const entity & obj,const point_particle_info & info)694 	point_particle_system(const entity& obj, const point_particle_info& info) : obj_(obj), info_(info), particle_generation_(0), generation_rate_millis_(info.generation_rate_millis), pos_x_(info.pos_x), pos_x_rand_(info.pos_x_rand), pos_y_(info.pos_y), pos_y_rand_(info.pos_y_rand) {
695 	}
696 
process(const entity & e)697 	void process(const entity& e) {
698 		particle_generation_ += generation_rate_millis_;
699 
700 		particles_.erase(std::remove_if(particles_.begin(), particles_.end(), particle_destroyed), particles_.end());
701 
702 		for(std::vector<particle>::iterator p = particles_.begin();
703 		    p != particles_.end(); ++p) {
704 			p->pos_x += p->velocity_x;
705 			p->pos_y += p->velocity_y;
706 			if(e.face_right()) {
707 				p->velocity_x += info_.accel_x/1000.0;
708 			} else {
709 				p->velocity_x -= info_.accel_x/1000.0;
710 			}
711 			p->velocity_y += info_.accel_y/1000.0;
712 			p->rgba[0] += info_.rgba_delta[0];
713 			p->rgba[1] += info_.rgba_delta[1];
714 			p->rgba[2] += info_.rgba_delta[2];
715 			p->rgba[3] += info_.rgba_delta[3];
716 			p->ttl--;
717 		}
718 
719 		while(particle_generation_ >= 1000) {
720 			//std::cerr << "PARTICLE X ORIGIN: " << pos_x_;
721 			particles_.push_back(particle());
722 			particle& p = particles_.back();
723 			p.ttl = info_.time_to_live;
724 			if(info_.time_to_live_max != info_.time_to_live) {
725 				p.ttl += rand()%(info_.time_to_live_max - info_.time_to_live);
726 			}
727 
728 			p.velocity_x = info_.velocity_x;
729 			p.velocity_y = info_.velocity_y;
730 
731 			if(info_.velocity_x_rand) {
732 				p.velocity_x += rand()%info_.velocity_x_rand;
733 			}
734 
735 			if(info_.velocity_y_rand) {
736 				p.velocity_y += rand()%info_.velocity_y_rand;
737 			}
738 
739 			p.pos_x = e.x()*1024 + pos_x_;
740 			p.pos_y = e.y()*1024 + pos_y_;
741 
742 			if(pos_x_rand_) {
743 				p.pos_x += rand()%pos_x_rand_;
744 			}
745 
746 			if(pos_y_rand_) {
747 				p.pos_y += rand()%pos_y_rand_;
748 			}
749 
750 			p.rgba[0] = info_.rgba[0];
751 			p.rgba[1] = info_.rgba[1];
752 			p.rgba[2] = info_.rgba[2];
753 			p.rgba[3] = info_.rgba[3];
754 
755 			if(info_.rgba_rand[0]) {
756 				p.rgba[0] += rand()%info_.rgba_rand[0];
757 			}
758 
759 			if(info_.rgba_rand[1]) {
760 				p.rgba[1] += rand()%info_.rgba_rand[1];
761 			}
762 
763 			if(info_.rgba_rand[2]) {
764 				p.rgba[2] += rand()%info_.rgba_rand[2];
765 			}
766 
767 			if(info_.rgba_rand[3]) {
768 				p.rgba[3] += rand()%info_.rgba_rand[3];
769 			}
770 
771 			particle_generation_ -= 1000;
772 		}
773 	}
774 
draw(const rect & area,const entity & e) const775 	void draw(const rect& area, const entity& e) const {
776 		if(particles_.empty()) {
777 			return;
778 		}
779 
780 		static std::vector<GLshort> vertex;
781 		static std::vector<unsigned int> colors;
782 		vertex.resize(particles_.size()*2);
783 		colors.resize(particles_.size());
784 
785 		unsigned int* c = &colors[0];
786 		GLshort* v = &vertex[0];
787 		for(std::vector<particle>::const_iterator p = particles_.begin();
788 		    p != particles_.end(); ++p) {
789 			*v++ = p->pos_x/1024;
790 			*v++ = p->pos_y/1024;
791 			if(info_.colors.size() >= 2) {
792 				*c++ = info_.colors[p->ttl/info_.ttl_divisor];
793 			} else {
794 				*c++ = p->color;
795 			}
796 		}
797 
798 		glColor4f(1.0, 1.0, 1.0, 1.0);
799 
800 #if defined(USE_GLES2)
801 		// Not dealing with GL_POINT_SMOOTH right now -- this would probably be better as a frgament shader.
802 		glPointSize(info_.dot_size);
803 		gles2::manager gles2_manager(gles2::get_simple_col_shader());
804 		gles2::active_shader()->shader()->vertex_array(2, GL_SHORT, GL_FALSE, 0, &vertex[0]);
805 		gles2::active_shader()->shader()->color_array(4, GL_UNSIGNED_BYTE, GL_TRUE, 0, &colors[0]);
806 		glDrawArrays(GL_POINTS, 0, particles_.size());
807 #else
808 		glDisable(GL_TEXTURE_2D);
809 		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
810 		glEnableClientState(GL_COLOR_ARRAY);
811 		if(info_.dot_rounded){
812 			glEnable( GL_POINT_SMOOTH );
813 		}
814 		glPointSize(info_.dot_size);
815 
816 		glVertexPointer(2, GL_SHORT, 0, &vertex[0]);
817 		glColorPointer(4, GL_UNSIGNED_BYTE, 0, &colors[0]);
818 		glDrawArrays(GL_POINTS, 0, particles_.size());
819 
820 		glDisableClientState(GL_COLOR_ARRAY);
821 		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
822 		glEnable(GL_TEXTURE_2D);
823 		if(info_.dot_rounded){
824 			glDisable( GL_POINT_SMOOTH );
825 		}
826 #endif
827 		glColor4f(1.0, 1.0, 1.0, 1.0);
828 	}
829 private:
830 	const entity& obj_;
831 	const point_particle_info& info_;
832 
833 	struct particle {
834 		GLshort velocity_x, velocity_y;
835 		int pos_x, pos_y;
836 		union { unsigned int color; unsigned char rgba[4]; };
837 		int ttl;
838 	};
839 
particle_destroyed(const particle & p)840 	static bool particle_destroyed(const particle& p) { return p.ttl <= 0; }
841 
842 	int particle_generation_;
843 	int generation_rate_millis_;
844 	int pos_x_, pos_x_rand_, pos_y_, pos_y_rand_;
845 	std::vector<particle> particles_;
846 
get_value(const std::string & key) const847 	variant get_value(const std::string& key) const {
848 		return variant();
849 	}
850 
set_value(const std::string & key,const variant & value)851 	void set_value(const std::string& key, const variant& value) {
852 		if(key == "generation_rate") {
853 			generation_rate_millis_ = value.as_int();
854 		} else if (key == "pos_x") {
855 			pos_x_ = value.as_int()*1024;
856 		} else if (key == "pos_x_rand") {
857 			pos_x_rand_ = value.as_int()*1024;
858 		} else if (key == "pos_y") {
859 			pos_y_ = value.as_int()*1024;
860 		} else if (key == "pos_y_rand") {
861 			pos_y_rand_ = value.as_int()*1024;
862 		}
863 	}
864 };
865 
866 class point_particle_system_factory : public particle_system_factory
867 {
868 public:
point_particle_system_factory(variant node)869 	explicit point_particle_system_factory(variant node)
870 	  : info_(node)
871 	{}
872 
create(const entity & e) const873 	particle_system_ptr create(const entity& e) const {
874 		return particle_system_ptr(new point_particle_system(e, info_));
875 	}
876 
877 private:
878 	point_particle_info info_;
879 };
880 
881 }
882 
create_factory(variant node)883 const_particle_system_factory_ptr particle_system_factory::create_factory(variant node)
884 {
885 	const std::string& type = node["type"].as_string();
886 	if(type == "simple") {
887 		return const_particle_system_factory_ptr(new simple_particle_system_factory(node));
888 	} else if (type == "weather") {
889 		return const_particle_system_factory_ptr(new weather_particle_system_factory(node));
890 	} else if (type == "water") {
891 		return const_particle_system_factory_ptr(new water_particle_system_factory(node));
892 	} else if(type == "point") {
893 		return const_particle_system_factory_ptr(new point_particle_system_factory(node));
894 	}
895 
896 	ASSERT_LOG(false, "Unrecognized particle system type: " << node["type"].as_string());
897 }
898 
~particle_system_factory()899 particle_system_factory::~particle_system_factory()
900 {
901 }
902 
~particle_system()903 particle_system::~particle_system()
904 {
905 }
906