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