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 "object/auto/autofactory.h"
22 
23 #include "common/make_unique.h"
24 
25 #include "level/robotmain.h"
26 
27 #include "level/parser/parser.h"
28 #include "level/parser/parserline.h"
29 #include "level/parser/parserparam.h"
30 
31 #include "math/geometry.h"
32 
33 #include "object/object_create_params.h"
34 #include "object/object_manager.h"
35 #include "object/old_object.h"
36 
37 #include "object/interface/program_storage_object.h"
38 #include "object/interface/programmable_object.h"
39 #include "object/interface/transportable_object.h"
40 
41 #include "physics/physics.h"
42 
43 #include "script/script.h"
44 
45 #include "sound/sound.h"
46 
47 #include "ui/controls/interface.h"
48 #include "ui/controls/window.h"
49 
50 
51 #include <boost/regex.hpp>
52 
53 
54 
55 // Object's constructor.
56 
CAutoFactory(COldObject * object)57 CAutoFactory::CAutoFactory(COldObject* object) : CAuto(object)
58 {
59     Init();
60     m_type  = OBJECT_MOBILEws;
61     m_phase = AFP_WAIT;  // paused until the first Init ()
62     m_channelSound = -1;
63 }
64 
65 // Object's destructor.
66 
~CAutoFactory()67 CAutoFactory::~CAutoFactory()
68 {
69 }
70 
71 
72 // Destroys the object.
73 
DeleteObject(bool all)74 void CAutoFactory::DeleteObject(bool all)
75 {
76     if ( !all )
77     {
78         CObject* cargo = SearchCargo();  // transform metal?
79         if ( cargo != nullptr )
80         {
81             CObjectManager::GetInstancePointer()->DeleteObject(cargo);
82         }
83 
84         CObject* vehicle = SearchVehicle();
85         if ( vehicle != nullptr )
86         {
87             CObjectManager::GetInstancePointer()->DeleteObject(vehicle);
88         }
89     }
90 
91     if ( m_channelSound != -1 )
92     {
93         m_sound->FlushEnvelope(m_channelSound);
94         m_sound->AddEnvelope(m_channelSound, 0.0f, 1.0f, 1.0f, SOPER_STOP);
95         m_channelSound = -1;
96     }
97 
98     CAuto::DeleteObject(all);
99 }
100 
101 
102 // Initialize the object.
103 
Init()104 void CAutoFactory::Init()
105 {
106     m_phase    = AFP_WAIT;
107     m_progress = 0.0f;
108     m_speed    = 1.0f/2.0f;
109 
110     m_time = 0.0f;
111     m_lastParticle = 0.0f;
112 
113     m_cargoPos = m_object->GetPosition();
114 
115     m_program = "";
116 
117     CAuto::Init();
118 }
119 
120 
121 // Starts an action
122 
StartAction(int param)123 Error CAutoFactory::StartAction(int param)
124 {
125     CObject*   cargo;
126     ObjectType type = static_cast<ObjectType>(param);
127 
128     if ( type != OBJECT_NULL )
129     {
130         if ( m_phase != AFP_WAIT )
131         {
132             return ERR_OBJ_BUSY;
133         }
134 
135         m_type = type;
136 
137         cargo = SearchCargo();  // transform metal?
138         if ( cargo == nullptr )
139         {
140             return ERR_FACTORY_NULL;
141         }
142         if ( NearestVehicle() )
143         {
144             return ERR_FACTORY_NEAR;
145         }
146 
147         m_program = "";
148         SetBusy(true);
149         InitProgressTotal(3.0f+2.0f+15.0f+2.0f+3.0f);
150         UpdateInterface();
151 
152         cargo->SetLock(true);  // usable metal
153         SoundManip(3.0f, 1.0f, 0.5f);
154 
155         m_phase    = AFP_CLOSE_S;
156         m_progress = 0.0f;
157         m_speed    = 1.0f/3.0f;
158         return ERR_OK;
159     }
160     return ERR_UNKNOWN;
161 }
162 
163 
164 // Sets program for created robot
165 
SetProgram(const std::string & program)166 void CAutoFactory::SetProgram(const std::string& program)
167 {
168     m_program = program;
169 }
170 
ObjectTypeFromFactoryButton(EventType eventType)171 static ObjectType ObjectTypeFromFactoryButton(EventType eventType)
172 {
173     if ( eventType == EVENT_OBJECT_FACTORYwa )  return OBJECT_MOBILEwa;
174     if ( eventType == EVENT_OBJECT_FACTORYta )  return OBJECT_MOBILEta;
175     if ( eventType == EVENT_OBJECT_FACTORYfa )  return OBJECT_MOBILEfa;
176     if ( eventType == EVENT_OBJECT_FACTORYia )  return OBJECT_MOBILEia;
177     if ( eventType == EVENT_OBJECT_FACTORYws )  return OBJECT_MOBILEws;
178     if ( eventType == EVENT_OBJECT_FACTORYts )  return OBJECT_MOBILEts;
179     if ( eventType == EVENT_OBJECT_FACTORYfs )  return OBJECT_MOBILEfs;
180     if ( eventType == EVENT_OBJECT_FACTORYis )  return OBJECT_MOBILEis;
181     if ( eventType == EVENT_OBJECT_FACTORYwc )  return OBJECT_MOBILEwc;
182     if ( eventType == EVENT_OBJECT_FACTORYtc )  return OBJECT_MOBILEtc;
183     if ( eventType == EVENT_OBJECT_FACTORYfc )  return OBJECT_MOBILEfc;
184     if ( eventType == EVENT_OBJECT_FACTORYic )  return OBJECT_MOBILEic;
185     if ( eventType == EVENT_OBJECT_FACTORYwi )  return OBJECT_MOBILEwi;
186     if ( eventType == EVENT_OBJECT_FACTORYti )  return OBJECT_MOBILEti;
187     if ( eventType == EVENT_OBJECT_FACTORYfi )  return OBJECT_MOBILEfi;
188     if ( eventType == EVENT_OBJECT_FACTORYii )  return OBJECT_MOBILEii;
189     if ( eventType == EVENT_OBJECT_FACTORYwb )  return OBJECT_MOBILEwb;
190     if ( eventType == EVENT_OBJECT_FACTORYtb )  return OBJECT_MOBILEtb;
191     if ( eventType == EVENT_OBJECT_FACTORYfb )  return OBJECT_MOBILEfb;
192     if ( eventType == EVENT_OBJECT_FACTORYib )  return OBJECT_MOBILEib;
193     if ( eventType == EVENT_OBJECT_FACTORYrt )  return OBJECT_MOBILErt;
194     if ( eventType == EVENT_OBJECT_FACTORYrc )  return OBJECT_MOBILErc;
195     if ( eventType == EVENT_OBJECT_FACTORYrr )  return OBJECT_MOBILErr;
196     if ( eventType == EVENT_OBJECT_FACTORYrs )  return OBJECT_MOBILErs;
197     if ( eventType == EVENT_OBJECT_FACTORYsa )  return OBJECT_MOBILEsa;
198     if ( eventType == EVENT_OBJECT_FACTORYtg )  return OBJECT_MOBILEtg;
199 
200     return OBJECT_NULL;
201 }
202 
203 // Management of an event.
204 
EventProcess(const Event & event)205 bool CAutoFactory::EventProcess(const Event &event)
206 {
207     ObjectType  type;
208     CObject*    cargo;
209     CObject*    vehicle;
210     Math::Matrix*   mat;
211     CPhysics*   physics;
212     Math::Vector    pos, speed;
213     Math::Point     dim;
214     float       zoom, angle, prog;
215     int         i;
216 
217     CAuto::EventProcess(event);
218 
219     if ( m_engine->GetPause() )  return true;
220 
221     if ( m_object->GetSelect() )  // factory selected?
222     {
223         if ( event.type == EVENT_UPDINTERFACE )
224         {
225             CreateInterface(true);
226         }
227 
228         type = ObjectTypeFromFactoryButton(event.type);
229 
230         Error err = StartAction(type);
231         if( err != ERR_OK && err != ERR_UNKNOWN )
232             m_main->DisplayError(err, m_object);
233 
234         if( err != ERR_UNKNOWN )
235             return false;
236     }
237 
238     if ( event.type != EVENT_FRAME )  return true;
239 
240     m_progress += event.rTime*m_speed;
241     EventProgress(event.rTime);
242 
243     if ( m_phase == AFP_WAIT )
244     {
245         if ( m_progress >= 1.0f )
246         {
247             m_phase    = AFP_WAIT;  // still waiting ...
248             m_progress = 0.0f;
249             m_speed    = 1.0f/2.0f;
250         }
251     }
252 
253     if ( m_phase == AFP_CLOSE_S )
254     {
255         if ( m_progress < 1.0f )
256         {
257             for ( i=0 ; i<9 ; i++ )
258             {
259                 zoom = 0.30f+(m_progress-0.5f+i/16.0f)*2.0f*0.70f;
260                 if ( zoom < 0.30f )  zoom = 0.30f;
261                 if ( zoom > 1.00f )  zoom = 1.00f;
262                 m_object->SetPartScaleZ( 1+i, zoom);
263                 m_object->SetPartScaleZ(10+i, zoom);
264             }
265         }
266         else
267         {
268             for ( i=0 ; i<9 ; i++ )
269             {
270                 m_object->SetPartScaleZ( 1+i, 1.0f);
271                 m_object->SetPartScaleZ(10+i, 1.0f);
272             }
273 
274             SoundManip(2.0f, 1.0f, 1.2f);
275 
276             m_phase    = AFP_CLOSE_T;
277             m_progress = 0.0f;
278             m_speed    = 1.0f/2.0f;
279         }
280     }
281 
282     if ( m_phase == AFP_CLOSE_T )
283     {
284         if ( m_progress < 1.0f )
285         {
286             for ( i=0 ; i<9 ; i++ )
287             {
288                 angle = -m_progress*(Math::PI/2.0f)+Math::PI/2.0f;
289                 m_object->SetPartRotationZ( 1+i,  angle);
290                 m_object->SetPartRotationZ(10+i, -angle);
291             }
292         }
293         else
294         {
295             for ( i=0 ; i<9 ; i++ )
296             {
297                 m_object->SetPartRotationZ( 1+i, 0.0f);
298                 m_object->SetPartRotationZ(10+i, 0.0f);
299             }
300 
301             m_channelSound = m_sound->Play(SOUND_FACTORY, m_object->GetPosition(), 0.0f, 1.0f, true);
302             m_sound->AddEnvelope(m_channelSound, 1.0f, 1.0f,  2.0f, SOPER_CONTINUE);
303             m_sound->AddEnvelope(m_channelSound, 1.0f, 1.0f, 11.0f, SOPER_CONTINUE);
304             m_sound->AddEnvelope(m_channelSound, 0.0f, 1.0f,  2.0f, SOPER_STOP);
305 
306             m_phase    = AFP_BUILD;
307             m_progress = 0.0f;
308             m_speed    = 1.0f/15.0f;
309         }
310     }
311 
312     if ( m_phase == AFP_BUILD )
313     {
314         if ( m_progress == 0.0f )
315         {
316             if ( !CreateVehicle() )
317             {
318                 cargo = SearchCargo();  // transform metal?
319                 if ( cargo != nullptr )
320                 {
321                     cargo->SetLock(false);  // metal usable again
322                 }
323 
324                 if ( m_channelSound != -1 )
325                 {
326                     m_sound->FlushEnvelope(m_channelSound);
327                     m_sound->AddEnvelope(m_channelSound, 0.0f, 1.0f, 1.0f, SOPER_STOP);
328                     m_channelSound = -1;
329                 }
330 
331                 m_phase    = AFP_OPEN_T;
332                 m_progress = 0.0f;
333                 m_speed    = 1.0f/2.0f;
334                 return true;
335             }
336         }
337 
338         if ( m_progress < 1.0f )
339         {
340             if ( m_type == OBJECT_MOBILErt ||
341                  m_type == OBJECT_MOBILErc ||
342                  m_type == OBJECT_MOBILErr ||
343                  m_type == OBJECT_MOBILErs )
344             {
345                 prog = 1.0f-m_progress*1.5f;
346                 if ( prog < 0.0f )  prog = 0.0f;
347             }
348             else
349             {
350                 prog = 1.0f-m_progress;
351             }
352             angle = powf(prog*10.0f, 2.0f)+m_object->GetRotationY();
353 
354             vehicle = SearchVehicle();
355             if ( vehicle != nullptr )
356             {
357                 vehicle->SetRotationY(angle+Math::PI);
358                 vehicle->SetScale(m_progress);
359             }
360 
361             cargo = SearchCargo();  // transform metal?
362             if ( cargo != nullptr )
363             {
364                 cargo->SetScale(1.0f-m_progress);
365             }
366 
367             if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
368             {
369                 m_lastParticle = m_time;
370 
371                 mat = m_object->GetWorldMatrix(0);
372                 pos = Math::Vector(-12.0f, 20.0f, -4.0f);  // position of chimney
373                 pos = Math::Transform(*mat, pos);
374                 pos.y += 2.0f;
375                 pos.x += (Math::Rand()-0.5f)*2.0f;
376                 pos.z += (Math::Rand()-0.5f)*2.0f;
377                 speed.x = 0.0f;
378                 speed.z = 0.0f;
379                 speed.y = 6.0f+Math::Rand()*6.0f;
380                 dim.x = Math::Rand()*1.5f+1.0f;
381                 dim.y = dim.x;
382                 m_particle->CreateParticle(pos, speed, dim, Gfx::PARTISMOKE3, 4.0f);
383             }
384         }
385         else
386         {
387             m_main->DisplayError(INFO_FACTORY, m_object);
388             SoundManip(2.0f, 1.0f, 1.2f);
389 
390             cargo = SearchCargo();  // transform metal?
391             if ( cargo != nullptr )
392             {
393                 CObjectManager::GetInstancePointer()->DeleteObject(cargo);
394             }
395 
396             vehicle = SearchVehicle();
397             if ( vehicle != nullptr )
398             {
399                 assert(vehicle->Implements(ObjectInterfaceType::Movable));
400                 physics = dynamic_cast<CMovableObject&>(*vehicle).GetPhysics();
401                 physics->SetFreeze(false);  // can move
402 
403                 vehicle->SetLock(false);  // vehicle useable
404                 vehicle->SetRotationY(m_object->GetRotationY()+Math::PI);
405                 vehicle->SetScale(1.0f);
406 
407                 if ( !m_program.empty() )
408                 {
409                     if (vehicle->Implements(ObjectInterfaceType::Programmable) && vehicle->Implements(ObjectInterfaceType::ProgramStorage))
410                     {
411                         Program* program = dynamic_cast<CProgramStorageObject&>(*vehicle).AddProgram();
412 
413                         if (boost::regex_match(m_program, boost::regex("[A-Za-z0-9_]+"))) // Public function name?
414                         {
415                             std::string code = "extern void object::Start_"+m_program+"()\n{\n\t\n\t//Automatically generated by object.factory()\n\t"+m_program+"();\n\t\n}\n";
416                             program->script->SendScript(code.c_str());
417                         }
418                         else if (boost::regex_match(m_program, boost::regex(".*\\.txt"))) // File name (with .txt extension)?
419                         {
420                             program->script->ReadScript(m_program.c_str());
421                         }
422                         else // Program code?
423                         {
424                             program->script->SendScript(m_program.c_str());
425                         }
426 
427                         dynamic_cast<CProgrammableObject&>(*vehicle).RunProgram(program);
428                     }
429                 }
430             }
431 
432             m_main->CreateShortcuts();
433 
434             m_phase    = AFP_OPEN_T;
435             m_progress = 0.0f;
436             m_speed    = 1.0f/2.0f;
437         }
438     }
439 
440     if ( m_phase == AFP_OPEN_T )
441     {
442         if ( m_progress < 1.0f )
443         {
444             for ( i=0 ; i<9 ; i++ )
445             {
446                 angle = -(1.0f-m_progress)*(Math::PI/2.0f)+Math::PI/2.0f;
447                 m_object->SetPartRotationZ( 1+i,  angle);
448                 m_object->SetPartRotationZ(10+i, -angle);
449             }
450 
451             if ( m_lastParticle+m_engine->ParticleAdapt(0.1f) <= m_time )
452             {
453                 m_lastParticle = m_time;
454 
455                 pos = m_cargoPos;
456                 pos.x += (Math::Rand()-0.5f)*10.0f;
457                 pos.z += (Math::Rand()-0.5f)*10.0f;
458                 pos.y += Math::Rand()*10.0f;
459                 speed = Math::Vector(0.0f, 0.0f, 0.0f);
460                 dim.x = 2.0f;
461                 dim.y = dim.x;
462                 m_particle->CreateParticle(pos, speed, dim, Gfx::PARTIGLINT, 2.0f, 0.0f, 0.0f);
463             }
464         }
465         else
466         {
467             for ( i=0 ; i<9 ; i++ )
468             {
469                 m_object->SetPartRotationZ( 1+i,  Math::PI/2.0f);
470                 m_object->SetPartRotationZ(10+i, -Math::PI/2.0f);
471             }
472 
473             SoundManip(3.0f, 1.0f, 0.5f);
474 
475             m_phase    = AFP_OPEN_S;
476             m_progress = 0.0f;
477             m_speed    = 1.0f/3.0f;
478         }
479     }
480 
481     if ( m_phase == AFP_OPEN_S )
482     {
483         if ( m_progress < 1.0f )
484         {
485             for ( i=0 ; i<9 ; i++ )
486             {
487                 zoom = 0.30f+((1.0f-m_progress)-0.5f+i/16.0f)*2.0f*0.70f;
488                 if ( zoom < 0.30f )  zoom = 0.30f;
489                 if ( zoom > 1.00f )  zoom = 1.00f;
490                 m_object->SetPartScaleZ( 1+i, zoom);
491                 m_object->SetPartScaleZ(10+i, zoom);
492             }
493 
494             if ( m_lastParticle+m_engine->ParticleAdapt(0.1f) <= m_time )
495             {
496                 m_lastParticle = m_time;
497 
498                 pos = m_cargoPos;
499                 pos.x += (Math::Rand()-0.5f)*10.0f;
500                 pos.z += (Math::Rand()-0.5f)*10.0f;
501                 pos.y += Math::Rand()*10.0f;
502                 speed = Math::Vector(0.0f, 0.0f, 0.0f);
503                 dim.x = 2.0f;
504                 dim.y = dim.x;
505                 m_particle->CreateParticle(pos, speed, dim, Gfx::PARTIGLINT, 2.0f, 0.0f, 0.0f);
506             }
507         }
508         else
509         {
510             for ( i=0 ; i<9 ; i++ )
511             {
512                 m_object->SetPartScaleZ( 1+i, 0.30f);
513                 m_object->SetPartScaleZ(10+i, 0.30f);
514             }
515 
516             SetBusy(false);
517             UpdateInterface();
518 
519             m_phase    = AFP_WAIT;
520             m_progress = 0.0f;
521             m_speed    = 1.0f/2.0f;
522         }
523     }
524 
525     return true;
526 }
527 
528 
529 // Saves all parameters of the controller.
530 
Write(CLevelParserLine * line)531 bool CAutoFactory::Write(CLevelParserLine* line)
532 {
533     if ( m_phase == AFP_WAIT )  return false;
534 
535     line->AddParam("aExist", MakeUnique<CLevelParserParam>(true));
536     CAuto::Write(line);
537     line->AddParam("aPhase", MakeUnique<CLevelParserParam>(static_cast<int>(m_phase)));
538     line->AddParam("aProgress", MakeUnique<CLevelParserParam>(m_progress));
539     line->AddParam("aSpeed", MakeUnique<CLevelParserParam>(m_speed));
540 
541     return true;
542 }
543 
544 // Restores all parameters of the controller
545 
Read(CLevelParserLine * line)546 bool CAutoFactory::Read(CLevelParserLine* line)
547 {
548     if ( !line->GetParam("aExist")->AsBool(false) )  return false;
549 
550     CAuto::Read(line);
551     m_phase = static_cast< AutoFactoryPhase >(line->GetParam("aPhase")->AsInt(AFP_WAIT));
552     m_progress = line->GetParam("aProgress")->AsFloat(0.0f);
553     m_speed = line->GetParam("aSpeed")->AsFloat(1.0f);
554 
555     m_lastParticle = 0.0f;
556     m_cargoPos = m_object->GetPosition();
557 
558     return true;
559 }
560 
561 
562 //Seeks the cargo.
563 
SearchCargo()564 CObject* CAutoFactory::SearchCargo()
565 {
566     for (CObject* obj : CObjectManager::GetInstancePointer()->GetAllObjects())
567     {
568         ObjectType type = obj->GetType();
569         if ( type != OBJECT_METAL )  continue;
570         if (IsObjectBeingTransported(obj))  continue;
571 
572         Math::Vector oPos = obj->GetPosition();
573         float dist = Math::Distance(oPos, m_cargoPos);
574 
575         if ( dist < 8.0f )  return obj;
576     }
577 
578     return nullptr;
579 }
580 
581 // Search if a vehicle is too close.
582 
NearestVehicle()583 bool CAutoFactory::NearestVehicle()
584 {
585     Math::Vector cPos = m_object->GetPosition();
586 
587     for (CObject* obj : CObjectManager::GetInstancePointer()->GetAllObjects())
588     {
589         ObjectType type = obj->GetType();
590         if ( type != OBJECT_HUMAN    &&
591              type != OBJECT_MOBILEfa &&
592              type != OBJECT_MOBILEta &&
593              type != OBJECT_MOBILEwa &&
594              type != OBJECT_MOBILEia &&
595              type != OBJECT_MOBILEfb &&
596              type != OBJECT_MOBILEtb &&
597              type != OBJECT_MOBILEwb &&
598              type != OBJECT_MOBILEib &&
599              type != OBJECT_MOBILEfc &&
600              type != OBJECT_MOBILEtc &&
601              type != OBJECT_MOBILEwc &&
602              type != OBJECT_MOBILEic &&
603              type != OBJECT_MOBILEfi &&
604              type != OBJECT_MOBILEti &&
605              type != OBJECT_MOBILEwi &&
606              type != OBJECT_MOBILEii &&
607              type != OBJECT_MOBILEfs &&
608              type != OBJECT_MOBILEts &&
609              type != OBJECT_MOBILEws &&
610              type != OBJECT_MOBILEis &&
611              type != OBJECT_MOBILErt &&
612              type != OBJECT_MOBILErc &&
613              type != OBJECT_MOBILErr &&
614              type != OBJECT_MOBILErs &&
615              type != OBJECT_MOBILEsa &&
616              type != OBJECT_MOBILEtg &&
617              type != OBJECT_MOBILEft &&
618              type != OBJECT_MOBILEtt &&
619              type != OBJECT_MOBILEwt &&
620              type != OBJECT_MOBILEit &&
621              type != OBJECT_MOBILErp &&
622              type != OBJECT_MOBILEst &&
623              type != OBJECT_MOBILEdr &&
624              type != OBJECT_MOTHER   &&
625              type != OBJECT_ANT      &&
626              type != OBJECT_SPIDER   &&
627              type != OBJECT_BEE      &&
628              type != OBJECT_WORM     )  continue;
629 
630         if (obj->GetCrashSphereCount() == 0) continue;
631 
632         auto crashSphere = obj->GetFirstCrashSphere();
633         if (Math::DistanceToSphere(cPos, crashSphere.sphere) < 10.0f)
634             return true;
635     }
636 
637     return false;
638 }
639 
640 
641 // Creates a vehicle.
642 
CreateVehicle()643 bool CAutoFactory::CreateVehicle()
644 {
645     float angle = m_object->GetRotationY();
646 
647     Math::Vector pos;
648     if ( m_type == OBJECT_MOBILErt ||
649          m_type == OBJECT_MOBILErc ||
650          m_type == OBJECT_MOBILErr ||
651          m_type == OBJECT_MOBILErs )
652     {
653         pos = Math::Vector(2.0f, 0.0f, 0.0f);
654     }
655     else
656     {
657         pos = Math::Vector(4.0f, 0.0f, 0.0f);
658     }
659     Math::Matrix* mat = m_object->GetWorldMatrix(0);
660     pos = Transform(*mat, pos);
661 
662     ObjectCreateParams params;
663     params.pos = pos;
664     params.angle = angle;
665     params.type = m_type;
666     params.team = m_object->GetTeam();
667     params.trainer = m_object->GetTrainer();
668     CObject* vehicle = CObjectManager::GetInstancePointer()->CreateObject(params);
669 
670     vehicle->SetLock(true);  // not usable
671 
672     assert(vehicle->Implements(ObjectInterfaceType::Movable));
673     CPhysics* physics = dynamic_cast<CMovableObject&>(*vehicle).GetPhysics();
674     physics->SetFreeze(true);  // it doesn't move
675 
676     if (vehicle->Implements(ObjectInterfaceType::ProgramStorage))
677     {
678         CProgramStorageObject* programStorage = dynamic_cast<CProgramStorageObject*>(vehicle);
679         for (const std::string& name : m_main->GetNewScriptNames(m_type))
680         {
681             Program* prog = programStorage->AddProgram();
682             programStorage->ReadProgram(prog, InjectLevelPathsForCurrentLevel(name, "ai"));
683             prog->readOnly = true;
684             prog->filename = name;
685         }
686     }
687 
688     return true;
689 }
690 
691 // Seeking the vehicle during manufacture.
692 
SearchVehicle()693 CObject* CAutoFactory::SearchVehicle()
694 {
695     for (CObject* obj : CObjectManager::GetInstancePointer()->GetAllObjects())
696     {
697         if ( !obj->GetLock() )  continue;
698 
699         ObjectType  type = obj->GetType();
700         if ( type != m_type )  continue;
701         if (IsObjectBeingTransported(obj))  continue;
702 
703         Math::Vector oPos = obj->GetPosition();
704         float dist = Math::Distance(oPos, m_cargoPos);
705 
706         if ( dist < 8.0f )  return obj;
707     }
708 
709     return nullptr;
710 }
711 
712 // Creates all the interface when the object is selected.
713 
CreateInterface(bool bSelect)714 bool CAutoFactory::CreateInterface(bool bSelect)
715 {
716     Ui::CWindow*    pw;
717     Math::Point     pos, dim, ddim;
718     float       ox, oy, sx, sy;
719 
720     CAuto::CreateInterface(bSelect);
721 
722     if ( !bSelect )  return true;
723 
724     pw = static_cast< Ui::CWindow* >(m_interface->SearchControl(EVENT_WINDOW0));
725     if ( pw == nullptr )  return false;
726 
727     dim.x = 33.0f/640.0f;
728     dim.y = 33.0f/480.0f;
729     ox = 3.0f/640.0f;
730     oy = 3.0f/480.0f;
731     sx = 33.0f/640.0f;
732     sy = 33.0f/480.0f;
733     if( !m_object->GetTrainer() )
734     {
735         pos.x = 0.0f;
736         pos.y = oy+sy*2.6f;
737         ddim.x = 138.0f/640.0f;
738         ddim.y = 258.0f/480.0f;
739         pw->CreateGroup(pos, ddim, 6, EVENT_WINDOW3);
740 
741         pos.x = ox+sx*0.0f;
742         pos.y = oy+sy*9.3f;
743         pw->CreateButton(pos, dim, 128+9, EVENT_OBJECT_FACTORYwa);
744         pos.x += dim.x;
745         pw->CreateButton(pos, dim, 128+10, EVENT_OBJECT_FACTORYta);
746         pos.x += dim.x;
747         pw->CreateButton(pos, dim, 128+11, EVENT_OBJECT_FACTORYfa);
748         pos.x += dim.x;
749         pw->CreateButton(pos, dim, 128+22, EVENT_OBJECT_FACTORYia);
750 
751         pos.x = ox+sx*0.0f;
752         pos.y = oy+sy*8.2f;
753         pw->CreateButton(pos, dim, 128+12, EVENT_OBJECT_FACTORYws);
754         pos.x += dim.x;
755         pw->CreateButton(pos, dim, 128+13, EVENT_OBJECT_FACTORYts);
756         pos.x += dim.x;
757         pw->CreateButton(pos, dim, 128+14, EVENT_OBJECT_FACTORYfs);
758         pos.x += dim.x;
759         pw->CreateButton(pos, dim, 128+24, EVENT_OBJECT_FACTORYis);
760 
761         pos.x = ox+sx*0.0f;
762         pos.y = oy+sy*7.1f;
763         pw->CreateButton(pos, dim, 128+15, EVENT_OBJECT_FACTORYwc);
764         pos.x += dim.x;
765         pw->CreateButton(pos, dim, 128+16, EVENT_OBJECT_FACTORYtc);
766         pos.x += dim.x;
767         pw->CreateButton(pos, dim, 128+17, EVENT_OBJECT_FACTORYfc);
768         pos.x += dim.x;
769         pw->CreateButton(pos, dim, 128+23, EVENT_OBJECT_FACTORYic);
770 
771         pos.x = ox+sx*0.0f;
772         pos.y = oy+sy*6.0f;
773         pw->CreateButton(pos, dim, 128+25, EVENT_OBJECT_FACTORYwi);
774         pos.x += dim.x;
775         pw->CreateButton(pos, dim, 128+26, EVENT_OBJECT_FACTORYti);
776         pos.x += dim.x;
777         pw->CreateButton(pos, dim, 128+27, EVENT_OBJECT_FACTORYfi);
778         pos.x += dim.x;
779         pw->CreateButton(pos, dim, 128+28, EVENT_OBJECT_FACTORYii);
780 
781         pos.x = ox+sx*0.0f;
782         pos.y = oy+sy*4.9f;
783         pw->CreateButton(pos, dim, 192+0, EVENT_OBJECT_FACTORYwb);
784         pos.x += dim.x;
785         pw->CreateButton(pos, dim, 192+1, EVENT_OBJECT_FACTORYtb);
786         pos.x += dim.x;
787         pw->CreateButton(pos, dim, 192+2, EVENT_OBJECT_FACTORYfb);
788         pos.x += dim.x;
789         pw->CreateButton(pos, dim, 192+3, EVENT_OBJECT_FACTORYib);
790 
791         pos.x = ox+sx*0.0f;
792         pos.y = oy+sy*3.8f;
793         pw->CreateButton(pos, dim, 128+18, EVENT_OBJECT_FACTORYrt);
794         pos.x += dim.x;
795         pw->CreateButton(pos, dim, 128+19, EVENT_OBJECT_FACTORYrc);
796         pos.x += dim.x;
797         pw->CreateButton(pos, dim, 128+20, EVENT_OBJECT_FACTORYrr);
798         pos.x += dim.x;
799         pw->CreateButton(pos, dim, 128+29, EVENT_OBJECT_FACTORYrs);
800 
801         pos.x = ox+sx*0.0f;
802         pos.y = oy+sy*2.7f;
803         pw->CreateButton(pos, dim, 128+21, EVENT_OBJECT_FACTORYsa);
804         pos.x += dim.x;
805         pw->CreateButton(pos, dim, 128+45, EVENT_OBJECT_FACTORYtg);
806     }
807 
808     pos.x = ox+sx*0.0f;
809     pos.y = oy+sy*0;
810     ddim.x = 66.0f/640.0f;
811     ddim.y = 66.0f/480.0f;
812     pw->CreateGroup(pos, ddim, 101, EVENT_OBJECT_TYPE);
813 
814     UpdateInterface();
815     return true;
816 }
817 
818 // Updates the status of all interface buttons.
819 
UpdateInterface()820 void CAutoFactory::UpdateInterface()
821 {
822     Ui::CWindow*    pw;
823 
824     if ( !m_object->GetSelect() )  return;
825 
826     CAuto::UpdateInterface();
827 
828     pw = static_cast< Ui::CWindow* >(m_interface->SearchControl(EVENT_WINDOW0));
829 
830     UpdateButton(pw, EVENT_OBJECT_FACTORYwa, m_bBusy);
831     UpdateButton(pw, EVENT_OBJECT_FACTORYta, m_bBusy);
832     UpdateButton(pw, EVENT_OBJECT_FACTORYfa, m_bBusy);
833     UpdateButton(pw, EVENT_OBJECT_FACTORYia, m_bBusy);
834     UpdateButton(pw, EVENT_OBJECT_FACTORYws, m_bBusy);
835     UpdateButton(pw, EVENT_OBJECT_FACTORYts, m_bBusy);
836     UpdateButton(pw, EVENT_OBJECT_FACTORYfs, m_bBusy);
837     UpdateButton(pw, EVENT_OBJECT_FACTORYis, m_bBusy);
838     UpdateButton(pw, EVENT_OBJECT_FACTORYwc, m_bBusy);
839     UpdateButton(pw, EVENT_OBJECT_FACTORYtc, m_bBusy);
840     UpdateButton(pw, EVENT_OBJECT_FACTORYfc, m_bBusy);
841     UpdateButton(pw, EVENT_OBJECT_FACTORYic, m_bBusy);
842     UpdateButton(pw, EVENT_OBJECT_FACTORYwi, m_bBusy);
843     UpdateButton(pw, EVENT_OBJECT_FACTORYti, m_bBusy);
844     UpdateButton(pw, EVENT_OBJECT_FACTORYfi, m_bBusy);
845     UpdateButton(pw, EVENT_OBJECT_FACTORYii, m_bBusy);
846     UpdateButton(pw, EVENT_OBJECT_FACTORYwb, m_bBusy);
847     UpdateButton(pw, EVENT_OBJECT_FACTORYtb, m_bBusy);
848     UpdateButton(pw, EVENT_OBJECT_FACTORYfb, m_bBusy);
849     UpdateButton(pw, EVENT_OBJECT_FACTORYib, m_bBusy);
850     UpdateButton(pw, EVENT_OBJECT_FACTORYrt, m_bBusy);
851     UpdateButton(pw, EVENT_OBJECT_FACTORYrc, m_bBusy);
852     UpdateButton(pw, EVENT_OBJECT_FACTORYrr, m_bBusy);
853     UpdateButton(pw, EVENT_OBJECT_FACTORYrs, m_bBusy);
854     UpdateButton(pw, EVENT_OBJECT_FACTORYsa, m_bBusy);
855     UpdateButton(pw, EVENT_OBJECT_FACTORYtg, m_bBusy);
856 }
857 
858 // Updates the status of one interface button.
859 
UpdateButton(Ui::CWindow * pw,EventType event,bool bBusy)860 void CAutoFactory::UpdateButton(Ui::CWindow *pw, EventType event, bool bBusy)
861 {
862     EnableInterface(pw, event, !bBusy);
863     DeadInterface(pw, event, m_main->CanFactory(ObjectTypeFromFactoryButton(event), m_object->GetTeam()));
864 }
865 
866 // Plays the sound of the manipulator arm.
867 
SoundManip(float time,float amplitude,float frequency)868 void CAutoFactory::SoundManip(float time, float amplitude, float frequency)
869 {
870     int     i;
871 
872     i = m_sound->Play(SOUND_MANIP, m_object->GetPosition(), 0.0f, 0.3f*frequency, true);
873     m_sound->AddEnvelope(i, 0.5f*amplitude, 1.0f*frequency, 0.1f, SOPER_CONTINUE);
874     m_sound->AddEnvelope(i, 0.5f*amplitude, 1.0f*frequency, time-0.1f, SOPER_CONTINUE);
875     m_sound->AddEnvelope(i, 0.0f, 0.3f*frequency, 0.1f, SOPER_STOP);
876 }
877