1 /*
2  * This file is part of the Colobot: Gold Edition source code
3  * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
4  * http://epsitec.ch; http://colobot.info; http://github.com/colobot
5  *
6  * This program 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  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see http://gnu.org/licenses
18  */
19 
20 
21 #include "graphics/engine/pyro.h"
22 
23 #include "app/app.h"
24 
25 #include "common/logger.h"
26 
27 #include "graphics/engine/lightman.h"
28 #include "graphics/engine/particle.h"
29 #include "graphics/engine/terrain.h"
30 
31 #include "level/robotmain.h"
32 
33 #include "math/geometry.h"
34 
35 #include "object/object_manager.h"
36 #include "object/old_object.h"
37 
38 #include "object/motion/motionhuman.h"
39 
40 #include "object/subclass/shielder.h"
41 
42 #include "sound/sound.h"
43 
44 
45 // Graphics module namespace
46 namespace Gfx
47 {
48 
49 
CPyro()50 CPyro::CPyro()
51 {
52     m_engine      = CEngine::GetInstancePointer();
53     m_main        = CRobotMain::GetInstancePointer();
54     m_terrain     = m_main->GetTerrain();
55     m_camera      = m_main->GetCamera();
56     m_particle    = m_engine->GetParticle();
57     m_lightMan    = m_engine->GetLightManager();
58     m_sound       = CApplication::GetInstancePointer()->GetSound();
59 }
60 
~CPyro()61 CPyro::~CPyro()
62 {
63 }
64 
DeleteObject()65 void CPyro::DeleteObject()
66 {
67     if ( m_lightRank != -1 )
68     {
69         m_lightMan->DeleteLight(m_lightRank);
70         m_lightRank = -1;
71     }
72 }
73 
Create(PyroType type,CObject * obj,float force)74 bool CPyro::Create(PyroType type, CObject* obj, float force)
75 {
76     m_object = obj;
77     m_force = force;
78 
79     ObjectType oType = obj->GetType();
80     int objRank = obj->GetObjectRank(0);
81     if (objRank == -1) return false;
82 
83     Math::Vector min, max;
84     m_engine->GetObjectBBox(objRank, min, max);
85     Math::Vector pos = obj->GetPosition();
86 
87     DisplayError(type, obj);  // displays eventual messages
88 
89     for (const auto& crashSphere : obj->GetAllCrashSpheres())
90     {
91         m_crashSpheres.push_back(crashSphere.sphere);
92     }
93 
94     // Calculates the size of the effect.
95     if ( oType == OBJECT_ANT    ||
96          oType == OBJECT_BEE    ||
97          oType == OBJECT_WORM   ||
98          oType == OBJECT_SPIDER )
99     {
100         m_size = 40.0f;
101     }
102     else
103     {
104         m_size = Math::Distance(min, max)*2.0f;
105         if ( m_size <  4.0f )  m_size =  4.0f;
106         if ( m_size > 80.0f )  m_size = 80.0f;
107     }
108     if ( oType == OBJECT_TNT  ||
109          oType == OBJECT_BOMB )
110     {
111         m_size *= 2.0f;
112     }
113 
114     m_pos = pos+(min+max)/2.0f;
115     m_type = type;
116     m_progress = 0.0f;
117     m_speed = 1.0f/20.0f;    m_time = 0.0f;
118     m_lastParticle = 0.0f;
119     m_lastParticleSmoke = 0.0f;
120     m_lightRank = -1;
121 
122     if ( oType == OBJECT_TEEN28 ||
123          oType == OBJECT_TEEN31 )
124     {
125         m_pos.y = pos.y+1.0f;
126     }
127 
128     // Seeking the position of the battery.
129 
130     CObject* power = nullptr;
131     if (obj->Implements(ObjectInterfaceType::Powered))
132         power = dynamic_cast<CPoweredObject&>(*obj).GetPower();
133 
134     if (power == nullptr)
135     {
136         m_power = false;
137     }
138     else
139     {
140         m_power = true;
141         pos = power->GetPosition();
142         pos.y += 1.0f;
143         Math::Matrix* mat = obj->GetWorldMatrix(0);
144         m_posPower = Math::Transform(*mat, pos);
145     }
146 
147     if ( oType == OBJECT_POWER   ||
148          oType == OBJECT_ATOMIC  ||
149          oType == OBJECT_URANIUM ||
150          oType == OBJECT_TNT     ||
151          oType == OBJECT_BOMB    )
152     {
153         m_power = true;
154         m_posPower = m_pos;
155         m_posPower.y += 1.0f;
156         m_pos = m_posPower;
157     }
158     if ( oType == OBJECT_STATION )
159     {
160         m_power = true;
161         Math::Matrix* mat = obj->GetWorldMatrix(0);
162         m_posPower = Math::Transform(*mat, Math::Vector(-15.0f, 7.0f, 0.0f));
163         m_pos = m_posPower;
164     }
165     if ( oType == OBJECT_ENERGY )
166     {
167         m_power = true;
168         Math::Matrix* mat = obj->GetWorldMatrix(0);
169         m_posPower = Math::Transform(*mat, Math::Vector(-7.0f, 6.0f, 0.0f));
170         m_pos = m_posPower;
171     }
172     if ( oType == OBJECT_NUCLEAR )
173     {
174         m_power = true;
175         m_posPower = m_pos;
176     }
177     if ( oType == OBJECT_PARA )
178     {
179         m_power = true;
180         m_posPower = m_pos;
181     }
182 
183     // Plays the sound of a pyrotechnic effect.
184     if ( type == PT_FRAGT  ||
185          type == PT_FRAGW  ||
186          type == PT_EXPLOT ||
187          type == PT_EXPLOW )
188     {
189         SoundType sound;
190         if ( m_power )
191         {
192             sound = SOUND_EXPLOp;
193         }
194         else
195         {
196             sound = SOUND_EXPLO;
197         }
198         if ( oType == OBJECT_STONE   ||
199              oType == OBJECT_METAL   ||
200              oType == OBJECT_BULLET  ||
201              oType == OBJECT_BBOX    ||
202              oType == OBJECT_KEYa    ||
203              oType == OBJECT_KEYb    ||
204              oType == OBJECT_KEYc    ||
205              oType == OBJECT_KEYd    )
206         {
207             sound = SOUND_EXPLOl;
208         }
209         if ( oType == OBJECT_URANIUM ||
210              oType == OBJECT_POWER   ||
211              oType == OBJECT_ATOMIC  ||
212              oType == OBJECT_TNT     ||
213              oType == OBJECT_BOMB    )
214         {
215             sound = SOUND_EXPLOlp;
216         }
217         m_sound->Play(sound, m_pos);
218     }
219     if ( type == PT_FRAGO  ||
220          type == PT_EXPLOO ||
221          type == PT_SPIDER ||
222          type == PT_SHOTM  )
223     {
224         m_sound->Play(SOUND_EXPLOi, m_pos);
225     }
226     if ( type == PT_FRAGV )
227     {
228         m_sound->Play(SOUND_BOUMv, m_pos);
229     }
230     if ( type == PT_BURNT ||
231          type == PT_BURNO )
232     {
233         m_soundChannel = m_sound->Play(SOUND_BURN, m_pos, 1.0f, 1.0f, true);
234         m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, 12.0f, SOPER_CONTINUE);
235         m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f,  5.0f, SOPER_STOP);
236     }
237     if ( type == PT_BURNO )
238     {
239         m_sound->Play(SOUND_DEADi, m_pos);
240         m_sound->Play(SOUND_DEADi, m_engine->GetEyePt());
241     }
242     if ( type == PT_EGG )
243     {
244         m_sound->Play(SOUND_EGG, m_pos);
245     }
246     if ( type == PT_WPCHECK  ||
247          type == PT_FLCREATE ||
248          type == PT_FLDELETE )
249     {
250         m_sound->Play(SOUND_WAYPOINT, m_pos);
251     }
252     if ( oType == OBJECT_HUMAN )
253     {
254         if ( type == PT_DEADG )
255         {
256             m_sound->Play(SOUND_DEADg, m_pos);
257         }
258         if ( type == PT_DEADW )
259         {
260             m_sound->Play(SOUND_DEADw, m_pos);
261         }
262         assert(m_object->Implements(ObjectInterfaceType::Controllable));
263         if ( type == PT_SHOTH && dynamic_cast<CControllableObject&>(*m_object).GetSelect() )
264         {
265             m_sound->Play(SOUND_AIE, m_pos);
266             m_sound->Play(SOUND_AIE, m_engine->GetEyePt());
267         }
268     }
269 
270     if ( m_type == PT_FRAGT ||
271          m_type == PT_FRAGO ||
272          m_type == PT_FRAGW ||
273          m_type == PT_FRAGV )
274     {
275         m_engine->DeleteShadowSpot(m_object->GetObjectRank(0));
276     }
277 
278     if ( m_type == PT_DEADG )
279     {
280         assert(m_object->Implements(ObjectInterfaceType::Destroyable));
281         dynamic_cast<CDestroyableObject&>(*m_object).SetDying(DeathType::Dead);
282 
283         assert(obj->Implements(ObjectInterfaceType::Movable));
284         dynamic_cast<CMovableObject&>(*obj).GetMotion()->SetAction(MHS_DEADg, 1.0f);
285 
286         m_camera->StartCentering(m_object, Math::PI*0.5f, 99.9f, 0.0f, 1.5f);
287         m_camera->StartOver(CAM_OVER_EFFECT_FADEOUT_WHITE, m_pos, 1.0f);
288         m_speed = 1.0f/10.0f;
289         return true;
290     }
291     if ( m_type == PT_DEADW )
292     {
293         assert(m_object->Implements(ObjectInterfaceType::Destroyable));
294         dynamic_cast<CDestroyableObject&>(*m_object).SetDying(DeathType::Dead);
295 
296         assert(obj->Implements(ObjectInterfaceType::Movable));
297         dynamic_cast<CMovableObject&>(*obj).GetMotion()->SetAction(MHS_DEADw, 1.0f);
298 
299         m_camera->StartCentering(m_object, Math::PI*0.5f, 99.9f, 0.0f, 3.0f);
300         m_camera->StartOver(CAM_OVER_EFFECT_FADEOUT_BLACK, m_pos, 1.0f);
301         m_speed = 1.0f/10.0f;
302         return true;
303     }
304 
305     if ( m_type == PT_SHOTT ||
306          m_type == PT_SHOTM )
307     {
308         m_camera->StartEffect(CAM_EFFECT_SHOT, m_pos, force);
309         m_speed = 1.0f/1.0f;
310         return true;
311     }
312     if ( m_type == PT_SHOTH )
313     {
314         assert(m_object->Implements(ObjectInterfaceType::Controllable));
315         if ( m_camera->GetBlood() && dynamic_cast<CControllableObject&>(*m_object).GetSelect() )
316         {
317             m_camera->StartOver(CAM_OVER_EFFECT_BLOOD, m_pos, force);
318         }
319         m_speed = 1.0f/0.2f;
320         return true;
321     }
322 
323     if ( m_type == PT_SHOTW )
324     {
325         m_speed = 1.0f/1.0f;
326     }
327 
328     if ( m_type == PT_BURNT )
329     {
330         BurnStart();
331     }
332 
333     if ( m_type == PT_WPCHECK )
334     {
335         m_speed = 1.0f/8.0f;
336         m_object->SetLock(true);  // object more functional
337     }
338     if ( m_type == PT_FLCREATE )
339     {
340         m_speed = 1.0f/2.0f;
341     }
342     if ( m_type == PT_FLDELETE )
343     {
344         m_speed = 1.0f/2.0f;
345         m_object->SetLock(true);  // object more functional
346     }
347     if ( m_type == PT_RESET )
348     {
349         m_speed = 1.0f/2.0f;
350         m_resetAngle = m_object->GetRotationY();
351     }
352     if ( m_type == PT_FINDING )
353     {
354         float limit = (m_size-1.0f)/4.0f;
355         if (limit > 8.0f) limit = 8.0f;
356         if (oType == OBJECT_APOLLO2) limit = 2.0f;
357         m_speed = 1.0f/limit;
358     }
359     if ( m_type == PT_SQUASH )
360     {
361         m_speed = 1.0f/2.0f;
362         m_object->SetLock(true);
363     }
364 
365 
366     if ( m_type == PT_EXPLOT ||
367          m_type == PT_EXPLOO ||
368          m_type == PT_EXPLOW )
369     {
370         CreateTriangle(obj, oType, 0);
371         m_engine->DeleteShadowSpot(m_object->GetObjectRank(0));
372         ExploStart();
373     }
374 
375     if ( m_type == PT_FALL )
376     {
377         FallStart();
378         return true;
379     }
380 
381     if ( m_type == PT_BURNT ||
382          m_type == PT_BURNO )
383     {
384         m_speed = 1.0f/15.0f;
385 
386         LightOperAdd(0.00f, 0.0f,  2.0f,  1.0f,  0.0f);  // red-orange
387         LightOperAdd(0.30f, 1.0f, -0.8f, -0.8f, -0.8f);  // dark gray
388         LightOperAdd(0.80f, 1.0f, -0.8f, -0.8f, -0.8f);  // dark gray
389         LightOperAdd(1.00f, 0.0f, -0.8f, -0.8f, -0.8f);  // dark gray
390         CreateLight(m_pos, 40.0f);
391         return true;
392     }
393 
394     if ( m_type == PT_SPIDER )
395     {
396         m_speed = 1.0f/15.0f;
397 
398         pos = Math::Vector(-3.0f, 2.0f, 0.0f);
399         Math::Matrix* mat = obj->GetWorldMatrix(0);
400         m_pos = Math::Transform(*mat, pos);
401 
402         m_engine->DeleteShadowSpot(m_object->GetObjectRank(0));
403     }
404 
405     if ( m_type != PT_FRAGV &&
406          m_type != PT_EGG   &&
407          m_type != PT_WIN   &&
408          m_type != PT_LOST  &&
409          m_type != PT_SQUASH)
410     {
411         float h = 40.0f;
412         if ( m_type == PT_FRAGO  ||
413              m_type == PT_EXPLOO )
414         {
415             LightOperAdd(0.00f, 0.0f, -1.0f, -0.5f, -1.0f);  // dark green
416             LightOperAdd(0.05f, 1.0f, -1.0f, -0.5f, -1.0f);  // dark green
417             LightOperAdd(1.00f, 0.0f, -1.0f, -0.5f, -1.0f);  // dark green
418         }
419         else if ( m_type == PT_FRAGT  ||
420                   m_type == PT_EXPLOT )
421         {
422             LightOperAdd(0.00f, 1.0f,  4.0f,  4.0f,  2.0f);  // yellow
423             LightOperAdd(0.02f, 1.0f,  4.0f,  2.0f,  0.0f);  // red-orange
424             LightOperAdd(0.16f, 1.0f, -0.8f, -0.8f, -0.8f);  // dark gray
425             LightOperAdd(1.00f, 0.0f, -0.8f, -0.8f, -0.8f);  // dark gray
426             h = m_size*2.0f;
427         }
428         else if ( m_type == PT_SPIDER )
429         {
430             LightOperAdd(0.00f, 0.0f, -0.5f, -1.0f, -1.0f);  // dark red
431             LightOperAdd(0.05f, 1.0f, -0.5f, -1.0f, -1.0f);  // dark red
432             LightOperAdd(1.00f, 0.0f, -0.5f, -1.0f, -1.0f);  // dark red
433         }
434         else if ( m_type == PT_FRAGW  ||
435                   m_type == PT_EXPLOW ||
436                   m_type == PT_SHOTW  )
437         {
438             LightOperAdd(0.00f, 0.0f, -0.5f, -0.5f, -1.0f);  // dark yellow
439             LightOperAdd(0.05f, 1.0f, -0.5f, -0.5f, -1.0f);  // dark yellow
440             LightOperAdd(1.00f, 0.0f, -0.5f, -0.5f, -1.0f);  // dark yellow
441         }
442         else if ( m_type == PT_WPCHECK  ||
443                   m_type == PT_FLCREATE ||
444                   m_type == PT_FLDELETE ||
445                   m_type == PT_RESET    ||
446                   m_type == PT_FINDING  )
447         {
448             LightOperAdd(0.00f, 1.0f,  4.0f,  4.0f,  2.0f);  // yellow
449             LightOperAdd(1.00f, 0.0f,  4.0f,  4.0f,  2.0f);  // yellow
450         }
451         else
452         {
453             LightOperAdd(0.00f, 0.0f, -0.8f, -0.8f, -0.8f);  // dark gray
454             LightOperAdd(0.05f, 1.0f, -0.8f, -0.8f, -0.8f);  // dark gray
455             LightOperAdd(1.00f, 0.0f, -0.8f, -0.8f, -0.8f);  // dark gray
456         }
457         CreateLight(m_pos, h);
458 
459         if ( m_type != PT_SHOTW    &&
460              m_type != PT_WPCHECK  &&
461              m_type != PT_FLCREATE &&
462              m_type != PT_FLDELETE &&
463              m_type != PT_RESET    &&
464              m_type != PT_FINDING  &&
465              m_type != PT_SQUASH   )
466         {
467             m_camera->StartEffect(CAM_EFFECT_EXPLO, m_pos, force);
468         }
469     }
470 
471     if ( m_type == PT_SHOTW )  return true;
472 
473     // Generates the triangles of the explosion.
474     if ( m_type == PT_FRAGT  ||
475          m_type == PT_FRAGO  ||
476          m_type == PT_FRAGW  ||
477          m_type == PT_FRAGV  ||
478          m_type == PT_SPIDER ||
479          m_type == PT_EGG    ||
480         (m_type == PT_EXPLOT && oType == OBJECT_MOBILEtg) ||
481         (m_type == PT_EXPLOT && oType == OBJECT_TEEN28  ) ||
482         (m_type == PT_EXPLOT && oType == OBJECT_TEEN31  ) )
483     {
484         for (int part = 0; part < OBJECTMAXPART; part++)
485         {
486             CreateTriangle(obj, oType, part);
487         }
488     }
489 
490     if ( m_type == PT_FRAGT  ||
491          m_type == PT_EXPLOT )
492     {
493         if ( m_power )
494         {
495             int total = static_cast<int>(10.0f*m_engine->GetParticleDensity());
496             if ( oType == OBJECT_TNT  ||
497                  oType == OBJECT_BOMB )  total *= 3;
498             for (int i = 0; i < total; i++)
499             {
500                 pos = m_posPower;
501                 Math::Vector speed;
502                 speed.x = (Math::Rand()-0.5f)*30.0f;
503                 speed.z = (Math::Rand()-0.5f)*30.0f;
504                 speed.y = Math::Rand()*30.0f;
505                 Math::Point dim;
506                 dim.x = 1.0f;
507                 dim.y = dim.x;
508                 float duration = Math::Rand()*3.0f+2.0f;
509                 float mass = Math::Rand()*10.0f+15.0f;
510                 m_particle->CreateTrack(pos, speed, dim, PARTITRACK1,
511                                          duration, mass, Math::Rand()+0.7f, 1.0f);
512             }
513         }
514 
515         if (m_size > 10.0f)  // large enough (freight excluded)?
516         {
517             if (m_power)
518             {
519                 pos = m_posPower;
520             }
521             else
522             {
523                 pos = m_pos;
524                 m_terrain->AdjustToFloor(pos);
525                 pos.y += 1.0f;
526             }
527             Math::Point dim;
528             dim.x = m_size*0.4f;
529             dim.y = dim.x;
530             m_particle->CreateParticle(pos, Math::Vector(0.0f,0.0f,0.0f), dim, PARTISPHERE0, 2.0f, 0.0f, 0.0f);
531         }
532     }
533 
534     if ( m_type == PT_FRAGO  ||
535          m_type == PT_EXPLOO )
536     {
537         int total = static_cast<int>(10.0f*m_engine->GetParticleDensity());
538         for (int i = 0; i < total; i++)
539         {
540             pos = m_pos;
541             Math::Vector speed;
542             speed.x = (Math::Rand()-0.5f)*30.0f;
543             speed.z = (Math::Rand()-0.5f)*30.0f;
544             speed.y = Math::Rand()*50.0f;
545             Math::Point dim;
546             dim.x = 1.0f;
547             dim.y = dim.x;
548             float duration = Math::Rand()*1.0f+0.8f;
549             float mass = Math::Rand()*10.0f+15.0f;
550             m_particle->CreateParticle(pos, speed, dim, PARTIORGANIC1,
551                                          duration, mass);
552         }
553         total = static_cast<int>(5.0f*m_engine->GetParticleDensity());
554         for (int i = 0; i < total; i++)
555         {
556             pos = m_pos;
557             Math::Vector speed;
558             speed.x = (Math::Rand()-0.5f)*30.0f;
559             speed.z = (Math::Rand()-0.5f)*30.0f;
560             speed.y = Math::Rand()*50.0f;
561             Math::Point dim;
562             dim.x = 1.0f;
563             dim.y = dim.x;
564             float duration = Math::Rand()*2.0f+1.4f;
565             float mass = Math::Rand()*10.0f+15.0f;
566             m_particle->CreateTrack(pos, speed, dim, PARTITRACK4,
567                                      duration, mass, duration*0.5f, dim.x*2.0f);
568         }
569     }
570 
571     if ( m_type == PT_SPIDER )
572     {
573         for (int i = 0; i < 50; i++)
574         {
575             pos = m_pos;
576             pos.x += (Math::Rand()-0.5f)*3.0f;
577             pos.z += (Math::Rand()-0.5f)*3.0f;
578             pos.y += (Math::Rand()-0.5f)*2.0f;
579             Math::Vector speed;
580             speed.x = (Math::Rand()-0.5f)*24.0f;
581             speed.z = (Math::Rand()-0.5f)*24.0f;
582             speed.y = 10.0f+Math::Rand()*10.0f;
583             Math::Point dim;
584             dim.x = 1.0f;
585             dim.y = dim.x;
586             int channel = m_particle->CreateParticle(pos, speed, dim, PARTIGUN3, 2.0f+Math::Rand()*2.0f, 10.0f);
587             m_particle->SetObjectFather(channel, obj);
588         }
589         int total = static_cast<int>(10.0f*m_engine->GetParticleDensity());
590         for (int i = 0; i < total; i++)
591         {
592             pos = m_pos;
593             pos.x += (Math::Rand()-0.5f)*3.0f;
594             pos.z += (Math::Rand()-0.5f)*3.0f;
595             pos.y += (Math::Rand()-0.5f)*2.0f;
596             Math::Vector speed;
597             speed.x = (Math::Rand()-0.5f)*24.0f;
598             speed.z = (Math::Rand()-0.5f)*24.0f;
599             speed.y = 7.0f+Math::Rand()*7.0f;
600             Math::Point dim;
601             dim.x = 1.0f;
602             dim.y = dim.x;
603             m_particle->CreateTrack(pos, speed, dim, PARTITRACK3,
604                                     2.0f+Math::Rand()*2.0f, 10.0f, 2.0f, 0.6f);
605         }
606     }
607 
608     if ( type == PT_FRAGT  ||
609          type == PT_FRAGW  ||
610          type == PT_EXPLOT ||
611          type == PT_EXPLOW )
612     {
613         if (m_size > 10.0f || m_power)
614         {
615             pos = m_pos;
616             Math::Vector speed(0.0f, 0.0f, 0.0f);
617             Math::Point dim;
618             dim.x = m_size;
619             dim.y = dim.x;
620             m_particle->CreateParticle(pos, speed, dim, PARTICHOC, 2.0f);
621         }
622     }
623 
624     return true;
625 }
626 
EventProcess(const Event & event)627 bool CPyro::EventProcess(const Event &event)
628 {
629     if (event.type != EVENT_FRAME) return true;
630     if (m_engine->GetPause()) return true;
631 
632     m_time += event.rTime;
633     m_progress += event.rTime*m_speed;
634 
635     if (m_soundChannel != -1 && m_object != nullptr)
636     {
637         Math::Vector pos = m_object->GetPosition();
638         m_sound->Position(m_soundChannel, pos);
639 
640         if (m_lightRank != -1)
641         {
642             pos.y += m_lightHeight;
643             m_lightMan->SetLightPos(m_lightRank, pos);
644         }
645     }
646 
647     if ( m_type == PT_SHOTT &&
648          m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
649     {
650         m_lastParticle = m_time;
651 
652         if (m_crashSpheres.size() > 0)
653         {
654             int i = rand() % m_crashSpheres.size();
655             Math::Vector pos = m_crashSpheres[i].pos;
656             float radius = m_crashSpheres[i].radius;
657             pos.x += (Math::Rand()-0.5f)*radius*2.0f;
658             pos.z += (Math::Rand()-0.5f)*radius*2.0f;
659             Math::Vector speed;
660             speed.x = (Math::Rand()-0.5f)*radius*0.5f;
661             speed.z = (Math::Rand()-0.5f)*radius*0.5f;
662             speed.y = Math::Rand()*radius*1.0f;
663             Math::Point dim;
664             dim.x = Math::Rand()*radius*0.5f+radius*0.75f*m_force;
665             dim.y = dim.x;
666             m_particle->CreateParticle(pos, speed, dim, PARTISMOKE1, 3.0f);
667         }
668         else
669         {
670             Math::Vector pos = m_pos;
671             pos.x += (Math::Rand()-0.5f)*m_size*0.3f;
672             pos.z += (Math::Rand()-0.5f)*m_size*0.3f;
673             Math::Vector speed;
674             speed.x = (Math::Rand()-0.5f)*m_size*0.1f;
675             speed.z = (Math::Rand()-0.5f)*m_size*0.1f;
676             speed.y = Math::Rand()*m_size*0.2f;
677             Math::Point dim;
678             dim.x = Math::Rand()*m_size/10.0f+m_size/10.0f*m_force;
679             dim.y = dim.x;
680             m_particle->CreateParticle(pos, speed, dim, PARTISMOKE1, 3.0f);
681         }
682     }
683 
684     if ( m_camera->GetBlood() && m_type == PT_SHOTH &&
685          m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
686     {
687         m_lastParticle = m_time;
688 
689         for (int i = 0; i < 10; i++)
690         {
691             Math::Vector pos = m_pos;
692             pos.x += (Math::Rand()-0.5f)*m_size*0.2f;
693             pos.z += (Math::Rand()-0.5f)*m_size*0.2f;
694             pos.y += (Math::Rand()-0.5f)*m_size*0.5f;
695             Math::Vector speed;
696             speed.x = (Math::Rand()-0.5f)*5.0f;
697             speed.z = (Math::Rand()-0.5f)*5.0f;
698             speed.y = Math::Rand()*1.0f;
699             Math::Point dim;
700             dim.x = 1.0f;
701             dim.y = dim.x;
702             m_particle->CreateParticle(pos, speed, dim, PARTIBLOOD, Math::Rand()*3.0f+3.0f, Math::Rand()*10.0f+15.0f, 0.5f);
703         }
704     }
705 
706     if ( m_camera->GetBlood() && m_type == PT_SHOTM &&
707          m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
708     {
709         m_lastParticle = m_time;
710 
711         int r = static_cast<int>(10.0f*m_engine->GetParticleDensity());
712         for (int i = 0; i < r; i++)
713         {
714             Math::Vector pos = m_pos;
715             pos.x += (Math::Rand()-0.5f)*20.0f;
716             pos.z += (Math::Rand()-0.5f)*20.0f;
717             pos.y += 8.0f;
718             Math::Vector speed;
719             speed.x = (Math::Rand()-0.5f)*40.0f;
720             speed.z = (Math::Rand()-0.5f)*40.0f;
721             speed.y = Math::Rand()*40.0f;
722             Math::Point dim;
723             dim.x = Math::Rand()*8.0f+8.0f*m_force;
724             dim.y = dim.x;
725 
726             m_particle->CreateParticle(pos, speed, dim, PARTIBLOODM, 2.0f, 50.0f, 0.0f);
727         }
728     }
729 
730     if ( m_type == PT_SHOTW &&
731          m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
732     {
733         m_lastParticle = m_time;
734 
735         if (m_crashSpheres.size() > 0)
736         {
737             int i = rand() % m_crashSpheres.size();
738             Math::Vector pos = m_crashSpheres[i].pos;
739             float radius = m_crashSpheres[i].radius;
740             pos.x += (Math::Rand()-0.5f)*radius*2.0f;
741             pos.z += (Math::Rand()-0.5f)*radius*2.0f;
742             Math::Vector speed;
743             speed.x = (Math::Rand()-0.5f)*radius*0.5f;
744             speed.z = (Math::Rand()-0.5f)*radius*0.5f;
745             speed.y = Math::Rand()*radius*1.0f;
746             Math::Point dim;
747             dim.x = 1.0f*m_force;
748             dim.y = dim.x;
749             m_particle->CreateParticle(pos, speed, dim, PARTIBLITZ, 0.5f, 0.0f, 0.0f);
750         }
751         else
752         {
753             Math::Vector pos = m_pos;
754             pos.x += (Math::Rand()-0.5f)*m_size*0.3f;
755             pos.z += (Math::Rand()-0.5f)*m_size*0.3f;
756             Math::Vector speed;
757             speed.x = (Math::Rand()-0.5f)*m_size*0.1f;
758             speed.z = (Math::Rand()-0.5f)*m_size*0.1f;
759             speed.y = Math::Rand()*m_size*0.2f;
760             Math::Point dim;
761             dim.x = 1.0f*m_force;
762             dim.y = dim.x;
763             m_particle->CreateParticle(pos, speed, dim, PARTIBLITZ, 0.5f, 0.0f, 0.0f);
764         }
765     }
766 
767     if ( m_type == PT_SHOTW &&
768          m_lastParticleSmoke+m_engine->ParticleAdapt(0.10f) <= m_time )
769     {
770         m_lastParticleSmoke = m_time;
771 
772         Math::Vector pos = m_pos;
773         pos.y -= 2.0f;
774         pos.x += (Math::Rand()-0.5f)*4.0f;
775         pos.z += (Math::Rand()-0.5f)*4.0f;
776         Math::Vector speed;
777         speed.x = 0.0f;
778         speed.z = 0.0f;
779         speed.y = 10.0f+Math::Rand()*10.0f;
780         Math::Point dim;
781         dim.x = Math::Rand()*2.5f+2.0f*m_force;
782         dim.y = dim.x;
783         m_particle->CreateParticle(pos, speed, dim, PARTICRASH, 4.0f);
784     }
785 
786     if ( (m_type == PT_FRAGT || m_type == PT_EXPLOT) &&
787          m_progress < 0.05f &&
788          m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
789     {
790         m_lastParticle = m_time;
791 
792         Math::Vector pos = m_pos;
793         Math::Vector speed;
794         speed.x = (Math::Rand()-0.5f)*m_size*1.0f;
795         speed.z = (Math::Rand()-0.5f)*m_size*1.0f;
796         speed.y = Math::Rand()*m_size*0.50f;
797         Math::Point dim;
798         dim.x = Math::Rand()*m_size/5.0f+m_size/5.0f;
799         dim.y = dim.x;
800 
801         m_particle->CreateParticle(pos, speed, dim, PARTIEXPLOT);
802     }
803 
804     if ( (m_type == PT_FRAGT || m_type == PT_EXPLOT) &&
805          m_progress < 0.10f &&
806          m_lastParticleSmoke+m_engine->ParticleAdapt(0.10f) <= m_time )
807     {
808         m_lastParticleSmoke = m_time;
809 
810         Math::Point dim;
811         dim.x = Math::Rand()*m_size/3.0f+m_size/3.0f;
812         dim.y = dim.x;
813         Math::Vector pos = m_pos;
814         pos.x += (Math::Rand()-0.5f)*m_size*0.5f;
815         pos.z += (Math::Rand()-0.5f)*m_size*0.5f;
816         m_terrain->AdjustToFloor(pos);
817         Math::Vector speed;
818         speed.x = 0.0f;
819         speed.z = 0.0f;
820         speed.y = -dim.x/2.0f/4.0f;
821         pos.y += dim.x/2.0f;
822 
823         ParticleType type;
824         int r = rand()%2;
825         if (r == 0) type = PARTISMOKE1;
826         if (r == 1) type = PARTISMOKE2;
827         m_particle->CreateParticle(pos, speed, dim, type, 6.0f);
828     }
829 
830     if ( (m_type == PT_FRAGO || m_type == PT_EXPLOO) &&
831          m_progress < 0.03f &&
832          m_lastParticle+m_engine->ParticleAdapt(0.1f) <= m_time )
833     {
834         m_lastParticle = m_time;
835 
836         Math::Vector pos = m_pos;
837         Math::Vector speed;
838         speed.x = (Math::Rand()-0.5f)*m_size*2.0f;
839         speed.z = (Math::Rand()-0.5f)*m_size*2.0f;
840         speed.y = Math::Rand()*m_size*1.0f;
841         Math::Point dim;
842         dim.x = Math::Rand()*m_size/2.0f+m_size/2.0f;
843         dim.y = dim.x;
844 
845         m_particle->CreateParticle(pos, speed, dim, PARTIEXPLOO);
846     }
847 
848     if ( (m_type == PT_FRAGW || m_type == PT_EXPLOW) &&
849          m_progress < 0.05f &&
850          m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
851     {
852         m_lastParticle = m_time;
853 
854         Math::Vector pos = m_pos;
855         Math::Vector speed;
856         speed.x = (Math::Rand()-0.5f)*m_size*1.0f;
857         speed.z = (Math::Rand()-0.5f)*m_size*1.0f;
858         speed.y = Math::Rand()*m_size*0.50f;
859         Math::Point dim;
860         dim.x = 1.0f;
861         dim.y = dim.x;
862 
863         m_particle->CreateParticle(pos, speed, dim, PARTIBLITZ, 0.5f, 0.0f, 0.0f);
864     }
865 
866     if ( (m_type == PT_FRAGW || m_type == PT_EXPLOW) &&
867          m_progress < 0.25f &&
868          m_lastParticleSmoke+m_engine->ParticleAdapt(0.05f) <= m_time )
869     {
870         m_lastParticleSmoke = m_time;
871 
872         Math::Vector pos = m_pos;
873         pos.y -= 2.0f;
874         pos.x += (Math::Rand()-0.5f)*4.0f;
875         pos.z += (Math::Rand()-0.5f)*4.0f;
876         Math::Vector speed;
877         speed.x = 0.0f;
878         speed.z = 0.0f;
879         speed.y = 4.0f+Math::Rand()*4.0f;
880         Math::Point dim;
881         dim.x = Math::Rand()*2.5f+2.0f;
882         dim.y = dim.x;
883         m_particle->CreateParticle(pos, speed, dim, PARTICRASH, 4.0f);
884     }
885 
886     if ( m_type == PT_WPCHECK )
887     {
888         float factor;
889         if (m_progress < 0.25f)
890             factor = 0.0f;
891         else
892             factor = powf((m_progress-0.25f)/0.75f, 2.0f)*30.0f;
893 
894         if ( m_progress < 0.85f &&
895              m_lastParticle+m_engine->ParticleAdapt(0.10f) <= m_time )
896         {
897             m_lastParticle = m_time;
898 
899             Math::Vector pos = m_pos;
900             pos.y += factor;
901             pos.x += (Math::Rand()-0.5f)*3.0f;
902             pos.z += (Math::Rand()-0.5f)*3.0f;
903             Math::Vector speed;
904             speed.x = 0.0f;
905             speed.z = 0.0f;
906             speed.y = 5.0f+Math::Rand()*5.0f;
907             Math::Point dim;
908             dim.x = Math::Rand()*1.5f+1.5f;
909             dim.y = dim.x;
910             m_particle->CreateParticle(pos, speed, dim, PARTIGLINT, 2.0f);
911         }
912 
913         if(m_object != nullptr)
914         {
915             Math::Vector angle = m_object->GetRotation();
916             angle.y = m_progress*20.0f;
917             angle.x = sinf(m_progress*49.0f)*0.3f;
918             angle.z = sinf(m_progress*47.0f)*0.2f;
919             m_object->SetRotation(angle);
920 
921             Math::Vector pos = m_pos;
922             pos.y += factor;
923             m_object->SetPosition(pos);
924 
925             if ( m_progress > 0.85f )
926             {
927                 m_object->SetScale(1.0f-(m_progress-0.85f)/0.15f);
928             }
929         }
930     }
931 
932     if ( m_type == PT_FLCREATE )
933     {
934         if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
935         {
936             m_lastParticle = m_time;
937 
938             Math::Vector pos = m_pos;
939             m_terrain->AdjustToFloor(pos);
940             pos.x += (Math::Rand()-0.5f)*1.0f;
941             pos.z += (Math::Rand()-0.5f)*1.0f;
942             Math::Vector speed;
943             speed.x = (Math::Rand()-0.5f)*2.0f;
944             speed.z = (Math::Rand()-0.5f)*2.0f;
945             speed.y = 2.0f+Math::Rand()*2.0f;
946             Math::Point dim;
947             dim.x = (Math::Rand()*1.0f+1.0f)*(0.2f+m_progress*0.8f);
948             dim.y = dim.x;
949             m_particle->CreateParticle(pos, speed, dim, PARTIGLINT, 2.0f, 0.0f, 0.0f);
950         }
951 
952         if(m_object != nullptr)
953         {
954             Math::Vector angle = m_object->GetRotation();
955             angle.x = sinf(m_progress*49.0f)*0.3f*(1.0f-m_progress);
956             angle.z = sinf(m_progress*47.0f)*0.2f*(1.0f-m_progress);
957             m_object->SetRotation(angle);
958 
959             m_object->SetScale(m_progress);
960         }
961     }
962 
963     if ( m_type == PT_FLDELETE )
964     {
965         if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
966         {
967             m_lastParticle = m_time;
968 
969             Math::Vector pos = m_pos;
970             m_terrain->AdjustToFloor(pos);
971             pos.x += (Math::Rand()-0.5f)*1.0f;
972             pos.z += (Math::Rand()-0.5f)*1.0f;
973             Math::Vector speed;
974             speed.x = (Math::Rand()-0.5f)*2.0f;
975             speed.z = (Math::Rand()-0.5f)*2.0f;
976             speed.y = 2.0f+Math::Rand()*2.0f;
977             Math::Point dim;
978             dim.x = (Math::Rand()*1.0f+1.0f)*(0.2f+m_progress*0.8f);
979             dim.y = dim.x;
980             m_particle->CreateParticle(pos, speed, dim, PARTIGLINT, 2.0f, 0.0f, 0.5f);
981         }
982 
983         if(m_object != nullptr)
984         {
985             Math::Vector angle = m_object->GetRotation();
986             angle.y = m_progress*20.0f;
987             angle.x = sinf(m_progress*49.0f)*0.3f;
988             angle.z = sinf(m_progress*47.0f)*0.2f;
989             m_object->SetRotation(angle);
990 
991             m_object->SetScale(1.0f-m_progress);
992         }
993     }
994 
995     if ( m_type == PT_RESET )
996     {
997         if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
998         {
999             m_lastParticle = m_time;
1000 
1001             Math::Vector pos = m_pos;
1002             pos.x += (Math::Rand()-0.5f)*5.0f;
1003             pos.z += (Math::Rand()-0.5f)*5.0f;
1004             Math::Vector speed;
1005             speed.x = 0.0f;
1006             speed.z = 0.0f;
1007             speed.y = 5.0f+Math::Rand()*5.0f;
1008             Math::Point dim;
1009             dim.x = Math::Rand()*2.0f+2.0f;
1010             dim.y = dim.x;
1011             m_particle->CreateParticle(pos, speed, dim, PARTIGLINTb, 2.0f);
1012 
1013             pos = m_pos;
1014             speed.x = (Math::Rand()-0.5f)*20.0f;
1015             speed.z = (Math::Rand()-0.5f)*20.0f;
1016             speed.y = Math::Rand()*10.0f;
1017             speed *= 0.5f+m_progress*0.5f;
1018             dim.x = 0.6f;
1019             dim.y = dim.x;
1020             pos.y += dim.y;
1021             float duration = Math::Rand()*1.5f+1.5f;
1022             m_particle->CreateTrack(pos, speed, dim, PARTITRACK6,
1023                                      duration, 0.0f,
1024                                      duration*0.9f, 0.7f);
1025         }
1026 
1027         if(m_object != nullptr)
1028         {
1029             float angle = m_resetAngle;
1030             m_object->SetRotationY(angle-powf((1.0f-m_progress)*5.0f, 2.0f));
1031             m_object->SetScale(m_progress);
1032         }
1033     }
1034 
1035     if ( m_type == PT_FINDING )
1036     {
1037         if ( m_object != nullptr &&
1038              m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
1039         {
1040             m_lastParticle = m_time;
1041 
1042             float factor = m_size*0.3f;
1043             if (m_object->GetType() == OBJECT_SAFE) factor *= 1.3f;
1044             if (factor > 40.0f) factor = 40.0f;
1045             Math::Vector pos = m_pos;
1046             m_terrain->AdjustToFloor(pos);
1047             pos.x += (Math::Rand()-0.5f)*factor;
1048             pos.z += (Math::Rand()-0.5f)*factor;
1049             Math::Vector speed;
1050             speed.x = (Math::Rand()-0.5f)*2.0f;
1051             speed.z = (Math::Rand()-0.5f)*2.0f;
1052             speed.y = 4.0f+Math::Rand()*4.0f;
1053             Math::Point dim;
1054             dim.x = (Math::Rand()*3.0f+3.0f)*(1.0f-m_progress*0.9f);
1055             dim.y = dim.x;
1056             m_particle->CreateParticle(pos, speed, dim, PARTIGLINT, 2.0f, 0.0f, 0.5f);
1057         }
1058     }
1059 
1060     if ( m_type == PT_SQUASH && m_object != nullptr )
1061     {
1062         m_object->SetScaleY(1.0f-sinf(m_progress)*0.5f);
1063     }
1064 
1065     if ( (m_type == PT_BURNT || m_type == PT_BURNO) &&
1066          m_object != nullptr )
1067     {
1068         if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
1069         {
1070             m_lastParticle = m_time;
1071 
1072             float factor = m_size/25.0f;  // 1 = standard size
1073 
1074             Math::Vector pos = m_object->GetPosition();
1075             pos.y -= m_object->GetCharacter()->height;
1076             pos.x += (Math::Rand()-0.5f)*(4.0f+8.0f*m_progress)*factor;
1077             pos.z += (Math::Rand()-0.5f)*(4.0f+8.0f*m_progress)*factor;
1078             Math::Vector speed;
1079             speed.x = 0.0f;
1080             speed.z = 0.0f;
1081             speed.y = 0.0f;
1082             Math::Point dim;
1083             dim.x = (Math::Rand()*2.5f+1.0f)*factor;
1084             dim.y = dim.x;
1085             m_particle->CreateParticle(pos, speed, dim, PARTIFLAME, 2.0f, 0.0f, 0.2f);
1086 
1087             pos = m_object->GetPosition();
1088             pos.y -= m_object->GetCharacter()->height;
1089             pos.x += (Math::Rand()-0.5f)*(2.0f+4.0f*m_progress)*factor;
1090             pos.z += (Math::Rand()-0.5f)*(2.0f+4.0f*m_progress)*factor;
1091             speed.x = 0.0f;
1092             speed.z = 0.0f;
1093             speed.y = (Math::Rand()*5.0f*m_progress+3.0f)*factor;
1094             dim.x = (Math::Rand()*2.0f+1.0f)*factor;
1095             dim.y = dim.x;
1096             m_particle->CreateParticle(pos, speed, dim, PARTIFLAME, 2.0f, 0.0f, 0.2f);
1097 
1098             pos = m_object->GetPosition();
1099             pos.y -= 2.0f;
1100             pos.x += (Math::Rand()-0.5f)*5.0f*factor;
1101             pos.z += (Math::Rand()-0.5f)*5.0f*factor;
1102             speed.x = 0.0f;
1103             speed.z = 0.0f;
1104             speed.y = (6.0f+Math::Rand()*6.0f+m_progress*6.0f)*factor;
1105             dim.x = (Math::Rand()*1.5f+1.0f+m_progress*3.0f)*factor;
1106             dim.y = dim.x;
1107             m_particle->CreateParticle(pos, speed, dim, PARTISMOKE3, 4.0f);
1108         }
1109 
1110         if ( m_type == PT_BURNT )
1111         {
1112             BurnProgress();
1113         }
1114         else
1115         {
1116             Math::Vector speed;
1117             speed.y = 0.0f;
1118             speed.x = (Math::Rand()-0.5f)*m_progress*1.0f;
1119             speed.z = (Math::Rand()-0.5f)*m_progress*1.0f;
1120             if ( m_progress > 0.8f )
1121             {
1122                 float prog = (m_progress-0.8f)/0.2f;  // 0..1
1123                 speed.y = -prog*6.0f;  // sinks into the ground
1124                 m_object->SetScale(1.0f-prog*0.5f);
1125             }
1126             m_object->SetLinVibration(speed);
1127         }
1128     }
1129 
1130     if ( m_type == PT_WIN &&
1131          m_object != nullptr )
1132     {
1133         if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
1134         {
1135             m_lastParticle = m_time;
1136 
1137             Math::Vector pos = m_object->GetPosition();
1138             pos.y += 1.5f;
1139             Math::Vector speed;
1140             speed.x = (Math::Rand()-0.5f)*10.0f;
1141             speed.z = (Math::Rand()-0.5f)*10.0f;
1142             speed.y = 8.0f+Math::Rand()*8.0f;
1143             Math::Point dim;
1144             dim.x = Math::Rand()*0.2f+0.2f;
1145             dim.y = dim.x;
1146             m_particle->CreateTrack(pos, speed, dim,
1147                                      static_cast<ParticleType>(PARTITRACK7+rand()%4),
1148                                      3.0f, 20.0f, 1.0f, 0.4f);
1149         }
1150     }
1151 
1152     if ( m_type == PT_LOST &&
1153          m_object != nullptr )
1154     {
1155         if ( m_lastParticle+m_engine->ParticleAdapt(0.10f) <= m_time )
1156         {
1157             m_lastParticle = m_time;
1158 
1159             Math::Vector pos = m_object->GetPosition();
1160             pos.y -= 2.0f;
1161             pos.x += (Math::Rand()-0.5f)*10.0f;
1162             pos.z += (Math::Rand()-0.5f)*10.0f;
1163             Math::Vector speed;
1164             speed.x = 0.0f;
1165             speed.z = 0.0f;
1166             speed.y = 1.0f+Math::Rand()*1.0f;
1167             Math::Point dim;
1168             dim.x = Math::Rand()*1.0f+1.0f;
1169             dim.y = dim.x;
1170             m_particle->CreateParticle(pos, speed, dim, PARTISMOKE1, 8.0f, 0.0f, 0.0f);
1171         }
1172     }
1173 
1174     if (m_type == PT_FALL)
1175         FallProgress(event.rTime);
1176 
1177     if (m_lightRank != -1)
1178         LightOperFrame(event.rTime);
1179 
1180     return true;
1181 }
1182 
IsEnded()1183 Error CPyro::IsEnded()
1184 {
1185     // Destroys the object that exploded.
1186     //It should not be destroyed at the end of the Create,
1187     //because it is sometimes the object itself that makes the Create:
1188     //  pyro->Create(PT_FRAGT, this);
1189     if ( m_type == PT_FRAGT  ||
1190          m_type == PT_FRAGO  ||
1191          m_type == PT_FRAGW  ||
1192          m_type == PT_FRAGV  ||
1193          m_type == PT_SPIDER ||
1194          m_type == PT_EGG    )
1195     {
1196         DeleteObject(true, true);
1197     }
1198 
1199     if ( m_type == PT_FALL )  // freight which grave?
1200     {
1201         return FallIsEnded();
1202     }
1203 
1204     if ( m_type == PT_WIN  ||
1205          m_type == PT_LOST )
1206     {
1207         return ERR_CONTINUE;
1208     }
1209 
1210     // End of the pyrotechnic effect?
1211     if ( m_progress < 1.0f )  return ERR_CONTINUE;
1212 
1213     if ( m_type == PT_EXPLOT ||
1214          m_type == PT_EXPLOO ||
1215          m_type == PT_EXPLOW )  // explosion?
1216     {
1217         ExploTerminate();
1218     }
1219 
1220     if ( m_type == PT_BURNT ||
1221          m_type == PT_BURNO )  // burning?
1222     {
1223         BurnTerminate();
1224     }
1225 
1226     if ( m_type == PT_WPCHECK  ||
1227          m_type == PT_FLDELETE )
1228     {
1229         DeleteObject(true, true);
1230     }
1231 
1232     if ( m_type == PT_FLCREATE )
1233     {
1234         m_object->SetRotationX(0.0f);
1235         m_object->SetRotationZ(0.0f);
1236         m_object->SetScale(1.0f);
1237     }
1238 
1239     if ( m_type == PT_RESET )
1240     {
1241         m_object->SetRotationY(m_resetAngle);
1242         m_object->SetScale(1.0f);
1243     }
1244 
1245     if ( m_type == PT_SQUASH )
1246     {
1247         m_object->SetType(OBJECT_PLANT19);
1248     }
1249 
1250     if ( m_lightRank != -1 )
1251     {
1252         m_lightMan->DeleteLight(m_lightRank);
1253         m_lightRank = -1;
1254     }
1255 
1256     return ERR_STOP;
1257 }
1258 
CutObjectLink(CObject * obj)1259 void CPyro::CutObjectLink(CObject* obj)
1260 {
1261     if (m_object == obj)
1262         m_object = nullptr;
1263 }
1264 
DisplayError(PyroType type,CObject * obj)1265 void CPyro::DisplayError(PyroType type, CObject* obj)
1266 {
1267     ObjectType oType = obj->GetType();
1268 
1269     if ( type == PT_FRAGT  ||
1270          type == PT_FRAGO  ||
1271          type == PT_FRAGW  ||
1272          type == PT_EXPLOT ||
1273          type == PT_EXPLOO ||
1274          type == PT_EXPLOW ||
1275          type == PT_BURNT  ||
1276          type == PT_BURNO  )
1277     {
1278         Error err = ERR_OK;
1279         if ( oType == OBJECT_MOTHER )  err = INFO_DELETEMOTHER;
1280         if ( oType == OBJECT_ANT    )  err = INFO_DELETEANT;
1281         if ( oType == OBJECT_BEE    )  err = INFO_DELETEBEE;
1282         if ( oType == OBJECT_WORM   )  err = INFO_DELETEWORM;
1283         if ( oType == OBJECT_SPIDER )  err = INFO_DELETESPIDER;
1284 
1285         if ( oType == OBJECT_MOBILEwa ||
1286              oType == OBJECT_MOBILEta ||
1287              oType == OBJECT_MOBILEfa ||
1288              oType == OBJECT_MOBILEia ||
1289              oType == OBJECT_MOBILEwb ||
1290              oType == OBJECT_MOBILEtb ||
1291              oType == OBJECT_MOBILEfb ||
1292              oType == OBJECT_MOBILEib ||
1293              oType == OBJECT_MOBILEwc ||
1294              oType == OBJECT_MOBILEtc ||
1295              oType == OBJECT_MOBILEfc ||
1296              oType == OBJECT_MOBILEic ||
1297              oType == OBJECT_MOBILEwi ||
1298              oType == OBJECT_MOBILEti ||
1299              oType == OBJECT_MOBILEfi ||
1300              oType == OBJECT_MOBILEii ||
1301              oType == OBJECT_MOBILEws ||
1302              oType == OBJECT_MOBILEts ||
1303              oType == OBJECT_MOBILEfs ||
1304              oType == OBJECT_MOBILEis ||
1305              oType == OBJECT_MOBILErt ||
1306              oType == OBJECT_MOBILErc ||
1307              oType == OBJECT_MOBILErr ||
1308              oType == OBJECT_MOBILErs ||
1309              oType == OBJECT_MOBILEsa ||
1310              oType == OBJECT_MOBILEwt ||
1311              oType == OBJECT_MOBILEtt ||
1312              oType == OBJECT_MOBILEft ||
1313              oType == OBJECT_MOBILEit ||
1314              oType == OBJECT_MOBILErp ||
1315              oType == OBJECT_MOBILEst ||
1316              oType == OBJECT_MOBILEdr )
1317         {
1318             err = ERR_DELETEMOBILE;
1319         }
1320 
1321         if ( oType == OBJECT_DERRICK  ||
1322              oType == OBJECT_FACTORY  ||
1323              oType == OBJECT_STATION  ||
1324              oType == OBJECT_CONVERT  ||
1325              oType == OBJECT_REPAIR   ||
1326              oType == OBJECT_DESTROYER||
1327              oType == OBJECT_TOWER    ||
1328              oType == OBJECT_RESEARCH ||
1329              oType == OBJECT_RADAR    ||
1330              oType == OBJECT_INFO     ||
1331              oType == OBJECT_ENERGY   ||
1332              oType == OBJECT_LABO     ||
1333              oType == OBJECT_NUCLEAR  ||
1334              oType == OBJECT_PARA     ||
1335              oType == OBJECT_SAFE     ||
1336              oType == OBJECT_HUSTON   ||
1337              oType == OBJECT_START    ||
1338              oType == OBJECT_END      )
1339         {
1340             err = ERR_DELETEBUILDING;
1341             m_main->DisplayError(err, obj->GetPosition(), 5.0f);
1342             return;
1343         }
1344 
1345         if ( err != ERR_OK )
1346         {
1347             m_main->DisplayError(err, obj);
1348         }
1349     }
1350 }
1351 
CreateLight(Math::Vector pos,float height)1352 void CPyro::CreateLight(Math::Vector pos, float height)
1353 {
1354     if (!m_engine->GetLightMode()) return;
1355 
1356     m_lightHeight = height;
1357 
1358     Gfx::Light light;
1359     light.type        = LIGHT_SPOT;
1360     light.ambient     = Gfx::Color(0.0f, 0.0f, 0.0f);
1361     light.position    = Math::Vector(pos.x, pos.y+height, pos.z);
1362     light.direction   = Math::Vector(0.0f, -1.0f, 0.0f);  // against the bottom
1363     light.spotIntensity = 1.0f;
1364     light.attenuation0 = 1.0f;
1365     light.attenuation1 = 0.0f;
1366     light.attenuation2 = 0.0f;
1367     light.spotAngle = Math::PI/4.0f;
1368 
1369     m_lightRank = m_lightMan->CreateLight();
1370 
1371     m_lightMan->SetLight(m_lightRank, light);
1372     m_lightMan->SetLightIntensity(m_lightRank, 0.0f);
1373 
1374     // Only illuminates the objects on the ground.
1375     m_lightMan->SetLightIncludeType(m_lightRank, ENG_OBJTYPE_TERRAIN);
1376 }
1377 
DeleteObject(bool primary,bool secondary)1378 void CPyro::DeleteObject(bool primary, bool secondary)
1379 {
1380     if (m_object == nullptr) return;
1381 
1382     ObjectType type = m_object->GetType();
1383     if ( secondary              &&
1384          type != OBJECT_FACTORY &&
1385          type != OBJECT_NUCLEAR &&
1386          type != OBJECT_ENERGY )
1387     {
1388         if (m_object->Implements(ObjectInterfaceType::Powered))
1389         {
1390             CPoweredObject* poweredObject = dynamic_cast<CPoweredObject*>(m_object);
1391             CObject* sub = poweredObject->GetPower();
1392             if (sub != nullptr)
1393             {
1394                 CObjectManager::GetInstancePointer()->DeleteObject(sub);
1395                 poweredObject->SetPower(nullptr);
1396             }
1397         }
1398 
1399         if (m_object->Implements(ObjectInterfaceType::Carrier))
1400         {
1401             CCarrierObject* carrierObject = dynamic_cast<CCarrierObject*>(m_object);
1402             CObject* sub = carrierObject->GetCargo();
1403             if (sub != nullptr)
1404             {
1405                 CObjectManager::GetInstancePointer()->DeleteObject(sub);
1406                 carrierObject->SetCargo(nullptr);
1407             }
1408         }
1409     }
1410 
1411     if (primary)
1412     {
1413         if (m_object->Implements(ObjectInterfaceType::Transportable))
1414         {
1415             // TODO: this should be handled in the object's destructor
1416             CObject* transporter = dynamic_cast<CTransportableObject&>(*m_object).GetTransporter();
1417             if (transporter != nullptr)
1418             {
1419                 if (transporter->Implements(ObjectInterfaceType::Powered))
1420                 {
1421                     CPoweredObject* powered = dynamic_cast<CPoweredObject*>(transporter);
1422                     if (powered->GetPower() == m_object)
1423                         powered->SetPower(nullptr);
1424                 }
1425 
1426                 if (transporter->Implements(ObjectInterfaceType::Carrier))
1427                 {
1428                     CCarrierObject* carrier = dynamic_cast<CCarrierObject*>(transporter);
1429                     if (carrier->GetCargo() == m_object)
1430                         carrier->SetCargo(nullptr);
1431                 }
1432             }
1433         }
1434 
1435         CObjectManager::GetInstancePointer()->DeleteObject(m_object);
1436         m_object = nullptr;
1437     }
1438 }
1439 
CreateTriangle(CObject * obj,ObjectType oType,int part)1440 void CPyro::CreateTriangle(CObject* obj, ObjectType oType, int part)
1441 {
1442     int objRank = obj->GetObjectRank(part);
1443     if (objRank == -1) return;
1444 
1445 
1446     int total = m_engine->GetObjectTotalTriangles(objRank);
1447 
1448     float percent = 0.10f;
1449     if (total < 50) percent = 0.25f;
1450     if (total < 20) percent = 0.50f;
1451 
1452     if ( m_type == PT_FRAGV || m_type == PT_EGG )
1453     {
1454         percent = 0.30f;
1455     }
1456 
1457     if (oType == OBJECT_POWER    ||
1458         oType == OBJECT_ATOMIC   ||
1459         oType == OBJECT_URANIUM  ||
1460         oType == OBJECT_TNT      ||
1461         oType == OBJECT_BOMB     ||
1462         oType == OBJECT_TEEN28)
1463     {
1464         percent = 0.75f;
1465     }
1466     else if (oType == OBJECT_MOBILEtg)
1467     {
1468         percent = 0.50f;
1469     }
1470 
1471     std::vector<EngineTriangle> buffer;
1472     total = m_engine->GetPartialTriangles(objRank, percent, 100, buffer);
1473 
1474     for (int i = 0; i < total; i++)
1475     {
1476         Math::Vector p1, p2, p3;
1477 
1478         p1.x = buffer[i].triangle[0].coord.x;
1479         p1.y = buffer[i].triangle[0].coord.y;
1480         p1.z = buffer[i].triangle[0].coord.z;
1481         p2.x = buffer[i].triangle[1].coord.x;
1482         p2.y = buffer[i].triangle[1].coord.y;
1483         p2.z = buffer[i].triangle[1].coord.z;
1484         p3.x = buffer[i].triangle[2].coord.x;
1485         p3.y = buffer[i].triangle[2].coord.y;
1486         p3.z = buffer[i].triangle[2].coord.z;
1487 
1488         float h;
1489 
1490         h = Math::Distance(p1, p2);
1491         if ( h > 5.0f )
1492         {
1493             p2.x = p1.x+((p2.x-p1.x)*5.0f/h);
1494             p2.y = p1.y+((p2.y-p1.y)*5.0f/h);
1495             p2.z = p1.z+((p2.z-p1.z)*5.0f/h);
1496         }
1497 
1498         h = Math::Distance(p2, p3);
1499         if ( h > 5.0f )
1500         {
1501             p3.x = p2.x+((p3.x-p2.x)*5.0f/h);
1502             p3.y = p2.y+((p3.y-p2.y)*5.0f/h);
1503             p3.z = p2.z+((p3.z-p2.z)*5.0f/h);
1504         }
1505 
1506         h = Math::Distance(p3, p1);
1507         if ( h > 5.0f )
1508         {
1509             p1.x = p3.x+((p1.x-p3.x)*5.0f/h);
1510             p1.y = p3.y+((p1.y-p3.y)*5.0f/h);
1511             p1.z = p3.z+((p1.z-p3.z)*5.0f/h);
1512         }
1513 
1514         buffer[i].triangle[0].coord.x = p1.x;
1515         buffer[i].triangle[0].coord.y = p1.y;
1516         buffer[i].triangle[0].coord.z = p1.z;
1517         buffer[i].triangle[1].coord.x = p2.x;
1518         buffer[i].triangle[1].coord.y = p2.y;
1519         buffer[i].triangle[1].coord.z = p2.z;
1520         buffer[i].triangle[2].coord.x = p3.x;
1521         buffer[i].triangle[2].coord.y = p3.y;
1522         buffer[i].triangle[2].coord.z = p3.z;
1523 
1524         Math::Vector offset;
1525         offset.x = (buffer[i].triangle[0].coord.x+buffer[i].triangle[1].coord.x+buffer[i].triangle[2].coord.x)/3.0f;
1526         offset.y = (buffer[i].triangle[0].coord.y+buffer[i].triangle[1].coord.y+buffer[i].triangle[2].coord.y)/3.0f;
1527         offset.z = (buffer[i].triangle[0].coord.z+buffer[i].triangle[1].coord.z+buffer[i].triangle[2].coord.z)/3.0f;
1528 
1529         buffer[i].triangle[0].coord.x -= offset.x;
1530         buffer[i].triangle[1].coord.x -= offset.x;
1531         buffer[i].triangle[2].coord.x -= offset.x;
1532 
1533         buffer[i].triangle[0].coord.y -= offset.y;
1534         buffer[i].triangle[1].coord.y -= offset.y;
1535         buffer[i].triangle[2].coord.y -= offset.y;
1536 
1537         buffer[i].triangle[0].coord.z -= offset.z;
1538         buffer[i].triangle[1].coord.z -= offset.z;
1539         buffer[i].triangle[2].coord.z -= offset.z;
1540 
1541         Math::Vector speed;
1542         float mass;
1543 
1544         Math::Matrix* mat = obj->GetWorldMatrix(part);
1545         Math::Vector pos = Math::Transform(*mat, offset);
1546         if ( m_type == PT_FRAGV || m_type == PT_EGG )
1547         {
1548             speed.x = (Math::Rand()-0.5f)*10.0f;
1549             speed.z = (Math::Rand()-0.5f)*10.0f;
1550             speed.y = Math::Rand()*15.0f;
1551             mass = Math::Rand()*20.0f+20.0f;
1552         }
1553         else if ( m_type == PT_SPIDER )
1554         {
1555             speed.x = (Math::Rand()-0.5f)*10.0f;
1556             speed.z = (Math::Rand()-0.5f)*10.0f;
1557             speed.y = Math::Rand()*20.0f;
1558             mass = Math::Rand()*10.0f+15.0f;
1559         }
1560         else
1561         {
1562             speed.x = (Math::Rand()-0.5f)*30.0f;
1563             speed.z = (Math::Rand()-0.5f)*30.0f;
1564             speed.y = Math::Rand()*30.0f;
1565             mass = Math::Rand()*10.0f+15.0f;
1566         }
1567         if ( oType == OBJECT_STONE   )  speed *= 0.5f;
1568         if ( oType == OBJECT_URANIUM )  speed *= 0.4f;
1569         float duration = Math::Rand()*3.0f+3.0f;
1570         m_particle->CreateFrag(pos, speed, &buffer[i], PARTIFRAG,
1571                                duration, mass, 0.5f);
1572     }
1573 }
1574 
ExploStart()1575 void CPyro::ExploStart()
1576 {
1577     m_burnType = m_object->GetType();
1578 
1579     Math::Vector oPos = m_object->GetPosition();
1580     m_burnFall = m_terrain->GetHeightToFloor(oPos, true);
1581 
1582     m_object->Simplify();
1583     m_object->SetLock(true);  // ruin not usable yet
1584     assert(m_object->Implements(ObjectInterfaceType::Destroyable));
1585     dynamic_cast<CDestroyableObject&>(*m_object).SetDying(DeathType::Exploding);  // being destroyed
1586     m_object->FlatParent();
1587 
1588     if ( m_object->Implements(ObjectInterfaceType::Controllable) && dynamic_cast<CControllableObject&>(*m_object).GetSelect() )
1589     {
1590         dynamic_cast<CControllableObject&>(*m_object).SetSelect(false);  // deselects the object
1591         m_camera->SetType(CAM_TYPE_EXPLO);
1592         m_main->DeselectAll();
1593     }
1594     m_main->RemoveFromSelectionHistory(m_object);
1595 
1596     for (int i = 0; i < OBJECTMAXPART; i++)
1597     {
1598         int objRank = m_object->GetObjectRank(i);
1599         if (objRank == -1) continue;
1600 
1601         // TODO: refactor later to material change
1602         int oldBaseObjRank = m_engine->GetObjectBaseRank(objRank);
1603         if (oldBaseObjRank != -1)
1604         {
1605             int newBaseObjRank = m_engine->CreateBaseObject();
1606             m_engine->CopyBaseObject(oldBaseObjRank, newBaseObjRank);
1607             m_engine->SetObjectBaseRank(objRank, newBaseObjRank);
1608 
1609             m_engine->ChangeSecondTexture(objRank, "dirty04.png");
1610         }
1611 
1612         // TODO: temporary hack (hopefully)
1613         assert(m_object->Implements(ObjectInterfaceType::Old));
1614         Math::Vector pos = dynamic_cast<COldObject&>(*m_object).GetPartPosition(i);
1615 
1616         Math::Vector speed;
1617         float weight;
1618 
1619         if (i == 0)  // main part?
1620         {
1621             weight = 0.0f;
1622 
1623             speed.y = -1.0f;
1624             speed.x = 0.0f;
1625             speed.z = 0.0f;
1626         }
1627         else
1628         {
1629             Math::Vector min, max;
1630             m_engine->GetObjectBBox(objRank, min, max);
1631             weight = Math::Distance(min, max);  // weight according to size!
1632 
1633             speed.y = 10.0f+Math::Rand()*20.0f;
1634             speed.x = (Math::Rand()-0.5f)*20.0f;
1635             speed.z = (Math::Rand()-0.5f)*20.0f;
1636         }
1637 
1638         int channel = m_particle->CreatePart(pos, speed, PARTIPART, 10.0f, 20.0f, weight, 0.5f);
1639         if (channel != -1)
1640             m_object->SetMasterParticle(i, channel);
1641     }
1642     m_engine->LoadTexture("textures/dirty04.png");
1643 
1644     DeleteObject(false, true);  // destroys the object transported + the battery
1645 }
ExploTerminate()1646 void CPyro::ExploTerminate()
1647 {
1648     DeleteObject(true, false);  // removes the main object
1649 }
1650 
BurnStart()1651 void CPyro::BurnStart()
1652 {
1653     m_burnType = m_object->GetType();
1654 
1655     Math::Vector oPos = m_object->GetPosition();
1656     m_burnFall = m_terrain->GetHeightToFloor(oPos, true);
1657 
1658     m_object->Simplify();
1659     m_object->SetLock(true);  // ruin not usable yet
1660 
1661     if ( m_object->Implements(ObjectInterfaceType::Controllable) && dynamic_cast<CControllableObject&>(*m_object).GetSelect() )
1662     {
1663         dynamic_cast<CControllableObject&>(*m_object).SetSelect(false);  // deselects the object
1664         m_camera->SetType(CAM_TYPE_EXPLO);
1665         m_main->DeselectAll();
1666     }
1667     m_main->RemoveFromSelectionHistory(m_object);
1668 
1669     for (int i = 0; i < OBJECTMAXPART; i++)
1670     {
1671         int objRank = m_object->GetObjectRank(i);
1672         if (objRank == -1) continue;
1673 
1674         // TODO: refactor later to material change
1675         int oldBaseObjRank = m_engine->GetObjectBaseRank(objRank);
1676         if (oldBaseObjRank != -1)
1677         {
1678             int newBaseObjRank = m_engine->CreateBaseObject();
1679             m_engine->CopyBaseObject(oldBaseObjRank, newBaseObjRank);
1680             m_engine->SetObjectBaseRank(objRank, newBaseObjRank);
1681 
1682             m_engine->ChangeSecondTexture(objRank, "dirty04.png");
1683         }
1684     }
1685     m_engine->LoadTexture("textures/dirty04.png");
1686 
1687     m_burnPartTotal = 0;
1688 
1689     Math::Vector pos, angle;
1690 
1691     if ( m_burnType == OBJECT_DERRICK  ||
1692          m_burnType == OBJECT_FACTORY  ||
1693          m_burnType == OBJECT_REPAIR   ||
1694          m_burnType == OBJECT_DESTROYER||
1695          m_burnType == OBJECT_CONVERT  ||
1696          m_burnType == OBJECT_TOWER    ||
1697          m_burnType == OBJECT_RESEARCH ||
1698          m_burnType == OBJECT_ENERGY   ||
1699          m_burnType == OBJECT_LABO     )
1700     {
1701         pos.x =   0.0f;
1702         pos.y = -(4.0f+Math::Rand()*4.0f);
1703         pos.z =   0.0f;
1704         angle.x = (Math::Rand()-0.5f)*0.4f;
1705         angle.y = 0.0f;
1706         angle.z = (Math::Rand()-0.5f)*0.4f;
1707     }
1708     else if ( m_burnType == OBJECT_STATION ||
1709               m_burnType == OBJECT_RADAR   ||
1710               m_burnType == OBJECT_INFO    )
1711     {
1712         pos.x =   0.0f;
1713         pos.y = -(1.0f+Math::Rand()*1.0f);
1714         pos.z =   0.0f;
1715         angle.x = (Math::Rand()-0.5f)*0.2f;
1716         angle.y = 0.0f;
1717         angle.z = (Math::Rand()-0.5f)*0.2f;
1718     }
1719     else if ( m_burnType == OBJECT_NUCLEAR )
1720     {
1721         pos.x =   0.0f;
1722         pos.y = -(10.0f+Math::Rand()*10.0f);
1723         pos.z =   0.0f;
1724         angle.x = (Math::Rand()-0.5f)*0.4f;
1725         angle.y = 0.0f;
1726         angle.z = (Math::Rand()-0.5f)*0.4f;
1727     }
1728     else if ( m_burnType == OBJECT_PARA )
1729     {
1730         pos.x =   0.0f;
1731         pos.y = -(10.0f+Math::Rand()*10.0f);
1732         pos.z =   0.0f;
1733         angle.x = (Math::Rand()-0.5f)*0.4f;
1734         angle.y = 0.0f;
1735         angle.z = (Math::Rand()-0.5f)*0.4f;
1736     }
1737     else if ( m_burnType == OBJECT_SAFE )
1738     {
1739         pos.x =   0.0f;
1740         pos.y = -(10.0f+Math::Rand()*10.0f);
1741         pos.z =   0.0f;
1742         angle.x = (Math::Rand()-0.5f)*0.4f;
1743         angle.y = 0.0f;
1744         angle.z = (Math::Rand()-0.5f)*0.4f;
1745     }
1746     else if ( m_burnType == OBJECT_HUSTON )
1747     {
1748         pos.x =   0.0f;
1749         pos.y = -(10.0f+Math::Rand()*10.0f);
1750         pos.z =   0.0f;
1751         angle.x = (Math::Rand()-0.5f)*0.4f;
1752         angle.y = 0.0f;
1753         angle.z = (Math::Rand()-0.5f)*0.4f;
1754     }
1755     else if ( m_burnType == OBJECT_MOBILEwa ||
1756               m_burnType == OBJECT_MOBILEwb ||
1757               m_burnType == OBJECT_MOBILEwc ||
1758               m_burnType == OBJECT_MOBILEwi ||
1759               m_burnType == OBJECT_MOBILEws ||
1760               m_burnType == OBJECT_MOBILEwt )
1761     {
1762         pos.x =   0.0f;
1763         pos.y = -(0.5f+Math::Rand()*1.0f);
1764         pos.z =   0.0f;
1765         angle.x = (Math::Rand()-0.5f)*0.8f;
1766         angle.y = 0.0f;
1767         angle.z = (Math::Rand()-0.5f)*0.4f;
1768     }
1769     else if ( m_burnType == OBJECT_TEEN31 )  // basket?
1770     {
1771         pos.x =   0.0f;
1772         pos.y =   0.0f;
1773         pos.z =   0.0f;
1774         angle.x = (Math::Rand()-0.5f)*0.8f;
1775         angle.y = 0.0f;
1776         angle.z = (Math::Rand()-0.5f)*0.2f;
1777     }
1778     else
1779     {
1780         pos.x =   0.0f;
1781         pos.y = -(2.0f+Math::Rand()*2.0f);
1782         pos.z =   0.0f;
1783         angle.x = (Math::Rand()-0.5f)*0.8f;
1784         angle.y = 0.0f;
1785         angle.z = (Math::Rand()-0.5f)*0.8f;
1786     }
1787     BurnAddPart(0, pos, angle);  // movement of the main part
1788 
1789     m_burnKeepPart[0] = -1;  // nothing to keep
1790 
1791     if ( m_burnType == OBJECT_DERRICK )
1792     {
1793         pos.x =   0.0f;
1794         pos.y = -40.0f;
1795         pos.z =   0.0f;
1796         angle.x = 0.0f;
1797         angle.y = 0.0f;
1798         angle.z = 0.0f;
1799         BurnAddPart(1, pos, angle);  // down the drill
1800     }
1801 
1802     if ( m_burnType == OBJECT_REPAIR )
1803     {
1804         pos.x =   0.0f;
1805         pos.y = -12.0f;
1806         pos.z =   0.0f;
1807         angle.x = (Math::Rand()-0.5f)*0.2f;
1808         angle.y = (Math::Rand()-0.5f)*0.2f;
1809         angle.z = -90.0f*Math::PI/180.0f;
1810         BurnAddPart(1, pos, angle);  // down the sensor
1811     }
1812 
1813     if ( m_burnType == OBJECT_DESTROYER )
1814     {
1815         pos.x =   0.0f;
1816         pos.y = -12.0f;
1817         pos.z =   0.0f;
1818         angle.x = (Math::Rand()-0.5f)*0.2f;
1819         angle.y = (Math::Rand()-0.5f)*0.2f;
1820         angle.z = -90.0f*Math::PI/180.0f;
1821         BurnAddPart(1, pos, angle);  // down the sensor
1822     }
1823 
1824     if ( m_burnType == OBJECT_CONVERT )
1825     {
1826         pos.x =    0.0f;
1827         pos.y = -200.0f;
1828         pos.z =    0.0f;
1829         angle.x = (Math::Rand()-0.5f)*0.5f;
1830         angle.y = (Math::Rand()-0.5f)*0.5f;
1831         angle.z = 0.0f;
1832         BurnAddPart(1, pos, angle);  // down the cover
1833         BurnAddPart(2, pos, angle);
1834         BurnAddPart(3, pos, angle);
1835     }
1836 
1837     if ( m_burnType == OBJECT_TOWER )
1838     {
1839         pos.x =  0.0f;
1840         pos.y = -7.0f;
1841         pos.z =  0.0f;
1842         angle.x = (Math::Rand()-0.5f)*0.4f;
1843         angle.y = (Math::Rand()-0.5f)*0.4f;
1844         angle.z = 0.0f;
1845         BurnAddPart(1, pos, angle);  // down the cannon
1846     }
1847 
1848     if ( m_burnType == OBJECT_RESEARCH )
1849     {
1850         pos.x =  0.0f;
1851         pos.y = -7.0f;
1852         pos.z =  0.0f;
1853         angle.x = (Math::Rand()-0.5f)*0.2f;
1854         angle.y = (Math::Rand()-0.5f)*0.2f;
1855         angle.z = 0.0f;
1856         BurnAddPart(1, pos, angle);  // down the anemometer
1857     }
1858 
1859     if ( m_burnType == OBJECT_RADAR )
1860     {
1861         pos.x =   0.0f;
1862         pos.y = -14.0f;
1863         pos.z =   0.0f;
1864         angle.x = (Math::Rand()-0.5f)*0.4f;
1865         angle.y = (Math::Rand()-0.5f)*0.4f;
1866         angle.z = 0.0f;
1867         BurnAddPart(1, pos, angle);  // down the radar
1868         BurnAddPart(2, pos, angle);
1869     }
1870 
1871     if ( m_burnType == OBJECT_INFO )
1872     {
1873         pos.x =   0.0f;
1874         pos.y = -14.0f;
1875         pos.z =   0.0f;
1876         angle.x = (Math::Rand()-0.5f)*0.4f;
1877         angle.y = (Math::Rand()-0.5f)*0.4f;
1878         angle.z = 0.0f;
1879         BurnAddPart(1, pos, angle);  // down the information terminal
1880         BurnAddPart(2, pos, angle);
1881     }
1882 
1883     if ( m_burnType == OBJECT_LABO )
1884     {
1885         pos.x =   0.0f;
1886         pos.y = -12.0f;
1887         pos.z =   0.0f;
1888         angle.x = 0.0f;
1889         angle.y = 0.0f;
1890         angle.z = 0.0f;
1891         BurnAddPart(1, pos, angle);  // down the arm
1892     }
1893 
1894     if ( m_burnType == OBJECT_NUCLEAR )
1895     {
1896         pos.x = 0.0f;
1897         pos.y = 0.0f;
1898         pos.z = 0.0f;
1899         angle.x = 0.0f;
1900         angle.y = 0.0f;
1901         angle.z = -135.0f*Math::PI/180.0f;
1902         BurnAddPart(1, pos, angle);  // down the cover
1903     }
1904 
1905     if ( m_burnType == OBJECT_MOBILEfa ||
1906          m_burnType == OBJECT_MOBILEta ||
1907          m_burnType == OBJECT_MOBILEwa ||
1908          m_burnType == OBJECT_MOBILEia )
1909     {
1910         pos.x =  2.0f;
1911         pos.y = -5.0f;
1912         pos.z =  0.0f;
1913         angle.x = (Math::Rand()-0.5f)*0.2f;
1914         angle.y = (Math::Rand()-0.5f)*0.2f;
1915         angle.z = 40.0f*Math::PI/180.0f;
1916         BurnAddPart(1, pos, angle);  // down the arm
1917     }
1918 
1919     if ( m_burnType == OBJECT_MOBILEfs ||
1920          m_burnType == OBJECT_MOBILEts ||
1921          m_burnType == OBJECT_MOBILEws ||
1922          m_burnType == OBJECT_MOBILEis )
1923     {
1924         pos.x =  0.0f;
1925         pos.y = -7.0f;
1926         pos.z =  0.0f;
1927         angle.x = (Math::Rand()-0.5f)*0.2f;
1928         angle.y = (Math::Rand()-0.5f)*0.2f;
1929         angle.z = 50.0f*Math::PI/180.0f;
1930         BurnAddPart(1, pos, angle);  // down the sensor
1931     }
1932 
1933     if ( m_burnType == OBJECT_MOBILEfc ||
1934          m_burnType == OBJECT_MOBILEtc ||
1935          m_burnType == OBJECT_MOBILEwc ||
1936          m_burnType == OBJECT_MOBILEic )
1937     {
1938         pos.x = -1.5f;
1939         pos.y = -5.0f;
1940         pos.z =  0.0f;
1941         angle.x = (Math::Rand()-0.5f)*0.2f;
1942         angle.y = (Math::Rand()-0.5f)*0.2f;
1943         angle.z = -25.0f*Math::PI/180.0f;
1944         BurnAddPart(1, pos, angle);  // down the cannon
1945     }
1946 
1947     if ( m_burnType == OBJECT_MOBILEfi ||
1948          m_burnType == OBJECT_MOBILEti ||
1949          m_burnType == OBJECT_MOBILEwi ||
1950          m_burnType == OBJECT_MOBILEii )
1951     {
1952         pos.x = -1.5f;
1953         pos.y = -5.0f;
1954         pos.z =  0.0f;
1955         angle.x = (Math::Rand()-0.5f)*0.2f;
1956         angle.y = (Math::Rand()-0.5f)*0.2f;
1957         angle.z = -25.0f*Math::PI/180.0f;
1958         BurnAddPart(1, pos, angle);  // down the insect-cannon
1959     }
1960 
1961     if ( m_burnType == OBJECT_MOBILEfb ||
1962          m_burnType == OBJECT_MOBILEtb ||
1963          m_burnType == OBJECT_MOBILEwb ||
1964          m_burnType == OBJECT_MOBILEib )
1965     {
1966         pos.x = -1.5f;
1967         pos.y = -5.0f;
1968         pos.z =  0.0f;
1969         angle.x = (Math::Rand()-0.5f)*0.2f;
1970         angle.y = (Math::Rand()-0.5f)*0.2f;
1971         angle.z = -25.0f*Math::PI/180.0f;
1972         BurnAddPart(1, pos, angle);  // down the neutron gun
1973     }
1974 
1975     if ( m_burnType == OBJECT_MOBILErt ||
1976          m_burnType == OBJECT_MOBILErc )
1977     {
1978         pos.x =   0.0f;
1979         pos.y = -10.0f;
1980         pos.z =   0.0f;
1981         angle.x = 0.0f;
1982         angle.y = 0.0f;
1983         angle.z = 0.0f;
1984         BurnAddPart(1, pos, angle);  // down the holder
1985 
1986         pos.x =   0.0f;
1987         pos.y = -10.0f;
1988         pos.z =   0.0f;
1989         angle.x = 0.0f;
1990         angle.y = 0.0f;
1991         angle.z = 0.0f;
1992         BurnAddPart(2, pos, angle);  // down the pestle/cannon
1993     }
1994 
1995     if ( m_burnType == OBJECT_MOBILErr )
1996     {
1997         pos.x =   0.0f;
1998         pos.y = -10.0f;
1999         pos.z =   0.0f;
2000         angle.x = 0.0f;
2001         angle.y = 0.0f;
2002         angle.z = 0.0f;
2003         BurnAddPart(1, pos, angle);  // down the holder
2004 
2005         pos.x =   0.0f;
2006         pos.y =   0.0f;
2007         pos.z =   0.0f;
2008         angle.x = 0.0f;
2009         angle.y = 0.0f;
2010         angle.z = -Math::PI/2.0f;
2011         BurnAddPart(4, pos, angle);
2012 
2013         pos.x =   0.0f;
2014         pos.y =   0.0f;
2015         pos.z =   0.0f;
2016         angle.x = 0.0f;
2017         angle.y = 0.0f;
2018         angle.z = Math::PI/2.5f;
2019         BurnAddPart(2, pos, angle);
2020     }
2021 
2022     if ( m_burnType == OBJECT_MOBILErs )
2023     {
2024         pos.x =   0.0f;
2025         pos.y = -10.0f;
2026         pos.z =   0.0f;
2027         angle.x = 0.0f;
2028         angle.y = 0.0f;
2029         angle.z = 0.0f;
2030         BurnAddPart(1, pos, angle);  // down the holder
2031 
2032         pos.x =   0.0f;
2033         pos.y =  -5.0f;
2034         pos.z =   0.0f;
2035         angle.x = 0.0f;
2036         angle.y = 0.0f;
2037         angle.z = 0.0f;
2038         BurnAddPart(2, pos, angle);
2039 
2040         pos.x =   0.0f;
2041         pos.y =  -5.0f;
2042         pos.z =   0.0f;
2043         angle.x = 0.0f;
2044         angle.y = 0.0f;
2045         angle.z = 0.0f;
2046         BurnAddPart(3, pos, angle);
2047     }
2048 
2049     if ( m_burnType == OBJECT_MOBILEsa )
2050     {
2051         pos.x =   0.0f;
2052         pos.y = -10.0f;
2053         pos.z =   0.0f;
2054         angle.x = 0.0f;
2055         angle.y = 0.0f;
2056         angle.z = 0.0f;
2057         BurnAddPart(1, pos, angle);  // down the holder
2058     }
2059 
2060     if ( m_burnType == OBJECT_MOBILEwa ||
2061          m_burnType == OBJECT_MOBILEwb ||
2062          m_burnType == OBJECT_MOBILEwc ||
2063          m_burnType == OBJECT_MOBILEwi ||
2064          m_burnType == OBJECT_MOBILEws ||
2065          m_burnType == OBJECT_MOBILEwt )  // wheels?
2066     {
2067         int i = 0;
2068         for (; i < 4; i++)
2069         {
2070             pos.x = 0.0f;
2071             pos.y = Math::Rand()*0.5f;
2072             pos.z = 0.0f;
2073             angle.x = (Math::Rand()-0.5f)*Math::PI/2.0f;
2074             angle.y = (Math::Rand()-0.5f)*Math::PI/2.0f;
2075             angle.z = 0.0f;
2076             BurnAddPart(6+i, pos, angle);  // wheel
2077 
2078             m_burnKeepPart[i] = 6+i;  // we keep the wheels
2079         }
2080         m_burnKeepPart[i] = -1;
2081     }
2082 
2083     if ( m_burnType == OBJECT_MOBILEta ||
2084          m_burnType == OBJECT_MOBILEtb ||
2085          m_burnType == OBJECT_MOBILEtc ||
2086          m_burnType == OBJECT_MOBILEti ||
2087          m_burnType == OBJECT_MOBILEts ||
2088          m_burnType == OBJECT_MOBILEtt ||
2089          m_burnType == OBJECT_MOBILErt ||
2090          m_burnType == OBJECT_MOBILErc ||
2091          m_burnType == OBJECT_MOBILErr ||
2092          m_burnType == OBJECT_MOBILErs ||
2093          m_burnType == OBJECT_MOBILErp ||
2094          m_burnType == OBJECT_MOBILEsa ||
2095          m_burnType == OBJECT_MOBILEst ||
2096          m_burnType == OBJECT_MOBILEdr )  // caterpillars?
2097     {
2098         pos.x =   0.0f;
2099         pos.y =  -4.0f;
2100         pos.z =   2.0f;
2101         angle.x = (Math::Rand()-0.5f)*20.0f*Math::PI/180.0f;
2102         angle.y = (Math::Rand()-0.5f)*10.0f*Math::PI/180.0f;
2103         angle.z = (Math::Rand()-0.5f)*30.0f*Math::PI/180.0f;
2104         BurnAddPart(6, pos, angle);  // down the right caterpillar
2105 
2106         pos.x =   0.0f;
2107         pos.y =  -4.0f;
2108         pos.z =  -2.0f;
2109         angle.x = (Math::Rand()-0.5f)*20.0f*Math::PI/180.0f;
2110         angle.y = (Math::Rand()-0.5f)*10.0f*Math::PI/180.0f;
2111         angle.z = (Math::Rand()-0.5f)*30.0f*Math::PI/180.0f;
2112         BurnAddPart(7, pos, angle);  // down the left caterpillar
2113     }
2114 
2115     if ( m_burnType == OBJECT_MOBILEfa ||
2116          m_burnType == OBJECT_MOBILEfb ||
2117          m_burnType == OBJECT_MOBILEfc ||
2118          m_burnType == OBJECT_MOBILEfi ||
2119          m_burnType == OBJECT_MOBILEfs ||
2120          m_burnType == OBJECT_MOBILEft )  // flying?
2121     {
2122         int i = 0;
2123         for (; i<3; i++)
2124         {
2125             pos.x =  0.0f;
2126             pos.y = -3.0f;
2127             pos.z =  0.0f;
2128             angle.x = 0.0f;
2129             angle.y = 0.0f;
2130             angle.z = (Math::Rand()-0.5f)*Math::PI/2.0f;
2131             BurnAddPart(6+i, pos, angle);  // foot
2132         }
2133         m_burnKeepPart[i] = -1;
2134     }
2135 
2136     if ( m_burnType == OBJECT_MOBILEia ||
2137          m_burnType == OBJECT_MOBILEib ||
2138          m_burnType == OBJECT_MOBILEic ||
2139          m_burnType == OBJECT_MOBILEii ||
2140          m_burnType == OBJECT_MOBILEis ||
2141          m_burnType == OBJECT_MOBILEit )  // legs?
2142     {
2143         for (int i = 0; i < 6; i++)
2144         {
2145             pos.x =  0.0f;
2146             pos.y = -3.0f;
2147             pos.z =  0.0f;
2148             angle.x = 0.0f;
2149             angle.y = (Math::Rand()-0.5f)*Math::PI/4.0f;
2150             angle.z = (Math::Rand()-0.5f)*Math::PI/4.0f;
2151             BurnAddPart(6+i, pos, angle);  // leg
2152         }
2153     }
2154 }
2155 
BurnAddPart(int part,Math::Vector pos,Math::Vector angle)2156 void CPyro::BurnAddPart(int part, Math::Vector pos, Math::Vector angle)
2157 {
2158     // TODO: temporary hack (hopefully)
2159     assert(m_object->Implements(ObjectInterfaceType::Old));
2160     COldObject* oldObj = dynamic_cast<COldObject*>(m_object);
2161 
2162     int i = m_burnPartTotal;
2163     m_burnPart[i].part = part;
2164     m_burnPart[i].initialPos = oldObj->GetPartPosition(part);
2165     m_burnPart[i].finalPos = m_burnPart[i].initialPos+pos;
2166     m_burnPart[i].initialAngle = oldObj->GetPartRotation(part);
2167     m_burnPart[i].finalAngle = m_burnPart[i].initialAngle+angle;
2168 
2169     m_burnPartTotal++;
2170 }
2171 
BurnProgress()2172 void CPyro::BurnProgress()
2173 {
2174     if ( m_burnType == OBJECT_TEEN31 )  // basket?
2175     {
2176         m_object->SetScaleY(1.0f-m_progress*0.5f);  // slight flattening
2177     }
2178 
2179     for (int i = 0; i < m_burnPartTotal; i++)
2180     {
2181         Math::Vector pos = m_burnPart[i].initialPos + m_progress*(m_burnPart[i].finalPos-m_burnPart[i].initialPos);
2182         if ( i == 0 && m_burnFall > 0.0f )
2183         {
2184             float h = powf(m_progress, 2.0f)*1000.0f;
2185             if ( h > m_burnFall )  h = m_burnFall;
2186             pos.y -= h;
2187         }
2188 
2189         // TODO: temporary hack (hopefully)
2190         assert(m_object->Implements(ObjectInterfaceType::Old));
2191         COldObject* oldObj = dynamic_cast<COldObject*>(m_object);
2192 
2193         oldObj->SetPartPosition(m_burnPart[i].part, pos);
2194 
2195         pos = m_burnPart[i].initialAngle + m_progress*(m_burnPart[i].finalAngle-m_burnPart[i].initialAngle);
2196         oldObj->SetPartRotation(m_burnPart[i].part, pos);
2197     }
2198 
2199     if (m_object->Implements(ObjectInterfaceType::Powered))
2200     {
2201         CObject* sub = dynamic_cast<CPoweredObject&>(*m_object).GetPower();
2202         if (sub != nullptr)  // is there a battery?
2203             sub->SetScaleY(1.0f - m_progress);  // complete flattening
2204     }
2205 }
2206 
BurnIsKeepPart(int part)2207 bool CPyro::BurnIsKeepPart(int part)
2208 {
2209     int i = 0;
2210     while (m_burnKeepPart[i] != -1)
2211     {
2212         if (part == m_burnKeepPart[i++]) return true;  // must keep
2213     }
2214     return false;  // must destroy
2215 }
2216 
BurnTerminate()2217 void CPyro::BurnTerminate()
2218 {
2219     if (m_object == nullptr)
2220         return;
2221 
2222     if (m_type == PT_BURNO)  // organic object is burning?
2223     {
2224         DeleteObject(true, true);  // removes the insect
2225         return;
2226     }
2227 
2228     for (int i = 1; i < OBJECTMAXPART; i++)
2229     {
2230         int objRank = m_object->GetObjectRank(i);
2231         if (objRank == -1) continue;
2232         if (BurnIsKeepPart(i)) continue;
2233 
2234         m_object->DeletePart(i);
2235     }
2236 
2237     DeleteObject(false, true);  // destroys the object transported + the battery
2238 
2239     if ( m_burnType == OBJECT_DERRICK  ||
2240          m_burnType == OBJECT_STATION  ||
2241          m_burnType == OBJECT_FACTORY  ||
2242          m_burnType == OBJECT_REPAIR   ||
2243          m_burnType == OBJECT_DESTROYER||
2244          m_burnType == OBJECT_CONVERT  ||
2245          m_burnType == OBJECT_TOWER    ||
2246          m_burnType == OBJECT_RESEARCH ||
2247          m_burnType == OBJECT_RADAR    ||
2248          m_burnType == OBJECT_INFO     ||
2249          m_burnType == OBJECT_ENERGY   ||
2250          m_burnType == OBJECT_LABO     ||
2251          m_burnType == OBJECT_NUCLEAR  ||
2252          m_burnType == OBJECT_PARA     ||
2253          m_burnType == OBJECT_SAFE     ||
2254          m_burnType == OBJECT_HUSTON   ||
2255          m_burnType == OBJECT_START    ||
2256          m_burnType == OBJECT_END      )
2257     {
2258         m_object->SetType(OBJECT_RUINfactory); // Ruin
2259     }
2260     else
2261     {
2262         m_object->SetType(OBJECT_RUINmobilew1); // Wreck (recoverable by Recycler)
2263     }
2264     dynamic_cast<CDestroyableObject*>(m_object)->SetDying(DeathType::Alive);
2265     m_object->SetLock(false);
2266 }
2267 
FallStart()2268 void CPyro::FallStart()
2269 {
2270     m_object->SetLock(true);  // usable
2271 
2272     Math::Vector pos = m_object->GetPosition();
2273     m_fallFloor = m_terrain->GetFloorLevel(pos);
2274     m_fallSpeed = 0.0f;
2275     m_fallBulletTime = 0.0f;
2276     m_fallEnding = false;
2277 }
2278 
FallSearchBeeExplo()2279 CObject* CPyro::FallSearchBeeExplo()
2280 {
2281     auto bulletCrashSphere = m_object->GetFirstCrashSphere();
2282 
2283     for (CObject* obj : CObjectManager::GetInstancePointer()->GetAllObjects())
2284     {
2285         if (obj == m_object) continue;
2286         if (obj->GetType() == OBJECT_BEE) continue;
2287         if ( !obj->Implements(ObjectInterfaceType::Destroyable) )  continue;
2288 
2289         if (IsObjectBeingTransported(obj)) continue;
2290 
2291         Math::Vector oPos = obj->GetPosition();
2292 
2293         if (obj->GetType() == OBJECT_MOBILErs)
2294         {
2295             float shieldRadius = dynamic_cast<CShielder&>(*obj).GetActiveShieldRadius();
2296             if ( shieldRadius > 0.0f )
2297             {
2298                 float distance = Math::Distance(oPos, bulletCrashSphere.sphere.pos);
2299                 if (distance <= shieldRadius)
2300                     return obj;
2301             }
2302         }
2303 
2304         if ( obj->GetType() == OBJECT_BASE )
2305         {
2306             float distance = Math::Distance(oPos, bulletCrashSphere.sphere.pos);
2307             if (distance < 25.0f)
2308                 return obj;
2309         }
2310 
2311         // Test the center of the object, which is necessary for objects
2312         // that have no sphere in the center (station).
2313         float distance = Math::Distance(oPos, bulletCrashSphere.sphere.pos)-4.0f;
2314         if (distance < 5.0f)
2315             return obj;
2316 
2317         // Test with all spheres of the object.
2318         for (const auto& objCrashSphere : obj->GetAllCrashSpheres())
2319         {
2320             if (Math::DistanceBetweenSpheres(objCrashSphere.sphere, bulletCrashSphere.sphere) <= 0.0f)
2321             {
2322                 return obj;
2323             }
2324         }
2325     }
2326 
2327     return nullptr;
2328 }
2329 
FallProgress(float rTime)2330 void CPyro::FallProgress(float rTime)
2331 {
2332     if (m_object == nullptr) return;
2333 
2334     m_fallSpeed += rTime*50.0f;  // v2 = v1 + a*dt
2335     Math::Vector pos;
2336     pos = m_object->GetPosition();
2337     pos.y -= m_fallSpeed*rTime;  // dd -= v2*dt
2338 
2339     bool floor = false;
2340 
2341     if (pos.y <= m_fallFloor)  // below the ground level?
2342     {
2343         pos.y = m_fallFloor;
2344         floor = true;
2345     }
2346     m_object->SetPosition(pos);
2347 
2348     if (m_object->GetType() == OBJECT_BULLET)
2349     {
2350         m_fallBulletTime += rTime;
2351 
2352         if (m_fallBulletTime > 0.2f || floor)
2353         {
2354             m_fallBulletTime = 0.0f;
2355 
2356             CObject* obj = FallSearchBeeExplo();
2357             if (obj == nullptr)
2358             {
2359                 if (floor)  // reaches the ground?
2360                 {
2361                     assert(m_object->Implements(ObjectInterfaceType::Destroyable));
2362                     // TODO: implement "killer"?
2363                     dynamic_cast<CDestroyableObject&>(*m_object).DestroyObject(DestructionType::Explosion);
2364                 }
2365             }
2366             else
2367             {
2368                 if (obj->GetType() == OBJECT_MOBILErs && dynamic_cast<CShielder&>(*obj).GetActiveShieldRadius() > 0.0f)  // protected by shield?
2369                 {
2370                     m_particle->CreateParticle(pos, Math::Vector(0.0f, 0.0f, 0.0f),
2371                                                 Math::Point(6.0f, 6.0f), PARTIGUNDEL, 2.0f, 0.0f, 0.0f);
2372                     m_sound->Play(SOUND_GUNDEL);
2373 
2374                     DeleteObject(true, true);  // removes the ball
2375                 }
2376                 else
2377                 {
2378                     assert(obj->Implements(ObjectInterfaceType::Damageable));
2379                     if (dynamic_cast<CDamageableObject&>(*obj).DamageObject(DamageType::FallingObject))
2380                     {
2381                         DeleteObject(true, true);  // removes the ball
2382                     }
2383                     else
2384                     {
2385                         assert(m_object->Implements(ObjectInterfaceType::Destroyable));
2386                         // TODO: implement "killer"?
2387                         dynamic_cast<CDestroyableObject&>(*m_object).DestroyObject(DestructionType::Explosion);
2388                     }
2389                 }
2390             }
2391 
2392             if (floor || obj != nullptr)
2393             {
2394                 m_fallEnding = true;
2395             }
2396         }
2397     }
2398 }
2399 
FallIsEnded()2400 Error CPyro::FallIsEnded()
2401 {
2402     if (m_fallEnding || m_object == nullptr) return ERR_STOP;
2403 
2404     Math::Vector pos = m_object->GetPosition();
2405     if (pos.y > m_fallFloor) return ERR_CONTINUE;
2406 
2407     m_sound->Play(SOUND_BOUM, pos);
2408     m_object->SetLock(false);  // usable again
2409 
2410     return ERR_STOP;
2411 }
2412 
LightOperFlush()2413 void CPyro::LightOperFlush()
2414 {
2415     m_lightOper.clear();
2416 }
2417 
LightOperAdd(float progress,float intensity,float r,float g,float b)2418 void CPyro::LightOperAdd(float progress, float intensity, float r, float g, float b)
2419 {
2420     PyroLightOper lightOper;
2421 
2422     lightOper.progress  = progress;
2423     lightOper.intensity = intensity;
2424     lightOper.color.r   = r;
2425     lightOper.color.g   = g;
2426     lightOper.color.b   = b;
2427 
2428     m_lightOper.push_back(lightOper);
2429 }
2430 
LightOperFrame(float rTime)2431 void CPyro::LightOperFrame(float rTime)
2432 {
2433     for (std::size_t i = 1; i < m_lightOper.size(); i++)
2434     {
2435         if ( m_progress < m_lightOper[i].progress )
2436         {
2437             float progress = (m_progress-m_lightOper[i-1].progress) / (m_lightOper[i].progress-m_lightOper[i-1].progress);
2438 
2439             float intensity = m_lightOper[i-1].intensity + (m_lightOper[i].intensity-m_lightOper[i-1].intensity)*progress;
2440             Gfx::Color color;
2441             color.r = m_lightOper[i-1].color.r + (m_lightOper[i].color.r-m_lightOper[i-1].color.r)*progress;
2442             color.g = m_lightOper[i-1].color.g + (m_lightOper[i].color.g-m_lightOper[i-1].color.g)*progress;
2443             color.b = m_lightOper[i-1].color.b + (m_lightOper[i].color.b-m_lightOper[i-1].color.b)*progress;
2444 
2445             m_lightMan->SetLightIntensity(m_lightRank, intensity);
2446             m_lightMan->SetLightColor(m_lightRank, color);
2447             break;
2448         }
2449     }
2450 }
2451 
2452 
2453 } // namespace Gfx
2454