1 /*
2  * Copyright 2011-2012 Arx Libertatis Team (see the AUTHORS file)
3  *
4  * This file is part of Arx Libertatis.
5  *
6  * Arx Libertatis is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Arx Libertatis 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 Arx Libertatis.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23 
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25 
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28 
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31 
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code.  If not, see
33 <http://www.gnu.org/licenses/>.
34 
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38 
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43 // Code: Cyril Meynier
44 //
45 // Copyright (c) 1999-2000 ARKANE Studios SA. All rights reserved
46 
47 #include "graphics/particle/ParticleEffects.h"
48 
49 #include <algorithm>
50 
51 #include "core/Application.h"
52 #include "core/Config.h"
53 #include "core/Core.h"
54 #include "core/GameTime.h"
55 
56 #include "game/Damage.h"
57 #include "game/EntityManager.h"
58 #include "game/NPC.h"
59 #include "game/Player.h"
60 
61 #include "gui/Interface.h"
62 
63 #include "graphics/Draw.h"
64 #include "graphics/GraphicsModes.h"
65 #include "graphics/Math.h"
66 #include "graphics/data/TextureContainer.h"
67 #include "graphics/effects/SpellEffects.h"
68 
69 #include "input/Input.h"
70 
71 #include "math/Random.h"
72 
73 #include "physics/Collisions.h"
74 
75 #include "scene/GameSound.h"
76 #include "scene/Interactive.h"
77 #include "scene/Light.h"
78 
79 using std::max;
80 
81 //TODO(lubosz): extern globals :(
82 extern Color ulBKGColor;
83 
84 struct OBJFX {
85 	Vec3f pos;
86 	Vec3f move;
87 	Vec3f scale;
88 	Color3f fade;
89 	EERIE_3DOBJ * obj;
90 	long special;
91 	Anglef spe[8];
92 	Anglef speinc[8];
93 	unsigned long tim_start;
94 	unsigned long duration;
95 	bool exist;
96 	long dynlight;
97 };
98 
99 static const size_t MAX_PARTICLES = 2200;
100 static long ParticleCount = 0;
101 static PARTICLE_DEF particle[MAX_PARTICLES];
102 
103 FLARETC			flaretc;
104 FLARES			flare[MAX_FLARES];
105 static TextureContainer * bloodsplat[6];
106 TextureContainer * water_splat[3];
107 static TextureContainer * water_drop[3];
108 static TextureContainer * smokeparticle = NULL;
109 static TextureContainer * bloodsplatter = NULL;
110 static TextureContainer * healing = NULL;
111 static TextureContainer * tzupouf = NULL;
112 TextureContainer * fire2=NULL;
113 
114 static OBJFX objfx[MAX_OBJFX];
115 long			BoomCount=0;
116 
117 long			flarenum=0;
118 short			OPIPOrgb=0;
119 short			PIPOrgb=0;
120 static short shinum = 1;
121 long			NewSpell=0;
122 
ARX_BOOMS_GetFree()123 long ARX_BOOMS_GetFree() {
124 	for(long i=0;i<MAX_POLYBOOM;i++)
125 	{
126 		if (!polyboom[i].exist)
127 			return i;
128 	}
129 
130 	return -1;
131 }
132 
getParticleCount()133 long getParticleCount() {
134 	return ParticleCount;
135 }
136 
LaunchDummyParticle()137 void LaunchDummyParticle() {
138 
139 	PARTICLE_DEF * pd = createParticle();
140 	if(!pd) {
141 		return;
142 	}
143 
144 	float f = radians(player.angle.b);
145 	pd->ov = player.pos + Vec3f(EEsin(f) * 100.f, 0.f, -EEcos(f) * 100.f);
146 	pd->tolive = 600;
147 	pd->tc = smokeparticle;
148 	pd->siz = 15.f;
149 	pd->scale = randomVec(-15.f, -10.f);
150 	pd->special = FIRE_TO_SMOKE;
151 }
152 
ARX_PARTICLES_Spawn_Lava_Burn(Vec3f * poss,Entity * io)153 void ARX_PARTICLES_Spawn_Lava_Burn(Vec3f * poss, Entity * io) {
154 
155 	Vec3f pos = *poss;
156 
157 	if(io && io->obj && !io->obj->facelist.empty()) {
158 		size_t num = 0;
159 		long notok = 10;
160 		while(notok--) {
161 			num = Random::get(0, io->obj->facelist.size() - 1);
162 			if(io->obj->facelist[num].facetype & POLY_HIDE) {
163 				continue;
164 			}
165 			if(EEfabs(pos.y-io->obj->vertexlist3[io->obj->facelist[num].vid[0]].v.y) > 50.f) {
166 				continue;
167 			}
168 			notok = 0;
169 		}
170 		// TODO notok is always -1 here!
171 		if(notok >= 0) {
172 			pos = io->obj->vertexlist3[io->obj->facelist[num].vid[0]].v;
173 		}
174 	}
175 
176 	PARTICLE_DEF * pd = createParticle();
177 	if(!pd) {
178 		return;
179 	}
180 
181 	pd->ov = pos;
182 	pd->move = Vec3f(rnd() * 2.f - 4.f, rnd() * -12.f - 15.f, rnd() * 2.f - 4.f);
183 	pd->tolive = 800;
184 	pd->tc = smokeparticle;
185 	pd->siz = 15.f;
186 	pd->scale = randomVec(15.f, 20.f);
187 	pd->special = FIRE_TO_SMOKE;
188 	if(rnd() > 0.5f) {
189 		pd->special |= SUBSTRACT;
190 	}
191 }
192 
ARX_PARTICLES_Spawn_Rogue_Blood(const Vec3f & pos,float dmgs,Color col)193 static void ARX_PARTICLES_Spawn_Rogue_Blood(const Vec3f & pos, float dmgs, Color col) {
194 
195 	PARTICLE_DEF * pd = createParticle();
196 	if(!pd) {
197 		return;
198 	}
199 
200 	pd->ov = pos;
201 	pd->siz = 3.1f * (dmgs * (1.f / 60) + .9f);
202 	pd->scale = Vec3f::repeat(-pd->siz * 0.25f);
203 	pd->special = PARTICLE_SUB2 | SUBSTRACT | GRAVITY | ROTATING | MODULATE_ROTATION
204 	              | SPLAT_GROUND;
205 	pd->tolive = 1600;
206 	pd->move = Vec3f(rnd() * 60.f - 30.f, rnd() * -10.f - 15.f, rnd() * 60.f - 30.f);
207 	pd->rgb = col.to<float>();
208 	long num = Random::get(0, 5);
209 	pd->tc = bloodsplat[num];
210 	pd->fparam = rnd() * (1.f/10) - .05f;
211 
212 }
213 
ARX_PARTICLES_Spawn_Blood3(const Vec3f & pos,float dmgs,Color col,long flags=0)214 static void ARX_PARTICLES_Spawn_Blood3(const Vec3f & pos, float dmgs, Color col,
215                                        long flags = 0) {
216 
217 	PARTICLE_DEF * pd = createParticle();
218 	if(pd) {
219 
220 		float power = (dmgs * (1.f/60)) + .9f;
221 		pd->ov = pos + Vec3f(-sin(float(arxtime) * 0.001f), sin(float(arxtime) * 0.001f),
222 		               cos(float(arxtime) * 0.001f)) * 30.f;
223 		pd->siz = 3.5f * power + sin(float(arxtime) * (1.f/1000));
224 		pd->scale = Vec3f::repeat(-pd->siz * 0.5f);
225 		pd->special = PARTICLE_SUB2 | SUBSTRACT | GRAVITY | ROTATING | MODULATE_ROTATION
226 		              | flags;
227 		pd->tolive = 1100;
228 		pd->rgb = col.to<float>();
229 		pd->tc = bloodsplatter;
230 		pd->fparam = rnd() * 0.1f - .05f;
231 	}
232 
233 	if(rnd() > .90f) {
234 		ARX_PARTICLES_Spawn_Rogue_Blood(pos, dmgs, col);
235 	}
236 
237 }
238 
239 #define SPLAT_MULTIPLY 1.f
240 
ARX_POLYSPLAT_Add(Vec3f * poss,Color3f * col,float size,long flags)241 void ARX_POLYSPLAT_Add(Vec3f * poss, Color3f * col, float size, long flags) {
242 
243 	if (BoomCount > (MAX_POLYBOOM >> 2) - 30) return;
244 
245 	if ((BoomCount>250.f)
246 		&& (size<10)) return;
247 
248 	float splatsize=90;
249 
250 	if (size>40.f) size=40.f;
251 
252 	size*=0.75f;
253 
254 	switch (config.video.levelOfDetail)
255 	{
256 		case 2:
257 
258 			if (BoomCount>160.f)  return;
259 
260 			splatsize=90;
261 			size*=1.f;
262 		break;
263 		case 1:
264 
265 			if (BoomCount>60.f)  return;
266 
267 			splatsize=60;
268 			size*=0.5f;
269 		break;
270 		default:
271 
272 			if (BoomCount>10.f)  return;
273 
274 			splatsize=30;
275 			size*=0.25f;
276 		break;
277 	}
278 
279 
280 	float py;
281 	EERIEPOLY * ep=CheckInPoly(poss->x,poss->y-40,poss->z,&py);
282 
283 	if (!ep) return;
284 
285 
286 	if (flags & 1) py=poss->y;
287 
288 	EERIEPOLY TheoricalSplat; // clockwise
289 	TheoricalSplat.v[0].p.x=-splatsize*SPLAT_MULTIPLY;
290 	TheoricalSplat.v[0].p.y = py;
291 	TheoricalSplat.v[0].p.z=-splatsize*SPLAT_MULTIPLY;
292 
293 	TheoricalSplat.v[1].p.x=-splatsize*SPLAT_MULTIPLY;
294 	TheoricalSplat.v[1].p.y = py;
295 	TheoricalSplat.v[1].p.z=+splatsize*SPLAT_MULTIPLY;
296 
297 	TheoricalSplat.v[2].p.x=+splatsize*SPLAT_MULTIPLY;
298 	TheoricalSplat.v[2].p.y = py;
299 	TheoricalSplat.v[2].p.z=+splatsize*SPLAT_MULTIPLY;
300 
301 	TheoricalSplat.v[3].p.x=+splatsize*SPLAT_MULTIPLY;
302 	TheoricalSplat.v[3].p.y = py;
303 	TheoricalSplat.v[3].p.z=-splatsize*SPLAT_MULTIPLY;
304 	TheoricalSplat.type=POLY_QUAD;
305 
306 	Vec3f RealSplatStart(-size, py, -size);
307 
308 	TheoricalSplat.v[0].p.x+=poss->x;
309 	TheoricalSplat.v[0].p.z+=poss->z;
310 
311 	TheoricalSplat.v[1].p.x+=poss->x;
312 	TheoricalSplat.v[1].p.z+=poss->z;
313 
314 	TheoricalSplat.v[2].p.x+=poss->x;
315 	TheoricalSplat.v[2].p.z+=poss->z;
316 
317 	TheoricalSplat.v[3].p.x+=poss->x;
318 	TheoricalSplat.v[3].p.z+=poss->z;
319 
320 	RealSplatStart.x+=poss->x;
321 	RealSplatStart.z+=poss->z;
322 
323 
324 	float hdiv,vdiv;
325 	hdiv=vdiv=1.f/(size*2);
326 
327 
328 	long x0,x1;
329 	long z0,z1,i,j;
330 	unsigned long tim;
331 	long n;
332 	EERIE_BKG_INFO * eg;
333 	tim = (unsigned long)(arxtime);
334 
335 	for (i=0;i<MAX_POLYBOOM;i++)
336 	{
337 		if (polyboom[i].exist)
338 		{
339 			polyboom[i].type|=128;
340 		}
341 	}
342 
343 	x0 = static_cast<long>(poss->x * ACTIVEBKG->Xmul);
344 	z0 = static_cast<long>(poss->z * ACTIVEBKG->Zmul);
345 	x1 = x0 + 3;
346 	x0 = x0 - 3;
347 	z1 = z0 + 3;
348 	z0 = z0 - 3;
349 
350 	if (x0<0) x0=0;
351 
352 	if (x0>=ACTIVEBKG->Xsize) x0=ACTIVEBKG->Xsize-1;
353 
354 	if (x1<0) x1=0;
355 
356 	if (x1>=ACTIVEBKG->Xsize) x1=ACTIVEBKG->Xsize-1;
357 
358 	if (z0<0) z0=0;
359 
360 	if (z0>=ACTIVEBKG->Zsize) z0=ACTIVEBKG->Zsize-1;
361 
362 	if (z1<0) z1=0;
363 
364 	if (z1>=ACTIVEBKG->Zsize) z1=ACTIVEBKG->Zsize-1;
365 
366 	long nbvert;
367 	float vratio=size*( 1.0f / 40 );
368 
369 
370 	(void)checked_range_cast<short>(z0);
371 	(void)checked_range_cast<short>(x0);
372 	(void)checked_range_cast<short>(z1);
373 	(void)checked_range_cast<short>(x1);
374 
375 
376 
377 	for (j=z0;j<=z1;j++)
378 	for (i=x0;i<=x1;i++)
379 	{
380 		eg=(EERIE_BKG_INFO *)&ACTIVEBKG->Backg[i+j*ACTIVEBKG->Xsize];
381 
382 			for (long l = 0; l < eg->nbpolyin; l++)
383 		{
384 				ep = eg->polyin[l];
385 
386 			if (flags & 2)
387 			{
388 				if (!(ep->type & POLY_WATER)) continue;
389 			}
390 
391 			if ((ep->type & POLY_TRANS) && !(ep->type & POLY_WATER)) continue;
392 
393 			if (ep->type & POLY_QUAD) nbvert=4;
394 			else nbvert=3;
395 
396 			long oki=0;
397 
398 			for (long k=0;k<nbvert;k++)
399 			{
400 				if ((PointIn2DPolyXZ(&TheoricalSplat, ep->v[k].p.x, ep->v[k].p.z))
401 					&& ((float)fabs(ep->v[k].p.y-py) < 100.f) )
402 				{
403 					 oki=1;
404 					break;
405 				}
406 
407 				if ((PointIn2DPolyXZ(&TheoricalSplat, (ep->v[k].p.x+ep->center.x)*( 1.0f / 2 ), (ep->v[k].p.z+ep->center.z)*( 1.0f / 2 )))
408 					&& ((float)fabs(ep->v[k].p.y-py) < 100.f) )
409 				{
410 					 oki=1;
411 					break;
412 				}
413 			}
414 
415 			if (!oki && (PointIn2DPolyXZ(&TheoricalSplat, ep->center.x, ep->center.z))
416 					&& (EEfabs(ep->center.y-py)<100.f) )
417 					oki=1;
418 
419 
420 			if (oki)
421 			{
422 				n=ARX_BOOMS_GetFree();
423 
424 				if (n>=0)
425 				{
426 					BoomCount++;
427 					POLYBOOM * pb=&polyboom[n];
428 					pb->type=1;
429 
430 					if (flags & 2)
431 						pb->type=2;
432 
433 					pb->exist=1;
434 					pb->ep=ep;
435 
436 					long num = Random::get(0, 5);
437 					pb->tc=bloodsplat[num];
438 
439 					float fRandom = rnd() * 2;
440 
441 					int t = checked_range_cast<int>(fRandom);
442 
443 					if (flags & 2)
444 						pb->tc = water_splat[t];
445 
446 					pb->tolive=(long)(float)(16000*vratio);
447 
448 					if (flags & 2)
449 						pb->tolive=1500;
450 
451 					pb->timecreation=tim;
452 
453 					pb->tx = static_cast<short>(i);
454 					pb->tz = static_cast<short>(j);
455 
456 					pb->rgb = *col;
457 
458 					for (int k=0;k<nbvert;k++)
459 					{
460 						float vdiff=EEfabs(ep->v[k].p.y-RealSplatStart.y);
461 						pb->u[k]=(ep->v[k].p.x-RealSplatStart.x)*hdiv;
462 
463 						if (pb->u[k]<0.5f)
464 							pb->u[k]-=vdiff*hdiv;
465 						else pb->u[k]+=vdiff*hdiv;
466 
467 						pb->v[k]=(ep->v[k].p.z-RealSplatStart.z)*vdiv;
468 
469 						if (pb->v[k]<0.5f)
470 							pb->v[k]-=vdiff*vdiv;
471 						else pb->v[k]+=vdiff*vdiv;
472 
473 					}
474 
475 					pb->nbvert=(short)nbvert;
476 				}
477 			}
478 		}
479 	}
480 }
481 
SpawnGroundSplat(EERIE_SPHERE * sp,Color3f * rgb,float size,long flags)482 void SpawnGroundSplat(EERIE_SPHERE * sp, Color3f * rgb, float size, long flags) {
483 	ARX_POLYSPLAT_Add(&sp->origin, rgb, size, flags);
484 }
485 
ARX_PARTICLES_Spawn_Blood2(const Vec3f & pos,float dmgs,Color col,Entity * io)486 void ARX_PARTICLES_Spawn_Blood2(const Vec3f & pos, float dmgs, Color col, Entity * io) {
487 
488 	bool isNpc = io && (io->ioflags & IO_NPC);
489 
490 	if(isNpc && io->_npcdata->SPLAT_TOT_NB) {
491 
492 		if(io->_npcdata->SPLAT_DAMAGES < 3) {
493 			return;
494 		}
495 
496 		float power = (io->_npcdata->SPLAT_DAMAGES * (1.f/60)) + .9f;
497 
498 		Vec3f vect = pos - io->_npcdata->last_splat_pos;
499 		float dist = vect.normalize();
500 		long nb = long(dist / 4.f * power);
501 		if(nb == 0) {
502 			nb = 1;
503 		}
504 
505 		long MAX_GROUND_SPLATS;
506 		switch(config.video.levelOfDetail) {
507 			case 2:  MAX_GROUND_SPLATS = 10; break;
508 			case 1:  MAX_GROUND_SPLATS = 5; break;
509 			default: MAX_GROUND_SPLATS = 1; break;
510 		}
511 
512 		for(long k = 0; k < nb; k++) {
513 			Vec3f posi = io->_npcdata->last_splat_pos + vect * k * 4.f * power;
514 			io->_npcdata->SPLAT_TOT_NB++;
515 			if(io->_npcdata->SPLAT_TOT_NB > MAX_GROUND_SPLATS) {
516 				ARX_PARTICLES_Spawn_Blood3(posi, io->_npcdata->SPLAT_DAMAGES, col, SPLAT_GROUND);
517 				io->_npcdata->SPLAT_TOT_NB=1;
518 			} else {
519 				ARX_PARTICLES_Spawn_Blood3(posi, io->_npcdata->SPLAT_DAMAGES, col);
520 			}
521 		}
522 
523 	} else {
524 		if(isNpc) {
525 			io->_npcdata->SPLAT_DAMAGES = (short)dmgs;
526 		}
527 		ARX_PARTICLES_Spawn_Blood3(pos, dmgs, col, SPLAT_GROUND);
528 		if(isNpc) {
529 			io->_npcdata->SPLAT_TOT_NB = 1;
530 		}
531 	}
532 
533 	if(isNpc) {
534 		io->_npcdata->last_splat_pos = pos;
535 	}
536 }
537 
ARX_PARTICLES_Spawn_Blood(Vec3f * pos,float dmgs,long source)538 void ARX_PARTICLES_Spawn_Blood(Vec3f * pos, float dmgs, long source) {
539 
540 	if(source < 0) {
541 		return;
542 	}
543 
544 	float nearest_dist = std::numeric_limits<float>::max();
545 	long nearest = -1;
546 	long count = entities[source]->obj->nbgroups;
547 	for(long i = 0; i < count; i += 2) {
548 		long vertex = entities[source]->obj->grouplist[i].origin;
549 		float dist = distSqr(*pos, entities[source]->obj->vertexlist3[vertex].v);
550 		if(dist < nearest_dist) {
551 			nearest_dist = dist;
552 			nearest = i;
553 		}
554 	}
555 	if(nearest < 0) {
556 		return;
557 	}
558 
559 	// Decides number of blood particles...
560 	long spawn_nb = clamp(long(dmgs * 2.f), 5l, 26l);
561 
562 	long totdelay = 0;
563 
564 	for(long k = 0; k < spawn_nb; k++) {
565 
566 		PARTICLE_DEF * pd = createParticle();
567 		if(!pd) {
568 			return;
569 		}
570 
571 		pd->siz = 0.f;
572 		pd->scale = Vec3f::repeat(float(spawn_nb));
573 		pd->special = GRAVITY | ROTATING | MODULATE_ROTATION | DELAY_FOLLOW_SOURCE;
574 		pd->source = &entities[source]->obj->vertexlist3[nearest].v;
575 		pd->sourceionum = source;
576 		pd->tolive = 1200 + spawn_nb * 5;
577 		totdelay += 45 + Random::get(0, 150 - spawn_nb);
578 		pd->delay = totdelay;
579 		pd->rgb = Color3f(.9f, 0.f, 0.f);
580 		pd->tc = bloodsplatter;
581 		pd->fparam = rnd() * 0.1f - 0.05f;
582 	}
583 }
584 
585 long SPARK_COUNT = 0;
586 
587 // flag & 1 punch failed
588 // flag & 2 punch success
ARX_PARTICLES_Spawn_Spark(Vec3f * pos,float dmgs,long flags)589 void ARX_PARTICLES_Spawn_Spark(Vec3f * pos, float dmgs, long flags) {
590 
591 	if(!pos) {
592 		return;
593 	}
594 
595 	long spawn_nb = dmgs;
596 
597 	if(SPARK_COUNT < 1000) {
598 		SPARK_COUNT += spawn_nb * 25;
599 	} else {
600 		SPARK_COUNT -= static_cast<long>(FrameDiff);
601 		return;
602 	}
603 
604 	for(long k = 0; k < spawn_nb; k++) {
605 
606 		PARTICLE_DEF * pd = createParticle();
607 		if(!pd) {
608 			return;
609 		}
610 
611 		pd->oldpos = pd->ov = *pos + randomVec(-5.f, 5.f);
612 		pd->siz = 2.f;
613 		pd->move = randomVec(-6.f, 6.f);
614 
615 		pd->special = PARTICLE_SPARK;
616 		float len = clamp(spawn_nb * (1.f / 3), 3.f, 8.f);
617 		pd->tolive = (unsigned long)(len * 90.f + float(spawn_nb));
618 
619 		if(flags == 0) {
620 			pd->rgb = Color3f(.3f, .3f, 0.f);
621 		} else if(flags & 1) {
622 			pd->rgb = Color3f(.2f, .2f, .1f);
623 		} else if(flags & 2) {
624 			pd->rgb = Color3f(.45f, .1f, 0.f);
625 		}
626 
627 		pd->fparam = len + rnd() * len; // Spark tail length
628 	}
629 }
630 
MakeCoolFx(Vec3f * pos)631 void MakeCoolFx(Vec3f * pos) {
632 	ARX_BOOMS_Add(pos,1);
633 }
634 
MakePlayerAppearsFX(Entity * io)635 void MakePlayerAppearsFX(Entity * io) {
636 	MakeCoolFx(&io->pos);
637 	MakeCoolFx(&io->pos);
638 	AddRandomSmoke(io, 30);
639 	ARX_PARTICLES_Add_Smoke(&io->pos, 1 | 2, 20); // flag 1 = randomize pos
640 }
641 
AddRandomSmoke(Entity * io,long amount)642 void AddRandomSmoke(Entity * io, long amount) {
643 
644 	if(!io) {
645 		return;
646 	}
647 
648 	for(long i = 0; i < amount; i++) {
649 
650 		PARTICLE_DEF * pd = createParticle();
651 		if(!pd) {
652 			return;
653 		}
654 
655 		long vertex = Random::get(0, io->obj->vertexlist.size());
656 		pd->ov = io->obj->vertexlist3[vertex].v + randomVec(-5.f, 5.f);
657 		pd->siz = rnd() * 8.f;
658 		if(pd->siz < 4.f) {
659 			pd->siz = 4.f;
660 		}
661 		pd->scale = Vec3f::repeat(10.f);
662 		pd->special = ROTATING | MODULATE_ROTATION | FADE_IN_AND_OUT;
663 		pd->tolive = Random::get(900, 1300);
664 		pd->move = Vec3f(0.25f - 0.5f * rnd(), -1.f * rnd() + 0.3f, 0.25f - 0.5f * rnd());
665 		pd->rgb = Color3f(0.3f, 0.3f, 0.34f);
666 		pd->tc = smokeparticle;
667 		pd->fparam = 0.001f;
668 	}
669 }
670 
671 // flag 1 = randomize pos
ARX_PARTICLES_Add_Smoke(Vec3f * pos,long flags,long amount,Color3f * rgb)672 void ARX_PARTICLES_Add_Smoke(Vec3f * pos, long flags, long amount, Color3f * rgb) {
673 
674 	Vec3f mod = (flags & 1) ? randomVec(-50.f, 50.f) : Vec3f::ZERO;
675 
676 	while(amount--) {
677 
678 		PARTICLE_DEF * pd = createParticle();
679 		if(!pd) {
680 			return;
681 		}
682 
683 		pd->ov = *pos + mod;
684 		if(flags & 2) {
685 			pd->siz = rnd() * 20.f + 15.f;
686 			pd->scale = randomVec(40.f, 55.f);
687 		} else {
688 			pd->siz = std::max(4.f, rnd() * 8.f + 5.f);
689 			pd->scale = randomVec(10.f, 15.f);
690 		}
691 		pd->special = ROTATING | MODULATE_ROTATION | FADE_IN_AND_OUT;
692 		pd->tolive = Random::get(1100, 1500);
693 		pd->delay = amount * 120 + Random::get(0, 100);
694 		pd->move = Vec3f(0.25f - 0.5f * rnd(), -1.f * rnd() + 0.3f, 0.25f - 0.5f * rnd());
695 		pd->rgb = (rgb) ? *rgb : Color3f(0.3f, 0.3f, 0.34f);
696 		pd->tc = smokeparticle;
697 		pd->fparam = 0.01f;
698 	}
699 }
700 
701 extern long cur_mr;
702 
ManageTorch()703 void ManageTorch() {
704 
705 	EERIE_LIGHT * el = &DynLight[0];
706 
707 	if(SHOW_TORCH) {
708 
709 		float rr = rnd();
710 		el->pos = player.pos;
711 		el->intensity = 1.6f;
712 		el->fallstart = 280.f + rr * 20.f;
713 		el->fallend = el->fallstart + 280.f;
714 		el->exist = 1;
715 		el->rgb = Color3f(Project.torch.r - rr * 0.1f, Project.torch.g - rr * 0.1f,
716 		                  Project.torch.b - rr * 0.1f);
717 		el->duration = 0;
718 		el->extras = 0;
719 
720 	} else if(cur_mr == 3) {
721 
722 		el->pos = player.pos;
723 		el->intensity = 1.8f;
724 		el->fallstart = 480.f;
725 		el->fallend = el->fallstart + 480.f;
726 		el->exist = 1;
727 		el->rgb = Color3f(1.f, .5f, .8f);
728 		el->duration = 0;
729 		el->extras = 0;
730 
731 	} else {
732 
733 		if(flarenum == 0) {
734 			el->exist = 0;
735 		} else {
736 
737 			long count = 0;
738 			for(long i = 0; i < MAX_FLARES; i++) {
739 				if(flare[i].exist && flare[i].flags == 0) {
740 					count++;
741 				}
742 			}
743 
744 			if(count) {
745 				float rr = rnd();
746 				el->pos = player.pos;
747 				el->fallstart = 140.f + float(count) * 0.333333f + rr * 5.f;
748 				el->fallend = 220.f + float(count) * 0.5f + rr * 5.f;
749 				el->intensity = 1.6f;
750 				el->exist = 1;
751 				el->rgb = Color3f(0.01f * count, 0.009f * count, 0.008f * count);
752 			}
753 		}
754 	}
755 
756 	if(entities.size() > 0 && entities.player() && entities.player()->obj
757 	   && entities.player()->obj->fastaccess.head_group_origin > -1) {
758 		short vertex = entities.player()->obj->fastaccess.head_group_origin;
759 		el->pos.y = entities.player()->obj->vertexlist3[vertex].v.y;
760 	}
761 }
762 
ARX_MAGICAL_FLARES_Draw(long FRAMETICKS)763 void ARX_MAGICAL_FLARES_Draw(long FRAMETICKS) {
764 
765 	shinum++;
766 	if(shinum >= 10) {
767 		shinum = 1;
768 	}
769 
770 	long TICKS = long(arxtime) - FRAMETICKS;
771 	if(TICKS < 0) {
772 		return;
773 	}
774 
775 	GRenderer->SetRenderState(Renderer::DepthWrite, false);
776 	GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
777 	GRenderer->SetRenderState(Renderer::AlphaBlending, true);
778 
779 	bool key = !GInput->actionPressed(CONTROLS_CUST_MAGICMODE);
780 
781 	for(long j = 1; j < 5; j++) {
782 
783 		TextureContainer * surf;
784 		switch(j) {
785 			case 2:  surf = flaretc.lumignon; break;
786 			case 3:  surf = flaretc.lumignon2; break;
787 			case 4:  surf = flaretc.plasm; break;
788 			default: surf = flaretc.shine[shinum]; break;
789 		}
790 
791 		for(long i = 0; i < MAX_FLARES; i++) {
792 
793 			if(!flare[i].exist || flare[i].type != j) {
794 				continue;
795 			}
796 
797 			flare[i].tolive -= float(TICKS * 2);
798 			if(flare[i].flags & 1) {
799 				flare[i].tolive -= float(TICKS * 4);
800 			} else if (key) {
801 				flare[i].tolive -= float(TICKS * 6);
802 			}
803 
804 			float z = (flare[i].tolive * 0.00025f);
805 			float s;
806 			if(flare[i].type == 1) {
807 				s = flare[i].size * 2 * z;
808 			} else if(flare[i].type == 4) {
809 				s = flare[i].size * 2.f * z + 10.f;
810 			} else {
811 				s = flare[i].size;
812 			}
813 
814 			if(flare[i].tolive <= 0.f || flare[i].y < -64.f || s < 3.f) {
815 
816 				if(flare[i].io && ValidIOAddress(flare[i].io)) {
817 					flare[i].io->flarecount--;
818 				}
819 
820 				if(ValidDynLight(flare[i].dynlight)) {
821 					DynLight[flare[i].dynlight].exist = 0;
822 				}
823 
824 				flare[i].dynlight = -1;
825 				flare[i].exist = 0;
826 				flarenum--;
827 
828 				continue;
829 			}
830 
831 			if(flare[i].type == 1 && z < 0.6f)  {
832 				z = 0.6f;
833 			}
834 
835 			Color3f c = flare[i].rgb * z;
836 			flare[i].tv.color = c.toBGR();
837 			flare[i].v.p = flare[i].tv.p;
838 
839 			DynLight[0].rgb = componentwise_max(DynLight[0].rgb, c);
840 
841 			if(ValidDynLight(flare[i].dynlight)) {
842 				EERIE_LIGHT * el = &DynLight[flare[i].dynlight];
843 				el->pos = flare[i].v.p;
844 				el->rgb = c;
845 			}
846 
847 			if(!flare[i].io) {
848 				GRenderer->SetRenderState(Renderer::DepthTest, false);
849 			} else {
850 				GRenderer->SetRenderState(Renderer::DepthTest, true);
851 			}
852 
853 			if(flare[i].bDrawBitmap) {
854 				s *= 2.f;
855 				EERIEDrawBitmap(flare[i].v.p.x, flare[i].v.p.y, s, s, flare[i].v.p.z,
856 				                surf, Color::fromBGRA(flare[i].tv.color));
857 			} else {
858 				EERIEDrawSprite(&flare[i].v, s * 0.025f + 1.f, surf,
859 				                Color::fromBGRA(flare[i].tv.color), 2.f);
860 			}
861 
862 		}
863 	}
864 
865 	DynLight[0].rgb = componentwise_min(DynLight[0].rgb, Color3f::white);
866 
867 	GRenderer->SetRenderState(Renderer::DepthWrite, true);
868 	GRenderer->SetRenderState(Renderer::DepthTest, true);
869 }
870 
ARX_BOOMS_ClearAllPolyBooms()871 void ARX_BOOMS_ClearAllPolyBooms() {
872 	for(long i = 0; i < MAX_POLYBOOM; i++) {
873 		polyboom[i].exist = 0;
874 	}
875 	BoomCount = 0;
876 }
877 
ARX_BOOMS_Add(Vec3f * poss,long type)878 void ARX_BOOMS_Add(Vec3f * poss,long type) {
879 
880 	PARTICLE_DEF * pd = createParticle(true);
881 	if(pd) {
882 
883 		static TextureContainer * tc1 = TextureContainer::Load("graph/particles/fire_hit");
884 
885 		pd->ov = *poss;
886 		pd->move = Vec3f(3.f - 6.f * rnd(), 4.f - 12.f * rnd(), 3.f - 6.f * rnd());
887 		pd->tolive = Random::get(600, 700);
888 		pd->tc = tc1;
889 		pd->siz = (100.f + 10.f * rnd()) * ((type == 1) ? 2.f : 1.f);
890 		pd->zdec = true;
891 		if(type == 1) {
892 			pd->rgb = Color3f(.4f, .4f, 1.f);
893 		}
894 
895 		pd = createParticle(true);
896 		if(pd) {
897 			pd->ov = *poss;
898 			pd->move = Vec3f(3.f - 6.f * rnd(), 4.f - 12.f * rnd(), 3.f - 6.f * rnd());
899 			pd->tolive = Random::get(600, 700);
900 			pd->tc = tc1;
901 			pd->siz = (40.f + 30.f * rnd()) * ((type == 1) ? 2.f : 1.f);
902 			pd->zdec = true;
903 			if(type == 1) {
904 				pd->rgb = Color3f(.4f, .4f, 1.f);
905 			}
906 		}
907 
908 	}
909 
910 	static TextureContainer * tc2 = TextureContainer::Load("graph/particles/boom");
911 
912 	// TODO was F2L at some point - should this be rounded?
913 	long x0 = long(poss->x * ACTIVEBKG->Xmul) - 3;
914 	long z0 = long(poss->z * ACTIVEBKG->Zmul) - 3;
915 	long x1 = x0 + 6;
916 	long z1 = z0 + 6;
917 	x0 = clamp(x0, 0l, ACTIVEBKG->Xsize - 1l);
918 	x1 = clamp(x1, 0l, ACTIVEBKG->Xsize - 1l);
919 	z0 = clamp(z0, 0l, ACTIVEBKG->Zsize - 1l);
920 	z1 = clamp(z1, 0l, ACTIVEBKG->Zsize - 1l);
921 
922 	for(long j = z0; j <= z1; j++) for(long i = x0; i <= x1;i++) {
923 		EERIE_BKG_INFO & eg = ACTIVEBKG->Backg[i + j * ACTIVEBKG->Xsize];
924 		for(long l = 0; l < eg.nbpoly; l++) {
925 			EERIEPOLY * ep = &eg.polydata[l];
926 
927 			if((ep->type & POLY_TRANS) && !(ep->type & POLY_WATER)) {
928 				continue;
929 			}
930 
931 			long nbvert = (ep->type & POLY_QUAD) ? 4 : 3;
932 
933 			float temp_uv1[4];
934 
935 			bool dod = true;
936 			for(long k = 0; k < nbvert; k++) {
937 				float ddd = fdist(ep->v[k].p, *poss);
938 				if(ddd > BOOM_RADIUS) {
939 					dod = false;
940 					break;
941 				} else {
942 					temp_uv1[k] = 0.5f - ddd * (0.5f / BOOM_RADIUS);
943 				}
944 			}
945 			if(!dod) {
946 				continue;
947 			}
948 
949 			long n = ARX_BOOMS_GetFree();
950 			if(n < 0) {
951 				continue;
952 			}
953 
954 			BoomCount++;
955 			POLYBOOM * pb = &polyboom[n];
956 			pb->exist = 1;
957 
958 			pb->type = 0;
959 			pb->ep = ep;
960 			pb->tc = tc2;
961 			pb->tolive = 10000;
962 			pb->timecreation = long(arxtime);
963 			pb->tx = short(i);
964 			pb->tz = short(j);
965 			for(int k = 0; k < nbvert; k++) {
966 				pb->v[k] = pb->u[k] = temp_uv1[k];
967 			}
968 			pb->nbvert = short(nbvert);
969 
970 		}
971 	}
972 }
973 
Add3DBoom(Vec3f * position)974 void Add3DBoom(Vec3f * position) {
975 
976 	Vec3f poss = *position;
977 	ARX_SOUND_PlaySFX(SND_SPELL_FIRE_HIT, &poss);
978 
979 	float dist = fdist(player.pos - Vec3f(0, 160.f, 0.f), *position);
980 	if(dist < 300) {
981 		Vec3f vect = (player.pos - *position - Vec3f(0.f, 160.f, 0.f)) / dist;
982 		player.physics.forces += vect * ((300.f - dist) * 0.0125f);
983 	}
984 
985 	for(size_t i = 0; i < entities.size(); i++) {
986 
987 		Entity * entity = entities[i];
988 		if(!entity || entity->show != 1 || !(entity->ioflags & IO_ITEM)) {
989 			continue;
990 		}
991 
992 		if(!entity->obj || !entity->obj->pbox) {
993 			continue;
994 		}
995 
996 		for(long k = 0; k < entity->obj->pbox->nb_physvert; k++) {
997 			float dist = fdist(entity->obj->pbox->vert[k].pos, *position);
998 			if(dist < 300.f) {
999 				entity->obj->pbox->active = 1;
1000 				entity->obj->pbox->stopcount = 0;
1001 				Vec3f vect = (entity->obj->pbox->vert[k].pos - *position) / dist;
1002 				entity->obj->pbox->vert[k].velocity += vect * ((300.f - dist) * 10.f);
1003 			}
1004 		}
1005 	}
1006 }
1007 
UpdateObjFx()1008 void UpdateObjFx() {
1009 
1010 	ColorBGRA c = Color::white.toBGR();
1011 	TexturedVertex v[3];
1012 	v[0] = TexturedVertex(Vec3f(0, 0, 0.001f), 1.f, c, 1, Vec2f::ZERO);
1013 	v[1] = TexturedVertex(Vec3f(0, 0, 0.001f), 1.f, c, 1, Vec2f::X_AXIS);
1014 	v[2] = TexturedVertex(Vec3f(0, 0, 0.001f), 1.f, c, 1, Vec2f::ONE);
1015 	TexturedVertex v2[3];
1016 	v[0] = TexturedVertex(Vec3f(0, 0, 0.001f), 1.f, c, 1, Vec2f::ZERO);
1017 	v[1] = TexturedVertex(Vec3f(0, 0, 0.001f), 1.f, c, 1, Vec2f::X_AXIS);
1018 	v[2] = TexturedVertex(Vec3f(0, 0, 0.001f), 1.f, c, 1, Vec2f::ONE);
1019 
1020 	GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
1021 	GRenderer->SetRenderState(Renderer::AlphaBlending, true);
1022 	GRenderer->SetRenderState(Renderer::DepthWrite, false);
1023 
1024 	for(long i = 0; i < MAX_OBJFX; i++) {
1025 
1026 		if(!objfx[i].exist) {
1027 			continue;
1028 		}
1029 
1030 		unsigned long framediff = (unsigned long)(arxtime) - objfx[i].tim_start;
1031 		if(framediff > objfx[i].duration) {
1032 			objfx[i].exist = false;
1033 			if(ValidDynLight(objfx[i].dynlight)) {
1034 				DynLight[objfx[i].dynlight].exist = 0;
1035 			}
1036 			objfx[i].dynlight = -1;
1037 			continue;
1038 		}
1039 
1040 		float val = float(framediff) / float(objfx[i].duration);
1041 		Vec3f pos = objfx[i].pos + objfx[i].move * val;
1042 
1043 
1044 		Color3f color = Color3f(1.f - objfx[i].fade.r * val, 0.f,
1045 		                        1.f - objfx[i].fade.b * val);
1046 		if(!Project.improve) {
1047 			color.g = 1.f - objfx[i].fade.g * val;
1048 		}
1049 
1050 		Vec3f scale = Vec3f::ONE + objfx[i].scale * val;
1051 		Anglef angle = Anglef::ZERO;
1052 
1053 		DrawEERIEObjEx(objfx[i].obj, &angle, &pos, &scale, &color);
1054 
1055 		if(objfx[i].dynlight != -1) {
1056 			DynLight[objfx[i].dynlight].fallend = 250.f + 450.f * val;
1057 			DynLight[objfx[i].dynlight].fallstart = 150.f + 50.f * val;
1058 			Color3f c(1.f - val, 0.9f-val * 0.9f, 0.5f - val * 0.5f);
1059 			DynLight[objfx[i].dynlight].rgb = c;
1060 			DynLight[objfx[i].dynlight].pos = pos;
1061 		}
1062 
1063 		if(!(objfx[i].special & SPECIAL_RAYZ)) {
1064 			continue;
1065 		}
1066 
1067 		GRenderer->SetCulling(Renderer::CullNone);
1068 
1069 		for(long k = 0; k < 8; k++) {
1070 			float aa = objfx[i].spe[k].g;
1071 			float bb = objfx[i].speinc[k].g;
1072 
1073 			if(!arxtime.is_paused()) {
1074 				objfx[i].spe[k].a += objfx[i].speinc[k].a;
1075 				objfx[i].spe[k].b += objfx[i].speinc[k].b;
1076 			}
1077 
1078 			Vec3f t = scale * 100.f;
1079 			v[0].p = pos;
1080 			v[1].p.x = pos.x - EEsin(radians(objfx[i].spe[k].b)) * t.x;
1081 			v[1].p.y = pos.y + EEsin(radians(objfx[i].spe[k].a)) * t.y;
1082 			v[1].p.z = pos.z + EEcos(radians(objfx[i].spe[k].b)) * t.z;
1083 			v[2].p.x = pos.x - EEsin(radians(MAKEANGLE(objfx[i].spe[k].b + bb))) * t.x;
1084 			v[2].p.y = pos.y + EEsin(radians(MAKEANGLE(objfx[i].spe[k].a + aa))) * t.y;
1085 			v[2].p.z = pos.z + EEcos(radians(MAKEANGLE(objfx[i].spe[k].b + bb))) * t.z;
1086 			EE_RTP(&v[0], &v2[0]);
1087 			EE_RTP(&v[1], &v2[1]);
1088 			EE_RTP(&v[2], &v2[2]);
1089 
1090 			if(Project.improve) {
1091 				for(long p = 0; p < 3; p++) {
1092 					v2[p].color = Color3f(color.r / (3.f + float(p)), 0.f,
1093 					                      color.b / (5.f + float(p))).toBGR();
1094 				}
1095 			} else {
1096 				for(long p = 0; p < 3; p++) {
1097 					v2[p].color = Color3f(color.r / (3.f + float(p)), color.g / (4.f + float(p)),
1098 					                      color.b / (5.f + float(p))).toBGR();
1099 				}
1100 			}
1101 
1102 			GRenderer->ResetTexture(0);
1103 			EERIEDRAWPRIM(Renderer::TriangleFan, v2);
1104 		}
1105 	}
1106 }
1107 
ARX_PARTICLES_FirstInit()1108 void ARX_PARTICLES_FirstInit() {
1109 
1110 	smokeparticle = TextureContainer::Load("graph/particles/smoke");
1111 
1112 	// TODO bloodsplat and water_splat cannot use mipmapping because they need a constant color border pixel
1113 	// this may also apply to other textures
1114 
1115 	bloodsplat[0] = TextureContainer::Load("graph/particles/new_blood", TextureContainer::NoMipmap);
1116 	bloodsplat[1] = TextureContainer::Load("graph/particles/new_blood_splat1", TextureContainer::NoMipmap);
1117 	bloodsplat[2] = TextureContainer::Load("graph/particles/new_blood_splat2", TextureContainer::NoMipmap);
1118 	bloodsplat[3] = TextureContainer::Load("graph/particles/new_blood_splat3", TextureContainer::NoMipmap);
1119 	bloodsplat[4] = TextureContainer::Load("graph/particles/new_blood_splat4", TextureContainer::NoMipmap);
1120 	bloodsplat[5] = TextureContainer::Load("graph/particles/new_blood_splat5", TextureContainer::NoMipmap);
1121 	bloodsplatter = bloodsplat[0];
1122 
1123 	water_splat[0] = TextureContainer::Load("graph/particles/[fx]_water01", TextureContainer::NoMipmap);
1124 	water_splat[1] = TextureContainer::Load("graph/particles/[fx]_water02", TextureContainer::NoMipmap);
1125 	water_splat[2] = TextureContainer::Load("graph/particles/[fx]_water03", TextureContainer::NoMipmap);
1126 
1127 	water_drop[0]=TextureContainer::Load("graph/particles/[fx]_water_drop01");
1128 	water_drop[1]=TextureContainer::Load("graph/particles/[fx]_water_drop02");
1129 	water_drop[2]=TextureContainer::Load("graph/particles/[fx]_water_drop03");
1130 	healing=TextureContainer::Load("graph/particles/heal_0005");
1131 	tzupouf=TextureContainer::Load("graph/obj3d/textures/(fx)_tsu_greypouf");
1132 	fire2=TextureContainer::Load("graph/particles/fire2");
1133 }
1134 
ARX_PARTICLES_ClearAll()1135 void ARX_PARTICLES_ClearAll() {
1136 	memset(particle, 0, sizeof(PARTICLE_DEF) * MAX_PARTICLES);
1137 	ParticleCount = 0;
1138 }
1139 
createParticle(bool allocateWhilePaused)1140 PARTICLE_DEF * createParticle(bool allocateWhilePaused) {
1141 
1142 	if(!allocateWhilePaused && arxtime.is_paused()) {
1143 		return NULL;
1144 	}
1145 
1146 	for(size_t i = 0; i < MAX_PARTICLES; i++) {
1147 
1148 		PARTICLE_DEF * pd = &particle[i];
1149 
1150 		if(pd->exist) {
1151 			continue;
1152 		}
1153 
1154 		ParticleCount++;
1155 		pd->exist = true;
1156 		pd->timcreation = long(arxtime);
1157 
1158 		pd->type = 0;
1159 		pd->rgb = Color3f::white;
1160 		pd->tc = NULL;
1161 		pd->special = 0;
1162 		pd->source = NULL;
1163 		pd->delay = 0;
1164 		pd->zdec = false;
1165 		pd->move = Vec3f::ZERO;
1166 		pd->scale = Vec3f::ONE;
1167 
1168 		return pd;
1169 	}
1170 
1171 	return NULL;
1172 }
1173 
MagFX(const Vec3f & pos)1174 void MagFX(const Vec3f & pos) {
1175 
1176 	PARTICLE_DEF * pd	=	createParticle();
1177 	if(!pd) {
1178 		return;
1179 	}
1180 
1181 	pd->ov = pos + Vec3f(rnd() * 6.f - rnd() * 12.f, rnd() * 6.f-rnd() * 12.f, 0.f);
1182 	pd->move = Vec3f(6.f - rnd() * 12.f, -8.f + rnd() * 16.f, 0.f);
1183 	pd->scale = Vec3f(4.4f, 4.4f, 1.f);
1184 	pd->tolive = Random::get(1500, 2400);
1185 	pd->tc = healing;
1186 	pd->rgb = Color3f::magenta;
1187 	pd->siz = 56.f;
1188 	pd->type = PARTICLE_2D;
1189 }
1190 
MakeBookFX(const Vec3f & pos)1191 void MakeBookFX(const Vec3f & pos) {
1192 
1193 	for(long i = 0; i < 12; i++) {
1194 
1195 		PARTICLE_DEF * pd = createParticle();
1196 		if(!pd) {
1197 			break;
1198 		}
1199 
1200 		pd->ov = pos + Vec3f(rnd() * 6.f - rnd() * 12.f, rnd() * 6.f - rnd() * 12.f, 0.f);
1201 		pd->move = Vec3f(6.f - rnd() * 12.f, -8.f + rnd() * 16.f, 0.f);
1202 		pd->scale = Vec3f(4.4f, 4.4f, 1.f);
1203 		pd->tolive = Random::get(1500, 2400);
1204 		pd->tc = healing;
1205 		pd->rgb = Color3f::magenta;
1206 		pd->siz = 56.f;
1207 		pd->type = PARTICLE_2D;
1208 	}
1209 
1210 	for(int i = 0; i < 5; i++) {
1211 
1212 		PARTICLE_DEF * pd = createParticle();
1213 		if(!pd) {
1214 			break;
1215 		}
1216 
1217 		pd->ov = pos - Vec3f(float(i * 2), float(i * 2), 0.f);
1218 		pd->move = Vec3f(-float(i) * 0.5f, -float(i) * 0.5f, 0.f);
1219 		pd->scale = Vec3f(float(i * 10), float(i * 10), 0.f);
1220 		pd->tolive = Random::get(1200, 1600);
1221 		pd->tc = ITC.Get("book");
1222 		pd->rgb = Color3f(1.f - float(i) * 0.1f, float(i) * 0.1f, 0.5f - float(i) * 0.1f);
1223 		pd->siz = 32.f + float(i * 4);
1224 		pd->type = PARTICLE_2D;
1225 	}
1226 
1227 	NewSpell = 1;
1228 }
1229 
createSphericalSparks(const Vec3f & pos,float r,TextureContainer * tc,const Color3f & color,int mask)1230 void createSphericalSparks(const Vec3f & pos, float r, TextureContainer * tc,
1231                            const Color3f & color, int mask) {
1232 
1233 	int nb = Random::get(0, 31);
1234 	for(int i = 0; i < nb; i++) {
1235 
1236 		PARTICLE_DEF * pd = createParticle(true);
1237 		if(!pd) {
1238 			return;
1239 		}
1240 
1241 		float a = radians(rnd() * 360.f);
1242 		float b = radians(rnd() * 360.f);
1243 		pd->type = PARTICLE_SPARK2;
1244 		pd->special = GRAVITY;
1245 		pd->ov = pd->oldpos = pos;
1246 		pd->move = Vec3f(EEsin(a) * EEcos(b), EEsin(a) * EEsin(b), EEcos(a)) * r;
1247 		pd->tolive = Random::get(1000, 1500);
1248 		pd->rgb = color;
1249 		pd->tc = tc;
1250 		pd->mask = mask;
1251 	}
1252 }
1253 
ARX_PARTICLES_Spawn_Splat(const Vec3f & pos,float dmgs,Color col)1254 void ARX_PARTICLES_Spawn_Splat(const Vec3f & pos, float dmgs, Color col) {
1255 
1256 	float power = (dmgs * (1.f / 60)) + .9f;
1257 
1258 	for(long kk = 0; kk < 20; kk++) {
1259 
1260 		PARTICLE_DEF * pd = createParticle(true);
1261 		if(!pd) {
1262 			return;
1263 		}
1264 
1265 		pd->special = PARTICLE_SUB2 | SUBSTRACT | GRAVITY;
1266 		pd->ov = pos;
1267 		pd->move = randomVec(-11.5f, 11.5f);
1268 		pd->tolive = (unsigned long)(1000 + dmgs*3);
1269 		pd->tc = blood_splat;
1270 		pd->siz = 0.3f + 0.01f * power;
1271 		pd->scale = Vec3f::repeat(0.2f + 0.3f * power);
1272 		pd->zdec = true;
1273 		pd->rgb = col.to<float>();
1274 	}
1275 }
1276 
ARX_PARTICLES_SpawnWaterSplash(const Vec3f * _ePos)1277 void ARX_PARTICLES_SpawnWaterSplash(const Vec3f * _ePos) {
1278 
1279 	long nbParticles = Random::get(15, 35);
1280 	for(long kk=0; kk < nbParticles; kk++) {
1281 
1282 		PARTICLE_DEF * pd = createParticle(true);
1283 		if(!pd) {
1284 			return;
1285 		}
1286 
1287 		pd->special = FADE_IN_AND_OUT | ROTATING | MODULATE_ROTATION | DISSIPATING
1288 		              | GRAVITY | SPLAT_WATER;
1289 		pd->ov = *_ePos + Vec3f(30.f * rnd(), -20.f * rnd(), 30.f * rnd());
1290 		pd->move = Vec3f(6.5f * frand2(), -11.5f * rnd(), 6.5f * frand2());
1291 		pd->tolive = Random::get(1000, 1300);
1292 
1293 		int t = Random::get(0, 2);
1294 		pd->tc = water_drop[t];
1295 		pd->siz = 0.4f;
1296 		float s = rnd();
1297 		pd->zdec = true;
1298 		pd->rgb = Color3f::gray(s);
1299 	}
1300 }
1301 
SpawnFireballTail(Vec3f * poss,Vec3f * vecto,float level,long flags)1302 void SpawnFireballTail(Vec3f * poss, Vec3f * vecto, float level, long flags) {
1303 
1304 	if(!explo[0]) {
1305 		return;
1306 	}
1307 
1308 	for(long nn = 0; nn < 2; nn++) {
1309 
1310 		PARTICLE_DEF * pd = createParticle(true);
1311 		if(!pd) {
1312 			return;
1313 		}
1314 
1315 		pd->special = FIRE_TO_SMOKE | FADE_IN_AND_OUT | PARTICLE_ANIMATED | ROTATING
1316 		              | MODULATE_ROTATION;
1317 		pd->fparam = 0.02f - rnd() * 0.02f;
1318 		pd->move = Vec3f(0.f, -rnd() * 3.f, 0.f);
1319 		pd->tc = explo[0];
1320 		pd->rgb = Color3f::gray(.7f);
1321 		pd->siz = (level + rnd()) * 2.f;
1322 
1323 		if(flags & 1) {
1324 			pd->tolive = Random::get(400, 500);
1325 			pd->siz *= 0.7f;
1326 			pd->scale = Vec3f::repeat(level * 1.4f);
1327 		} else {
1328 			pd->scale = Vec3f::repeat(level * 2.f);
1329 			pd->tolive=Random::get(800, 900);
1330 		}
1331 
1332 		pd->cval1 = 0;
1333 		pd->cval2 = MAX_EXPLO - 1;
1334 
1335 		if(nn == 1) {
1336 			pd->delay = Random::get(150, 250);
1337 			pd->ov = *poss + *vecto * pd->delay;
1338 		} else {
1339 			pd->ov = *poss;
1340 		}
1341 	}
1342 }
1343 
LaunchFireballBoom(Vec3f * poss,float level,Vec3f * direction,Color3f * rgb)1344 void LaunchFireballBoom(Vec3f * poss, float level, Vec3f * direction, Color3f * rgb) {
1345 
1346 	level *= 1.6f;
1347 
1348 	if(explo[0] == NULL) {
1349 		return;
1350 	}
1351 
1352 	PARTICLE_DEF * pd = createParticle(true);
1353 	if(!pd) {
1354 		return;
1355 	}
1356 
1357 	pd->special = FIRE_TO_SMOKE | FADE_IN_AND_OUT | PARTICLE_ANIMATED;
1358 	pd->ov = *poss;
1359 	pd->move = (direction) ? *direction : Vec3f(0.f, -rnd() * 5.f, 0.f);
1360 	pd->tolive = Random::get(1600, 2200);
1361 	pd->tc = explo[0];
1362 	pd->siz = level * 3.f + 2.f * rnd();
1363 	pd->scale = Vec3f::repeat(level * 3.f);
1364 	pd->zdec = true;
1365 	pd->cval1 = 0;
1366 	pd->cval2 = MAX_EXPLO - 1;
1367 	if(rgb) {
1368 		pd->rgb = *rgb;
1369 	}
1370 
1371 }
1372 
ARX_PARTICLES_Render(EERIE_CAMERA * cam)1373 void ARX_PARTICLES_Render(EERIE_CAMERA * cam)  {
1374 
1375 	if(!ACTIVEBKG) {
1376 		return;
1377 	}
1378 
1379 	TreatBackgroundActions();
1380 
1381 	if(ParticleCount == 0) {
1382 		return;
1383 	}
1384 
1385 	TexturedVertex in, inn, out;
1386 
1387 	unsigned long tim = (unsigned long)arxtime;
1388 
1389 	GRenderer->SetCulling(Renderer::CullNone);
1390 	GRenderer->SetFogColor(Color::none);
1391 
1392 	long pcc = ParticleCount;
1393 
1394 	for(size_t i = 0; i < MAX_PARTICLES && pcc > 0; i++) {
1395 
1396 		PARTICLE_DEF * part = &particle[i];
1397 		if(!part->exist) {
1398 			continue;
1399 		}
1400 
1401 		long framediff = part->timcreation + part->tolive - tim;
1402 		long framediff2 = tim - part->timcreation;
1403 
1404 		if(framediff2 < long(part->delay)) {
1405 			continue;
1406 		}
1407 
1408 		if(part->delay > 0) {
1409 			part->timcreation += part->delay;
1410 			part->delay=0;
1411 			if((part->special & DELAY_FOLLOW_SOURCE) && part->sourceionum >= 0
1412 					&& entities[part->sourceionum]) {
1413 				part->ov = *part->source;
1414 				Entity * target = entities[part->sourceionum];
1415 				Vec3f vector = (part->ov - target->pos) * Vec3f(1.f, 0.5f, 1.f);
1416 				vector.normalize();
1417 				part->move = vector * Vec3f(18.f, 5.f, 18.f) + randomVec(-0.5f, 0.5f);
1418 
1419 			}
1420 			continue;
1421 		}
1422 
1423 		if(!(part->type & PARTICLE_2D)) {
1424 			long xx = part->ov.x * ACTIVEBKG->Xmul;
1425 			long yy = part->ov.z * ACTIVEBKG->Zmul;
1426 			if(xx < 0 || yy < 0 || xx > ACTIVEBKG->Xsize || yy > ACTIVEBKG->Zsize) {
1427 				part->exist = false;
1428 				ParticleCount--;
1429 				continue;
1430 			}
1431 			FAST_BKG_DATA & feg = ACTIVEBKG->fastdata[xx][yy];
1432 			if(!feg.treat) {
1433 				part->exist = false;
1434 				ParticleCount--;
1435 				continue;
1436 			}
1437 		}
1438 
1439 		if(framediff <= 0) {
1440 			if((part->special & FIRE_TO_SMOKE) && rnd() > 0.7f) {
1441 
1442 				part->ov += part->move;
1443 				part->tolive += (part->tolive / 4) + (part->tolive / 8);
1444 				part->special &= ~FIRE_TO_SMOKE;
1445 				part->tc = smokeparticle;
1446 				part->scale *= 2.4f;
1447 				if(part->scale.x < 0.f) {
1448 					part->scale.x *= -1.f;
1449 				}
1450 				if(part->scale.y < 0.f) {
1451 					part->scale.y *= -1.f;
1452 				}
1453 				if(part->scale.z < 0.f) {
1454 					part->scale.z *= -1.f;
1455 				}
1456 				part->rgb = Color3f::gray(.45f);
1457 				part->move *= 0.5f;
1458 				part->siz *= 1.f / 3;
1459 				part->special &= ~FIRE_TO_SMOKE;
1460 				part->timcreation = tim;
1461 				part->tc = smokeparticle;
1462 
1463 				framediff = part->tolive;
1464 
1465 			} else {
1466 				part->exist = false;
1467 				ParticleCount--;
1468 				continue;
1469 			}
1470 		}
1471 
1472 		if((part->special & FIRE_TO_SMOKE2)
1473 				&& framediff2 > long(part->tolive - (part->tolive / 4))) {
1474 
1475 			part->special &= ~FIRE_TO_SMOKE2;
1476 
1477 			PARTICLE_DEF * pd = createParticle(true);
1478 			if(pd) {
1479 				*pd = *part;
1480 				pd->timcreation = tim;
1481 				pd->zdec = false;
1482 				pd->special |= SUBSTRACT;
1483 				pd->ov = part->oldpos;
1484 				pd->tc = tzupouf;
1485 				pd->scale *= 4.f;
1486 				if(pd->scale.x < 0.f) {
1487 					pd->scale.x *= -1.f;
1488 				}
1489 				if(pd->scale.y < 0.f) {
1490 					pd->scale.y *= -1.f;
1491 				}
1492 				if(pd->scale.z < 0.f) {
1493 					pd->scale.z *= -1.f;
1494 				}
1495 				pd->rgb = Color3f::white;
1496 				pd->move *= 0.5f;
1497 				pd->siz *= 1.f / 3;
1498 			}
1499 		}
1500 
1501 		float val = (part->tolive - framediff) * 0.01f;
1502 
1503 		if((part->special & FOLLOW_SOURCE) && part->sourceionum >= 0
1504 				&& entities[part->sourceionum]) {
1505 			inn.p = in.p = *part->source;
1506 		} else if((part->special & FOLLOW_SOURCE2) && part->sourceionum >= 0
1507 							&& entities[part->sourceionum]) {
1508 			inn.p = in.p = *part->source + part->move * val;
1509 		} else {
1510 			inn.p = in.p = part->ov + part->move * val;
1511 		}
1512 
1513 		if(part->special & GRAVITY) {
1514 			in.p.y = inn.p.y = inn.p.y + 1.47f * val * val;
1515 		}
1516 
1517 		if(part->special & PARTICLE_NOZBUFFER) {
1518 			GRenderer->SetRenderState(Renderer::DepthTest, false);
1519 		} else {
1520 			GRenderer->SetRenderState(Renderer::DepthTest, true);
1521 		}
1522 
1523 		float fd = float(framediff2) / float(part->tolive);
1524 		float r = 1.f - fd;
1525 		if(part->special & FADE_IN_AND_OUT) {
1526 			long t = part->tolive / 2;
1527 			if(framediff2 <= t) {
1528 				r = float(framediff2) / float(t);
1529 			} else {
1530 				r = 1.f - float(framediff2 - t) / float(t);
1531 			}
1532 		}
1533 
1534 		if(!(part->type & PARTICLE_2D)) {
1535 
1536 			EERIE_SPHERE sp;
1537 			sp.origin = in.p;
1538 			EERIETreatPoint(&inn, &out);
1539 			if(out.rhw < 0 || out.p.z > cam->cdepth * fZFogEnd) {
1540 				continue;
1541 			}
1542 
1543 			if(part->special & PARTICLE_SPARK) {
1544 
1545 				if(part->special & NO_TRANS) {
1546 					GRenderer->SetRenderState(Renderer::AlphaBlending, false);
1547 				} else {
1548 					GRenderer->SetRenderState(Renderer::AlphaBlending, true);
1549 					if(part->special & SUBSTRACT) {
1550 						GRenderer->SetBlendFunc(Renderer::BlendZero, Renderer::BlendInvSrcColor);
1551 					} else {
1552 						GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
1553 					}
1554 				}
1555 
1556 				GRenderer->SetCulling(Renderer::CullNone);
1557 				Vec3f vect = part->oldpos - in.p;
1558 				fnormalize(vect);
1559 				TexturedVertex tv[3];
1560 				tv[0].color = part->rgb.toBGR();
1561 				tv[1].color = 0xFF666666;
1562 				tv[2].color = 0xFF000000;
1563 				tv[0].p = out.p;
1564 				tv[0].rhw = out.rhw;
1565 				TexturedVertex temp;
1566 				temp.p = in.p + Vec3f(rnd() * 0.5f, 0.8f, rnd() * 0.5f);
1567 				EERIETreatPoint(&temp, &tv[1]);
1568 				temp.p = in.p + vect * part->fparam;
1569 
1570 				EERIETreatPoint(&temp, &tv[2]);
1571 				GRenderer->ResetTexture(0);
1572 
1573 				EERIEDRAWPRIM(Renderer::TriangleStrip, tv);
1574 				if(!arxtime.is_paused()) {
1575 					part->oldpos = in.p;
1576 				}
1577 
1578 				continue;
1579 			}
1580 
1581 			if(part->special & SPLAT_GROUND) {
1582 				float siz = part->siz + part->scale.x * fd;
1583 				sp.radius = siz * 10.f;
1584 				if(CheckAnythingInSphere(&sp, 0, CAS_NO_NPC_COL)) {
1585 					if(rnd() < 0.9f) {
1586 						Color3f rgb = part->rgb;
1587 						SpawnGroundSplat(&sp, &rgb, sp.radius, 0);
1588 					}
1589 					part->exist = false;
1590 					ParticleCount--;
1591 					continue;
1592 				}
1593 			}
1594 
1595 			if(part->special & SPLAT_WATER) {
1596 				float siz = part->siz + part->scale.x * fd;
1597 				sp.radius = siz * (10.f + rnd() * 20.f);
1598 				if(CheckAnythingInSphere(&sp, 0, CAS_NO_NPC_COL)) {
1599 					if(rnd() < 0.9f) {
1600 						Color3f rgb = part->rgb * 0.5f;
1601 						SpawnGroundSplat(&sp, &rgb, sp.radius, 2);
1602 					}
1603 					part->exist = false;
1604 					ParticleCount--;
1605 					continue;
1606 				}
1607 			}
1608 
1609 		}
1610 
1611 		if((part->special & DISSIPATING) && out.p.z < 0.05f) {
1612 			out.p.z *= 20.f;
1613 			r *= out.p.z;
1614 		}
1615 
1616 		if(r <= 0.f) {
1617 			pcc--;
1618 			continue;
1619 		}
1620 
1621 		if(part->special & NO_TRANS) {
1622 			GRenderer->SetRenderState(Renderer::AlphaBlending, false);
1623 		} else {
1624 			GRenderer->SetRenderState(Renderer::AlphaBlending, true);
1625 			if(part->special & SUBSTRACT) {
1626 				GRenderer->SetBlendFunc(Renderer::BlendZero, Renderer::BlendInvSrcColor);
1627 			} else {
1628 				GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
1629 			}
1630 		}
1631 
1632 		Vec3f op = part->oldpos;
1633 		if(!arxtime.is_paused()) {
1634 			part->oldpos = in.p;
1635 		}
1636 
1637 		if(part->special & PARTICLE_GOLDRAIN) {
1638 			float v = (rnd() - 0.5f) * 0.2f;
1639 			if(part->rgb.r + v <= 1.f && part->rgb.r + v > 0.f
1640 				&& part->rgb.g + v <= 1.f && part->rgb.g + v > 0.f
1641 				&& part->rgb.b + v <= 1.f && part->rgb.b + v > 0.f) {
1642 				part->rgb = Color3f(part->rgb.r + v, part->rgb.g + v, part->rgb.b + v);
1643 			}
1644 		}
1645 
1646 		Color color = (part->rgb * r).to<u8>();
1647 		if(Project.improve) {
1648 			color.g = 0;
1649 		}
1650 
1651 		TextureContainer * tc = part->tc;
1652 		if(tc == explo[0] && (part->special & PARTICLE_ANIMATED)) {
1653 			long animrange = part->cval2 - part->cval1;
1654 			long num = long(float(framediff2) / float(part->tolive) * animrange);
1655 			num = clamp(num, part->cval1, part->cval2);
1656 			tc = explo[num];
1657 		}
1658 
1659 		float siz = part->siz + part->scale.x * fd;
1660 
1661 		if(part->special & ROTATING) {
1662 			if(!(part->type & PARTICLE_2D)) {
1663 
1664 				float rott;
1665 				if(part->special & MODULATE_ROTATION) {
1666 					rott = MAKEANGLE(float(tim + framediff2) * part->fparam);
1667 				} else {
1668 					rott = MAKEANGLE(float(tim + framediff2 * 2) * 0.25f);
1669 				}
1670 
1671 				float temp = (part->zdec) ? 0.0001f : 2.f;
1672 				if(part->special & PARTICLE_SUB2) {
1673 					TexturedVertex in2 = in;
1674 					GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
1675 					EERIEDrawRotatedSprite(&in, siz, tc, color, temp, rott);
1676 					GRenderer->SetBlendFunc(Renderer::BlendZero, Renderer::BlendInvSrcColor);
1677 					EERIEDrawRotatedSprite(&in2, siz, tc, Color::white, temp, rott);
1678 				} else {
1679 					EERIEDrawRotatedSprite(&in, siz, tc, color, temp, rott);
1680 				}
1681 
1682 			}
1683 		} else if(part->type & PARTICLE_2D) {
1684 
1685 			float siz2 = part->siz + part->scale.y * fd;
1686 			if(part->special & PARTICLE_SUB2) {
1687 				TexturedVertex in2 = in;
1688 				GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
1689 				EERIEDrawBitmap(in.p.x, in.p.y, siz, siz2, in.p.z, tc, color);
1690 				GRenderer->SetBlendFunc(Renderer::BlendZero, Renderer::BlendInvSrcColor);
1691 				EERIEDrawBitmap(in2.p.x, in.p.y, siz, siz2, in.p.z, tc, Color::white);
1692 			} else {
1693 				EERIEDrawBitmap(in.p.x, in.p.y, siz, siz2, in.p.z, tc, color);
1694 			}
1695 
1696 		} else if(part->type & PARTICLE_SPARK2) {
1697 
1698 			Vec3f pos = in.p;
1699 			Color col = (part->rgb * r).to<u8>();
1700 			Vec3f end = pos - (pos - op) * 2.5f;
1701 			Color masked = Color::fromBGRA(col.toBGRA() & part->mask);
1702 			Draw3DLineTex2(end, pos, 2.f, masked, col);
1703 			EERIEDrawSprite(&in, 0.7f, tc, col, 2.f);
1704 
1705 		} else {
1706 
1707 			float temp = (part->zdec) ? 0.0001f : 2.f;
1708 			if(part->special & PARTICLE_SUB2) {
1709 				TexturedVertex in2 = in;
1710 				GRenderer->SetBlendFunc(Renderer::BlendOne, Renderer::BlendOne);
1711 				EERIEDrawSprite(&in, siz, tc, color, temp);
1712 				GRenderer->SetBlendFunc(Renderer::BlendZero, Renderer::BlendInvSrcColor);
1713 				EERIEDrawSprite(&in2, siz, tc, Color::white, temp);
1714 			} else {
1715 				EERIEDrawSprite(&in, siz, tc, color, temp);
1716 			}
1717 		}
1718 
1719 		pcc--;
1720 	}
1721 
1722 	GRenderer->SetFogColor(ulBKGColor);
1723 	GRenderer->SetRenderState(Renderer::DepthTest, true);
1724 }
1725 
RestoreAllLightsInitialStatus()1726 void RestoreAllLightsInitialStatus() {
1727 	for(size_t i = 0; i < MAX_LIGHTS; i++) {
1728 		if(GLight[i]) {
1729 			GLight[i]->status = (GLight[i]->extras & EXTRAS_STARTEXTINGUISHED) ? 0 : 1;
1730 			if(GLight[i]->status == 0) {
1731 				if(ValidDynLight(GLight[i]->tl)) {
1732 					DynLight[GLight[i]->tl].exist = 0;
1733 				}
1734 				GLight[i]->tl = -1;
1735 			}
1736 		}
1737 	}
1738 }
1739 
1740 extern long FRAME_COUNT;
1741 
1742 // Draws Flame Particles
TreatBackgroundActions()1743 void TreatBackgroundActions() {
1744 
1745 	if(FRAME_COUNT > 0) {
1746 		return;
1747 	}
1748 
1749 	float fZFar = square(ACTIVECAM->cdepth * fZFogEnd * 1.3f);
1750 
1751 	for(size_t i = 0; i < MAX_LIGHTS; i++) {
1752 
1753 		EERIE_LIGHT * gl = GLight[i];
1754 		if(!gl) {
1755 			continue;
1756 		}
1757 
1758 		float dist = distSqr(gl->pos,	ACTIVECAM->pos);
1759 		if(dist > fZFar) {
1760 			// Out of treat range
1761 			ARX_SOUND_Stop(gl->sample);
1762 			gl->sample = audio::INVALID_ID;
1763 			continue;
1764 		}
1765 
1766 		if((gl->extras & EXTRAS_SPAWNFIRE) && gl->status) {
1767 			long id = ARX_DAMAGES_GetFree();
1768 			if(id !=-1) {
1769 				damages[id].radius = gl->ex_radius;
1770 				damages[id].damages = gl->ex_radius * (1.0f / 7);
1771 				damages[id].area = DAMAGE_FULL;
1772 				damages[id].duration = 1;
1773 				damages[id].source = -5;
1774 				damages[id].flags = 0;
1775 				damages[id].type = DAMAGE_TYPE_MAGICAL | DAMAGE_TYPE_FIRE | DAMAGE_TYPE_NO_FIX;
1776 				damages[id].exist = true;
1777 				damages[id].pos = gl->pos;
1778 			}
1779 		}
1780 
1781 		if(!(gl->extras & (EXTRAS_SPAWNFIRE | EXTRAS_SPAWNSMOKE)) || !gl->status) {
1782 			if(!gl->status && gl->sample != audio::INVALID_ID) {
1783 				ARX_SOUND_Stop(gl->sample);
1784 				gl->sample = audio::INVALID_ID;
1785 			}
1786 			continue;
1787 		}
1788 
1789 		if(gl->sample == audio::INVALID_ID) {
1790 			gl->sample = SND_FIREPLACE;
1791 			float pitch = 0.95f + 0.1f * rnd();
1792 			ARX_SOUND_PlaySFX(gl->sample, &gl->pos, pitch, ARX_SOUND_PLAY_LOOPED);
1793 		} else {
1794 			ARX_SOUND_RefreshPosition(gl->sample, &gl->pos);
1795 		}
1796 
1797 		long count = 2;
1798 		if(dist < square(ACTIVECAM->cdepth * (1.f / 6))) {
1799 			count = 4;
1800 		} else if(dist > square(ACTIVECAM->cdepth * (1.f / 3))) {
1801 			count = 3;
1802 		}
1803 
1804 		for(long n = 0; n < count; n++) {
1805 
1806 			if(rnd() < gl->ex_frequency) {
1807 				PARTICLE_DEF * pd = createParticle();
1808 				if(pd) {
1809 					float t = rnd() * PI;
1810 					Vec3f s = Vec3f(EEsin(t), EEsin(t), EEcos(t)) * randomVec();
1811 					pd->ov = gl->pos + s * gl->ex_radius;
1812 					pd->move = Vec3f(2.f - 4.f * rnd(), 2.f - 22.f * rnd(), 2.f - 4.f * rnd());
1813 					pd->move *= gl->ex_speed;
1814 					pd->siz = 7.f * gl->ex_size;
1815 					pd->tolive = 500 + Random::get(0, 1000 * gl->ex_speed);
1816 					if((gl->extras & EXTRAS_SPAWNFIRE) && (gl->extras & EXTRAS_SPAWNSMOKE)) {
1817 						pd->special = FIRE_TO_SMOKE;
1818 					}
1819 					pd->tc = (gl->extras & EXTRAS_SPAWNFIRE) ? fire2 : smokeparticle;
1820 					pd->special |= ROTATING | MODULATE_ROTATION;
1821 					pd->fparam = 0.1f - rnd() * 0.2f * gl->ex_speed;
1822 					pd->scale = Vec3f::repeat(-8.f);
1823 					pd->rgb = (gl->extras & EXTRAS_COLORLEGACY) ? gl->rgb : Color3f::white;
1824 				}
1825 			}
1826 
1827 			if(!(gl->extras & EXTRAS_SPAWNFIRE) || rnd() <= 0.95f) {
1828 				continue;
1829 			}
1830 
1831 			if(rnd() < gl->ex_frequency) {
1832 				PARTICLE_DEF * pd = createParticle();
1833 				if(pd) {
1834 					float t = rnd() * (PI * 2.f) - PI;
1835 					Vec3f s = Vec3f(EEsin(t), EEsin(t), EEcos(t)) * randomVec();
1836 					pd->ov = gl->pos + s * gl->ex_radius;
1837 					Vec3f vect = (pd->ov - gl->pos).getNormalized();
1838 					float d = (gl->extras & EXTRAS_FIREPLACE) ? 6.f : 4.f;
1839 					pd->move = Vec3f(vect.x * d, -10.f - 8.f * rnd(), vect.z * d) * gl->ex_speed;
1840 					pd->siz = 4.f * gl->ex_size * 0.3f;
1841 					pd->tolive = 1200 + Random::get(0, 500 * gl->ex_speed);
1842 					pd->tc = fire2;
1843 					pd->special |= ROTATING | MODULATE_ROTATION | GRAVITY;
1844 					pd->fparam = 0.1f - rnd() * 0.2f * gl->ex_speed;
1845 					pd->scale = Vec3f::repeat(-3.f);
1846 					pd->rgb = (gl->extras & EXTRAS_COLORLEGACY) ? gl->rgb : Color3f::white;
1847 				}
1848 			}
1849 
1850 		}
1851 	}
1852 }
1853 
ARX_MAGICAL_FLARES_FirstInit()1854 void ARX_MAGICAL_FLARES_FirstInit() {
1855 	flarenum = 0;
1856 	for(long i = 0; i < MAX_FLARES; i++) {
1857 		flare[i].exist = 0;
1858 	}
1859 }
1860 
ARX_MAGICAL_FLARES_KillAll()1861 void ARX_MAGICAL_FLARES_KillAll()
1862 {
1863 	for (long i=0;i<MAX_FLARES;i++)
1864 	{
1865 		if (flare[i].exist)
1866 		{
1867 			if (flare[i].io)
1868 			{
1869 				flare[i].io->flarecount--;
1870 			}
1871 
1872 			flare[i].exist=0;
1873 			flare[i].tolive=0;
1874 			flarenum--;
1875 
1876 			if (ValidDynLight(flare[i].dynlight!=-1))
1877 				DynLight[flare[i].dynlight].exist=0;
1878 
1879 			flare[i].dynlight=-1;
1880 		}
1881 	}
1882 
1883 	flarenum=0;
1884 }
1885 
AddFlare(Vec2s * pos,float sm,short typ,Entity * io)1886 void AddFlare(Vec2s * pos, float sm, short typ, Entity * io) {
1887 
1888 	long i;
1889 	for(i = 0; i < MAX_FLARES; i++) {
1890 		if(!flare[i].exist) {
1891 			break;
1892 		}
1893 	}
1894 	if(i >= MAX_FLARES) {
1895 		return;
1896 	}
1897 
1898 	FLARES * fl = &flare[i];
1899 	fl->exist = 1;
1900 	flarenum++;
1901 
1902 	fl->bDrawBitmap = 0;
1903 	fl->io = io;
1904 	if(io) {
1905 		fl->flags = 1;
1906 		io->flarecount++;
1907 	} else {
1908 		fl->flags = 0;
1909 	}
1910 
1911 	fl->x = pos->x - rnd() * 4.f;
1912 	fl->y = pos->y - rnd() * 4.f - 50.f;
1913 	fl->tv.rhw = fl->v.rhw = 1.f;
1914 	fl->tv.specular = fl->v.specular = 1;
1915 
1916 	EERIE_CAMERA ka = *Kam;
1917 	ka.angle = Anglef(360.f, 360.f, 360.f) - ka.angle;
1918 	EERIE_CAMERA * oldcam = ACTIVECAM;
1919 	SetActiveCamera(&ka);
1920 	PrepareCamera(&ka);
1921 	fl->v.p += ka.pos;
1922 	EE_RTT(&fl->tv, &fl->v);
1923 	fl->v.p += ka.pos;
1924 
1925 	float vx = -(fl->x - subj.center.x) * 0.2173913f;
1926 	float vy = (fl->y - subj.center.y) * 0.1515151515151515f;
1927 	if(io) {
1928 		fl->v.p.x = io->pos.x - EEsin(radians(MAKEANGLE(io->angle.b + vx))) * 100.f;
1929 		fl->v.p.y = io->pos.y + EEsin(radians(MAKEANGLE(io->angle.a + vy))) * 100.f - 150.f;
1930 		fl->v.p.z = io->pos.z + EEcos(radians(MAKEANGLE(io->angle.b + vx))) * 100.f;
1931 	} else {
1932 		fl->v.p.x = float(pos->x - (DANAESIZX / 2)) * 150.f / float(DANAESIZX);
1933 		fl->v.p.y = float(pos->y - (DANAESIZY / 2)) * 150.f / float(DANAESIZX);
1934 		fl->v.p.z = 75.f;
1935 		ka = *oldcam;
1936 		SetActiveCamera(&ka);
1937 		PrepareCamera(&ka);
1938 		float temp = (fl->v.p.y * -ka.Xsin) + (fl->v.p.z * ka.Xcos);
1939 		fl->v.p.y = (fl->v.p.y * ka.Xcos) - (-fl->v.p.z * ka.Xsin);
1940 		fl->v.p.z = (temp * ka.Ycos) - (-fl->v.p.x * ka.Ysin);
1941 		fl->v.p.x = (temp * -ka.Ysin) + (fl->v.p.x * ka.Ycos);
1942 		fl->v.p += oldcam->pos;
1943 	}
1944 	fl->tv.p = fl->v.p;
1945 	SetActiveCamera(oldcam);
1946 
1947 	switch(PIPOrgb) {
1948 		case 0: {
1949 			fl->rgb = Color3f(rnd() * (2.f/3) + .4f, rnd() * (2.f/3), rnd() * (2.f/3) + .4f);
1950 			break;
1951 		}
1952 		case 1: {
1953 			fl->rgb = Color3f(rnd() * .625f + .5f, rnd() * .625f + .5f, rnd() * .55f);
1954 			break;
1955 		}
1956 		case 2: {
1957 			fl->rgb = Color3f(rnd() * (2.f/3) + .4f, rnd() * .55f, rnd() * .55f);
1958 			break;
1959 		}
1960 	}
1961 
1962 	if(typ == -1) {
1963 		float zz = (EERIEMouseButton & 1) ? 0.29f : ((sm > 0.5f) ? rnd() : 1.f);
1964 		if(zz < 0.2f) {
1965 			fl->type = 2;
1966 			fl->size = rnd() * 42.f + 42.f;
1967 			fl->tolive = (800.f + rnd() * 800.f) * FLARE_MUL;
1968 		} else if(zz < 0.5f) {
1969 			fl->type = 3;
1970 			fl->size = rnd() * 52.f + 16.f;
1971 			fl->tolive = (800.f + rnd() * 800.f) * FLARE_MUL;
1972 		} else {
1973 			fl->type = 1;
1974 			fl->size = (rnd() * 24.f + 32.f) * sm;
1975 			fl->tolive = (1700.f + rnd() * 500.f) * FLARE_MUL;
1976 		}
1977 	} else {
1978 		fl->type = (rnd() > 0.8f) ? 1 : 4;
1979 		fl->size = (rnd() * 38.f + 64.f) * sm;
1980 		fl->tolive = (1700.f + rnd() * 500.f) * FLARE_MUL;
1981 	}
1982 
1983 	fl->dynlight = -1;
1984 	fl->move = OPIPOrgb;
1985 
1986 	for(long kk = 0; kk < 3; kk++) {
1987 
1988 		if(rnd() < 0.5f) {
1989 			continue;
1990 		}
1991 
1992 		PARTICLE_DEF * pd = createParticle();
1993 		if(!pd) {
1994 			break;
1995 		}
1996 
1997 		pd->special = FADE_IN_AND_OUT | ROTATING | MODULATE_ROTATION | DISSIPATING;
1998 		if(!io) {
1999 			pd->special |= PARTICLE_NOZBUFFER;
2000 		}
2001 		pd->ov = fl->v.p + randomVec(-5.f, 5.f);
2002 		pd->move = Vec3f(0.f, 5.f, 0.f);
2003 		pd->scale = Vec3f::repeat(-2.f);
2004 		pd->tolive = 1300 + kk * 100 + Random::get(0, 800);
2005 		pd->tc = fire2;
2006 		if(kk == 1) {
2007 			pd->move.y = 4.f;
2008 			pd->siz = 1.5f;
2009 		} else {
2010 			pd->siz = 1.f + rnd();
2011 		}
2012 		pd->rgb = Color3f(fl->rgb.r * (2.f/3), fl->rgb.g * (2.f/3), fl->rgb.b * (2.f/3));
2013 		pd->fparam = 1.2f;
2014 	}
2015 }
2016 
AddFlare2(Vec2s * pos,float sm,short typ,Entity * io)2017 void AddFlare2(Vec2s * pos, float sm, short typ, Entity * io) {
2018 
2019 	long i;
2020 	for(i = 0; i < MAX_FLARES; i++) {
2021 		if(!flare[i].exist) {
2022 			break;
2023 		}
2024 	}
2025 	if(i >= MAX_FLARES) {
2026 		return;
2027 	}
2028 
2029 	FLARES * fl = &flare[i];
2030 	fl->exist = 1;
2031 	flarenum++;
2032 
2033 	fl->bDrawBitmap = 1;
2034 	fl->io = io;
2035 	if(io) {
2036 		fl->flags = 1;
2037 		io->flarecount++;
2038 	} else {
2039 		fl->flags = 0;
2040 	}
2041 
2042 	fl->x = float(pos->x) - rnd() * 4.f;
2043 	fl->y = float(pos->y) - rnd() * 4.f - 50.f;
2044 	fl->tv.rhw = fl->v.rhw = 1.f;
2045 	fl->tv.specular = fl->v.specular = 1;
2046 	fl->tv.p = Vec3f(fl->x, fl->y, 0.001f);
2047 	switch(PIPOrgb)  {
2048 		case 0: {
2049 			fl->rgb = Color3f(rnd() * (2.f/3) + .4f, rnd() * (2.f/3), rnd() * (2.f/3) + .4f);
2050 			break;
2051 		}
2052 		case 1: {
2053 			fl->rgb = Color3f(rnd() * .625f + .5f, rnd() * .625f + .5f, rnd() * .55f);
2054 			break;
2055 		}
2056 		case 2: {
2057 			fl->rgb = Color3f(rnd() * (2.f/3) + .4f, rnd() * .55f, rnd() * .55f);
2058 			break;
2059 		}
2060 	}
2061 
2062 	if(typ == -1) {
2063 		float zz = (EERIEMouseButton & 1) ? 0.29f : ((sm > 0.5f) ? rnd() : 1.f);
2064 		if(zz < 0.2f) {
2065 			fl->type = 2;
2066 			fl->size = rnd() * 42.f + 42.f;
2067 			fl->tolive = (800.f + rnd() * 800.f) * FLARE_MUL;
2068 		} else if(zz < 0.5f) {
2069 			fl->type = 3;
2070 			fl->size = rnd() * 52.f + 16.f;
2071 			fl->tolive = (800.f + rnd() * 800.f) * FLARE_MUL;
2072 		} else {
2073 			fl->type = 1;
2074 			fl->size = (rnd() * 24.f + 32.f) * sm;
2075 			fl->tolive = (1700.f + rnd() * 500.f) * FLARE_MUL;
2076 		}
2077 	} else {
2078 		fl->type = (rnd() > 0.8f) ? 1 : 4;
2079 		fl->size = (rnd() * 38.f + 64.f) * sm;
2080 		fl->tolive = (1700.f + rnd() * 500.f) * FLARE_MUL;
2081 	}
2082 
2083 	fl->dynlight = -1;
2084 	fl->move = OPIPOrgb;
2085 
2086 	for(long kk = 0; kk < 3; kk++) {
2087 
2088 		if(rnd() < 0.5f) {
2089 			continue;
2090 		}
2091 
2092 		PARTICLE_DEF * pd = createParticle();
2093 		if(!pd) {
2094 			break;
2095 		}
2096 
2097 		pd->special = FADE_IN_AND_OUT;
2098 		pd->ov = fl->v.p + randomVec(-5.f, 5.f);
2099 		pd->move = Vec3f(0.f, 5.f, 0.f);
2100 		pd->scale = Vec3f::repeat(-2.f);
2101 		pd->tolive = 1300 + kk * 100 + Random::get(0, 800);
2102 		pd->tc = fire2;
2103 		if(kk == 1) {
2104 			pd->move.y = 4.f;
2105 			pd->siz = 1.5f;
2106 		} else {
2107 			pd->siz = 1.f + rnd();
2108 		}
2109 		pd->rgb = Color3f(fl->rgb.r * (2.f/3), fl->rgb.g * (2.f/3), fl->rgb.b * (2.f/3));
2110 		pd->fparam = 1.2f;
2111 		pd->type = PARTICLE_2D;
2112 	}
2113 }
2114 
2115 //-----------------------------------------------------------------------------
AddLFlare(float x,float y,Entity * io)2116 void AddLFlare(float x, float y,Entity * io)
2117 {
2118 	Vec2s pos;
2119 	pos.x=(short)x;
2120 	pos.y=(short)y;
2121 	AddFlare(&pos,0.45f,1,io);
2122 }
2123 
2124 //-----------------------------------------------------------------------------
FlareLine(Vec2s * pos0,Vec2s * pos1,Entity * io)2125 void FlareLine(Vec2s * pos0, Vec2s * pos1, Entity * io)
2126 {
2127 	float dx,dy,adx,ady,m;
2128 	long i;
2129 	long z;
2130 	float x0=pos0->x;
2131 	float x1=pos1->x;
2132 	float y0=pos0->y;
2133 	float y1=pos1->y;
2134 	dx=(x1-x0);
2135 	adx=EEfabs(dx);
2136 	dy=(y1-y0);
2137 	ady=EEfabs(dy);
2138 
2139 	if (adx>ady)
2140 	{
2141 		if (x0>x1)
2142 		{
2143 			z = x1;
2144 			x1 = x0;
2145 			x0 = z;
2146 			z = y1;
2147 			y0 = z;
2148 		}
2149 
2150 		if (x0<x1)
2151 		{
2152 			m=dy/dx;
2153 
2154 			i = x0;
2155 
2156 			while(i<x1)
2157 			{
2158 				z = rnd()*FLARELINERND;
2159 				z+=FLARELINESTEP;
2160 				i+=z;
2161 				y0+=m*z;
2162 				AddLFlare((float)i,y0,io);
2163 			}
2164 		}
2165 		else
2166 		{
2167 			m = dy / dx;
2168 			i = x1;
2169 
2170 			while(i<x0)
2171 			{
2172 				z = rnd()*FLARELINERND;
2173 				z+=FLARELINESTEP;
2174 				i+=z;
2175 				y0+=m*z;
2176 				AddLFlare((float)i,y0,io);
2177 			}
2178 		}
2179 	}
2180 	else
2181 	{
2182 		if (y0>y1)
2183 		{
2184 			z = x1;
2185 			x0=z;
2186 			z = y1;
2187 			y1=y0;
2188 			y0=z;
2189 		}
2190 
2191 		if (y0<y1)
2192 		{
2193 			m = dx/dy;
2194 			i = y0;
2195 
2196 			while(i<y1)
2197 			{
2198 				z = rnd()*FLARELINERND;
2199 			    z+=FLARELINESTEP;
2200 				i+=z;
2201 				x0+=m*z;
2202 				AddLFlare(x0,(float)i,io);
2203 			}
2204 		}
2205 		else
2206 		{
2207 			m=dx/dy;
2208 			i = y1;
2209 
2210 			while(i<y0)
2211 			{
2212 				z = rnd()*FLARELINERND;
2213 				z+=FLARELINESTEP;
2214 				i+=z;
2215 				x0+=m*z;
2216 				AddLFlare(x0,(float)i,io);
2217 			}
2218 		}
2219 	}
2220 }
2221