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 #include "level/robotmain.h"
21
22 #include "CBot/CBot.h"
23
24 #include "app/app.h"
25 #include "app/input.h"
26 #include "app/pausemanager.h"
27
28 #include "common/config_file.h"
29 #include "common/event.h"
30 #include "common/logger.h"
31 #include "common/make_unique.h"
32 #include "common/restext.h"
33 #include "common/settings.h"
34 #include "common/stringutils.h"
35
36 #include "common/resources/inputstream.h"
37 #include "common/resources/outputstream.h"
38 #include "common/resources/resourcemanager.h"
39
40 #include "graphics/engine/camera.h"
41 #include "graphics/engine/cloud.h"
42 #include "graphics/engine/engine.h"
43 #include "graphics/engine/lightman.h"
44 #include "graphics/engine/lightning.h"
45 #include "graphics/engine/oldmodelmanager.h"
46 #include "graphics/engine/particle.h"
47 #include "graphics/engine/planet.h"
48 #include "graphics/engine/pyro_manager.h"
49 #include "graphics/engine/terrain.h"
50 #include "graphics/engine/text.h"
51 #include "graphics/engine/water.h"
52
53 #include "graphics/model/model_manager.h"
54
55 #include "level/mainmovie.h"
56 #include "level/player_profile.h"
57 #include "level/scene_conditions.h"
58 #include "level/scoreboard.h"
59
60 #include "level/parser/parser.h"
61
62 #include "math/const.h"
63 #include "math/func.h"
64 #include "math/geometry.h"
65
66 #include "object/object.h"
67 #include "object/object_create_exception.h"
68 #include "object/object_manager.h"
69
70 #include "object/auto/auto.h"
71
72 #include "object/motion/motion.h"
73 #include "object/motion/motionhuman.h"
74 #include "object/motion/motiontoto.h"
75
76 #include "object/subclass/exchange_post.h"
77
78 #include "object/task/task.h"
79 #include "object/task/taskbuild.h"
80 #include "object/task/taskmanip.h"
81
82 #include "physics/physics.h"
83
84 #include "script/cbottoken.h"
85 #include "script/script.h"
86 #include "script/scriptfunc.h"
87
88 #include "sound/sound.h"
89
90 #include "ui/debug_menu.h"
91 #include "ui/displayinfo.h"
92 #include "ui/displaytext.h"
93 #include "ui/maindialog.h"
94 #include "ui/mainmap.h"
95 #include "ui/mainshort.h"
96 #include "ui/mainui.h"
97
98 #include "ui/controls/button.h"
99 #include "ui/controls/edit.h"
100 #include "ui/controls/group.h"
101 #include "ui/controls/interface.h"
102 #include "ui/controls/label.h"
103 #include "ui/controls/map.h"
104 #include "ui/controls/shortcut.h"
105 #include "ui/controls/slider.h"
106 #include "ui/controls/window.h"
107
108 #include "ui/screen/screen_loading.h"
109
110 #include <algorithm>
111 #include <iomanip>
112 #include <stdexcept>
113 #include <ctime>
114
115 #include <boost/lexical_cast.hpp>
116
117
118 // Global variables.
119
120 const float UNIT = 4.0f; // default for g_unit
121 float g_unit; // conversion factor
122
123 // Min/max values for the game speed.
124 const float MIN_SPEED = 1/8.0f;
125 const float MAX_SPEED = 256.0f;
126
127 // Reference colors used when recoloring textures, see ChangeColor()
128 const Gfx::Color COLOR_REF_BOT = Gfx::Color( 10.0f/256.0f, 166.0f/256.0f, 254.0f/256.0f); // blue
129 const Gfx::Color COLOR_REF_ALIEN = Gfx::Color(135.0f/256.0f, 170.0f/256.0f, 13.0f/256.0f); // green
130 const Gfx::Color COLOR_REF_GREEN = Gfx::Color(135.0f/256.0f, 170.0f/256.0f, 13.0f/256.0f); // green
131 const Gfx::Color COLOR_REF_WATER = Gfx::Color( 25.0f/256.0f, 255.0f/256.0f, 240.0f/256.0f); // cyan
132
133 //! Constructor of robot application
CRobotMain()134 CRobotMain::CRobotMain()
135 {
136 m_app = CApplication::GetInstancePointer();
137
138 m_eventQueue = m_app->GetEventQueue();
139 m_sound = m_app->GetSound();
140
141 m_engine = Gfx::CEngine::GetInstancePointer();
142 m_oldModelManager = m_engine->GetModelManager();
143 m_lightMan = m_engine->GetLightManager();
144 m_particle = m_engine->GetParticle();
145 m_water = m_engine->GetWater();
146 m_cloud = m_engine->GetCloud();
147 m_lightning = m_engine->GetLightning();
148 m_planet = m_engine->GetPlanet();
149 m_input = CInput::GetInstancePointer();
150
151 m_modelManager = MakeUnique<Gfx::CModelManager>();
152 m_settings = MakeUnique<CSettings>();
153 m_pause = MakeUnique<CPauseManager>();
154 m_interface = MakeUnique<Ui::CInterface>();
155 m_terrain = MakeUnique<Gfx::CTerrain>();
156 m_camera = MakeUnique<Gfx::CCamera>();
157 m_displayText = MakeUnique<Ui::CDisplayText>();
158 m_movie = MakeUnique<CMainMovie>();
159 m_ui = MakeUnique<Ui::CMainUserInterface>();
160 m_short = MakeUnique<Ui::CMainShort>();
161 m_map = MakeUnique<Ui::CMainMap>();
162
163 m_objMan = MakeUnique<CObjectManager>(
164 m_engine,
165 m_terrain.get(),
166 m_oldModelManager,
167 m_modelManager.get(),
168 m_particle);
169
170 m_debugMenu = MakeUnique<Ui::CDebugMenu>(this, m_engine, m_objMan.get(), m_sound);
171
172 m_time = 0.0f;
173 m_gameTime = 0.0f;
174 m_gameTimeAbsolute = 0.0f;
175
176 m_levelCategory = LevelCategory::Exercises;
177 m_levelChap = 0;
178 m_levelRank = 0;
179 m_sceneReadPath = "";
180
181 m_missionTimerEnabled = false;
182 m_missionTimerStarted = false;
183 m_missionTimer = 0.0f;
184
185 m_phase = PHASE_PLAYER_SELECT;
186 m_visitLast = EVENT_NULL;
187 m_visitObject = nullptr;
188 m_visitArrow = nullptr;
189 m_audioTrack = "";
190 m_audioRepeat = true;
191 m_satcomTrack = "";
192 m_satcomRepeat = true;
193 m_editorTrack = "";
194 m_editorRepeat = true;
195 m_selectObject = nullptr;
196 m_infoUsed = 0;
197
198 m_controller = nullptr;
199 m_missionType = MISSION_NORMAL;
200 m_immediatSatCom = false;
201 m_beginSatCom = false;
202 m_lockedSatCom = false;
203 m_movieLock = false;
204 m_satComLock = false;
205 m_editLock = false;
206 m_editFull = false;
207 m_hilite = false;
208 m_cheatSelectInsect = false;
209 m_cheatShowSoluce = false;
210
211 m_codeBattleInit = false;
212 m_codeBattleStarted = false;
213
214 m_teamNames.clear();
215
216 #if DEV_BUILD
217 m_cheatAllMission = true; // for development
218 #else
219 m_cheatAllMission = false;
220 #endif
221
222 m_cheatRadar = false;
223 m_fixScene = false;
224 m_cheatTrainerPilot = false;
225 m_friendAim = false;
226 m_resetCreate = false;
227 m_shortCut = true;
228
229 m_commandHistoryIndex = -1;
230
231 m_movieInfoIndex = -1;
232
233 m_tooltipPos = Math::Point(0.0f, 0.0f);
234 m_tooltipName.clear();
235 m_tooltipTime = 0.0f;
236
237 m_winTerminate = false;
238
239 m_globalMagnifyDamage = 1.0f;
240
241 m_autosave = true;
242 m_autosaveInterval = 5;
243 m_autosaveSlots = 3;
244 m_autosaveLast = 0.0f;
245
246 m_shotSaving = 0;
247
248 m_build = 0;
249 m_researchDone.clear(); // no research done
250 m_researchDone[0] = 0;
251 m_researchEnable = 0;
252 g_unit = UNIT;
253
254 for (int i = 0; i < MAXSHOWLIMIT; i++)
255 {
256 m_showLimit[i].used = false;
257 m_showLimit[i].total = 0;
258 m_showLimit[i].link = nullptr;
259 }
260
261 m_debugCrashSpheres = false;
262
263 m_engine->SetTerrain(m_terrain.get());
264
265 m_app->SetMouseMode(MOUSE_ENGINE);
266
267 m_movie->Flush();
268
269 FlushDisplayInfo();
270
271 InitEye();
272
273 m_engine->SetTracePrecision(1.0f);
274
275 m_settings->LoadSettings();
276 m_settings->SaveSettings();
277 m_settings->SaveResolutionSettings(m_app->GetVideoConfig());
278
279 SelectPlayer(CPlayerProfile::GetLastName());
280
281 CScriptFunctions::Init();
282 }
283
284 //! Destructor of robot application
~CRobotMain()285 CRobotMain::~CRobotMain()
286 {
287 }
288
GetCamera()289 Gfx::CCamera* CRobotMain::GetCamera()
290 {
291 return m_camera.get();
292 }
293
GetTerrain()294 Gfx::CTerrain* CRobotMain::GetTerrain()
295 {
296 return m_terrain.get();
297 }
298
GetInterface()299 Ui::CInterface* CRobotMain::GetInterface()
300 {
301 return m_interface.get();
302 }
303
GetDisplayText()304 Ui::CDisplayText* CRobotMain::GetDisplayText()
305 {
306 return m_displayText.get();
307 }
308
GetPauseManager()309 CPauseManager* CRobotMain::GetPauseManager()
310 {
311 return m_pause.get();
312 }
313
PhaseToString(Phase phase)314 std::string PhaseToString(Phase phase)
315 {
316 if (phase == PHASE_WELCOME1) return "PHASE_WELCOME1";
317 if (phase == PHASE_WELCOME2) return "PHASE_WELCOME2";
318 if (phase == PHASE_WELCOME3) return "PHASE_WELCOME3";
319 if (phase == PHASE_PLAYER_SELECT) return "PHASE_PLAYER_SELECT";
320 if (phase == PHASE_APPERANCE) return "PHASE_APPERANCE";
321 if (phase == PHASE_MAIN_MENU) return "PHASE_MAIN_MENU";
322 if (phase == PHASE_LEVEL_LIST) return "PHASE_LEVEL_LIST";
323 if (phase == PHASE_MOD_LIST) return "PHASE_MOD_LIST";
324 if (phase == PHASE_SIMUL) return "PHASE_SIMUL";
325 if (phase == PHASE_SETUPd) return "PHASE_SETUPd";
326 if (phase == PHASE_SETUPg) return "PHASE_SETUPg";
327 if (phase == PHASE_SETUPp) return "PHASE_SETUPp";
328 if (phase == PHASE_SETUPc) return "PHASE_SETUPc";
329 if (phase == PHASE_SETUPs) return "PHASE_SETUPs";
330 if (phase == PHASE_SETUPds) return "PHASE_SETUPds";
331 if (phase == PHASE_SETUPgs) return "PHASE_SETUPgs";
332 if (phase == PHASE_SETUPps) return "PHASE_SETUPps";
333 if (phase == PHASE_SETUPcs) return "PHASE_SETUPcs";
334 if (phase == PHASE_SETUPss) return "PHASE_SETUPss";
335 if (phase == PHASE_WRITEs) return "PHASE_WRITEs";
336 if (phase == PHASE_READ) return "PHASE_READ";
337 if (phase == PHASE_READs) return "PHASE_READs";
338 if (phase == PHASE_WIN) return "PHASE_WIN";
339 if (phase == PHASE_LOST) return "PHASE_LOST";
340 if (phase == PHASE_QUIT_SCREEN) return "PHASE_QUIT_SCREEN";
341 if (phase == PHASE_SATCOM) return "PHASE_SATCOM";
342 return "(unknown)";
343 }
344
IsInSimulationConfigPhase(Phase phase)345 bool IsInSimulationConfigPhase(Phase phase)
346 {
347 return (phase >= PHASE_SETUPds && phase <= PHASE_SETUPss) || phase == PHASE_READs || phase == PHASE_WRITEs;
348 }
349
IsPhaseWithWorld(Phase phase)350 bool IsPhaseWithWorld(Phase phase)
351 {
352 if (phase == PHASE_SIMUL ) return true;
353 if (phase == PHASE_WIN ) return true;
354 if (phase == PHASE_LOST ) return true;
355 if (phase == PHASE_APPERANCE) return true;
356 if (IsInSimulationConfigPhase(phase)) return true;
357 return false;
358 }
359
IsMainMenuPhase(Phase phase)360 bool IsMainMenuPhase(Phase phase)
361 {
362 if (phase == PHASE_APPERANCE) return true;
363 return !IsPhaseWithWorld(phase);
364 }
365
366 //! Changes phase
ChangePhase(Phase phase)367 void CRobotMain::ChangePhase(Phase phase)
368 {
369 bool resetWorld = false;
370 if ((IsPhaseWithWorld(m_phase) || IsPhaseWithWorld(phase)) && !IsInSimulationConfigPhase(m_phase) && !IsInSimulationConfigPhase(phase))
371 {
372 if (IsPhaseWithWorld(m_phase) && !IsPhaseWithWorld(phase) && m_exitAfterMission)
373 {
374 GetLogger()->Info("Mission finished in single mission mode, exiting\n");
375 m_eventQueue->AddEvent(Event(EVENT_QUIT));
376 return;
377 }
378
379 GetLogger()->Info("Reseting world on phase change...\n");
380 resetWorld = true;
381 }
382
383 if (resetWorld)
384 {
385 m_missionTimerEnabled = m_missionTimerStarted = false;
386 m_missionTimer = 0.0f;
387
388 if (m_phase == PHASE_SIMUL) // ends a simulation?
389 {
390 SaveAllScript();
391 m_sound->StopMusic(0.0f);
392 m_camera->SetControllingObject(nullptr);
393
394 if (m_gameTime > 10.0f) // did you play at least 10 seconds?
395 {
396 m_playerProfile->IncrementLevelTryCount(m_levelCategory, m_levelChap, m_levelRank);
397 }
398
399 if (m_userPause != nullptr)
400 {
401 m_pause->DeactivatePause(m_userPause);
402 m_userPause = nullptr;
403 }
404 }
405
406 if (phase == PHASE_WIN) // wins a simulation?
407 {
408 m_playerProfile->SetLevelPassed(m_levelCategory, m_levelChap, m_levelRank, true);
409 m_ui->NextMission(); // passes to the next mission
410 }
411
412 DeleteAllObjects(); // removes all the current 3D Scene
413 }
414
415 m_phase = phase;
416
417 if (m_phase != PHASE_SIMUL)
418 {
419 Ui::CWindow* pw = static_cast<Ui::CWindow*>(m_interface->SearchControl(EVENT_WINDOW6));
420 if ( pw != nullptr ) pw->ClearState(Ui::STATE_VISIBLE | Ui::STATE_ENABLE);
421 }
422
423 if (resetWorld)
424 {
425 m_winDelay = 0.0f;
426 m_lostDelay = 0.0f;
427 m_beginSatCom = false;
428 m_movieLock = false;
429 m_satComLock = false;
430 m_editLock = false;
431 m_resetCreate = false;
432 m_infoObject = nullptr;
433
434 m_pause->FlushPause();
435 m_freePhotoPause = nullptr;
436 m_userPause = nullptr;
437 m_focusPause = nullptr;
438 FlushDisplayInfo();
439 m_engine->SetRankView(0);
440 m_terrain->FlushRelief();
441 m_engine->DeleteAllObjects();
442 m_oldModelManager->DeleteAllModelCopies();
443 m_engine->SetWaterAddColor(Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f));
444 m_engine->SetBackground("");
445 m_engine->SetBackForce(false);
446 m_engine->SetForegroundName("");
447 m_engine->SetOverColor();
448 m_engine->DeleteGroundMark(0);
449 SetSpeed(1.0f);
450 m_terrain->SetWind(Math::Vector(0.0f, 0.0f, 0.0f));
451 m_terrain->FlushBuildingLevel();
452 m_terrain->FlushFlyingLimit();
453 m_lightMan->FlushLights();
454 m_particle->FlushParticle();
455 m_water->Flush();
456 m_cloud->Flush();
457 m_lightning->Flush();
458 m_planet->Flush();
459 m_interface->Flush();
460 m_newScriptName.clear();
461 m_sound->SetListener(Math::Vector(0.0f, 0.0f, 0.0f), Math::Vector(0.0f, 0.0f, 1.0f));
462 m_sound->StopAll();
463 m_camera->SetType(Gfx::CAM_TYPE_NULL);
464 m_movie->Flush();
465 m_movieInfoIndex = -1;
466 m_shortCut = true;
467
468 m_viewpoints.clear();
469 }
470 ClearInterface();
471
472 Math::Point dim, pos;
473
474 // Creates and hide the command console.
475 dim.x = 200.0f/640.0f;
476 dim.y = 18.0f/480.0f;
477 pos.x = 20.0f/640.0f;
478 pos.y = 100.0f/480.0f;
479 Ui::CEdit* pe = m_interface->CreateEdit(pos, dim, 0, EVENT_CMD);
480 pe->ClearState(Ui::STATE_VISIBLE);
481 pe->SetMaxChar(100);
482 m_cmdEdit = false; // hidden for now
483
484 // Creates the speedometer.
485 dim.x = 30.0f/640.0f;
486 dim.y = 20.0f/480.0f;
487 pos.x = 4.0f/640.0f;
488 pos.y = 426.0f/480.0f;
489
490 // Creates the save indicator
491 Ui::CButton* pb = m_interface->CreateButton(pos, dim, 0, EVENT_SPEED);
492 pb->SetState(Ui::STATE_SIMPLY);
493 pb->ClearState(Ui::STATE_VISIBLE);
494
495 if (m_phase == PHASE_PLAYER_SELECT)
496 {
497 if (CResourceManager::DirectoryExists("crashsave"))
498 {
499 GetLogger()->Info("Pre-crash save found!\n");
500 m_ui->GetDialog()->StartQuestion(
501 "Your game seems to have crashed. Do you want to restore pre-crash state?", false, false, false,
502 [&]()
503 {
504 GetLogger()->Info("Trying to restore pre-crash state...\n");
505 assert(m_playerProfile != nullptr);
506 m_playerProfile->LoadScene("../../crashsave");
507 CResourceManager::RemoveDirectory("crashsave");
508 },
509 [&]()
510 {
511 GetLogger()->Info("Not restoring pre-crash state\n");
512 CResourceManager::RemoveDirectory("crashsave");
513 }
514 );
515 }
516 }
517
518 m_ui->ChangePhase(m_phase);
519 if (m_phase == PHASE_SATCOM)
520 {
521 m_interface->DeleteControl(EVENT_WINDOW5);
522 StartDisplayInfo(InjectLevelPathsForCurrentLevel("cbot.txt", "help/%lng%"), 0);
523 }
524
525 if (!resetWorld) return;
526
527 dim.x = 32.0f/640.0f;
528 dim.y = 32.0f/480.0f;
529 float ox = 3.0f/640.0f;
530 float oy = 3.0f/480.0f;
531 float sx = (32.0f+2.0f)/640.0f;
532 float sy = (32.0f+2.0f)/480.0f;
533
534 if (m_phase != PHASE_APPERANCE)
535 {
536 m_engine->SetDrawWorld(true);
537 m_engine->SetDrawFront(false);
538 m_fixScene = false;
539 }
540
541 if (m_phase == PHASE_SIMUL)
542 {
543 bool loading = !m_sceneReadPath.empty();
544
545 m_ui->ShowLoadingScreen(true);
546 m_ui->GetLoadingScreen()->SetProgress(0.0f, RT_LOADING_INIT);
547
548 m_map->CreateMap();
549 m_map->ShowMap(false);
550
551 try
552 {
553 CreateScene(m_ui->GetSceneSoluce(), false, false); // interactive scene
554 if (m_mapImage)
555 m_map->SetFixImage(m_mapFilename);
556
557 m_app->ResetTimeAfterLoading();
558
559 m_sound->StopMusic(0.0f);
560 if (m_base == nullptr || loading) StartMusic();
561
562 if (m_immediatSatCom && !loading &&
563 m_infoFilename[SATCOM_HUSTON][0] != 0)
564 StartDisplayInfo(SATCOM_HUSTON, false); // shows the instructions
565 }
566 catch (const std::runtime_error& e)
567 {
568 LevelLoadingError("An error occurred while trying to load a level", e);
569 }
570 }
571
572 if (m_phase == PHASE_WIN)
573 {
574 m_sound->StopAll();
575 if (m_endingWin.empty())
576 {
577 ChangePhase(PHASE_LEVEL_LIST);
578 }
579 else
580 {
581 m_winTerminate = (m_endingWin.substr(m_endingWin.find_last_of("/")+1) == "win904.txt");
582 m_levelFile = m_endingWin;
583 try
584 {
585 CreateScene(false, true, false); // sets scene
586
587 pos.x = ox+sx*1; pos.y = oy+sy*1;
588 Math::Point ddim;
589 ddim.x = dim.x*2; ddim.y = dim.y*2;
590 m_interface->CreateButton(pos, ddim, 16, EVENT_BUTTON_OK);
591
592 if (m_winTerminate)
593 {
594 pos.x = ox+sx*3; pos.y = oy+sy*0.2f;
595 ddim.x = dim.x*15; ddim.y = dim.y*3.0f;
596 pe = m_interface->CreateEdit(pos, ddim, 0, EVENT_EDIT0);
597 pe->SetGenericMode(true);
598 pe->SetFontType(Gfx::FONT_COMMON);
599 pe->SetEditCap(false);
600 pe->SetHighlightCap(false);
601 pe->ReadText(std::string("help/") + m_app->GetLanguageChar() + std::string("/win.txt"));
602 }
603 else
604 {
605 m_displayText->DisplayError(INFO_WIN, Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 1000.0f);
606 }
607 StartMusic();
608 }
609 catch (const std::runtime_error& e)
610 {
611 LevelLoadingError("An error occurred while trying to load win scene", e);
612 }
613 }
614 }
615
616 if (m_phase == PHASE_LOST)
617 {
618 m_sound->StopAll();
619 if (m_endingLost.empty())
620 {
621 ChangePhase(PHASE_LEVEL_LIST);
622 }
623 else
624 {
625 m_winTerminate = false;
626 m_levelFile = m_endingLost;
627 try
628 {
629 CreateScene(false, true, false); // sets scene
630
631 pos.x = ox+sx*1; pos.y = oy+sy*1;
632 Math::Point ddim;
633 ddim.x = dim.x*2; ddim.y = dim.y*2;
634 m_interface->CreateButton(pos, ddim, 16, EVENT_BUTTON_OK);
635 m_displayText->DisplayError(INFO_LOST, Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 1000.0f);
636
637 StartMusic();
638 }
639 catch (const std::runtime_error& e)
640 {
641 LevelLoadingError("An error occurred while trying to load lost scene", e);
642 }
643 }
644 }
645
646 m_engine->LoadAllTextures();
647 }
648
GetPhase()649 Phase CRobotMain::GetPhase()
650 {
651 return m_phase;
652 }
653
654 //! Processes an event
ProcessEvent(Event & event)655 bool CRobotMain::ProcessEvent(Event &event)
656 {
657 if (!m_ui->EventProcess(event)) return false;
658 if (m_phase == PHASE_SIMUL)
659 {
660 if (!m_editFull)
661 m_camera->EventProcess(event);
662 }
663 if (!m_debugMenu->EventProcess(event)) return false;
664
665 if (event.type == EVENT_FRAME)
666 {
667 if (!m_movie->EventProcess(event)) // end of the movie?
668 {
669 MainMovieType type = m_movie->GetStopType();
670 if (type == MM_SATCOMopen)
671 {
672 m_pause->DeactivatePause(m_satcomMoviePause);
673 m_satcomMoviePause = nullptr;
674 SelectObject(m_infoObject, false); // hands over the command buttons
675 m_map->ShowMap(m_mapShow);
676 m_displayText->HideText(false);
677 int i = m_movieInfoIndex;
678 StartDisplayInfo(m_movieInfoIndex, false);
679 m_movieInfoIndex = i;
680 }
681 }
682
683 m_displayText->EventProcess(event);
684
685 if (m_displayInfo != nullptr) // current edition?
686 m_displayInfo->EventProcess(event);
687
688 UpdateInfoText();
689
690 return EventFrame(event);
691 }
692
693 if (event.type == EVENT_RELOAD_TEXTURES)
694 {
695 if (IsPhaseWithWorld(m_phase))
696 {
697 ChangeColor();
698 UpdateMap();
699 }
700 m_engine->LoadAllTextures();
701 }
702
703 if (event.type == EVENT_RESOLUTION_CHANGED)
704 {
705 // Recreate the interface (needed if the aspect ratio changes)
706 // TODO: This can sometimes cause unwanted side effects, like hidden windows reappearing. To be fixed during CEGUI refactoring.
707 m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE));
708 CreateShortcuts();
709 }
710
711 if (event.type == EVENT_FOCUS_LOST)
712 {
713 GetLogger()->Trace("Window unfocused\n");
714 if (m_settings->GetFocusLostPause())
715 {
716 m_focusPause = m_pause->ActivatePause(PAUSE_ENGINE);
717 }
718
719 if (m_settings->GetFocusLostMute())
720 {
721 m_sound->SetAudioVolume(0);
722 m_sound->SetMusicVolume(0);
723 }
724 return false;
725 }
726
727 if (event.type == EVENT_FOCUS_GAINED)
728 {
729 GetLogger()->Trace("Window focused\n");
730 if (m_focusPause != nullptr)
731 {
732 m_pause->DeactivatePause(m_focusPause);
733 m_focusPause = nullptr;
734 }
735
736 if (m_settings->GetFocusLostMute())
737 {
738 int volume;
739 // Set music volume
740 if (GetConfigFile().GetIntProperty("Setup", "MusicVolume", volume))
741 {
742 m_sound->SetMusicVolume(volume);
743 }
744 else
745 {
746 m_sound->SetMusicVolume(MAXVOLUME*3/4);
747 }
748 // Set audio volume
749 if (GetConfigFile().GetIntProperty("Setup", "AudioVolume", volume))
750 {
751 m_sound->SetAudioVolume(volume);
752 }
753 else
754 {
755 m_sound->SetAudioVolume(MAXVOLUME);
756 }
757 }
758
759 return false;
760 }
761
762 if (event.type == EVENT_WRITE_SCENE_FINISHED)
763 {
764 IOWriteSceneFinished();
765 return false;
766 }
767
768 if (event.type == EVENT_UPDINTERFACE)
769 {
770 if (m_missionType == MISSION_CODE_BATTLE && !m_codeBattleStarted)
771 {
772 CreateCodeBattleInterface();
773 }
774 }
775
776 if (event.type == EVENT_CODE_BATTLE_START)
777 {
778 m_pause->DeactivatePause(m_userPause);
779 m_userPause = nullptr;
780 }
781
782 if (event.type == EVENT_CODE_BATTLE_SPECTATOR)
783 {
784 SetCodeBattleSpectatorMode(!m_codeBattleSpectator);
785 }
786
787 if (event.type >= EVENT_VIEWPOINT0 && event.type <= EVENT_VIEWPOINT9)
788 {
789 m_camera->SetType(Gfx::CAM_TYPE_SCRIPT);
790 m_camera->SetSmooth(Gfx::CAM_SMOOTH_HARD);
791 m_camera->SetScriptCameraAnimate(m_viewpoints[event.type - EVENT_VIEWPOINT0].eye, m_viewpoints[event.type - EVENT_VIEWPOINT0].look);
792 }
793
794 // Management of the console.
795 if (event.type == EVENT_KEY_DOWN)
796 {
797 auto data = event.GetData<KeyEventData>();
798
799 if (data->slot == INPUT_SLOT_CMDLINE)
800 {
801 if (m_phase != PHASE_PLAYER_SELECT &&
802 !m_movie->IsExist() &&
803 !m_movieLock && !m_editLock && !m_cmdEdit)
804 {
805 Ui::CEdit* pe = static_cast<Ui::CEdit*>(m_interface->SearchControl(EVENT_CMD));
806 if (pe == nullptr) return false;
807 pe->SetState(Ui::STATE_VISIBLE);
808 m_interface->SetFocus(pe);
809 if (m_phase == PHASE_SIMUL) m_cmdEditPause = m_pause->ActivatePause(PAUSE_ENGINE);
810 m_cmdEdit = true;
811 m_commandHistoryIndex = -1; // no element selected in command history
812 }
813 return false;
814 }
815
816 if (IsPhaseWithWorld(m_phase))
817 {
818 if (data->key == KEY(F10))
819 {
820 m_debugMenu->ToggleInterface();
821 return false;
822 }
823 }
824 }
825
826 // Browse forward command history with UP key
827 if (event.type == EVENT_KEY_DOWN &&
828 event.GetData<KeyEventData>()->key == KEY(UP) && m_cmdEdit)
829 {
830 Ui::CEdit* pe = static_cast<Ui::CEdit*>(m_interface->SearchControl(EVENT_CMD));
831 if (pe == nullptr) return false;
832 std::string cmd = GetNextFromCommandHistory();
833 if (!cmd.empty()) pe->SetText(cmd);
834 return false;
835 }
836
837 // Browse backward command history with DOWN key
838 if (event.type == EVENT_KEY_DOWN &&
839 event.GetData<KeyEventData>()->key == KEY(DOWN) && m_cmdEdit)
840 {
841 Ui::CEdit* pe = static_cast<Ui::CEdit*>(m_interface->SearchControl(EVENT_CMD));
842 if (pe == nullptr) return false;
843 std::string cmd = GetPreviousFromCommandHistory();
844 if (!cmd.empty()) pe->SetText(cmd);
845 return false;
846 }
847
848 if (event.type == EVENT_KEY_DOWN &&
849 event.GetData<KeyEventData>()->key == KEY(RETURN) && m_cmdEdit)
850 {
851 std::string cmd;
852 Ui::CEdit* pe = static_cast<Ui::CEdit*>(m_interface->SearchControl(EVENT_CMD));
853 if (pe == nullptr) return false;
854 cmd = pe->GetText(50);
855 pe->SetText("");
856 pe->ClearState(Ui::STATE_VISIBLE);
857 m_interface->SetFocus(nullptr);
858 if (m_phase == PHASE_SIMUL)
859 {
860 m_pause->DeactivatePause(m_cmdEditPause);
861 m_cmdEditPause = nullptr;
862 }
863 ExecuteCmd(cmd);
864 PushToCommandHistory(cmd);
865 m_cmdEdit = false;
866 return false;
867 }
868
869 if (event.type == EVENT_KEY_DOWN && m_cmdEdit)
870 return false; // cheat console active, so ignore keys
871
872 // Management of the speed change.
873 if (event.type == EVENT_SPEED)
874 SetSpeed(1.0f);
875
876 if (!m_displayText->EventProcess(event))
877 return false;
878
879 if (event.type == EVENT_MOUSE_MOVE)
880 {
881 HiliteObject(event.mousePos);
882 }
883
884 if (m_displayInfo != nullptr) // current info?
885 {
886 m_displayInfo->EventProcess(event);
887
888 if (event.type == EVENT_KEY_DOWN)
889 {
890 auto data = event.GetData<KeyEventData>();
891
892 if (data->slot == INPUT_SLOT_HELP ||
893 data->slot == INPUT_SLOT_PROG ||
894 data->key == KEY(ESCAPE))
895 {
896 StopDisplayInfo();
897 }
898 }
899
900 if (event.type == EVENT_OBJECT_INFOOK)
901 StopDisplayInfo();
902
903 if (m_displayInfo == nullptr && m_phase == PHASE_SATCOM)
904 ChangePhase(PHASE_MAIN_MENU);
905
906 return false;
907 }
908
909 CObject* obj;
910
911 // Simulation phase of the game
912 if (m_phase == PHASE_SIMUL)
913 {
914 switch (event.type)
915 {
916 case EVENT_KEY_DOWN:
917 {
918 auto data = event.GetData<KeyEventData>();
919
920 HiliteClear();
921 if (m_editLock) // current edition?
922 {
923 if (data->slot == INPUT_SLOT_HELP)
924 {
925 StartDisplayInfo(SATCOM_HUSTON, false);
926 return false;
927 }
928 if (data->slot == INPUT_SLOT_PROG)
929 {
930 StartDisplayInfo(SATCOM_PROG, false);
931 return false;
932 }
933 break;
934 }
935 if (m_movieLock) // current movie?
936 {
937 if (data->slot == INPUT_SLOT_QUIT ||
938 data->key == KEY(ESCAPE))
939 {
940 AbortMovie();
941 }
942 return false;
943 }
944 if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT)
945 {
946 if (data->slot == INPUT_SLOT_VISIT)
947 {
948 StartDisplayVisit(EVENT_NULL);
949 }
950 if (data->slot == INPUT_SLOT_QUIT ||
951 data->key == KEY(ESCAPE))
952 {
953 StopDisplayVisit();
954 }
955 return false;
956 }
957 if (data->slot == INPUT_SLOT_QUIT)
958 {
959 if (m_movie->IsExist())
960 StartDisplayInfo(SATCOM_HUSTON, false);
961 else if (m_winDelay > 0.0f)
962 ChangePhase(PHASE_WIN);
963 else if (m_lostDelay > 0.0f)
964 ChangePhase(PHASE_LOST);
965 else if (!m_cmdEdit)
966 m_ui->GetDialog()->StartPauseMenu(); // do you want to leave?
967 }
968 if (data->slot == INPUT_SLOT_PAUSE)
969 {
970 if (m_userPause == nullptr)
971 {
972 if (!m_pause->IsPauseType(PAUSE_ENGINE))
973 {
974 m_userPause = m_pause->ActivatePause(PAUSE_ENGINE);
975 }
976 }
977 else
978 {
979 m_pause->DeactivatePause(m_userPause);
980 m_userPause = nullptr;
981 }
982 }
983 if (data->slot == INPUT_SLOT_CAMERA)
984 {
985 ChangeCamera();
986 }
987 if (data->slot == INPUT_SLOT_DESEL)
988 {
989 if (m_shortCut)
990 DeselectObject();
991 }
992 if (data->slot == INPUT_SLOT_HUMAN)
993 {
994 SelectObject(SearchHuman());
995 }
996 if (data->slot == INPUT_SLOT_NEXT && ((event.kmodState & KEY_MOD(CTRL)) != 0))
997 {
998 m_short->SelectShortcut(EVENT_OBJECT_SHORTCUT_MODE); // switch bots <-> buildings
999 return false;
1000 }
1001 if (data->slot == INPUT_SLOT_NEXT)
1002 {
1003 if (m_shortCut)
1004 m_short->SelectNext();
1005 }
1006 if (data->slot == INPUT_SLOT_HELP)
1007 {
1008 StartDisplayInfo(SATCOM_HUSTON, true);
1009 }
1010 if (data->slot == INPUT_SLOT_PROG)
1011 {
1012 StartDisplayInfo(SATCOM_PROG, true);
1013 }
1014 if (data->slot == INPUT_SLOT_VISIT)
1015 {
1016 StartDisplayVisit(EVENT_NULL);
1017 }
1018 if (data->slot == INPUT_SLOT_SPEED_DEC)
1019 {
1020 SetSpeed(GetSpeed()*0.5f);
1021 }
1022 if (data->slot == INPUT_SLOT_SPEED_RESET)
1023 {
1024 SetSpeed(1.0f);
1025 }
1026 if (data->slot == INPUT_SLOT_SPEED_INC)
1027 {
1028 SetSpeed(GetSpeed()*2.0f);
1029 }
1030 if (data->slot == INPUT_SLOT_QUICKSAVE)
1031 {
1032 QuickSave();
1033 }
1034 if (data->slot == INPUT_SLOT_QUICKLOAD)
1035 {
1036 QuickLoad();
1037 }
1038 if (data->key == KEY(c) && ((event.kmodState & KEY_MOD(CTRL)) != 0) && m_engine->GetShowStats())
1039 {
1040 CObject* obj = GetSelect();
1041 if (obj != nullptr)
1042 {
1043 CLevelParserLine line("CreateObject");
1044 line.AddParam("type", MakeUnique<CLevelParserParam>(obj->GetType()));
1045
1046 Math::Vector pos = obj->GetPosition()/g_unit;
1047 pos.y = 0.0f;
1048 line.AddParam("pos", MakeUnique<CLevelParserParam>(pos));
1049
1050 float dir = Math::NormAngle(obj->GetRotationY()) / Math::PI;
1051 line.AddParam("dir", MakeUnique<CLevelParserParam>(dir));
1052
1053 std::stringstream ss;
1054 ss << line;
1055 SDL_SetClipboardText(ss.str().c_str());
1056 }
1057 }
1058 break;
1059 }
1060
1061 case EVENT_MOUSE_BUTTON_DOWN:
1062 {
1063 if (event.GetData<MouseButtonEventData>()->button != MOUSE_BUTTON_LEFT) // only left mouse button
1064 break;
1065
1066 obj = DetectObject(event.mousePos);
1067 if (!m_shortCut) obj = nullptr;
1068 if (obj != nullptr && obj->GetType() == OBJECT_TOTO)
1069 {
1070 if (m_displayInfo != nullptr) // current info?
1071 {
1072 StopDisplayInfo();
1073 }
1074 else
1075 {
1076 if (!m_editLock)
1077 StartDisplayInfo(SATCOM_HUSTON, true);
1078 }
1079 }
1080 else
1081 {
1082 SelectObject(obj);
1083 }
1084 break;
1085 }
1086
1087 case EVENT_OBJECT_LIMIT:
1088 StartShowLimit();
1089 break;
1090
1091 case EVENT_OBJECT_DESELECT:
1092 if (m_shortCut)
1093 DeselectObject();
1094 break;
1095
1096 case EVENT_OBJECT_HELP:
1097 HelpObject();
1098 break;
1099
1100 case EVENT_OBJECT_CAMERA:
1101 ChangeCamera();
1102 break;
1103
1104 case EVENT_OBJECT_DELETE:
1105 m_ui->GetDialog()->StartQuestion(
1106 RT_DIALOG_DELOBJ, true, false, false,
1107 [&]()
1108 {
1109 DestroySelectedObject();
1110 }
1111 );
1112 break;
1113
1114 case EVENT_OBJECT_BHELP:
1115 StartDisplayInfo(SATCOM_HUSTON, true);
1116 break;
1117
1118 case EVENT_OBJECT_SOLUCE:
1119 StartDisplayInfo(SATCOM_SOLUCE, true);
1120 break;
1121
1122 case EVENT_OBJECT_MAPZOOM:
1123 m_map->ZoomMap();
1124 break;
1125
1126 case EVENT_DT_VISIT0:
1127 case EVENT_DT_VISIT1:
1128 case EVENT_DT_VISIT2:
1129 case EVENT_DT_VISIT3:
1130 case EVENT_DT_VISIT4:
1131 StartDisplayVisit(event.type);
1132 break;
1133
1134 case EVENT_DT_END:
1135 StopDisplayVisit();
1136 break;
1137
1138 case EVENT_OBJECT_MOVIELOCK:
1139 AbortMovie();
1140 break;
1141
1142 case EVENT_WIN:
1143 m_missionTimerEnabled = m_missionTimerStarted = false;
1144 ChangePhase(PHASE_WIN);
1145 break;
1146
1147 case EVENT_LOST:
1148 m_missionTimerEnabled = m_missionTimerStarted = false;
1149 ChangePhase(PHASE_LOST);
1150 break;
1151
1152 default:
1153 break;
1154 }
1155
1156 if (event.type >= EVENT_OBJECT_SHORTCUT_MODE && event.type <= EVENT_OBJECT_SHORTCUT_MAX)
1157 {
1158 m_short->SelectShortcut(event.type);
1159 }
1160
1161 EventObject(event);
1162 return false;
1163 }
1164
1165 if (m_phase == PHASE_APPERANCE)
1166 EventObject(event);
1167
1168 if (m_phase == PHASE_WIN ||
1169 m_phase == PHASE_LOST)
1170 {
1171 EventObject(event);
1172
1173 switch (event.type)
1174 {
1175 case EVENT_KEY_DOWN:
1176 {
1177 auto data = event.GetData<KeyEventData>();
1178
1179 if (data->key == KEY(ESCAPE) ||
1180 data->key == KEY(RETURN))
1181 {
1182 if (m_winTerminate)
1183 ChangePhase(PHASE_MAIN_MENU);
1184 else
1185 ChangePhase(PHASE_LEVEL_LIST);
1186 }
1187 break;
1188 }
1189
1190 case EVENT_BUTTON_OK:
1191 if (m_winTerminate)
1192 ChangePhase(PHASE_MAIN_MENU);
1193 else
1194 ChangePhase(PHASE_LEVEL_LIST);
1195
1196 break;
1197
1198 default:
1199 break;
1200 }
1201 }
1202
1203 return true;
1204 }
1205
1206
1207
1208 //! Executes a command
ExecuteCmd(const std::string & cmd)1209 void CRobotMain::ExecuteCmd(const std::string& cmd)
1210 {
1211 if (cmd.empty()) return;
1212
1213 if (m_phase == PHASE_SIMUL)
1214 {
1215 if (cmd == "winmission")
1216 m_eventQueue->AddEvent(Event(EVENT_WIN));
1217
1218 if (cmd == "lostmission")
1219 m_eventQueue->AddEvent(Event(EVENT_LOST));
1220
1221 if (cmd == "trainerpilot")
1222 {
1223 m_cheatTrainerPilot = !m_cheatTrainerPilot;
1224 return;
1225 }
1226
1227 if (cmd == "fly")
1228 {
1229 m_researchDone[0] |= RESEARCH_FLY;
1230
1231 m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE));
1232 return;
1233 }
1234
1235 if (cmd == "allresearch")
1236 {
1237 m_researchDone[0] = -1; // all research are done
1238
1239 m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE));
1240 return;
1241 }
1242
1243 if (cmd == "allbuildings")
1244 {
1245 m_build = -1; // all buildings are available
1246
1247 m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE));
1248 return;
1249 }
1250
1251 if (cmd == "all")
1252 {
1253 m_researchDone[0] = -1; // all research are done
1254 m_build = -1; // all buildings are available
1255
1256 m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE));
1257 return;
1258 }
1259
1260 if (cmd == "nolimit")
1261 {
1262 m_terrain->SetFlyingMaxHeight(280.0f);
1263 return;
1264 }
1265
1266 if (cmd == "controller")
1267 {
1268 if (m_controller == nullptr)
1269 {
1270 GetLogger()->Error("No LevelController on the map to select\n");
1271 return;
1272 }
1273
1274 // Don't use SelectObject because it checks if the object is selectable
1275 if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT)
1276 StopDisplayVisit();
1277
1278 CObject* prev = DeselectAll();
1279 if (prev != nullptr && prev != m_controller)
1280 PushToSelectionHistory(prev);
1281
1282 SelectOneObject(m_controller, true);
1283 m_short->UpdateShortcuts();
1284 return;
1285 }
1286
1287 if (cmd == "photo1")
1288 {
1289 if (m_freePhotoPause == nullptr)
1290 {
1291 m_camera->SetType(Gfx::CAM_TYPE_FREE);
1292 m_freePhotoPause = m_pause->ActivatePause(PAUSE_ENGINE|PAUSE_PHOTO|PAUSE_OBJECT_UPDATES);
1293 }
1294 else
1295 {
1296 m_camera->SetType(Gfx::CAM_TYPE_BACK);
1297 m_pause->DeactivatePause(m_freePhotoPause);
1298 m_freePhotoPause = nullptr;
1299 }
1300 return;
1301 }
1302
1303 if (cmd == "photo2")
1304 {
1305 if (m_freePhotoPause == nullptr)
1306 {
1307 m_camera->SetType(Gfx::CAM_TYPE_FREE);
1308 DeselectAll(); // removes the control buttons
1309 m_freePhotoPause = m_pause->ActivatePause(PAUSE_ENGINE|PAUSE_PHOTO|PAUSE_OBJECT_UPDATES);
1310 m_map->ShowMap(false);
1311 m_displayText->HideText(true);
1312 }
1313 else
1314 {
1315 m_camera->SetType(Gfx::CAM_TYPE_BACK);
1316 m_pause->DeactivatePause(m_freePhotoPause);
1317 m_freePhotoPause = nullptr;
1318 m_map->ShowMap(m_mapShow);
1319 m_displayText->HideText(false);
1320 }
1321 return;
1322 }
1323
1324 int camtype;
1325 if (sscanf(cmd.c_str(), "camtype %d", &camtype) > 0)
1326 {
1327 m_camera->SetType(static_cast<Gfx::CameraType>(camtype));
1328 return;
1329 }
1330
1331 float camspeed;
1332 if (sscanf(cmd.c_str(), "camspeed %f", &camspeed) > 0)
1333 {
1334 m_camera->SetCameraSpeed(camspeed);
1335 return;
1336 }
1337
1338 if (cmd == "freecam")
1339 {
1340 m_camera->SetType(Gfx::CAM_TYPE_FREE);
1341 return;
1342 }
1343
1344 if (cmd == "noclip")
1345 {
1346 CObject* object = GetSelect();
1347 if (object != nullptr)
1348 object->SetCollisions(false);
1349 return;
1350 }
1351
1352 if (cmd == "clip")
1353 {
1354 CObject* object = GetSelect();
1355 if (object != nullptr)
1356 object->SetCollisions(true);
1357 return;
1358 }
1359
1360 if (cmd == "addhusky")
1361 {
1362 CObject* object = GetSelect();
1363 if (object != nullptr && object->Implements(ObjectInterfaceType::Shielded))
1364 dynamic_cast<CShieldedObject&>(*object).SetMagnifyDamage(dynamic_cast<CShieldedObject&>(*object).GetMagnifyDamage()*0.1f);
1365 return;
1366 }
1367
1368 if (cmd == "addfreezer")
1369 {
1370 CObject* object = GetSelect();
1371 if (object != nullptr && object->Implements(ObjectInterfaceType::JetFlying))
1372 dynamic_cast<CJetFlyingObject&>(*object).SetRange(dynamic_cast<CJetFlyingObject&>(*object).GetRange()*10.0f);
1373 return;
1374 }
1375
1376 if (cmd == "\155\157\157")
1377 {
1378 // VGhpcyBpcyBlYXN0ZXItZWdnIGFuZCBzbyBpdCBzaG91bGQgYmUgb2JmdXNjYXRlZCEgRG8gbm90
1379 // IGNsZWFuLXVwIHRoaXMgY29kZSEK
1380 GetLogger()->Info(" _________________________\n");
1381 GetLogger()->Info("< \x50\x6F\x6C\x73\x6B\x69 \x50\x6F\x72\x74\x61\x6C C\x6F\x6C\x6F\x62\x6F\x74\x61! \x3E\n");
1382 GetLogger()->Info(" -------------------------\n");
1383 GetLogger()->Info(" \x5C\x20\x20\x20\x5E\x5F\x5F\x5E\n");
1384 GetLogger()->Info(" \x20\x5C\x20\x20\x28\x6F\x6F\x29\x5C\x5F\x5F\x5F\x5F\x5F\x5F\x5F\n");
1385 GetLogger()->Info(" \x28\x5F\x5F\x29\x5C \x20\x20\x20\x20\x29\x5C\x2F\x5C\n");
1386 GetLogger()->Info(" \x20\x20\x20\x20\x7C|\x2D\x2D\x2D\x2D\x77\x20\x7C\n");
1387 GetLogger()->Info(" \x20\x20 \x7C\x7C\x20\x20\x20\x20 ||\n");
1388 }
1389
1390 if (cmd == "fullpower")
1391 {
1392 CObject* object = GetSelect();
1393 if (object != nullptr)
1394 {
1395 if (object->Implements(ObjectInterfaceType::Powered))
1396 {
1397 CObject* power = dynamic_cast<CPoweredObject&>(*object).GetPower();
1398 if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer))
1399 dynamic_cast<CPowerContainerObject&>(*power).SetEnergyLevel(1.0f);
1400 }
1401
1402 if (object->Implements(ObjectInterfaceType::Shielded))
1403 dynamic_cast<CShieldedObject&>(*object).SetShield(1.0f);
1404
1405 if (object->Implements(ObjectInterfaceType::JetFlying))
1406 dynamic_cast<CJetFlyingObject&>(*object).SetReactorRange(1.0f);
1407 }
1408 return;
1409 }
1410
1411 if (cmd == "fullenergy")
1412 {
1413 CObject* object = GetSelect();
1414
1415 if (object != nullptr)
1416 {
1417 if (object->Implements(ObjectInterfaceType::Powered))
1418 {
1419 CObject* power = dynamic_cast<CPoweredObject&>(*object).GetPower();
1420 if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer))
1421 dynamic_cast<CPowerContainerObject&>(*power).SetEnergyLevel(1.0f);
1422 }
1423 }
1424 return;
1425 }
1426
1427 if (cmd == "fullshield")
1428 {
1429 CObject* object = GetSelect();
1430 if (object != nullptr && object->Implements(ObjectInterfaceType::Shielded))
1431 dynamic_cast<CShieldedObject&>(*object).SetShield(1.0f);
1432 return;
1433 }
1434
1435 if (cmd == "fullrange")
1436 {
1437 CObject* object = GetSelect();
1438 if (object != nullptr)
1439 {
1440 if (object->Implements(ObjectInterfaceType::JetFlying))
1441 dynamic_cast<CJetFlyingObject&>(*object).SetReactorRange(1.0f);
1442 }
1443 return;
1444 }
1445 }
1446
1447 if (cmd == "debugmode")
1448 {
1449 if (m_app->IsDebugModeActive(DEBUG_ALL))
1450 {
1451 m_app->SetDebugModeActive(DEBUG_ALL, false);
1452 }
1453 else
1454 {
1455 m_app->SetDebugModeActive(DEBUG_ALL, true);
1456 }
1457 return;
1458 }
1459
1460 if (cmd == "showstat")
1461 {
1462 m_engine->SetShowStats(!m_engine->GetShowStats());
1463 return;
1464 }
1465
1466 if (cmd == "invui")
1467 {
1468 m_engine->SetRenderInterface(!m_engine->GetRenderInterface());
1469 return;
1470 }
1471
1472 if (cmd == "selectinsect")
1473 {
1474 m_cheatSelectInsect = !m_cheatSelectInsect;
1475 return;
1476 }
1477
1478 if (cmd == "showsoluce")
1479 {
1480 m_cheatShowSoluce = !m_cheatShowSoluce;
1481 m_ui->ShowSoluceUpdate();
1482 return;
1483 }
1484
1485 if (cmd == "allmission")
1486 {
1487 m_cheatAllMission = !m_cheatAllMission;
1488 m_ui->AllMissionUpdate();
1489 return;
1490 }
1491
1492 if (cmd == "invradar")
1493 {
1494 m_cheatRadar = !m_cheatRadar;
1495 return;
1496 }
1497
1498 float speed;
1499 if (sscanf(cmd.c_str(), "speed %f", &speed) > 0)
1500 {
1501 SetSpeed(speed);
1502 UpdateSpeedLabel();
1503 return;
1504 }
1505
1506 if (m_phase == PHASE_SIMUL)
1507 m_displayText->DisplayError(ERR_CMD, Math::Vector(0.0f,0.0f,0.0f));
1508 }
1509
1510
1511
1512 //! Returns the type of current movie
GetMainMovie()1513 MainMovieType CRobotMain::GetMainMovie()
1514 {
1515 return m_movie->GetType();
1516 }
1517
1518
1519 //! Clears the display of instructions
FlushDisplayInfo()1520 void CRobotMain::FlushDisplayInfo()
1521 {
1522 for (int i = 0; i < SATCOM_MAX; i++)
1523 {
1524 m_infoFilename[i][0] = 0;
1525 }
1526 strcpy(m_infoFilename[SATCOM_OBJECT], "objects.txt");
1527 }
1528
1529 //! Beginning of the displaying of instructions.
1530 //! index: SATCOM_*
StartDisplayInfo(int index,bool movie)1531 void CRobotMain::StartDisplayInfo(int index, bool movie)
1532 {
1533 if (m_cmdEdit || m_satComLock || m_lockedSatCom) return;
1534
1535 CObject* obj = GetSelect();
1536 bool human = obj != nullptr && obj->GetType() == OBJECT_HUMAN;
1537
1538 if (!m_editLock && movie && !m_movie->IsExist() && human)
1539 {
1540 assert(obj->Implements(ObjectInterfaceType::Movable));
1541 if (dynamic_cast<CMovableObject&>(*obj).GetMotion()->GetAction() == -1)
1542 {
1543 m_movieInfoIndex = index;
1544 m_movie->Start(MM_SATCOMopen, 2.5f);
1545 m_satcomMoviePause = m_pause->ActivatePause(PAUSE_ENGINE|PAUSE_HIDE_SHORTCUTS);
1546 m_infoObject = DeselectAll(); // removes the control buttons
1547 m_displayText->HideText(true);
1548 return;
1549 }
1550 }
1551
1552 if (m_movie->IsExist())
1553 {
1554 m_movie->Stop();
1555 m_pause->DeactivatePause(m_satcomMoviePause);
1556 m_satcomMoviePause = nullptr;
1557 SelectObject(m_infoObject, false); // hands over the command buttons
1558 m_displayText->HideText(false);
1559 }
1560
1561 StartDisplayInfo(m_infoFilename[index], index);
1562 }
1563
1564 //! Beginning of the displaying of instructions
StartDisplayInfo(const std::string & filename,int index)1565 void CRobotMain::StartDisplayInfo(const std::string& filename, int index)
1566 {
1567 if (m_cmdEdit) return;
1568
1569 m_movieInfoIndex = -1;
1570 ClearInterface(); // removes setting evidence and tooltip
1571
1572 if (!m_editLock)
1573 {
1574 m_infoObject = DeselectAll(); // removes the control buttons
1575 m_displayText->HideText(true);
1576 m_sound->MuteAll(true);
1577 }
1578
1579 bool soluce = m_ui->GetSceneSoluce();
1580
1581 m_displayInfo = MakeUnique<Ui::CDisplayInfo>();
1582 m_displayInfo->StartDisplayInfo(filename, index, soluce);
1583 m_displayInfo->SetPosition(0);
1584 }
1585
1586 //! End of displaying of instructions
StopDisplayInfo()1587 void CRobotMain::StopDisplayInfo()
1588 {
1589 if (m_cmdEdit) return;
1590
1591 if (m_movieInfoIndex != -1) // film to read the SatCom?
1592 m_movie->Start(MM_SATCOMclose, 2.0f);
1593
1594 m_displayInfo->StopDisplayInfo();
1595
1596 m_displayInfo.reset();
1597
1598 if (!m_editLock)
1599 {
1600 SelectObject(m_infoObject, false); // gives the command buttons
1601 m_displayText->HideText(false);
1602
1603 m_sound->MuteAll(false);
1604 }
1605
1606 if (m_infoUsed == 0)
1607 m_displayText->ClearText(); // removes message "see SatCom ..."
1608 m_infoUsed ++;
1609 }
1610
1611 //! Returns the name of the text display
GetDisplayInfoName(int index)1612 char* CRobotMain::GetDisplayInfoName(int index)
1613 {
1614 return m_infoFilename[index];
1615 }
1616
1617
1618 //! Beginning of a dialogue during the game
StartSuspend()1619 void CRobotMain::StartSuspend()
1620 {
1621 if (m_suspend != nullptr) return; // already suspended
1622 if (!IsPhaseWithWorld(m_phase)) return;
1623 GetLogger()->Info("Start suspend\n");
1624
1625 m_sound->MuteAll(true);
1626 ClearInterface();
1627 m_suspend = m_pause->ActivatePause(PAUSE_ENGINE | PAUSE_HIDE_SHORTCUTS | PAUSE_MUTE_SOUND | PAUSE_CAMERA);
1628 m_engine->SetOverFront(false); // over flat behind
1629 CreateShortcuts();
1630
1631 m_map->ShowMap(false);
1632 m_infoObject = DeselectAll(); // removes the control buttons
1633 m_displayText->HideText(true);
1634
1635 m_engine->EnablePauseBlur();
1636 }
1637
1638 //! End of dialogue during the game
StopSuspend()1639 void CRobotMain::StopSuspend()
1640 {
1641 if (m_suspend == nullptr) return; // not suspended
1642 GetLogger()->Info("Stop suspend\n");
1643
1644 m_sound->MuteAll(false);
1645 ClearInterface();
1646 m_pause->DeactivatePause(m_suspend);
1647 m_suspend = nullptr;
1648 m_engine->SetOverFront(true); // over flat front
1649 CreateShortcuts();
1650
1651 if (m_infoObject != nullptr)
1652 SelectObject(m_infoObject, false); // gives the command buttons
1653 m_map->ShowMap(m_mapShow);
1654 m_displayText->HideText(false);
1655
1656 m_engine->DisablePauseBlur();
1657 }
1658
1659
1660 //! Returns the absolute time of the game
GetGameTime()1661 float CRobotMain::GetGameTime()
1662 {
1663 return m_gameTime;
1664 }
1665
1666
1667 //! Start of the visit instead of an error
StartDisplayVisit(EventType event)1668 void CRobotMain::StartDisplayVisit(EventType event)
1669 {
1670 if (m_editLock) return;
1671
1672 if (m_visitPause)
1673 {
1674 m_pause->DeactivatePause(m_visitPause);
1675 m_visitPause = nullptr;
1676 }
1677
1678 Ui::CWindow* pw = static_cast<Ui::CWindow*>(m_interface->SearchControl(EVENT_WINDOW2));
1679 if (pw == nullptr) return;
1680
1681 if (event == EVENT_NULL) // visit by keyboard shortcut?
1682 {
1683 int i;
1684 if (m_visitLast != EVENT_NULL) // already a current visit?
1685 i = m_visitLast-EVENT_DT_VISIT0;
1686 else
1687 i = Ui::MAXDTLINE;
1688
1689 // Seeks the last.
1690 for (int j = 0; j < Ui::MAXDTLINE; j++)
1691 {
1692 i --;
1693 if (i < 0) i = Ui::MAXDTLINE-1;
1694
1695 Ui::CButton* button = static_cast<Ui::CButton*>(pw->SearchControl(static_cast<EventType>(EVENT_DT_VISIT0+i)));
1696 if (button == nullptr || !button->TestState(Ui::STATE_ENABLE)) continue;
1697
1698 Ui::CGroup* group = static_cast<Ui::CGroup*>(pw->SearchControl(static_cast<EventType>(EVENT_DT_GROUP0+i)));
1699 if (group != nullptr)
1700 {
1701 event = static_cast<EventType>(EVENT_DT_VISIT0+i);
1702 break;
1703 }
1704 }
1705 }
1706 if (event == EVENT_NULL)
1707 {
1708 m_sound->Play(SOUND_TZOING); // nothing to do!
1709 return;
1710 }
1711
1712 m_visitLast = event;
1713
1714 ClearInterface(); // removes setting evidence and tooltip
1715
1716 if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT) // already a current visit?
1717 {
1718 m_camera->StopVisit();
1719 m_displayText->ClearVisit();
1720 }
1721 else
1722 {
1723 m_visitObject = DeselectAll(); // removes the control buttons
1724 }
1725
1726 // Creates the "continue" button.
1727 if (m_interface->SearchControl(EVENT_DT_END) == nullptr)
1728 {
1729 Math::Point pos, dim;
1730 pos.x = 10.0f/640.0f;
1731 pos.y = 10.0f/480.0f;
1732 dim.x = 50.0f/640.0f;
1733 dim.y = 50.0f/480.0f;
1734 m_interface->CreateButton(pos, dim, 16, EVENT_DT_END);
1735 }
1736
1737 // Creates the arrow to show the place.
1738 if (m_visitArrow != nullptr)
1739 {
1740 CObjectManager::GetInstancePointer()->DeleteObject(m_visitArrow);
1741 m_visitArrow = nullptr;
1742 }
1743
1744 ObjectCreateParams params;
1745 params.pos = m_displayText->GetVisitGoal(event);
1746 params.type = OBJECT_SHOW;
1747 params.height = 10.0f;
1748 m_visitArrow = m_objMan->CreateObject(params);
1749
1750 m_visitPos = m_visitArrow->GetPosition();
1751 m_visitPosArrow = m_visitPos;
1752 m_visitPosArrow.y += m_displayText->GetVisitHeight(event);
1753 m_visitArrow->SetPosition(m_visitPosArrow);
1754
1755 m_visitTime = 0.0;
1756 m_visitParticle = 0.0f;
1757
1758 m_particle->DeleteParticle(Gfx::PARTISHOW);
1759
1760 m_camera->StartVisit(m_displayText->GetVisitGoal(event),
1761 m_displayText->GetVisitDist(event));
1762 m_displayText->SetVisit(event);
1763 m_visitPause = m_pause->ActivatePause(PAUSE_ENGINE);
1764 }
1765
1766 //! Move the arrow to visit
FrameVisit(float rTime)1767 void CRobotMain::FrameVisit(float rTime)
1768 {
1769 if (m_visitArrow == nullptr) return;
1770
1771 // Moves the arrow.
1772 m_visitTime += rTime;
1773
1774 Math::Vector pos = m_visitPosArrow;
1775 pos.y += 1.5f+sinf(m_visitTime*4.0f)*4.0f;
1776 m_visitArrow->SetPosition(pos);
1777 m_visitArrow->SetRotationY(m_visitTime*2.0f);
1778
1779 // Manages the particles "arrows".
1780 m_visitParticle -= rTime;
1781 if (m_visitParticle <= 0.0f)
1782 {
1783 m_visitParticle = 1.5f;
1784
1785 pos = m_visitPos;
1786 float level = m_terrain->GetFloorLevel(pos)+2.0f;
1787 if (pos.y < level) pos.y = level; // not below the ground
1788 Math::Vector speed(0.0f, 0.0f, 0.0f);
1789 Math::Point dim;
1790 dim.x = 30.0f;
1791 dim.y = dim.x;
1792 m_particle->CreateParticle(pos, speed, dim, Gfx::PARTISHOW, 2.0f);
1793 }
1794 }
1795
1796 //! End of the visit instead of an error
StopDisplayVisit()1797 void CRobotMain::StopDisplayVisit()
1798 {
1799 m_visitLast = EVENT_NULL;
1800
1801 // Removes the button.
1802 m_interface->DeleteControl(EVENT_DT_END);
1803
1804 // Removes the arrow.
1805 if (m_visitArrow != nullptr)
1806 {
1807 CObjectManager::GetInstancePointer()->DeleteObject(m_visitArrow);
1808 m_visitArrow = nullptr;
1809 }
1810
1811 // Removes particles "arrows".
1812 m_particle->DeleteParticle(Gfx::PARTISHOW);
1813
1814 m_camera->StopVisit();
1815 m_displayText->ClearVisit();
1816 m_pause->DeactivatePause(m_visitPause);
1817 m_visitPause = nullptr;
1818 if (m_visitObject != nullptr)
1819 {
1820 SelectObject(m_visitObject, false); // gives the command buttons
1821 m_visitObject = nullptr;
1822 }
1823 }
1824
1825
1826
UpdateShortcuts()1827 void CRobotMain::UpdateShortcuts()
1828 {
1829 m_short->UpdateShortcuts();
1830 }
1831
GetSelectObject()1832 CObject* CRobotMain::GetSelectObject()
1833 {
1834 if (m_selectObject != nullptr) return m_selectObject;
1835 return SearchHuman();
1836 }
1837
DeselectAll()1838 CObject* CRobotMain::DeselectAll()
1839 {
1840 CObject* prev = nullptr;
1841 for (CObject* obj : m_objMan->GetAllObjects())
1842 {
1843 if (!obj->Implements(ObjectInterfaceType::Controllable)) continue;
1844 auto controllableObj = dynamic_cast<CControllableObject*>(obj);
1845 if (controllableObj->GetSelect()) prev = obj;
1846 controllableObj->SetSelect(false);
1847 }
1848 return prev;
1849 }
1850
1851 //! Selects an object, without attending to deselect the rest
SelectOneObject(CObject * obj,bool displayError)1852 void CRobotMain::SelectOneObject(CObject* obj, bool displayError)
1853 {
1854 assert(obj->Implements(ObjectInterfaceType::Controllable));
1855 dynamic_cast<CControllableObject&>(*obj).SetSelect(true, displayError);
1856 m_camera->SetControllingObject(obj);
1857
1858 ObjectType type = obj->GetType();
1859 if ( type == OBJECT_HUMAN ||
1860 type == OBJECT_MOBILEfa ||
1861 type == OBJECT_MOBILEta ||
1862 type == OBJECT_MOBILEwa ||
1863 type == OBJECT_MOBILEia ||
1864 type == OBJECT_MOBILEfb ||
1865 type == OBJECT_MOBILEtb ||
1866 type == OBJECT_MOBILEwb ||
1867 type == OBJECT_MOBILEib ||
1868 type == OBJECT_MOBILEfc ||
1869 type == OBJECT_MOBILEtc ||
1870 type == OBJECT_MOBILEwc ||
1871 type == OBJECT_MOBILEic ||
1872 type == OBJECT_MOBILEfi ||
1873 type == OBJECT_MOBILEti ||
1874 type == OBJECT_MOBILEwi ||
1875 type == OBJECT_MOBILEii ||
1876 type == OBJECT_MOBILEfs ||
1877 type == OBJECT_MOBILEts ||
1878 type == OBJECT_MOBILEws ||
1879 type == OBJECT_MOBILEis ||
1880 type == OBJECT_MOBILErt ||
1881 type == OBJECT_MOBILErc ||
1882 type == OBJECT_MOBILErr ||
1883 type == OBJECT_MOBILErs ||
1884 type == OBJECT_MOBILEsa ||
1885 type == OBJECT_MOBILEft ||
1886 type == OBJECT_MOBILEtt ||
1887 type == OBJECT_MOBILEwt ||
1888 type == OBJECT_MOBILEit ||
1889 type == OBJECT_MOBILErp ||
1890 type == OBJECT_MOBILEst ||
1891 type == OBJECT_MOBILEdr ||
1892 type == OBJECT_APOLLO2 )
1893 {
1894 m_camera->SetType(dynamic_cast<CControllableObject&>(*obj).GetCameraType());
1895 }
1896 else
1897 {
1898 m_camera->SetType(Gfx::CAM_TYPE_BACK);
1899 }
1900 }
1901
SelectObject(CObject * obj,bool displayError)1902 bool CRobotMain::SelectObject(CObject* obj, bool displayError)
1903 {
1904 if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT)
1905 StopDisplayVisit();
1906
1907 if (m_movieLock || m_editLock) return false;
1908 if (m_movie->IsExist()) return false;
1909 if (obj != nullptr &&
1910 (!obj->Implements(ObjectInterfaceType::Controllable) || !(dynamic_cast<CControllableObject&>(*obj).GetSelectable() || m_cheatSelectInsect))) return false;
1911
1912 if (m_missionType == MISSION_CODE_BATTLE && m_codeBattleStarted && m_codeBattleSpectator)
1913 {
1914 DeselectAll();
1915
1916 // During code battles, only change camera
1917 m_camera->SetControllingObject(obj);
1918 if (obj != nullptr)
1919 {
1920 m_camera->SetType(Gfx::CAM_TYPE_PLANE);
1921 }
1922 else
1923 {
1924 m_camera->SetType(Gfx::CAM_TYPE_FREE);
1925 }
1926 }
1927 else
1928 {
1929 if (obj == nullptr) return false;
1930 CObject* prev = DeselectAll();
1931
1932 if (prev != nullptr && prev != obj)
1933 PushToSelectionHistory(prev);
1934
1935 SelectOneObject(obj, displayError);
1936 }
1937 m_short->UpdateShortcuts();
1938 return true;
1939 }
1940
DeselectObject()1941 bool CRobotMain::DeselectObject()
1942 {
1943 DeselectAll();
1944
1945 CObject* obj = PopFromSelectionHistory();
1946 if (obj == nullptr)
1947 obj = SearchHuman();
1948
1949 if (obj != nullptr)
1950 SelectOneObject(obj);
1951 else
1952 m_camera->SetType(Gfx::CAM_TYPE_FREE);
1953
1954 m_short->UpdateShortcuts();
1955 return true;
1956 }
1957
1958 //! Quickly removes all objects
DeleteAllObjects()1959 void CRobotMain::DeleteAllObjects()
1960 {
1961 m_engine->GetPyroManager()->DeleteAll();
1962
1963 // Removes the arrow.
1964 if (m_visitArrow != nullptr)
1965 {
1966 CObjectManager::GetInstancePointer()->DeleteObject(m_visitArrow);
1967 m_visitArrow = nullptr;
1968 }
1969
1970 for (int i = 0; i < MAXSHOWLIMIT; i++)
1971 FlushShowLimit(i);
1972
1973 m_objMan->DeleteAllObjects();
1974 }
1975
SearchHuman()1976 CObject* CRobotMain::SearchHuman()
1977 {
1978 return m_objMan->FindNearest(nullptr, OBJECT_HUMAN);
1979 }
1980
GetSelect()1981 CObject* CRobotMain::GetSelect()
1982 {
1983 for (CObject* obj : m_objMan->GetAllObjects())
1984 {
1985 if (!obj->Implements(ObjectInterfaceType::Controllable)) continue;
1986 if (dynamic_cast<CControllableObject&>(*obj).GetSelect())
1987 return obj;
1988 }
1989 return nullptr;
1990 }
1991
1992 //! Detects the object aimed by the mouse
DetectObject(Math::Point pos)1993 CObject* CRobotMain::DetectObject(Math::Point pos)
1994 {
1995 Math::Vector p;
1996 int objRank = m_engine->DetectObject(pos, p);
1997
1998 for (CObject* obj : m_objMan->GetAllObjects())
1999 {
2000 if (!obj->GetDetectable()) continue;
2001
2002 CObject* transporter = nullptr;
2003 if (obj->Implements(ObjectInterfaceType::Transportable))
2004 transporter = dynamic_cast<CTransportableObject&>(*obj).GetTransporter();
2005
2006 if (transporter != nullptr && !transporter->GetDetectable()) continue;
2007 if (obj->GetProxyActivate()) continue;
2008
2009 CObject* target = obj;
2010 if (obj->Implements(ObjectInterfaceType::PowerContainer) && obj->Implements(ObjectInterfaceType::Transportable))
2011 {
2012 target = dynamic_cast<CTransportableObject&>(*obj).GetTransporter(); // battery connected
2013 if (target == nullptr)
2014 {
2015 target = obj; // standalone battery
2016 }
2017 else
2018 {
2019 if (!target->Implements(ObjectInterfaceType::Powered) || dynamic_cast<CPoweredObject&>(*target).GetPower() != obj)
2020 {
2021 // transported, but not in the power slot
2022 target = obj;
2023 }
2024 }
2025 }
2026
2027 if (!obj->Implements(ObjectInterfaceType::Old)) continue;
2028 for (int j = 0; j < OBJECTMAXPART; j++)
2029 {
2030 int rank = obj->GetObjectRank(j);
2031 if (rank == -1) continue;
2032 if (rank != objRank) continue;
2033 return target;
2034 }
2035 }
2036 return nullptr;
2037 }
2038
2039
2040 //! Deletes the selected object
DestroySelectedObject()2041 bool CRobotMain::DestroySelectedObject()
2042 {
2043 CObject* obj = GetSelect();
2044 if (obj == nullptr) return false;
2045 assert(obj->Implements(ObjectInterfaceType::Controllable));
2046
2047 m_engine->GetPyroManager()->Create(Gfx::PT_FRAGT, obj);
2048
2049 dynamic_cast<CControllableObject&>(*obj).SetSelect(false); // deselects the object
2050 m_camera->SetType(Gfx::CAM_TYPE_EXPLO);
2051 DeselectAll();
2052 RemoveFromSelectionHistory(obj);
2053
2054 return true;
2055 }
2056
2057
2058 //! Removes setting evidence of the object with the mouse hovers over
HiliteClear()2059 void CRobotMain::HiliteClear()
2060 {
2061 ClearTooltip();
2062 m_tooltipName.clear(); // really removes the tooltip
2063
2064 if (!m_hilite) return;
2065
2066 int rank = -1;
2067 m_engine->SetHighlightRank(&rank); // nothing more selected
2068
2069 for (CObject* obj : m_objMan->GetAllObjects())
2070 {
2071 if (!obj->Implements(ObjectInterfaceType::Controllable)) continue;
2072 dynamic_cast<CControllableObject&>(*obj).SetHighlight(false);
2073 }
2074 m_map->SetHighlight(nullptr);
2075 m_short->SetHighlight(nullptr);
2076
2077 m_hilite = false;
2078 }
2079
2080 //! Highlights the object with the mouse hovers over
HiliteObject(Math::Point pos)2081 void CRobotMain::HiliteObject(Math::Point pos)
2082 {
2083 if (m_fixScene && m_phase != PHASE_APPERANCE) return;
2084 if (m_movieLock) return;
2085 if (m_movie->IsExist()) return;
2086 if (m_app->GetMouseMode() == MOUSE_NONE) return;
2087
2088 ClearInterface(); // removes setting evidence and tooltip
2089
2090 CObject* obj = m_short->DetectShort(pos);
2091
2092 std::string interfaceTooltipName;
2093 if (m_settings->GetTooltips() && m_interface->GetTooltip(pos, interfaceTooltipName))
2094 {
2095 m_tooltipPos = pos;
2096 m_tooltipName = interfaceTooltipName;
2097 m_tooltipTime = 0.0f;
2098 if (obj == nullptr) return;
2099 }
2100
2101 if (m_suspend != nullptr) return;
2102
2103 if (obj == nullptr)
2104 {
2105 bool inMap = false;
2106 obj = m_map->DetectMap(pos, inMap);
2107 if (obj == nullptr)
2108 {
2109 if (inMap) return;
2110
2111 obj = DetectObject(pos);
2112
2113 if ((m_camera->GetType() == Gfx::CAM_TYPE_ONBOARD) &&
2114 (m_camera->GetControllingObject() == obj))
2115 return;
2116 }
2117 }
2118
2119 if (obj != nullptr)
2120 {
2121 if (m_settings->GetTooltips())
2122 {
2123 std::string objectTooltipName = obj->GetTooltipText();
2124 if (!objectTooltipName.empty())
2125 {
2126 m_tooltipPos = pos;
2127 m_tooltipName = objectTooltipName;
2128 m_tooltipTime = 0.0f;
2129 }
2130 }
2131
2132 if (obj->Implements(ObjectInterfaceType::Controllable) && (dynamic_cast<CControllableObject&>(*obj).GetSelectable() || m_cheatSelectInsect))
2133 {
2134 if (dynamic_cast<CControllableObject&>(*obj).GetSelectable())
2135 {
2136 // Don't highlight objects that would not be selectable without selectinsect
2137 dynamic_cast<CControllableObject&>(*obj).SetHighlight(true);
2138 }
2139 m_map->SetHighlight(obj);
2140 m_short->SetHighlight(obj);
2141 m_hilite = true;
2142 }
2143 }
2144 }
2145
2146 //! Highlights the object with the mouse hovers over
HiliteFrame(float rTime)2147 void CRobotMain::HiliteFrame(float rTime)
2148 {
2149 if (m_fixScene && m_phase != PHASE_APPERANCE) return;
2150 if (m_movieLock) return;
2151 if (m_movie->IsExist()) return;
2152
2153 m_tooltipTime += rTime;
2154
2155 ClearTooltip();
2156
2157 if (m_tooltipTime >= 0.2f && !m_tooltipName.empty())
2158 {
2159 CreateTooltip(m_tooltipPos, m_tooltipName);
2160 }
2161 }
2162
2163 //! Creates a tooltip
CreateTooltip(Math::Point pos,const std::string & text)2164 void CRobotMain::CreateTooltip(Math::Point pos, const std::string& text)
2165 {
2166 Math::Point corner;
2167 corner.x = pos.x+0.022f;
2168 corner.y = pos.y-0.052f;
2169
2170 Math::Point start, end;
2171
2172 m_engine->GetText()->SizeText(text, Gfx::FONT_COMMON, Gfx::FONT_SIZE_SMALL,
2173 corner, Gfx::TEXT_ALIGN_LEFT,
2174 start, end);
2175
2176 start.x -= 0.010f;
2177 start.y -= 0.006f;
2178 end.x += 0.010f;
2179 end.y += 0.008f; // small'ish margin
2180
2181 pos.x = start.x;
2182 pos.y = start.y;
2183
2184 Math::Point dim;
2185 dim.x = end.x-start.x;
2186 dim.y = end.y-start.y;
2187
2188 Math::Point offset;
2189 offset.x = 0.0f;
2190 offset.y = 0.0f;
2191 if (pos.x+dim.x > 1.0f) offset.x = 1.0f-(pos.x+dim.x);
2192 if (pos.y < 0.0f) offset.y = -pos.y;
2193
2194 corner.x += offset.x;
2195 corner.y += offset.y;
2196 pos.x += offset.x;
2197 pos.y += offset.y;
2198
2199 m_interface->CreateWindows(pos, dim, 1, EVENT_TOOLTIP);
2200
2201 Ui::CWindow* pw = static_cast<Ui::CWindow*>(m_interface->SearchControl(EVENT_TOOLTIP));
2202 if (pw != nullptr)
2203 {
2204 pw->SetState(Ui::STATE_SHADOW);
2205 pw->SetTrashEvent(false);
2206
2207 pos.y -= m_engine->GetText()->GetHeight(Gfx::FONT_COMMON, Gfx::FONT_SIZE_SMALL) / 2.0f;
2208 pw->CreateLabel(pos, dim, -1, EVENT_LABEL2, text);
2209 }
2210 }
2211
2212 //! Clears the previous tooltip
ClearTooltip()2213 void CRobotMain::ClearTooltip()
2214 {
2215 m_interface->DeleteControl(EVENT_TOOLTIP);
2216 }
2217
2218
2219 //! Displays help for an object
HelpObject()2220 void CRobotMain::HelpObject()
2221 {
2222 CObject* obj = GetSelect();
2223 if (obj == nullptr) return;
2224
2225 std::string filename = GetHelpFilename(obj->GetType());
2226 if (filename.empty()) return;
2227
2228 StartDisplayInfo(filename, -1);
2229 }
2230
2231
2232 //! Change the mode of the camera
ChangeCamera()2233 void CRobotMain::ChangeCamera()
2234 {
2235 CObject* obj = GetSelect();
2236 if (obj == nullptr) return;
2237 assert(obj->Implements(ObjectInterfaceType::Controllable));
2238 auto controllableObj = dynamic_cast<CControllableObject*>(obj);
2239
2240 if (controllableObj->GetCameraLock()) return;
2241
2242 ObjectType oType = obj->GetType();
2243 Gfx::CameraType type = controllableObj->GetCameraType();
2244
2245 if ( oType != OBJECT_HUMAN &&
2246 oType != OBJECT_TECH &&
2247 oType != OBJECT_MOBILEfa &&
2248 oType != OBJECT_MOBILEta &&
2249 oType != OBJECT_MOBILEwa &&
2250 oType != OBJECT_MOBILEia &&
2251 oType != OBJECT_MOBILEfb &&
2252 oType != OBJECT_MOBILEtb &&
2253 oType != OBJECT_MOBILEwb &&
2254 oType != OBJECT_MOBILEib &&
2255 oType != OBJECT_MOBILEfc &&
2256 oType != OBJECT_MOBILEtc &&
2257 oType != OBJECT_MOBILEwc &&
2258 oType != OBJECT_MOBILEic &&
2259 oType != OBJECT_MOBILEfi &&
2260 oType != OBJECT_MOBILEti &&
2261 oType != OBJECT_MOBILEwi &&
2262 oType != OBJECT_MOBILEii &&
2263 oType != OBJECT_MOBILEfs &&
2264 oType != OBJECT_MOBILEts &&
2265 oType != OBJECT_MOBILEws &&
2266 oType != OBJECT_MOBILEis &&
2267 oType != OBJECT_MOBILErt &&
2268 oType != OBJECT_MOBILErc &&
2269 oType != OBJECT_MOBILErr &&
2270 oType != OBJECT_MOBILErs &&
2271 oType != OBJECT_MOBILEsa &&
2272 oType != OBJECT_MOBILEtg &&
2273 oType != OBJECT_MOBILEft &&
2274 oType != OBJECT_MOBILEtt &&
2275 oType != OBJECT_MOBILEwt &&
2276 oType != OBJECT_MOBILEit &&
2277 oType != OBJECT_MOBILErp &&
2278 oType != OBJECT_MOBILEst &&
2279 oType != OBJECT_MOBILEdr &&
2280 oType != OBJECT_APOLLO2 ) return;
2281
2282 if (oType == OBJECT_MOBILEdr) // designer?
2283 {
2284 if (type == Gfx::CAM_TYPE_PLANE ) type = Gfx::CAM_TYPE_BACK;
2285 else if (type == Gfx::CAM_TYPE_BACK ) type = Gfx::CAM_TYPE_PLANE;
2286 }
2287 else if (controllableObj->GetTrainer()) // trainer?
2288 {
2289 if (type == Gfx::CAM_TYPE_ONBOARD) type = Gfx::CAM_TYPE_FIX;
2290 else if (type == Gfx::CAM_TYPE_FIX ) type = Gfx::CAM_TYPE_PLANE;
2291 else if (type == Gfx::CAM_TYPE_PLANE ) type = Gfx::CAM_TYPE_BACK;
2292 else if (type == Gfx::CAM_TYPE_BACK ) type = Gfx::CAM_TYPE_ONBOARD;
2293 }
2294 else
2295 {
2296 if (type == Gfx::CAM_TYPE_ONBOARD) type = Gfx::CAM_TYPE_BACK;
2297 else if (type == Gfx::CAM_TYPE_BACK ) type = Gfx::CAM_TYPE_ONBOARD;
2298 }
2299
2300 controllableObj->SetCameraType(type);
2301 m_camera->SetType(type);
2302 }
2303
2304
2305 //! Cancels the current movie
AbortMovie()2306 void CRobotMain::AbortMovie()
2307 {
2308 for (CObject* obj : m_objMan->GetAllObjects())
2309 {
2310 if (obj->Implements(ObjectInterfaceType::Old))
2311 {
2312 CAuto* automat = obj->GetAuto();
2313 if (automat != nullptr)
2314 automat->Abort();
2315 }
2316 }
2317 }
2318
2319
TimeFormat(float time)2320 static std::string TimeFormat(float time)
2321 {
2322 int minutes = static_cast<int>(floor(time/60));
2323 double time2 = fmod(time, 60);
2324 double seconds;
2325 double fraction = modf(time2, &seconds)*100;
2326 std::ostringstream sstream;
2327 sstream << std::setfill('0') << std::setw(2) << minutes << ":" << std::setfill('0') << std::setw(2) << floor(seconds) << "." << std::setfill('0') << std::setw(2) << floor(fraction);
2328 return sstream.str();
2329 }
2330
2331 //! Updates the text information
UpdateInfoText()2332 void CRobotMain::UpdateInfoText()
2333 {
2334 if (m_phase == PHASE_SIMUL)
2335 {
2336 CObject* obj = GetSelect();
2337 if (obj != nullptr)
2338 {
2339 Math::Vector pos = obj->GetPosition();
2340 m_engine->SetStatisticPos(pos / g_unit);
2341 }
2342 }
2343 m_engine->SetTimerDisplay(m_missionTimerEnabled && m_missionTimerStarted ? TimeFormat(m_missionTimer) : "");
2344 }
2345
2346
2347 //! Initializes the view
InitEye()2348 void CRobotMain::InitEye()
2349 {
2350 if (m_phase == PHASE_SIMUL)
2351 m_camera->Init(Math::Vector( 0.0f, 10.0f, 0.0f),
2352 Math::Vector(10.0f, 5.0f, 0.0f), 0.0f);
2353 }
2354
2355 //! Advances the entire scene
EventFrame(const Event & event)2356 bool CRobotMain::EventFrame(const Event &event)
2357 {
2358 m_time += event.rTime;
2359
2360 m_water->EventProcess(event);
2361 m_cloud->EventProcess(event);
2362 m_lightning->EventProcess(event);
2363 m_planet->EventProcess(event);
2364
2365 UpdateDebugCrashSpheres();
2366
2367 Ui::CMap* pm = nullptr;
2368 Ui::CWindow* pw = static_cast<Ui::CWindow*>(m_interface->SearchControl(EVENT_WINDOW1));
2369 if (pw == nullptr)
2370 {
2371 pm = nullptr;
2372 }
2373 else
2374 {
2375 pm = static_cast<Ui::CMap*>(pw->SearchControl(EVENT_OBJECT_MAP));
2376 if (pm != nullptr) pm->FlushObject();
2377 }
2378
2379 CObject* toto = nullptr;
2380 if (!m_pause->IsPauseType(PAUSE_OBJECT_UPDATES))
2381 {
2382 // Advances all the robots, but not toto.
2383 for (CObject* obj : m_objMan->GetAllObjects())
2384 {
2385 if (pm != nullptr)
2386 pm->UpdateObject(obj);
2387
2388 if (IsObjectBeingTransported(obj))
2389 continue;
2390
2391 if (obj->GetType() == OBJECT_TOTO)
2392 toto = obj;
2393 else if (obj->Implements(ObjectInterfaceType::Interactive))
2394 dynamic_cast<CInteractiveObject&>(*obj).EventProcess(event);
2395
2396 if ( obj->GetProxyActivate() ) // active if it is near?
2397 {
2398 Math::Vector eye = m_engine->GetLookatPt();
2399 float dist = Math::Distance(eye, obj->GetPosition());
2400 if ( dist < obj->GetProxyDistance() )
2401 {
2402 obj->SetProxyActivate(false);
2403 CreateShortcuts();
2404 m_sound->Play(SOUND_FINDING);
2405 m_engine->GetPyroManager()->Create(Gfx::PT_FINDING, obj, 0.0f);
2406 DisplayError(INFO_FINDING, obj);
2407 }
2408 }
2409 }
2410 // Advances all objects transported by robots.
2411 for (CObject* obj : m_objMan->GetAllObjects())
2412 {
2413 if (! IsObjectBeingTransported(obj))
2414 continue;
2415
2416 if (obj->Implements(ObjectInterfaceType::Interactive))
2417 dynamic_cast<CInteractiveObject&>(*obj).EventProcess(event);
2418 }
2419
2420 m_engine->GetPyroManager()->EventProcess(event);
2421 }
2422
2423 // The camera follows the object, because its position
2424 // may depend on the selected object (Gfx::CAM_TYPE_ONBOARD or Gfx::CAM_TYPE_BACK).
2425 if (m_phase == PHASE_SIMUL && !m_editFull)
2426 {
2427 m_camera->EventProcess(event);
2428
2429 if (m_engine->GetFog())
2430 m_camera->SetOverBaseColor(m_particle->GetFogColor(m_engine->GetEyePt()));
2431 }
2432 if (m_phase == PHASE_APPERANCE ||
2433 m_phase == PHASE_WIN ||
2434 m_phase == PHASE_LOST)
2435 {
2436 m_camera->EventProcess(event);
2437 }
2438
2439 // Advances toto following the camera, because its position depends on the camera.
2440 if (toto != nullptr)
2441 dynamic_cast<CInteractiveObject&>(*toto).EventProcess(event);
2442
2443 // NOTE: m_movieLock is set only after the first update of CAutoBase finishes
2444
2445 if (m_phase == PHASE_SIMUL)
2446 {
2447 if (!m_immediatSatCom && !m_beginSatCom && !m_movieLock)
2448 {
2449 m_displayText->DisplayError(INFO_BEGINSATCOM, Math::Vector(0.0f, 0.0f, 0.0f));
2450 m_beginSatCom = true; // message appears
2451 }
2452
2453 if (!m_pause->IsPauseType(PAUSE_ENGINE) && !m_movieLock)
2454 {
2455 m_gameTime += event.rTime;
2456 m_gameTimeAbsolute += m_app->GetRealRelTime() / 1e9f;
2457
2458 if (m_missionTimerStarted)
2459 m_missionTimer += event.rTime;
2460
2461 if (m_autosave && m_gameTimeAbsolute >= m_autosaveLast + (m_autosaveInterval * 60))
2462 {
2463 if (m_levelCategory == LevelCategory::Missions ||
2464 m_levelCategory == LevelCategory::FreeGame ||
2465 m_levelCategory == LevelCategory::GamePlus ||
2466 m_levelCategory == LevelCategory::CustomLevels)
2467 {
2468 if (!IOIsBusy() && m_missionType != MISSION_CODE_BATTLE)
2469 {
2470 m_autosaveLast = m_gameTimeAbsolute;
2471 Autosave();
2472 }
2473 }
2474 }
2475 }
2476 }
2477
2478 HiliteFrame(event.rTime);
2479
2480 // Moves the film indicator.
2481 if (m_movieLock && !m_editLock) // movie in progress?
2482 {
2483 Ui::CControl* pc = m_interface->SearchControl(EVENT_OBJECT_MOVIELOCK);
2484 if (pc != nullptr)
2485 {
2486 Math::Point pos, dim;
2487
2488 dim.x = 32.0f/640.0f;
2489 dim.y = 32.0f/480.0f;
2490 pos.x = 20.0f/640.0f;
2491 pos.y = (480.0f-24.0f)/480.0f;
2492
2493 float zoom = 1.0f+sinf(m_time*6.0f)*0.1f; // 0.9 .. 1.1
2494 dim.x *= zoom;
2495 dim.y *= zoom;
2496 pos.x -= dim.x/2.0f;
2497 pos.y -= dim.y/2.0f;
2498
2499 pc->SetPos(pos);
2500 pc->SetDim(dim);
2501 }
2502 }
2503
2504 // Moves edition indicator.
2505 if (m_editLock || m_pause->IsPauseType(PAUSE_ENGINE)) // edition in progress?
2506 {
2507 Ui::CControl* pc = m_interface->SearchControl(EVENT_OBJECT_EDITLOCK);
2508 if (pc != nullptr)
2509 {
2510 Math::Point pos, dim;
2511
2512 if (m_editFull || m_editLock)
2513 {
2514 dim.x = 10.0f/640.0f;
2515 dim.y = 10.0f/480.0f;
2516 pos.x = -20.0f/640.0f;
2517 pos.y = -20.0f/480.0f; // invisible!
2518 }
2519 else
2520 {
2521 dim.x = 32.0f/640.0f;
2522 dim.y = 32.0f/480.0f;
2523 pos.x = (640.0f-24.0f)/640.0f;
2524 pos.y = (480.0f-24.0f)/480.0f;
2525
2526 float zoom = 1.0f+sinf(m_time*6.0f)*0.1f; // 0.9 .. 1.1
2527 dim.x *= zoom;
2528 dim.y *= zoom;
2529 pos.x -= dim.x/2.0f;
2530 pos.y -= dim.y/2.0f;
2531 }
2532 pc->SetPos(pos);
2533 pc->SetDim(dim);
2534 }
2535 }
2536
2537 Ui::CControl* pc = m_interface->SearchControl(EVENT_OBJECT_SAVING);
2538 if (pc != nullptr)
2539 {
2540 Math::Point pos, dim;
2541
2542 if (m_shotSaving <= 0)
2543 {
2544 dim.x = 10.0f/640.0f;
2545 dim.y = 10.0f/480.0f;
2546 pos.x = -20.0f/640.0f;
2547 pos.y = -20.0f/480.0f; // invisible!
2548 }
2549 else
2550 {
2551 dim.x = 32.0f/640.0f;
2552 dim.y = 32.0f/480.0f;
2553 pos.x = (640.0f-24.0f)/640.0f;
2554 pos.y = (480.0f-24.0f)/480.0f;
2555
2556 float zoom = 1.0f+sinf(m_time*6.0f)*0.1f; // 0.9 .. 1.1
2557 dim.x *= zoom;
2558 dim.y *= zoom;
2559 pos.x -= dim.x/2.0f;
2560 pos.y -= dim.y/2.0f;
2561 }
2562 pc->SetPos(pos);
2563 pc->SetDim(dim);
2564 }
2565
2566 // Will move the arrow to visit.
2567 if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT)
2568 FrameVisit(event.rTime);
2569
2570 // Moves the boundaries.
2571 FrameShowLimit(event.rTime);
2572
2573 if (m_phase == PHASE_SIMUL)
2574 {
2575 if (!m_editLock && !m_engine->GetPause())
2576 {
2577 CheckEndMission(true);
2578 UpdateAudio(true);
2579 if (m_scoreboard)
2580 m_scoreboard->UpdateObjectCount();
2581 }
2582
2583 if (m_winDelay > 0.0f && !m_editLock)
2584 {
2585 m_winDelay -= event.rTime;
2586 if (m_winDelay <= 0.0f)
2587 {
2588 if (m_movieLock)
2589 m_winDelay = 1.0f;
2590 else
2591 m_eventQueue->AddEvent(Event(EVENT_WIN));
2592 }
2593 }
2594
2595 if (m_lostDelay > 0.0f && !m_editLock)
2596 {
2597 m_lostDelay -= event.rTime;
2598 if (m_lostDelay <= 0.0f)
2599 {
2600 if (m_movieLock)
2601 m_lostDelay = 1.0f;
2602 else
2603 m_eventQueue->AddEvent(Event(EVENT_LOST));
2604 }
2605 }
2606
2607 if (GetMissionType() == MISSION_CODE_BATTLE)
2608 {
2609 if (!m_codeBattleInit)
2610 {
2611 // NOTE: It's important to do this AFTER the first update event finished processing
2612 // because otherwise all robot parts are misplaced
2613 m_userPause = m_pause->ActivatePause(PAUSE_ENGINE);
2614 m_codeBattleInit = true; // Will start on resume
2615 }
2616
2617 if (!m_codeBattleStarted && m_userPause == nullptr)
2618 {
2619 m_codeBattleStarted = true;
2620 ApplyCodeBattleInterface();
2621 CreateCodeBattleInterface();
2622
2623 SetCodeBattleSpectatorMode(true);
2624
2625 m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE));
2626 }
2627
2628 UpdateCodeBattleInterface();
2629 }
2630 }
2631
2632 return true;
2633 }
2634
ShowSaveIndicator(bool show)2635 void CRobotMain::ShowSaveIndicator(bool show)
2636 {
2637 Ui::CControl* pc = m_interface->SearchControl(EVENT_OBJECT_SAVING);
2638 if (pc != nullptr)
2639 {
2640 Math::Point pos, dim;
2641
2642 if (!show)
2643 {
2644 dim.x = 10.0f/640.0f;
2645 dim.y = 10.0f/480.0f;
2646 pos.x = -20.0f/640.0f;
2647 pos.y = -20.0f/480.0f; // invisible!
2648 }
2649 else
2650 {
2651 dim.x = 32.0f/640.0f;
2652 dim.y = 32.0f/480.0f;
2653 pos.x = (640.0f-24.0f)/640.0f;
2654 pos.y = (480.0f-24.0f)/480.0f;
2655
2656 pos.x -= dim.x/2.0f;
2657 pos.y -= dim.y/2.0f;
2658 }
2659 pc->SetPos(pos);
2660 pc->SetDim(dim);
2661 }
2662 }
2663
2664 //! Makes the event for all robots
EventObject(const Event & event)2665 bool CRobotMain::EventObject(const Event &event)
2666 {
2667 if (m_pause->IsPauseType(PAUSE_OBJECT_UPDATES)) return true;
2668
2669 m_resetCreate = false;
2670
2671 for (CObject* obj : m_objMan->GetAllObjects())
2672 {
2673 if (obj->Implements(ObjectInterfaceType::Interactive))
2674 {
2675 dynamic_cast<CInteractiveObject&>(*obj).EventProcess(event);
2676 }
2677 }
2678
2679 if (m_resetCreate)
2680 ResetCreate();
2681
2682 return true;
2683 }
2684
2685
2686
ScenePerso()2687 void CRobotMain::ScenePerso()
2688 {
2689 DeleteAllObjects(); // removes all the current 3D Scene
2690 m_terrain->FlushRelief();
2691 m_engine->DeleteAllObjects();
2692 m_oldModelManager->DeleteAllModelCopies();
2693 m_terrain->FlushBuildingLevel();
2694 m_terrain->FlushFlyingLimit();
2695 m_lightMan->FlushLights();
2696 m_particle->FlushParticle();
2697
2698 m_levelFile = "levels/other/perso.txt";
2699 try
2700 {
2701 CreateScene(false, true, false); // sets scene
2702 }
2703 catch (const std::runtime_error& e)
2704 {
2705 LevelLoadingError("An error occurred while trying to load apperance scene", e, PHASE_PLAYER_SELECT);
2706 }
2707
2708 m_engine->SetDrawWorld(false); // does not draw anything on the interface
2709 m_engine->SetDrawFront(true); // draws on the human interface
2710 CObject* obj = SearchHuman();
2711 if (obj != nullptr)
2712 {
2713 obj->SetDrawFront(true); // draws the interface
2714
2715 assert(obj->Implements(ObjectInterfaceType::Movable));
2716 CMotionHuman* mh = static_cast<CMotionHuman*>(dynamic_cast<CMovableObject&>(*obj).GetMotion());
2717 mh->StartDisplayPerso();
2718 }
2719 }
2720
2721 //! Creates the whole scene
CreateScene(bool soluce,bool fixScene,bool resetObject)2722 void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
2723 {
2724 m_fixScene = fixScene;
2725
2726 m_base = nullptr;
2727
2728 if (!resetObject)
2729 {
2730 m_build = 0;
2731 m_researchDone.clear(); // no research done
2732 m_researchDone[0] = 0;
2733 m_researchEnable = 0;
2734
2735 g_unit = UNIT;
2736
2737 FlushDisplayInfo();
2738 m_terrain->FlushMaterials();
2739 m_audioTrack = "";
2740 m_audioRepeat = true;
2741 m_satcomTrack = "";
2742 m_satcomRepeat = true;
2743 m_editorTrack = "";
2744 m_editorRepeat = true;
2745 m_displayText->SetDelay(1.0f);
2746 m_displayText->SetEnable(true);
2747 m_immediatSatCom = false;
2748 m_lockedSatCom = false;
2749 m_endingWin = "";
2750 m_endingLost = "";
2751 m_audioChange.clear();
2752 m_endTake.clear();
2753 m_endTakeImmediat = false;
2754 m_endTakeResearch = 0;
2755 m_endTakeTimeout = -1.0f;
2756 m_endTakeTeamImmediateWin = false;
2757 m_endTakeWinDelay = 2.0f;
2758 m_endTakeLostDelay = 2.0f;
2759 m_teamFinished.clear();
2760 m_scoreboard.reset();
2761 m_globalMagnifyDamage = 1.0f;
2762 m_obligatoryTokens.clear();
2763 m_mapShow = true;
2764 m_mapImage = false;
2765 m_mapFilename[0] = 0;
2766
2767 m_controller = nullptr;
2768
2769 m_colorNewBot.clear();
2770 m_colorNewBot[0] = COLOR_REF_BOT;
2771 m_colorNewAlien = COLOR_REF_ALIEN;
2772 m_colorNewGreen = COLOR_REF_GREEN;
2773 m_colorNewWater = COLOR_REF_WATER;
2774
2775 m_engine->SetAmbientColor(Gfx::Color(0.5f, 0.5f, 0.5f, 0.5f), 0);
2776 m_engine->SetAmbientColor(Gfx::Color(0.5f, 0.5f, 0.5f, 0.5f), 1);
2777 m_engine->SetFogColor(Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f), 0);
2778 m_engine->SetFogColor(Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f), 1);
2779 m_engine->SetDeepView(1000.0f, 0);
2780 m_engine->SetDeepView(1000.0f, 1);
2781 m_engine->SetFogStart(0.75f, 0);
2782 m_engine->SetFogStart(0.75f, 1);
2783 m_engine->SetSecondTexture("");
2784 m_engine->SetForegroundName("");
2785
2786 GetResource(RES_TEXT, RT_SCRIPT_NEW, m_scriptName);
2787 m_scriptFile = "";
2788
2789 m_missionType = MISSION_NORMAL;
2790 m_codeBattleInit = false;
2791 m_codeBattleStarted = false;
2792
2793 m_teamNames.clear();
2794
2795 m_missionResult = ERR_MISSION_NOTERM;
2796 m_missionResultFromScript = false;
2797 }
2798
2799 // NOTE: Reset timer always, even when only resetting object positions
2800 m_missionTimerEnabled = false;
2801 m_missionTimerStarted = false;
2802 m_missionTimer = 0.0f;
2803
2804 std::string backgroundPath = "";
2805 Gfx::Color backgroundUp = Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f);
2806 Gfx::Color backgroundDown = Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f);
2807 Gfx::Color backgroundCloudUp = Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f);
2808 Gfx::Color backgroundCloudDown = Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f);
2809 bool backgroundFull = false;
2810
2811 auto LoadingWarning = [&](const std::string& message)
2812 {
2813 GetLogger()->Warn("%s\n", message.c_str());
2814 m_ui->GetDialog()->StartInformation("Level loading warning", "This level contains problems. It may stop working in future versions of the game.", message);
2815 };
2816
2817 try
2818 {
2819 m_ui->GetLoadingScreen()->SetProgress(0.05f, RT_LOADING_PROCESSING);
2820 GetLogger()->Info("Loading level: %s\n", m_levelFile.c_str());
2821 CLevelParser levelParser(m_levelFile);
2822 levelParser.SetLevelPaths(m_levelCategory, m_levelChap, m_levelRank);
2823 levelParser.Load();
2824 int numObjects = levelParser.CountLines("CreateObject");
2825 m_ui->GetLoadingScreen()->SetProgress(0.1f, RT_LOADING_LEVEL_SETTINGS);
2826
2827 int rankObj = 0;
2828 CObject* sel = nullptr;
2829
2830 for (auto& line : levelParser.GetLines())
2831 {
2832 if (line->GetCommand() == "Title" && !resetObject)
2833 {
2834 //strcpy(m_title, line->GetParam("text")->AsString().c_str());
2835 continue;
2836 }
2837
2838 if (line->GetCommand() == "Resume" && !resetObject)
2839 {
2840 //strcpy(m_resume, line->GetParam("text")->AsString().c_str());
2841 continue;
2842 }
2843
2844 if (line->GetCommand() == "ScriptName" && !resetObject)
2845 {
2846 m_scriptName = line->GetParam("text")->AsString();
2847 continue;
2848 }
2849
2850 if (line->GetCommand() == "ScriptFile" && !resetObject)
2851 {
2852 m_scriptFile = line->GetParam("name")->AsString();
2853 continue;
2854 }
2855
2856 if (line->GetCommand() == "Instructions" && !resetObject)
2857 {
2858 strcpy(m_infoFilename[SATCOM_HUSTON], line->GetParam("name")->AsPath("help/%lng%").c_str());
2859
2860 m_immediatSatCom = line->GetParam("immediat")->AsBool(false);
2861 m_beginSatCom = m_lockedSatCom = line->GetParam("lock")->AsBool(false);
2862 if (m_app->GetSceneTestMode()) m_immediatSatCom = false;
2863 continue;
2864 }
2865
2866 if (line->GetCommand() == "Satellite" && !resetObject)
2867 {
2868 strcpy(m_infoFilename[SATCOM_SAT], line->GetParam("name")->AsPath("help/%lng%").c_str());
2869 continue;
2870 }
2871
2872 if (line->GetCommand() == "Loading" && !resetObject)
2873 {
2874 strcpy(m_infoFilename[SATCOM_LOADING], line->GetParam("name")->AsPath("help/%lng%").c_str());
2875 continue;
2876 }
2877
2878 if (line->GetCommand() == "HelpFile" && !resetObject)
2879 {
2880 strcpy(m_infoFilename[SATCOM_PROG], line->GetParam("name")->AsPath("help/%lng%").c_str());
2881 continue;
2882 }
2883 if (line->GetCommand() == "SoluceFile" && !resetObject)
2884 {
2885 strcpy(m_infoFilename[SATCOM_SOLUCE], line->GetParam("name")->AsPath("help/%lng%").c_str());
2886 continue;
2887 }
2888
2889 if (line->GetCommand() == "EndingFile" && !resetObject)
2890 {
2891 auto Process = [&](const std::string& type) -> std::string
2892 {
2893 if (line->GetParam(type)->IsDefined())
2894 {
2895 try
2896 {
2897 int rank = boost::lexical_cast<int>(line->GetParam(type)->GetValue());
2898 if (rank >= 0)
2899 {
2900 // TODO: Fix default levels and add a future removal warning
2901 GetLogger()->Warn("This level is using deprecated way of defining %1$s scene. Please change the %1$s= parameter in EndingFile from %2$d to \"levels/other/%1$s%2$03d.txt\".\n", type.c_str(), rank);
2902 std::stringstream ss;
2903 ss << "levels/other/" << type << std::setfill('0') << std::setw(3) << rank << ".txt";
2904 return ss.str();
2905 }
2906 else
2907 {
2908 // TODO: Fix default levels and add a future removal warning
2909 GetLogger()->Warn("This level is using deprecated way of defining %1$s scene. Please remove the %1$s= parameter in EndingFile.\n", type.c_str());
2910 return "";
2911 }
2912
2913 }
2914 catch (boost::bad_lexical_cast &e)
2915 {
2916 return line->GetParam(type)->AsPath("levels");
2917 }
2918 }
2919 return "";
2920 };
2921 m_endingWin = Process("win");
2922 m_endingLost = Process("lost");
2923 continue;
2924 }
2925
2926 if (line->GetCommand() == "MessageDelay" && !resetObject)
2927 {
2928 m_displayText->SetDelay(line->GetParam("factor")->AsFloat());
2929 continue;
2930 }
2931
2932 if (line->GetCommand() == "MissionTimer")
2933 {
2934 m_missionTimerEnabled = line->GetParam("enabled")->AsBool();
2935 if (!line->GetParam("program")->AsBool(false))
2936 {
2937 m_missionTimerStarted = true;
2938 }
2939 continue;
2940 }
2941
2942 if (line->GetCommand() == "TeamName")
2943 {
2944 int team = line->GetParam("team")->AsInt();
2945 std::string name = line->GetParam("name")->AsString();
2946 m_teamNames[team] = name;
2947 continue;
2948 }
2949
2950 if (line->GetCommand() == "CacheAudio" && !resetObject)
2951 {
2952 std::string filename = line->GetParam("filename")->AsPath("music");
2953 m_ui->GetLoadingScreen()->SetProgress(0.15f, RT_LOADING_MUSIC, filename);
2954 m_sound->CacheMusic(filename);
2955 continue;
2956 }
2957
2958 if (line->GetCommand() == "AudioChange" && !resetObject)
2959 {
2960 auto audioChange = MakeUnique<CAudioChangeCondition>();
2961 audioChange->Read(line.get());
2962 m_ui->GetLoadingScreen()->SetProgress(0.15f, RT_LOADING_MUSIC, audioChange->music);
2963 m_sound->CacheMusic(audioChange->music);
2964 m_audioChange.push_back(std::move(audioChange));
2965
2966 if (!line->GetParam("pos")->IsDefined() || !line->GetParam("dist")->IsDefined())
2967 {
2968 LoadingWarning("The defaults for pos= and dist= are going to change, specify them explicitly. See issue #759 (https://git.io/vVBzH)");
2969 }
2970 continue;
2971 }
2972
2973 if (line->GetCommand() == "Audio" && !resetObject)
2974 {
2975 if (line->GetParam("track")->IsDefined())
2976 {
2977 if (line->GetParam("filename")->IsDefined())
2978 throw CLevelParserException("You can't use track and filename at the same time");
2979
2980 GetLogger()->Warn("Using track= is deprecated. Please replace this with filename=\n");
2981 int trackid = line->GetParam("track")->AsInt();
2982 if (trackid != 0)
2983 {
2984 std::stringstream filenameStr;
2985 filenameStr << "music/music" << std::setfill('0') << std::setw(3) << trackid << ".ogg";
2986 m_audioTrack = filenameStr.str();
2987 }
2988 else
2989 {
2990 m_audioTrack = "";
2991 }
2992 }
2993 else
2994 {
2995 if (line->GetParam("filename")->IsDefined())
2996 {
2997 m_audioTrack = line->GetParam("filename")->AsPath("music");
2998 }
2999 else
3000 {
3001 m_audioTrack = "";
3002 }
3003 }
3004 if (!m_audioTrack.empty())
3005 {
3006 m_audioRepeat = line->GetParam("repeat")->AsBool(true);
3007 }
3008
3009 if (line->GetParam("satcom")->IsDefined())
3010 {
3011 m_satcomTrack = line->GetParam("satcom")->AsPath("music");
3012 m_satcomRepeat = line->GetParam("satcomRepeat")->AsBool(true);
3013 }
3014 else
3015 {
3016 m_satcomTrack = "";
3017 }
3018
3019 if (line->GetParam("editor")->IsDefined())
3020 {
3021 m_editorTrack = line->GetParam("editor")->AsPath("music");
3022 m_editorRepeat = line->GetParam("editorRepeat")->AsBool(true);
3023 }
3024 else
3025 {
3026 m_editorTrack = "";
3027 }
3028
3029 if (!m_audioTrack.empty())
3030 {
3031 m_ui->GetLoadingScreen()->SetProgress(0.15f, RT_LOADING_MUSIC, m_audioTrack);
3032 m_sound->CacheMusic(m_audioTrack);
3033 }
3034 if (!m_satcomTrack.empty())
3035 {
3036 m_ui->GetLoadingScreen()->SetProgress(0.15f, RT_LOADING_MUSIC, m_satcomTrack);
3037 m_sound->CacheMusic(m_satcomTrack);
3038 }
3039 if (!m_editorTrack.empty())
3040 {
3041 m_ui->GetLoadingScreen()->SetProgress(0.15f, RT_LOADING_MUSIC, m_editorTrack);
3042 m_sound->CacheMusic(m_editorTrack);
3043 }
3044 continue;
3045 }
3046
3047 if (line->GetCommand() == "AmbientColor" && !resetObject)
3048 {
3049 m_engine->SetAmbientColor(line->GetParam("air")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)), 0);
3050 m_engine->SetAmbientColor(line->GetParam("water")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)), 1);
3051 continue;
3052 }
3053
3054 if (line->GetCommand() == "FogColor" && !resetObject)
3055 {
3056 m_engine->SetFogColor(line->GetParam("air")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)), 0);
3057 m_engine->SetFogColor(line->GetParam("water")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)), 1);
3058 continue;
3059 }
3060
3061 if (line->GetCommand() == "VehicleColor" && !resetObject)
3062 {
3063 m_colorNewBot[line->GetParam("team")->AsInt(0)] = line->GetParam("color")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f));
3064 continue;
3065 }
3066
3067 if (line->GetCommand() == "InsectColor" && !resetObject)
3068 {
3069 m_colorNewAlien = line->GetParam("color")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f));
3070 continue;
3071 }
3072
3073 if (line->GetCommand() == "GreeneryColor" && !resetObject)
3074 {
3075 m_colorNewGreen = line->GetParam("color")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f));
3076 continue;
3077 }
3078
3079 if (line->GetCommand() == "DeepView" && !resetObject)
3080 {
3081 m_engine->SetDeepView(line->GetParam("air")->AsFloat(500.0f)*g_unit, 0, false);
3082 m_engine->SetDeepView(line->GetParam("water")->AsFloat(100.0f)*g_unit, 1, false);
3083 continue;
3084 }
3085
3086 if (line->GetCommand() == "FogStart" && !resetObject)
3087 {
3088 m_engine->SetFogStart(line->GetParam("air")->AsFloat(0.5f), 0);
3089 m_engine->SetFogStart(line->GetParam("water")->AsFloat(0.5f), 1);
3090 continue;
3091 }
3092
3093 if (line->GetCommand() == "SecondTexture" && !resetObject)
3094 {
3095 if (line->GetParam("rank")->IsDefined())
3096 {
3097 char tex[20] = { 0 };
3098 sprintf(tex, "dirty%.2d.png", line->GetParam("rank")->AsInt());
3099 m_engine->SetSecondTexture(tex);
3100 }
3101 else
3102 {
3103 m_engine->SetSecondTexture("../" + line->GetParam("texture")->AsPath("textures"));
3104 }
3105 continue;
3106 }
3107
3108 if (line->GetCommand() == "Background" && !resetObject)
3109 {
3110 if (line->GetParam("image")->IsDefined())
3111 backgroundPath = line->GetParam("image")->AsPath("textures");
3112 backgroundUp = line->GetParam("up")->AsColor(backgroundUp);
3113 backgroundDown = line->GetParam("down")->AsColor(backgroundDown);
3114 backgroundCloudUp = line->GetParam("cloudUp")->AsColor(backgroundCloudUp);
3115 backgroundCloudDown = line->GetParam("cloudDown")->AsColor(backgroundCloudDown);
3116 backgroundFull = line->GetParam("full")->AsBool(backgroundFull);
3117 continue;
3118 }
3119
3120 if (line->GetCommand() == "Planet" && !resetObject)
3121 {
3122 Math::Vector ppos, uv1, uv2;
3123
3124 ppos = line->GetParam("pos")->AsPoint();
3125 uv1 = line->GetParam("uv1")->AsPoint();
3126 uv2 = line->GetParam("uv2")->AsPoint();
3127 m_planet->Create(line->GetParam("mode")->AsPlanetType(),
3128 Math::Point(ppos.x, ppos.z),
3129 line->GetParam("dim")->AsFloat(0.2f),
3130 line->GetParam("speed")->AsFloat(0.0f),
3131 line->GetParam("dir")->AsFloat(0.0f),
3132 line->GetParam("image")->AsPath("textures"),
3133 Math::Point(uv1.x, uv1.z),
3134 Math::Point(uv2.x, uv2.z),
3135 line->GetParam("image")->AsPath("textures").find("planet") != std::string::npos // TODO: add transparent op or modify textures
3136 );
3137 continue;
3138 }
3139
3140 if (line->GetCommand() == "ForegroundName" && !resetObject)
3141 {
3142 m_engine->SetForegroundName(line->GetParam("image")->AsPath("textures"));
3143 continue;
3144 }
3145
3146 if (line->GetCommand() == "Level" && !resetObject)
3147 {
3148 g_unit = line->GetParam("unitScale")->AsFloat(4.0f);
3149 m_engine->SetTracePrecision(line->GetParam("traceQuality")->AsFloat(1.0f));
3150 m_shortCut = line->GetParam("shortcut")->AsBool(true);
3151
3152 m_missionType = line->GetParam("type")->AsMissionType(MISSION_NORMAL);
3153 m_globalMagnifyDamage = line->GetParam("magnifyDamage")->AsFloat(1.0f);
3154 m_globalNuclearCapacity = line->GetParam("nuclearCapacity")->AsFloat(10.0f);
3155 m_globalCellCapacity = line->GetParam("cellCapacity")->AsFloat(1.0f);
3156
3157 continue;
3158 }
3159
3160 if (line->GetCommand() == "TerrainGenerate" && !resetObject)
3161 {
3162 m_ui->GetLoadingScreen()->SetProgress(0.2f, RT_LOADING_TERRAIN);
3163 m_terrain->Generate(line->GetParam("mosaic")->AsInt(20),
3164 line->GetParam("brick")->AsInt(3),
3165 line->GetParam("size")->AsFloat(20.0f),
3166 line->GetParam("vision")->AsFloat(500.0f)*g_unit,
3167 line->GetParam("depth")->AsInt(2),
3168 line->GetParam("hard")->AsFloat(0.5f));
3169 continue;
3170 }
3171
3172 if (line->GetCommand() == "TerrainWind" && !resetObject)
3173 {
3174 m_terrain->SetWind(line->GetParam("speed")->AsPoint());
3175 continue;
3176 }
3177
3178 if (line->GetCommand() == "TerrainRelief" && !resetObject)
3179 {
3180 m_ui->GetLoadingScreen()->SetProgress(0.2f+(1.f/5.f)*0.05f, RT_LOADING_TERRAIN, RT_LOADING_TERRAIN_RELIEF);
3181 m_terrain->LoadRelief(
3182 line->GetParam("image")->AsPath("textures"),
3183 line->GetParam("factor")->AsFloat(1.0f),
3184 line->GetParam("border")->AsBool(true));
3185 continue;
3186 }
3187
3188 if (line->GetCommand() == "TerrainRandomRelief" && !resetObject)
3189 {
3190 m_ui->GetLoadingScreen()->SetProgress(0.2f+(1.f/5.f)*0.05f, RT_LOADING_TERRAIN, RT_LOADING_TERRAIN_RELIEF);
3191 m_terrain->RandomizeRelief();
3192 continue;
3193 }
3194
3195 if (line->GetCommand() == "TerrainResource" && !resetObject)
3196 {
3197 m_ui->GetLoadingScreen()->SetProgress(0.2f+(2.f/5.f)*0.05f, RT_LOADING_TERRAIN, RT_LOADING_TERRAIN_RES);
3198 m_terrain->LoadResources(line->GetParam("image")->AsPath("textures"));
3199 continue;
3200 }
3201
3202 if (line->GetCommand() == "TerrainWater" && !resetObject)
3203 {
3204 Math::Vector pos;
3205 pos.x = line->GetParam("moveX")->AsFloat(0.0f);
3206 pos.y = line->GetParam("moveY")->AsFloat(0.0f);
3207 pos.z = pos.x;
3208 m_water->Create(line->GetParam("air")->AsWaterType(Gfx::WATER_TT),
3209 line->GetParam("water")->AsWaterType(Gfx::WATER_TT),
3210 line->GetParam("image")->AsPath("textures"),
3211 line->GetParam("diffuse")->AsColor(Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f)),
3212 line->GetParam("ambient")->AsColor(Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f)),
3213 line->GetParam("level")->AsFloat(100.0f)*g_unit,
3214 line->GetParam("glint")->AsFloat(1.0f),
3215 pos);
3216 m_colorNewWater = line->GetParam("color")->AsColor(COLOR_REF_WATER);
3217 m_colorShiftWater = line->GetParam("brightness")->AsFloat(0.0f);
3218 continue;
3219 }
3220
3221 if (line->GetCommand() == "TerrainLava" && !resetObject)
3222 {
3223 m_water->SetLava(line->GetParam("mode")->AsBool());
3224 continue;
3225 }
3226
3227 if (line->GetCommand() == "TerrainCloud" && !resetObject)
3228 {
3229 std::string path = "";
3230 if (line->GetParam("image")->IsDefined())
3231 path = line->GetParam("image")->AsPath("textures");
3232 m_cloud->Create(path,
3233 line->GetParam("diffuse")->AsColor(Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f)),
3234 line->GetParam("ambient")->AsColor(Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f)),
3235 line->GetParam("level")->AsFloat(500.0f)*g_unit);
3236 continue;
3237 }
3238
3239 if (line->GetCommand() == "TerrainBlitz" && !resetObject)
3240 {
3241 m_lightning->Create(line->GetParam("sleep")->AsFloat(0.0f),
3242 line->GetParam("delay")->AsFloat(3.0f),
3243 line->GetParam("magnetic")->AsFloat(50.0f)*g_unit);
3244 continue;
3245 }
3246
3247 if (line->GetCommand() == "TerrainInitTextures" && !resetObject)
3248 {
3249 m_ui->GetLoadingScreen()->SetProgress(0.2f+(3.f/5.f)*0.05f, RT_LOADING_TERRAIN, RT_LOADING_TERRAIN_TEX);
3250 std::string name = "../" + line->GetParam("image")->AsPath("textures");
3251 if (name.find(".") == std::string::npos)
3252 name += ".png";
3253 unsigned int dx = line->GetParam("dx")->AsInt(1);
3254 unsigned int dy = line->GetParam("dy")->AsInt(1);
3255
3256 int tt[100]; //TODO: I have no idea how TerrainInitTextures works, but maybe we shuld remove the limit to 100?
3257 if (dx*dy > 100)
3258 throw CLevelParserException("In TerrainInitTextures: dx*dy must be <100");
3259 if (line->GetParam("table")->IsDefined())
3260 {
3261 auto& table = line->GetParam("table")->AsArray();
3262
3263 if (table.size() > dx*dy)
3264 throw CLevelParserException("In TerrainInitTextures: table size must be dx*dy");
3265
3266 for (unsigned int i = 0; i < dx*dy; i++)
3267 {
3268 if (i >= table.size())
3269 {
3270 tt[i] = 0;
3271 }
3272 else
3273 {
3274 tt[i] = table[i]->AsInt();
3275 }
3276 }
3277 }
3278 else
3279 {
3280 for (unsigned int i = 0; i < dx*dy; i++)
3281 {
3282 tt[i] = 0;
3283 }
3284 }
3285
3286 m_terrain->InitTextures(name.c_str(), tt, dx, dy);
3287 continue;
3288 }
3289
3290 if (line->GetCommand() == "TerrainInit" && !resetObject)
3291 {
3292 m_terrain->InitMaterials(line->GetParam("id")->AsInt(1));
3293 continue;
3294 }
3295
3296 if (line->GetCommand() == "TerrainMaterial" && !resetObject)
3297 {
3298 std::string name = line->GetParam("image")->AsPath("textures");
3299 if (name.find(".") == std::string::npos)
3300 name += ".png";
3301 name = "../" + name;
3302
3303 m_terrain->AddMaterial(line->GetParam("id")->AsInt(0),
3304 name.c_str(),
3305 Math::Point(line->GetParam("u")->AsFloat(),
3306 line->GetParam("v")->AsFloat()),
3307 line->GetParam("up")->AsInt(),
3308 line->GetParam("right")->AsInt(),
3309 line->GetParam("down")->AsInt(),
3310 line->GetParam("left")->AsInt(),
3311 line->GetParam("hard")->AsFloat(0.5f));
3312 continue;
3313 }
3314
3315 if (line->GetCommand() == "TerrainLevel" && !resetObject)
3316 {
3317 m_ui->GetLoadingScreen()->SetProgress(0.2f+(3.f/5.f)*0.05f, RT_LOADING_TERRAIN, RT_LOADING_TERRAIN_TEX);
3318 int id[50]; //TODO: I have no idea how TerrainLevel works, but maybe we should remove the limit to 50?
3319 if (line->GetParam("id")->IsDefined())
3320 {
3321 auto& idArray = line->GetParam("id")->AsArray();
3322
3323 if (idArray.size() > 50)
3324 throw CLevelParserException("In TerrainLevel: id array size must be < 50");
3325
3326 unsigned int i = 0;
3327 while (i < 50)
3328 {
3329 id[i] = idArray[i]->AsInt();
3330 i++;
3331 if (i >= idArray.size()) break;
3332 }
3333 id[i] = 0;
3334 }
3335
3336 m_terrain->GenerateMaterials(id,
3337 line->GetParam("min")->AsFloat(0.0f)*g_unit,
3338 line->GetParam("max")->AsFloat(100.0f)*g_unit,
3339 line->GetParam("slope")->AsFloat(5.0f),
3340 line->GetParam("freq")->AsFloat(100.0f),
3341 line->GetParam("center")->AsPoint(Math::Vector(0.0f, 0.0f, 0.0f))*g_unit,
3342 line->GetParam("radius")->AsFloat(0.0f)*g_unit);
3343 continue;
3344 }
3345
3346 if (line->GetCommand() == "TerrainCreate" && !resetObject)
3347 {
3348 m_ui->GetLoadingScreen()->SetProgress(0.2f+(4.f/5.f)*0.05f, RT_LOADING_TERRAIN, RT_LOADING_TERRAIN_GEN);
3349 m_terrain->CreateObjects();
3350 continue;
3351 }
3352
3353 if (line->GetCommand() == "BeginObject")
3354 {
3355 InitEye();
3356 SetMovieLock(false);
3357
3358 if (!resetObject)
3359 ChangeColor(); // changes the colors of texture
3360
3361 if (!m_sceneReadPath.empty()) // loading file ?
3362 {
3363 m_ui->GetLoadingScreen()->SetProgress(0.25f, RT_LOADING_OBJECTS_SAVED);
3364 sel = IOReadScene(m_sceneReadPath + "/data.sav", m_sceneReadPath + "/cbot.run");
3365 }
3366 else
3367 {
3368 m_ui->GetLoadingScreen()->SetProgress(0.25f, RT_LOADING_OBJECTS);
3369 }
3370
3371 continue;
3372 }
3373
3374 if (line->GetCommand() == "LevelController" && m_sceneReadPath.empty())
3375 {
3376 if (m_controller != nullptr)
3377 {
3378 throw CLevelParserException("There can be only one LevelController in the level");
3379 }
3380
3381 m_controller = m_objMan->CreateObject(Math::Vector(0.0f, 0.0f, 0.0f), 0.0f, OBJECT_CONTROLLER);
3382 assert(m_controller->Implements(ObjectInterfaceType::Programmable));
3383 assert(m_controller->Implements(ObjectInterfaceType::ProgramStorage));
3384
3385 assert(m_controller->Implements(ObjectInterfaceType::Old));
3386 dynamic_cast<COldObject&>(*m_controller).SetCheckToken(false);
3387
3388 if (line->GetParam("script")->IsDefined())
3389 {
3390 CProgramStorageObject* programStorage = dynamic_cast<CProgramStorageObject*>(m_controller);
3391 Program* program = programStorage->AddProgram();
3392 programStorage->ReadProgram(program, line->GetParam("script")->AsPath("ai"));
3393 program->readOnly = true;
3394 dynamic_cast<CProgrammableObject&>(*m_controller).RunProgram(program);
3395 }
3396 continue;
3397 }
3398
3399 if (line->GetCommand() == "CreateObject" && m_sceneReadPath.empty())
3400 {
3401 ObjectCreateParams params = CObject::ReadCreateParams(line.get());
3402
3403 float objectProgress = static_cast<float>(rankObj) / static_cast<float>(numObjects);
3404 std::string details = StrUtils::ToString<int>(rankObj+1)+" / "+StrUtils::ToString<int>(numObjects);
3405 #if DEV_BUILD
3406 // Object categories may spoil the level a bit, so hide them in release builds
3407 details += ": "+CLevelParserParam::FromObjectType(params.type);
3408 #endif
3409 m_ui->GetLoadingScreen()->SetProgress(0.25f+objectProgress*0.75f, RT_LOADING_OBJECTS, details);
3410
3411 try
3412 {
3413 CObject* obj = m_objMan->CreateObject(params);
3414 obj->Read(line.get());
3415
3416 if (m_fixScene && obj->GetType() == OBJECT_HUMAN)
3417 {
3418 assert(obj->Implements(ObjectInterfaceType::Movable));
3419 CMotion* motion = dynamic_cast<CMovableObject&>(*obj).GetMotion();
3420 if (m_phase == PHASE_WIN ) motion->SetAction(MHS_WIN, 0.4f);
3421 if (m_phase == PHASE_LOST) motion->SetAction(MHS_LOST, 0.5f);
3422 }
3423
3424 if (obj->Implements(ObjectInterfaceType::Controllable) && line->GetParam("select")->AsBool(false))
3425 sel = obj;
3426
3427 if (obj->GetType() == OBJECT_BASE)
3428 m_base = obj;
3429
3430 if (obj->Implements(ObjectInterfaceType::ProgramStorage))
3431 {
3432 CProgramStorageObject* programStorage = dynamic_cast<CProgramStorageObject*>(obj);
3433
3434 if (obj->Implements(ObjectInterfaceType::Controllable) && dynamic_cast<CControllableObject&>(*obj).GetSelectable() && obj->GetType() != OBJECT_HUMAN)
3435 {
3436 programStorage->SetProgramStorageIndex(rankObj);
3437 }
3438
3439 char categoryChar = GetLevelCategoryDir(m_levelCategory)[0];
3440 programStorage->LoadAllProgramsForLevel(
3441 line.get(),
3442 m_playerProfile->GetSaveFile(StrUtils::Format("%c%.3d%.3d", categoryChar, m_levelChap, m_levelRank)),
3443 soluce
3444 );
3445 }
3446 }
3447 catch (const CObjectCreateException& e)
3448 {
3449 GetLogger()->Error("Error loading level object: %s\n", e.what());
3450 throw;
3451 }
3452
3453 rankObj ++;
3454 continue;
3455 }
3456
3457 if (line->GetCommand() == "CreateFog" && !resetObject)
3458 {
3459 Gfx::ParticleType type = static_cast<Gfx::ParticleType>(Gfx::PARTIFOG0+(line->GetParam("type")->AsInt()));
3460 Math::Vector pos = line->GetParam("pos")->AsPoint()*g_unit;
3461 float height = line->GetParam("height")->AsFloat(1.0f)*g_unit;
3462 float ddim = line->GetParam("dim")->AsFloat(50.0f)*g_unit;
3463 float delay = line->GetParam("delay")->AsFloat(2.0f);
3464 m_terrain->AdjustToFloor(pos);
3465 pos.y += height;
3466 Math::Point dim;
3467 dim.x = ddim;
3468 dim.y = dim.x;
3469 m_particle->CreateParticle(pos, Math::Vector(0.0f, 0.0f, 0.0f), dim, type, delay, 0.0f, 0.0f);
3470 continue;
3471 }
3472
3473 if (line->GetCommand() == "CreateLight" && !resetObject)
3474 {
3475 Gfx::EngineObjectType type;
3476
3477 int lightRank = CreateLight(line->GetParam("dir")->AsPoint(),
3478 line->GetParam("color")->AsColor(Gfx::Color(0.5f, 0.5f, 0.5f, 1.0f)));
3479
3480 type = line->GetParam("type")->AsTerrainType(Gfx::ENG_OBJTYPE_NULL);
3481
3482 if (type == Gfx::ENG_OBJTYPE_TERRAIN)
3483 {
3484 m_lightMan->SetLightPriority(lightRank, Gfx::LIGHT_PRI_HIGHEST);
3485 m_lightMan->SetLightIncludeType(lightRank, Gfx::ENG_OBJTYPE_TERRAIN);
3486 }
3487
3488 if (type == Gfx::ENG_OBJTYPE_QUARTZ)
3489 m_lightMan->SetLightIncludeType(lightRank, Gfx::ENG_OBJTYPE_QUARTZ);
3490
3491 if (type == Gfx::ENG_OBJTYPE_METAL)
3492 m_lightMan->SetLightIncludeType(lightRank, Gfx::ENG_OBJTYPE_METAL);
3493
3494 if (type == Gfx::ENG_OBJTYPE_FIX)
3495 m_lightMan->SetLightExcludeType(lightRank, Gfx::ENG_OBJTYPE_TERRAIN);
3496
3497 continue;
3498 }
3499 if (line->GetCommand() == "CreateSpot" && !resetObject)
3500 {
3501 Gfx::EngineObjectType type;
3502
3503 int rankLight = CreateSpot(line->GetParam("pos")->AsPoint()*g_unit,
3504 line->GetParam("color")->AsColor(Gfx::Color(0.5f, 0.5f, 0.5f, 1.0f)));
3505
3506 type = line->GetParam("type")->AsTerrainType(Gfx::ENG_OBJTYPE_NULL);
3507 if (type == Gfx::ENG_OBJTYPE_TERRAIN)
3508 m_lightMan->SetLightIncludeType(rankLight, Gfx::ENG_OBJTYPE_TERRAIN);
3509
3510 if (type == Gfx::ENG_OBJTYPE_QUARTZ)
3511 m_lightMan->SetLightIncludeType(rankLight, Gfx::ENG_OBJTYPE_QUARTZ);
3512
3513 if (type == Gfx::ENG_OBJTYPE_METAL)
3514 m_lightMan->SetLightIncludeType(rankLight, Gfx::ENG_OBJTYPE_METAL);
3515
3516 if (type == Gfx::ENG_OBJTYPE_FIX)
3517 m_lightMan->SetLightExcludeType(rankLight, Gfx::ENG_OBJTYPE_TERRAIN);
3518
3519 continue;
3520 }
3521
3522 if (line->GetCommand() == "GroundSpot" && !resetObject)
3523 {
3524 int rank = m_engine->CreateGroundSpot();
3525 if (rank != -1)
3526 {
3527 m_engine->SetObjectGroundSpotPos(rank, line->GetParam("pos")->AsPoint(Math::Vector(0.0f, 0.0f, 0.0f))*g_unit);
3528 m_engine->SetObjectGroundSpotRadius(rank, line->GetParam("radius")->AsFloat(10.0f)*g_unit);
3529 m_engine->SetObjectGroundSpotColor(rank, line->GetParam("color")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)));
3530 m_engine->SetObjectGroundSpotSmooth(rank, line->GetParam("smooth")->AsFloat(1.0f));
3531 m_engine->SetObjectGroundSpotMinMax(rank, line->GetParam("min")->AsFloat(0.0f)*g_unit,
3532 line->GetParam("max")->AsFloat(0.0f)*g_unit);
3533 }
3534 continue;
3535 }
3536
3537 if (line->GetCommand() == "WaterColor" && !resetObject)
3538 {
3539 m_engine->SetWaterAddColor(line->GetParam("color")->AsColor());
3540 continue;
3541 }
3542
3543 if (line->GetCommand() == "MapColor" && !resetObject)
3544 {
3545 m_map->FloorColorMap(line->GetParam("floor")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)),
3546 line->GetParam("water")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)));
3547 m_mapShow = line->GetParam("show")->AsBool(true);
3548 m_map->SetToy(line->GetParam("toyIcon")->AsBool(false));
3549 m_mapImage = line->GetParam("image")->AsBool(false);
3550 if (m_mapImage)
3551 {
3552 Math::Vector offset;
3553 strcpy(m_mapFilename, line->GetParam("filename")->AsPath("textures").c_str());
3554 offset = line->GetParam("offset")->AsPoint(Math::Vector(0.0f, 0.0f, 0.0f));
3555 m_map->SetFixParam(line->GetParam("zoom")->AsFloat(1.0f),
3556 offset.x, offset.z,
3557 line->GetParam("angle")->AsFloat(0.0f)*Math::PI/180.0f,
3558 line->GetParam("mode")->AsInt(0),
3559 line->GetParam("debug")->AsBool(false));
3560 }
3561 continue;
3562 }
3563
3564 if (line->GetCommand() == "MapZoom" && !resetObject)
3565 {
3566 m_map->ZoomMap(line->GetParam("factor")->AsFloat(2.0f));
3567 m_map->MapEnable(line->GetParam("enable")->AsBool(true));
3568 continue;
3569 }
3570
3571 if (line->GetCommand() == "MaxFlyingHeight" && !resetObject)
3572 {
3573 m_terrain->SetFlyingMaxHeight(line->GetParam("max")->AsFloat(280.0f)*g_unit);
3574 continue;
3575 }
3576
3577 if (line->GetCommand() == "AddFlyingHeight" && !resetObject)
3578 {
3579 m_terrain->AddFlyingLimit(line->GetParam("center")->AsPoint()*g_unit,
3580 line->GetParam("extRadius")->AsFloat(20.0f)*g_unit,
3581 line->GetParam("intRadius")->AsFloat(10.0f)*g_unit,
3582 line->GetParam("maxHeight")->AsFloat(200.0f));
3583 continue;
3584 }
3585
3586 if (line->GetCommand() == "Camera")
3587 {
3588 m_camera->Init(line->GetParam("eye")->AsPoint(Math::Vector(0.0f, 0.0f, 0.0f))*g_unit,
3589 line->GetParam("lookat")->AsPoint(Math::Vector(0.0f, 0.0f, 0.0f))*g_unit,
3590 resetObject ? 0.0f : line->GetParam("delay")->AsFloat(0.0f));
3591
3592 if (line->GetParam("fadeIn")->AsBool(false))
3593 m_camera->StartOver(Gfx::CAM_OVER_EFFECT_FADEIN_WHITE, Math::Vector(0.0f, 0.0f, 0.0f), 1.0f);
3594 continue;
3595 }
3596
3597 //! Note: This feature may be changed in next releases,
3598 //! Places new viewpoint, which can be selected later in (currently only in Code Battle) UI.
3599 //! Usage: View eye=x; y; z lookat=x; y; z
3600
3601 if (line->GetCommand() == "View")
3602 {
3603 if(m_viewpoints.size() == 10)
3604 {
3605 GetLogger()->Warn("Reached limit of 10 viewpoints, next ones will be ommited.\n");
3606 continue;
3607 }
3608 Viewpoint tmp;
3609 tmp.eye = line->GetParam("eye")->AsPoint()*g_unit;
3610 tmp.look = line->GetParam("lookat")->AsPoint()*g_unit;
3611 tmp.button = line->GetParam("button")->AsInt(13); // 13 is the camera button
3612 m_viewpoints.push_back(tmp);
3613 continue;
3614 }
3615
3616 if (line->GetCommand() == "EndMissionTake" && !resetObject)
3617 {
3618 auto endTake = MakeUnique<CSceneEndCondition>();
3619 endTake->Read(line.get());
3620 if (endTake->immediat)
3621 m_endTakeImmediat = true;
3622 m_endTake.push_back(std::move(endTake));
3623
3624 if (!line->GetParam("pos")->IsDefined() || !line->GetParam("dist")->IsDefined())
3625 {
3626 LoadingWarning("The defaults for pos= and dist= are going to change, specify them explicitly. See issue #759 (https://git.io/vVBzH)");
3627 }
3628 continue;
3629 }
3630 if (line->GetCommand() == "EndMissionTeams" && !resetObject)
3631 {
3632 m_endTakeTeamImmediateWin = line->GetParam("immediateWin")->AsBool(false); // false = finishing removes the team that finished, true = finishing for one team ends the whole game
3633 continue;
3634 }
3635 if (line->GetCommand() == "EndMissionDelay" && !resetObject)
3636 {
3637 m_endTakeWinDelay = line->GetParam("win")->AsFloat(2.0f);
3638 m_endTakeLostDelay = line->GetParam("lost")->AsFloat(2.0f);
3639 continue;
3640 }
3641 if (line->GetCommand() == "EndMissionResearch" && !resetObject) // This is not used in any original Colobot levels, but we'll keep it for userlevel creators
3642 {
3643 m_endTakeResearch |= line->GetParam("type")->AsResearchFlag();
3644 continue;
3645 }
3646 if (line->GetCommand() == "EndMissionTimeout" && !resetObject)
3647 {
3648 m_endTakeTimeout = line->GetParam("time")->AsFloat();
3649 continue;
3650 }
3651
3652 if (line->GetCommand() == "Scoreboard" && !resetObject)
3653 {
3654 if (line->GetParam("enable")->AsBool(false))
3655 {
3656 // Create the scoreboard
3657 m_scoreboard = MakeUnique<CScoreboard>();
3658 m_scoreboard->SetSortType(line->GetParam("sort")->AsSortType(CScoreboard::SortType::SORT_ID));
3659 }
3660 continue;
3661 }
3662
3663 if (line->GetCommand() == "ScoreboardKillRule" && !resetObject)
3664 {
3665 if (!m_scoreboard)
3666 throw CLevelParserException("ScoreboardKillRule encountered but scoreboard is not enabled");
3667 auto rule = MakeUnique<CScoreboard::CScoreboardKillRule>();
3668 rule->Read(line.get());
3669 m_scoreboard->AddKillRule(std::move(rule));
3670 continue;
3671 }
3672 if (line->GetCommand() == "ScoreboardObjectRule" && !resetObject)
3673 {
3674 if (!m_scoreboard)
3675 throw CLevelParserException("ScoreboardObjectRule encountered but scoreboard is not enabled");
3676 auto rule = MakeUnique<CScoreboard::CScoreboardObjectRule>();
3677 rule->Read(line.get());
3678 m_scoreboard->AddObjectRule(std::move(rule));
3679 continue;
3680 }
3681 if (line->GetCommand() == "ScoreboardEndTakeRule" && !resetObject)
3682 {
3683 if (!m_scoreboard)
3684 throw CLevelParserException("ScoreboardEndTakeRule encountered but scoreboard is not enabled");
3685 auto rule = MakeUnique<CScoreboard::CScoreboardEndTakeRule>();
3686 rule->Read(line.get());
3687 m_scoreboard->AddEndTakeRule(std::move(rule));
3688 continue;
3689 }
3690
3691 if (line->GetCommand() == "ObligatoryToken" && !resetObject)
3692 {
3693 std::string token = line->GetParam("text")->AsString();
3694 if (!line->GetParam("min")->IsDefined() && !line->GetParam("max")->IsDefined())
3695 GetLogger()->Warn("ObligatoryToken without specifying min/max is provided only for backwards compatibility - instead, do this: ObligatoryToken text=\"%s\" min=1\n", token.c_str());
3696 if (m_obligatoryTokens.count(token))
3697 throw CLevelParserException("Incorrect ObligatoryToken specification - you cannot define a token twice");
3698
3699 m_obligatoryTokens[token].min = line->GetParam("min")->AsInt(line->GetParam("max")->IsDefined() ? -1 : 1); // BACKWARDS COMPATIBILITY: if neither min or max are defined, default to min=1
3700 m_obligatoryTokens[token].max = line->GetParam("max")->AsInt(-1);
3701 if (m_obligatoryTokens[token].min >= 0 && m_obligatoryTokens[token].max >= 0 && m_obligatoryTokens[token].min > m_obligatoryTokens[token].max)
3702 {
3703 throw CLevelParserException("Incorrect ObligatoryToken specification - min cannot be greater than max");
3704 }
3705 continue;
3706 }
3707
3708 if (line->GetCommand() == "ProhibitedToken" && !resetObject) // NOTE: Kept only for backwards compatibility
3709 {
3710 std::string token = line->GetParam("text")->AsString();
3711 GetLogger()->Warn("ProhibitedToken is only provided for backwards compatibility - instead, do this: ObligatoryToken text=\"%s\" max=0\n", token.c_str());
3712 if (m_obligatoryTokens.count(token))
3713 throw CLevelParserException("Incorrect ObligatoryToken specification - you cannot define a token twice");
3714
3715 m_obligatoryTokens[token].min = -1;
3716 m_obligatoryTokens[token].max = 0;
3717 continue;
3718 }
3719
3720 if (line->GetCommand() == "EnableBuild" && !resetObject)
3721 {
3722 m_build |= line->GetParam("type")->AsBuildFlag();
3723 continue;
3724 }
3725
3726 if (line->GetCommand() == "EnableResearch" && !resetObject)
3727 {
3728 m_researchEnable |= line->GetParam("type")->AsResearchFlag();
3729 continue;
3730 }
3731
3732 if (line->GetCommand() == "DoneResearch" && m_sceneReadPath.empty() && !resetObject) // not loading file?
3733 {
3734 m_researchDone[0] |= line->GetParam("type")->AsResearchFlag();
3735 continue;
3736 }
3737
3738 if (line->GetCommand() == "NewScript" && !resetObject)
3739 {
3740 m_newScriptName.push_back(NewScriptName(line->GetParam("type")->AsObjectType(OBJECT_NULL), line->GetParam("name")->AsString("")));
3741 continue;
3742 }
3743
3744 if (!m_sceneReadPath.empty()) continue; // ignore errors when loading saved game (TODO: don't report ones that are just not loaded when loading saved game)
3745 if (resetObject) continue; // ignore when reseting just objects (TODO: see above)
3746
3747 throw CLevelParserException("Unknown command: '" + line->GetCommand() + "' in " + line->GetLevelFilename() + ":" + boost::lexical_cast<std::string>(line->GetLineNumber()));
3748 }
3749
3750 // Do this here to prevent the first frame from taking a long time to render
3751 m_engine->UpdateGroundSpotTextures();
3752
3753 m_ui->GetLoadingScreen()->SetProgress(1.0f, RT_LOADING_FINISHED);
3754 if (m_ui->GetLoadingScreen()->IsVisible())
3755 {
3756 // Force render of the "Loading finished" screen because it looks weird when the progress bar disappears in the middle
3757 m_app->Render();
3758 }
3759
3760 if (!resetObject)
3761 {
3762 m_engine->SetBackground(backgroundPath,
3763 backgroundUp,
3764 backgroundDown,
3765 backgroundCloudUp,
3766 backgroundCloudDown,
3767 backgroundFull);
3768 }
3769
3770 if (m_levelCategory == LevelCategory::Missions && !resetObject) // mission?
3771 {
3772 m_playerProfile->SetFreeGameResearchUnlock(m_playerProfile->GetFreeGameResearchUnlock() | m_researchDone[0]);
3773 m_playerProfile->SetFreeGameBuildUnlock(m_playerProfile->GetFreeGameBuildUnlock() | m_build);
3774 }
3775
3776 if (m_levelCategory == LevelCategory::FreeGame && !resetObject) // free play?
3777 {
3778 m_researchDone[0] = m_playerProfile->GetFreeGameResearchUnlock();
3779
3780 m_build = m_playerProfile->GetFreeGameBuildUnlock();
3781 m_build &= ~BUILD_RESEARCH;
3782 m_build &= ~BUILD_LABO;
3783 m_build |= BUILD_FACTORY;
3784 m_build |= BUILD_GFLAT;
3785 m_build |= BUILD_FLAG;
3786 }
3787
3788 if (m_levelCategory == LevelCategory::GamePlus && !m_ui->GetPlusResearch() && !resetObject) // new game plus?
3789 {
3790 m_researchDone[0] |= m_playerProfile->GetFreeGameResearchUnlock();
3791 m_build |= m_playerProfile->GetFreeGameBuildUnlock();
3792 }
3793
3794 if (!resetObject)
3795 {
3796 m_short->SetMode(false); // vehicles?
3797 }
3798
3799 m_map->ShowMap(m_mapShow);
3800 m_map->UpdateMap();
3801 // TODO: m_engine->TimeInit(); ??
3802 m_input->ResetKeyStates();
3803 m_time = 0.0f;
3804 if (m_sceneReadPath.empty()) m_gameTime = 0.0f;
3805 m_gameTimeAbsolute = 0.0f;
3806 m_autosaveLast = 0.0f;
3807 m_infoUsed = 0;
3808
3809 m_selectObject = sel;
3810
3811 if (m_base == nullptr && // no main base?
3812 !m_fixScene) // interractive scene?
3813 {
3814 CObject* obj = sel;
3815 if (sel == nullptr)
3816 obj = SearchHuman();
3817
3818 if (obj != nullptr)
3819 {
3820 assert(obj->Implements(ObjectInterfaceType::Controllable));
3821 SelectObject(obj);
3822 m_camera->SetControllingObject(obj);
3823 m_camera->SetType(dynamic_cast<CControllableObject&>(*obj).GetCameraType());
3824 }
3825 }
3826
3827 if (m_fixScene)
3828 m_camera->SetType(Gfx::CAM_TYPE_SCRIPT);
3829
3830 if (!m_sceneReadPath.empty() && sel != nullptr) // loading file?
3831 {
3832 Math::Vector pos = sel->GetPosition();
3833 m_camera->Init(pos, pos, 0.0f);
3834
3835 SelectObject(sel);
3836 m_camera->SetControllingObject(sel);
3837
3838 m_beginSatCom = true; // message already displayed
3839 }
3840 }
3841 catch (...)
3842 {
3843 m_sceneReadPath = "";
3844 throw;
3845 }
3846 m_sceneReadPath = "";
3847
3848 if (m_app->GetSceneTestMode())
3849 m_eventQueue->AddEvent(Event(EVENT_QUIT));
3850
3851 m_ui->ShowLoadingScreen(false);
3852 if (m_missionType == MISSION_CODE_BATTLE)
3853 {
3854 CreateCodeBattleInterface();
3855 }
3856 CreateShortcuts();
3857 }
3858
LevelLoadingError(const std::string & error,const std::runtime_error & exception,Phase exitPhase)3859 void CRobotMain::LevelLoadingError(const std::string& error, const std::runtime_error& exception, Phase exitPhase)
3860 {
3861 m_ui->ShowLoadingScreen(false);
3862
3863 GetLogger()->Error("%s\n", error.c_str());
3864 GetLogger()->Error("%s\n", exception.what());
3865 ChangePhase(exitPhase);
3866 m_ui->GetDialog()->StartInformation("Loading error", error, exception.what(), true, false);
3867 }
3868
3869 //! Creates a directional light
CreateLight(Math::Vector direction,Gfx::Color color)3870 int CRobotMain::CreateLight(Math::Vector direction, Gfx::Color color)
3871 {
3872 if (direction.x == 0.0f &&
3873 direction.y == 0.0f &&
3874 direction.z == 0.0f)
3875 {
3876 direction.y = -1.0f;
3877 }
3878
3879 Gfx::Light light;
3880 light.type = Gfx::LIGHT_DIRECTIONAL;
3881 light.diffuse = color;
3882 light.ambient = color * 0.1f;
3883 light.direction = direction;
3884 int obj = m_lightMan->CreateLight(Gfx::LIGHT_PRI_HIGH);
3885 m_lightMan->SetLight(obj, light);
3886
3887 return obj;
3888 }
3889
3890 //! Creates a light spot
CreateSpot(Math::Vector pos,Gfx::Color color)3891 int CRobotMain::CreateSpot(Math::Vector pos, Gfx::Color color)
3892 {
3893 if (!m_engine->GetLightMode()) return -1;
3894
3895 pos.y += m_terrain->GetFloorLevel(pos);
3896
3897 Gfx::Light light;
3898 light.type = Gfx::LIGHT_SPOT;
3899 light.diffuse = color;
3900 light.ambient = color * 0.1f;
3901 light.position = pos;
3902 light.direction = Math::Vector(0.0f, -1.0f, 0.0f);
3903 light.spotIntensity = 1.0f;
3904 light.spotAngle = 90.0f*Math::PI/180.0f;
3905 light.attenuation0 = 2.0f;
3906 light.attenuation1 = 0.0f;
3907 light.attenuation2 = 0.0f;
3908 int obj = m_lightMan->CreateLight(Gfx::LIGHT_PRI_HIGH);
3909 m_lightMan->SetLight(obj, light);
3910
3911 return obj;
3912 }
3913
3914
3915 //! Change the colors and textures
ChangeColor()3916 void CRobotMain::ChangeColor()
3917 {
3918 if (m_phase != PHASE_SIMUL &&
3919 m_phase != PHASE_SETUPds &&
3920 m_phase != PHASE_SETUPgs &&
3921 m_phase != PHASE_SETUPps &&
3922 m_phase != PHASE_SETUPcs &&
3923 m_phase != PHASE_SETUPss &&
3924 m_phase != PHASE_MOD_LIST &&
3925 m_phase != PHASE_WIN &&
3926 m_phase != PHASE_LOST &&
3927 m_phase != PHASE_APPERANCE ) return;
3928
3929 // Player texture
3930
3931 Math::Point ts = Math::Point(0.0f, 0.0f);
3932 Math::Point ti = Math::Point(1.0f, 1.0f); // the entire image
3933
3934 Gfx::Color colorRef1, colorNew1, colorRef2, colorNew2;
3935
3936 colorRef1.a = 0.0f;
3937 colorRef2.a = 0.0f;
3938
3939 colorRef1.r = 206.0f/256.0f;
3940 colorRef1.g = 206.0f/256.0f;
3941 colorRef1.b = 204.0f/256.0f; // ~white
3942 colorNew1 = m_playerProfile->GetApperance().colorCombi;
3943 colorRef2.r = 255.0f/256.0f;
3944 colorRef2.g = 132.0f/256.0f;
3945 colorRef2.b = 1.0f/256.0f; // orange
3946 colorNew2 = m_playerProfile->GetApperance().colorBand;
3947
3948 Math::Point exclu[6];
3949 exclu[0] = Math::Point(192.0f/256.0f, 0.0f/256.0f);
3950 exclu[1] = Math::Point(256.0f/256.0f, 64.0f/256.0f); // crystals + cylinders
3951 exclu[2] = Math::Point(208.0f/256.0f, 224.0f/256.0f);
3952 exclu[3] = Math::Point(256.0f/256.0f, 256.0f/256.0f); // SatCom screen
3953 exclu[4] = Math::Point(0.0f, 0.0f);
3954 exclu[5] = Math::Point(0.0f, 0.0f); // terminator
3955 m_engine->ChangeTextureColor("textures/objects/human.png", colorRef1, colorNew1, colorRef2, colorNew2, 0.30f, 0.01f, ts, ti, exclu);
3956
3957 float tolerance;
3958
3959 int face = GetGamerFace();
3960 if (face == 0) // normal?
3961 {
3962 colorRef1.r = 90.0f/256.0f;
3963 colorRef1.g = 95.0f/256.0f;
3964 colorRef1.b = 85.0f/256.0f; // black
3965 tolerance = 0.15f;
3966 }
3967 if (face == 1) // bald?
3968 {
3969 colorRef1.r = 74.0f/256.0f;
3970 colorRef1.g = 58.0f/256.0f;
3971 colorRef1.b = 46.0f/256.0f; // brown
3972 tolerance = 0.20f;
3973 }
3974 if (face == 2) // carlos?
3975 {
3976 colorRef1.r = 70.0f/256.0f;
3977 colorRef1.g = 40.0f/256.0f;
3978 colorRef1.b = 8.0f/256.0f; // brown
3979 tolerance = 0.30f;
3980 }
3981 if (face == 3) // blonde?
3982 {
3983 colorRef1.r = 74.0f/256.0f;
3984 colorRef1.g = 16.0f/256.0f;
3985 colorRef1.b = 0.0f/256.0f; // yellow
3986 tolerance = 0.20f;
3987 }
3988 colorNew1 = m_playerProfile->GetApperance().colorHair;
3989 colorRef2.r = 0.0f;
3990 colorRef2.g = 0.0f;
3991 colorRef2.b = 0.0f;
3992 colorNew2.r = 0.0f;
3993 colorNew2.g = 0.0f;
3994 colorNew2.b = 0.0f;
3995
3996 char name[100];
3997 sprintf(name, "textures/objects/face%.2d.png", face+1);
3998 exclu[0] = Math::Point(105.0f/256.0f, 47.0f/166.0f);
3999 exclu[1] = Math::Point(153.0f/256.0f, 79.0f/166.0f); // blue canister
4000 exclu[2] = Math::Point(0.0f, 0.0f);
4001 exclu[3] = Math::Point(0.0f, 0.0f); // terminator
4002 m_engine->ChangeTextureColor(name, colorRef1, colorNew1, colorRef2, colorNew2, tolerance, 0.00f, ts, ti, exclu);
4003
4004 colorRef2.r = 0.0f;
4005 colorRef2.g = 0.0f;
4006 colorRef2.b = 0.0f;
4007 colorNew2.r = 0.0f;
4008 colorNew2.g = 0.0f;
4009 colorNew2.b = 0.0f;
4010
4011 // VehicleColor
4012
4013 for (auto it : m_colorNewBot)
4014 {
4015 int team = it.first;
4016 Gfx::Color newColor = it.second;
4017 std::string teamStr = StrUtils::ToString<int>(team);
4018 if(team == 0) teamStr = "";
4019
4020 m_engine->ChangeTextureColor("textures/objects/base1.png"+teamStr, "textures/objects/base1.png", COLOR_REF_BOT, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, nullptr, 0, true);
4021 m_engine->ChangeTextureColor("textures/objects/convert.png"+teamStr, "textures/objects/convert.png", COLOR_REF_BOT, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, nullptr, 0, true);
4022 m_engine->ChangeTextureColor("textures/objects/derrick.png"+teamStr, "textures/objects/derrick.png", COLOR_REF_BOT, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, nullptr, 0, true);
4023 m_engine->ChangeTextureColor("textures/objects/factory.png"+teamStr, "textures/objects/factory.png", COLOR_REF_BOT, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, nullptr, 0, true);
4024 m_engine->ChangeTextureColor("textures/objects/lemt.png"+teamStr, "textures/objects/lemt.png", COLOR_REF_BOT, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, nullptr, 0, true);
4025 m_engine->ChangeTextureColor("textures/objects/roller.png"+teamStr, "textures/objects/roller.png", COLOR_REF_BOT, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, nullptr, 0, true);
4026 m_engine->ChangeTextureColor("textures/objects/search.png"+teamStr, "textures/objects/search.png", COLOR_REF_BOT, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, nullptr, 0, true);
4027 m_engine->ChangeTextureColor("textures/objects/rollert.png"+teamStr, "textures/objects/rollert.png", COLOR_REF_BOT, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, nullptr, 0, true);
4028
4029 exclu[0] = Math::Point( 0.0f/256.0f, 160.0f/256.0f);
4030 exclu[1] = Math::Point(256.0f/256.0f, 256.0f/256.0f); // pencils
4031 exclu[2] = Math::Point(0.0f, 0.0f);
4032 exclu[3] = Math::Point(0.0f, 0.0f); // terminator
4033 m_engine->ChangeTextureColor("textures/objects/drawer.png"+teamStr, "textures/objects/drawer.png", COLOR_REF_BOT, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, exclu, 0, true);
4034
4035 exclu[0] = Math::Point(237.0f/256.0f, 176.0f/256.0f);
4036 exclu[1] = Math::Point(256.0f/256.0f, 220.0f/256.0f); // blue canister
4037 exclu[2] = Math::Point(106.0f/256.0f, 150.0f/256.0f);
4038 exclu[3] = Math::Point(130.0f/256.0f, 214.0f/256.0f); // safe location
4039 exclu[4] = Math::Point(0.0f, 0.0f);
4040 exclu[5] = Math::Point(0.0f, 0.0f); // terminator
4041 m_engine->ChangeTextureColor("textures/objects/subm.png"+teamStr, "textures/objects/subm.png", COLOR_REF_BOT, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, exclu, 0, true);
4042 }
4043
4044 // AlienColor
4045
4046 exclu[0] = Math::Point(128.0f/256.0f, 160.0f/256.0f);
4047 exclu[1] = Math::Point(256.0f/256.0f, 256.0f/256.0f); // SatCom
4048 exclu[2] = Math::Point(0.0f, 0.0f);
4049 exclu[3] = Math::Point(0.0f, 0.0f); // terminator
4050 m_engine->ChangeTextureColor("textures/objects/ant.png", COLOR_REF_ALIEN, m_colorNewAlien, colorRef2, colorNew2, 0.50f, -1.0f, ts, ti, exclu);
4051 m_engine->ChangeTextureColor("textures/objects/mother.png", COLOR_REF_ALIEN, m_colorNewAlien, colorRef2, colorNew2, 0.50f, -1.0f, ts, ti);
4052
4053 // GreeneryColor
4054 m_engine->ChangeTextureColor("textures/objects/plant.png", COLOR_REF_GREEN, m_colorNewGreen, colorRef2, colorNew2, 0.50f, -1.0f, ts, ti);
4055
4056 // water color
4057
4058 // PARTIPLOUF0 and PARTIDROP :
4059 ts = Math::Point(0.500f, 0.500f);
4060 ti = Math::Point(0.875f, 0.750f);
4061 m_engine->ChangeTextureColor("textures/effect00.png", COLOR_REF_WATER, m_colorNewWater, colorRef2, colorNew2, 0.20f, -1.0f, ts, ti, nullptr, m_colorShiftWater, true);
4062
4063 // PARTIFLIC :
4064 ts = Math::Point(0.00f, 0.75f);
4065 ti = Math::Point(0.25f, 1.00f);
4066 m_engine->ChangeTextureColor("textures/effect02.png", COLOR_REF_WATER, m_colorNewWater, colorRef2, colorNew2, 0.20f, -1.0f, ts, ti, nullptr, m_colorShiftWater, true);
4067 }
4068
4069 //! Calculates the distance to the nearest object
4070 namespace
4071 {
SearchNearestObject(CObjectManager * objMan,Math::Vector center,CObject * exclu)4072 float SearchNearestObject(CObjectManager* objMan, Math::Vector center, CObject* exclu)
4073 {
4074 float min = 100000.0f;
4075 for (CObject* obj : objMan->GetAllObjects())
4076 {
4077 if (!obj->GetDetectable()) continue; // inactive?
4078 if (IsObjectBeingTransported(obj)) continue;
4079
4080 if (obj == exclu) continue;
4081
4082 ObjectType type = obj->GetType();
4083
4084 if (type == OBJECT_BASE)
4085 {
4086 Math::Vector oPos = obj->GetPosition();
4087 if (oPos.x != center.x ||
4088 oPos.z != center.z)
4089 {
4090 float dist = Math::Distance(center, oPos) - 80.0f;
4091 if (dist < 0.0f) dist = 0.0f;
4092 min = Math::Min(min, dist);
4093 continue;
4094 }
4095 }
4096
4097 if (type == OBJECT_STATION ||
4098 type == OBJECT_REPAIR ||
4099 type == OBJECT_DESTROYER)
4100 {
4101 Math::Vector oPos = obj->GetPosition();
4102 float dist = Math::Distance(center, oPos) - 8.0f;
4103 if (dist < 0.0f) dist = 0.0f;
4104 min = Math::Min(min, dist);
4105 }
4106
4107 for (const auto &crashSphere : obj->GetAllCrashSpheres())
4108 {
4109 Math::Vector oPos = crashSphere.sphere.pos;
4110 float oRadius = crashSphere.sphere.radius;
4111
4112 float dist = Math::Distance(center, oPos) - oRadius;
4113 if (dist < 0.0f) dist = 0.0f;
4114 min = Math::Min(min, dist);
4115 }
4116 }
4117 return min;
4118 }
4119 }
4120
4121 //! Calculates a free space
FreeSpace(Math::Vector & center,float minRadius,float maxRadius,float space,CObject * exclu)4122 bool CRobotMain::FreeSpace(Math::Vector ¢er, float minRadius, float maxRadius,
4123 float space, CObject *exclu)
4124 {
4125 if (minRadius < maxRadius) // from internal to external?
4126 {
4127 for (float radius = minRadius; radius <= maxRadius; radius += space)
4128 {
4129 float ia = space/radius;
4130 for (float angle = 0.0f; angle < Math::PI*2.0f; angle += ia)
4131 {
4132 Math::Point p;
4133 p.x = center.x+radius;
4134 p.y = center.z;
4135 p = Math::RotatePoint(Math::Point(center.x, center.z), angle, p);
4136 Math::Vector pos;
4137 pos.x = p.x;
4138 pos.z = p.y;
4139 pos.y = 0.0f;
4140 m_terrain->AdjustToFloor(pos, true);
4141 float dist = SearchNearestObject(m_objMan.get(), pos, exclu);
4142 if (dist >= space)
4143 {
4144 float flat = m_terrain->GetFlatZoneRadius(pos, dist/2.0f);
4145 if (flat >= dist/2.0f)
4146 {
4147 center = pos;
4148 return true;
4149 }
4150 }
4151 }
4152 }
4153 }
4154 else // from external to internal?
4155 {
4156 for (float radius=maxRadius; radius >= minRadius; radius -= space)
4157 {
4158 float ia = space/radius;
4159 for (float angle=0.0f ; angle<Math::PI*2.0f ; angle+=ia )
4160 {
4161 Math::Point p;
4162 p.x = center.x+radius;
4163 p.y = center.z;
4164 p = Math::RotatePoint(Math::Point(center.x, center.z), angle, p);
4165 Math::Vector pos;
4166 pos.x = p.x;
4167 pos.z = p.y;
4168 pos.y = 0.0f;
4169 m_terrain->AdjustToFloor(pos, true);
4170 float dist = SearchNearestObject(m_objMan.get(), pos, exclu);
4171 if (dist >= space)
4172 {
4173 float flat = m_terrain->GetFlatZoneRadius(pos, dist/2.0f);
4174 if (flat >= dist/2.0f)
4175 {
4176 center = pos;
4177 return true;
4178 }
4179 }
4180 }
4181 }
4182 }
4183 return false;
4184 }
4185
4186 //! Calculates a flat free space
FlatFreeSpace(Math::Vector & center,float minFlat,float minRadius,float maxRadius,float space,CObject * exclu)4187 bool CRobotMain::FlatFreeSpace(Math::Vector ¢er, float minFlat, float minRadius, float maxRadius,
4188 float space, CObject *exclu)
4189 {
4190 if (minRadius < maxRadius) // from internal to external?
4191 {
4192 for (float radius = minRadius; radius <= maxRadius; radius += space)
4193 {
4194 float ia = space/radius;
4195 for (float angle = 0.0f; angle < Math::PI*2.0f; angle += ia)
4196 {
4197 Math::Point p;
4198 p.x = center.x+radius;
4199 p.y = center.z;
4200 p = Math::RotatePoint(Math::Point(center.x, center.z), angle, p);
4201 Math::Vector pos;
4202 pos.x = p.x;
4203 pos.z = p.y;
4204 pos.y = 0.0f;
4205 m_terrain->AdjustToFloor(pos, true);
4206 float dist = SearchNearestObject(m_objMan.get(), pos, exclu);
4207 if (dist >= space)
4208 {
4209 float flat = m_terrain->GetFlatZoneRadius(pos, dist/2.0f);
4210 if (flat >= dist/2.0f)
4211 {
4212 flat = m_terrain->GetFlatZoneRadius(pos, minFlat);
4213 if(flat >= minFlat)
4214 {
4215 center = pos;
4216 return true;
4217 }
4218 }
4219 }
4220 }
4221 }
4222 }
4223 else // from external to internal?
4224 {
4225 for (float radius=maxRadius; radius >= minRadius; radius -= space)
4226 {
4227 float ia = space/radius;
4228 for (float angle=0.0f ; angle<Math::PI*2.0f ; angle+=ia )
4229 {
4230 Math::Point p;
4231 p.x = center.x+radius;
4232 p.y = center.z;
4233 p = Math::RotatePoint(Math::Point(center.x, center.z), angle, p);
4234 Math::Vector pos;
4235 pos.x = p.x;
4236 pos.z = p.y;
4237 pos.y = 0.0f;
4238 m_terrain->AdjustToFloor(pos, true);
4239 float dist = SearchNearestObject(m_objMan.get(), pos, exclu);
4240 if (dist >= space)
4241 {
4242 float flat = m_terrain->GetFlatZoneRadius(pos, dist/2.0f);
4243 if (flat >= dist/2.0f)
4244 {
4245 flat = m_terrain->GetFlatZoneRadius(pos, minFlat);
4246 if(flat >= minFlat)
4247 {
4248 center = pos;
4249 return true;
4250 }
4251 }
4252 }
4253 }
4254 }
4255 }
4256 return false;
4257 }
4258
4259 //! Calculates the maximum radius of a free space
GetFlatZoneRadius(Math::Vector center,float maxRadius,CObject * exclu)4260 float CRobotMain::GetFlatZoneRadius(Math::Vector center, float maxRadius,
4261 CObject *exclu)
4262 {
4263 float dist = SearchNearestObject(m_objMan.get(), center, exclu);
4264 if (dist == 0.0f) return 0.0f;
4265 if (dist < maxRadius)
4266 maxRadius = dist;
4267
4268 return m_terrain->GetFlatZoneRadius(center, maxRadius);
4269 }
4270
4271
4272 //! Hides buildable area when a cube of metal is taken up
HideDropZone(CObject * metal)4273 void CRobotMain::HideDropZone(CObject* metal)
4274 {
4275 if (m_showLimit[1].used &&
4276 m_showLimit[1].link == metal)
4277 {
4278 FlushShowLimit(1);
4279 }
4280
4281 if (m_showLimit[2].used &&
4282 m_showLimit[2].link == metal)
4283 {
4284 FlushShowLimit(2);
4285 }
4286 }
4287
4288 //! Shows the buildable area when a cube of metal is deposited
ShowDropZone(CObject * metal,CObject * transporter)4289 void CRobotMain::ShowDropZone(CObject* metal, CObject* transporter)
4290 {
4291 if (metal == nullptr) return;
4292
4293 Math::Vector center = metal->GetPosition();
4294
4295 // Calculates the maximum radius possible depending on other items.
4296 float oMax = 30.0f; // radius to build the biggest building
4297 float tMax;
4298 for (CObject* obj : m_objMan->GetAllObjects())
4299 {
4300 if (!obj->GetDetectable()) continue; // inactive?
4301 if (IsObjectBeingTransported(obj)) continue;
4302
4303 if (obj == metal) continue;
4304 if (obj == transporter) continue;
4305
4306 Math::Vector oPos;
4307
4308 ObjectType type = obj->GetType();
4309 if (type == OBJECT_BASE)
4310 {
4311 oPos = obj->GetPosition();
4312 float dist = Math::Distance(center, oPos)-80.0f;
4313 oMax = Math::Min(oMax, dist);
4314 }
4315 else
4316 {
4317 for (const auto& crashSphere : obj->GetAllCrashSpheres())
4318 {
4319 float dist = Math::Distance(center, crashSphere.sphere.pos)-crashSphere.sphere.radius;
4320 oMax = Math::Min(oMax, dist);
4321 }
4322 }
4323
4324 if ( type == OBJECT_DERRICK ||
4325 type == OBJECT_FACTORY ||
4326 type == OBJECT_STATION ||
4327 type == OBJECT_CONVERT ||
4328 type == OBJECT_REPAIR ||
4329 type == OBJECT_DESTROYER||
4330 type == OBJECT_TOWER ||
4331 type == OBJECT_RESEARCH ||
4332 type == OBJECT_RADAR ||
4333 type == OBJECT_ENERGY ||
4334 type == OBJECT_LABO ||
4335 type == OBJECT_NUCLEAR ||
4336 type == OBJECT_START ||
4337 type == OBJECT_END ||
4338 type == OBJECT_INFO ||
4339 type == OBJECT_PARA ||
4340 type == OBJECT_SAFE ||
4341 type == OBJECT_HUSTON ) // building?
4342 {
4343 for (const auto& crashSphere : obj->GetAllCrashSpheres())
4344 {
4345 float dist = Math::Distance(center, crashSphere.sphere.pos)-crashSphere.sphere.radius-BUILDMARGIN;
4346 oMax = Math::Min(oMax, dist);
4347 }
4348 }
4349 }
4350
4351 // Calculates the maximum possible radius depending on terrain.
4352 if (oMax >= 2.0f)
4353 tMax = m_terrain->GetFlatZoneRadius(center, 30.0f);
4354 else
4355 tMax = 0.0f;
4356
4357 float radius = Math::Min(oMax, tMax);
4358 if (radius >= 2.0f)
4359 SetShowLimit(1, Gfx::PARTILIMIT2, metal, center, radius, 10.0f);
4360 }
4361
4362 //! Erases the boundaries shown
FlushShowLimit(int i)4363 void CRobotMain::FlushShowLimit(int i)
4364 {
4365 for (int j = 0; j < m_showLimit[i].total; j++)
4366 {
4367 if (m_showLimit[i].parti[j] == 0) continue;
4368
4369 m_particle->DeleteParticle(m_showLimit[i].parti[j]);
4370 m_showLimit[i].parti[j] = 0;
4371 }
4372
4373 m_showLimit[i].total = 0;
4374 m_showLimit[i].link = nullptr;
4375 m_showLimit[i].used = false;
4376 }
4377
4378 //! Specifies the boundaries to show
SetShowLimit(int i,Gfx::ParticleType parti,CObject * obj,Math::Vector pos,float radius,float duration)4379 void CRobotMain::SetShowLimit(int i, Gfx::ParticleType parti, CObject *obj,
4380 Math::Vector pos, float radius, float duration)
4381 {
4382 FlushShowLimit(i); // erases the current boundaries
4383
4384 if (radius <= 0.0f) return;
4385
4386 Math::Point dim;
4387 float dist;
4388 if (radius <= 50.0f)
4389 {
4390 dim = Math::Point(0.3f, 0.3f);
4391 dist = 2.5f;
4392 }
4393 else
4394 {
4395 dim = Math::Point(1.5f, 1.5f);
4396 dist = 10.0f;
4397 }
4398
4399 m_showLimit[i].used = true;
4400 m_showLimit[i].link = obj;
4401 m_showLimit[i].pos = pos;
4402 m_showLimit[i].radius = radius;
4403 m_showLimit[i].duration = duration;
4404 m_showLimit[i].total = static_cast<int>((radius*2.0f*Math::PI)/dist);
4405 if (m_showLimit[i].total > MAXSHOWPARTI) m_showLimit[i].total = MAXSHOWPARTI;
4406 m_showLimit[i].time = 0.0f;
4407
4408 for (int j = 0; j < m_showLimit[i].total; j++)
4409 {
4410 m_showLimit[i].parti[j] = m_particle->CreateParticle(pos, Math::Vector(0.0f, 0.0f, 0.0f), dim, parti, duration);
4411 }
4412 }
4413
4414 //! Mount the boundaries of the selected object
StartShowLimit()4415 void CRobotMain::StartShowLimit()
4416 {
4417 CObject* obj = GetSelect();
4418 if (obj == nullptr) return;
4419 if (!obj->Implements(ObjectInterfaceType::Ranged)) return;
4420 float range = dynamic_cast<CRangedObject&>(*obj).GetShowLimitRadius();
4421 if (range == 0.0f) return;
4422 SetShowLimit(0, Gfx::PARTILIMIT1, obj, obj->GetPosition(), range);
4423 }
4424
4425 //! Advances the boundaries shown
FrameShowLimit(float rTime)4426 void CRobotMain::FrameShowLimit(float rTime)
4427 {
4428 if (m_engine->GetPause()) return;
4429
4430 for (int i = 0; i < MAXSHOWLIMIT; i++)
4431 {
4432 if (!m_showLimit[i].used) continue;
4433
4434 m_showLimit[i].time += rTime;
4435
4436 if (m_showLimit[i].time >= m_showLimit[i].duration)
4437 {
4438 FlushShowLimit(i);
4439 continue;
4440 }
4441
4442 float factor;
4443 if (m_showLimit[i].time < 1.0f)
4444 factor = m_showLimit[i].time;
4445 else if (m_showLimit[i].time > m_showLimit[i].duration-1.0f)
4446 factor = m_showLimit[i].duration-m_showLimit[i].time;
4447 else
4448 factor = 1.0f;
4449
4450 float speed = 0.4f-m_showLimit[i].radius*0.001f;
4451 if (speed < 0.1f) speed = 0.1f;
4452 float angle = m_showLimit[i].time*speed;
4453
4454 if (m_showLimit[i].link != nullptr)
4455 {
4456 m_showLimit[i].pos = m_showLimit[i].link->GetPosition();
4457 }
4458
4459 for (int j = 0; j < m_showLimit[i].total; j++)
4460 {
4461 if (m_showLimit[i].parti[j] == 0) continue;
4462
4463 Math::Point center;
4464 center.x = m_showLimit[i].pos.x;
4465 center.y = m_showLimit[i].pos.z;
4466 Math::Point rotate;
4467 rotate.x = center.x+m_showLimit[i].radius*factor;
4468 rotate.y = center.y;
4469 rotate = Math::RotatePoint(center, angle, rotate);
4470
4471 Math::Vector pos;
4472 pos.x = rotate.x;
4473 pos.z = rotate.y;
4474 pos.y = 0.0f;
4475 m_terrain->AdjustToFloor(pos, true);
4476 if (m_showLimit[i].radius <= 50.0f) pos.y += 0.5f;
4477 else pos.y += 2.0f;
4478 m_particle->SetPosition(m_showLimit[i].parti[j], pos);
4479
4480 angle += (2.0f*Math::PI)/m_showLimit[i].total;
4481 }
4482 }
4483 }
4484
4485 //! Saves all programs of all the robots
SaveAllScript()4486 void CRobotMain::SaveAllScript()
4487 {
4488 for (CObject* obj : m_objMan->GetAllObjects())
4489 {
4490 SaveOneScript(obj);
4491 }
4492 }
4493
4494 //! Saves all programs of the robot.
SaveOneScript(CObject * obj)4495 void CRobotMain::SaveOneScript(CObject *obj)
4496 {
4497 if (! obj->Implements(ObjectInterfaceType::ProgramStorage)) return;
4498
4499 CProgramStorageObject* programStorage = dynamic_cast<CProgramStorageObject*>(obj);
4500
4501 char categoryChar = GetLevelCategoryDir(m_levelCategory)[0];
4502 programStorage->SaveAllUserPrograms(m_playerProfile->GetSaveFile(StrUtils::Format("%c%.3d%.3d", categoryChar, m_levelChap, m_levelRank)));
4503 }
4504
4505 //! Saves the stack of the program in execution of a robot
SaveFileStack(CObject * obj,std::ostream & ostr)4506 bool CRobotMain::SaveFileStack(CObject *obj, std::ostream &ostr)
4507 {
4508 if (! obj->Implements(ObjectInterfaceType::Programmable)) return true;
4509
4510 CProgrammableObject* programmable = dynamic_cast<CProgrammableObject*>(obj);
4511
4512 ObjectType type = obj->GetType();
4513 if (type == OBJECT_HUMAN) return true;
4514
4515 long status = 1;
4516 std::stringstream sstr("");
4517
4518 if (!programmable->WriteStack(sstr))
4519 {
4520 GetLogger()->Error("WriteStack failed at object id = %i\n", obj->GetID());
4521 status = 100; // marked bad
4522 }
4523
4524 if (!CBot::WriteLong(ostr, status)) return false;
4525 if (!CBot::WriteStream(ostr, sstr)) return false;
4526
4527 return true;
4528 }
4529
4530 //! Resumes the execution stack of the program in a robot
ReadFileStack(CObject * obj,std::istream & istr)4531 bool CRobotMain::ReadFileStack(CObject *obj, std::istream &istr)
4532 {
4533 if (! obj->Implements(ObjectInterfaceType::Programmable)) return true;
4534
4535 CProgrammableObject* programmable = dynamic_cast<CProgrammableObject*>(obj);
4536
4537 ObjectType type = obj->GetType();
4538 if (type == OBJECT_HUMAN) return true;
4539
4540 long status;
4541 if (!CBot::ReadLong(istr, status)) return false;
4542
4543 if (status == 100) // was marked bad ?
4544 {
4545 if (!CBot::ReadLong(istr, status)) return false;
4546 if (!istr.seekg(status, istr.cur)) return false;
4547 return true; // next program
4548 }
4549
4550 if (status == 1)
4551 {
4552 std::stringstream sstr("");
4553 if (!CBot::ReadStream(istr, sstr)) return false;
4554
4555 if (!programmable->ReadStack(sstr))
4556 {
4557 GetLogger()->Error("ReadStack failed at object id = %i\n", obj->GetID());
4558 }
4559 return true; // next program
4560 }
4561
4562 return false; // error: status == ??
4563 }
4564
GetNewScriptNames(ObjectType type)4565 std::vector<std::string> CRobotMain::GetNewScriptNames(ObjectType type)
4566 {
4567 std::vector<std::string> names;
4568 for (const auto& newScript : m_newScriptName)
4569 {
4570 if (newScript.type == type ||
4571 newScript.type == OBJECT_NULL )
4572 {
4573 names.push_back(newScript.name);
4574 }
4575 }
4576
4577 return names;
4578 }
4579
4580
4581 //! Seeks if an object occupies in a spot, to prevent a backup of the game
IOIsBusy()4582 bool CRobotMain::IOIsBusy()
4583 {
4584 if (CScriptFunctions::CheckOpenFiles()) return true;
4585
4586 for (CObject* obj : m_objMan->GetAllObjects())
4587 {
4588 if (! obj->Implements(ObjectInterfaceType::TaskExecutor)) continue;
4589
4590 if (obj->Implements(ObjectInterfaceType::Programmable) && dynamic_cast<CProgrammableObject&>(*obj).IsProgram()) continue; // TODO: I'm not sure if this is correct but this is how it worked earlier
4591 if (dynamic_cast<CTaskExecutorObject&>(*obj).IsForegroundTask()) return true;
4592 }
4593 return false;
4594 }
4595
4596 //! Writes an object into the backup file
IOWriteObject(CLevelParserLine * line,CObject * obj,const std::string & programDir,int objRank)4597 void CRobotMain::IOWriteObject(CLevelParserLine* line, CObject* obj, const std::string& programDir, int objRank)
4598 {
4599 line->AddParam("type", MakeUnique<CLevelParserParam>(obj->GetType()));
4600 line->AddParam("id", MakeUnique<CLevelParserParam>(obj->GetID()));
4601 line->AddParam("pos", MakeUnique<CLevelParserParam>(obj->GetPosition()/g_unit));
4602 line->AddParam("angle", MakeUnique<CLevelParserParam>(obj->GetRotation() * Math::RAD_TO_DEG));
4603 line->AddParam("zoom", MakeUnique<CLevelParserParam>(obj->GetScale()));
4604
4605 if (obj->Implements(ObjectInterfaceType::Old))
4606 {
4607 line->AddParam("option", MakeUnique<CLevelParserParam>(obj->GetOption()));
4608 }
4609
4610 if (obj->Implements(ObjectInterfaceType::Controllable))
4611 {
4612 auto controllableObj = dynamic_cast<CControllableObject*>(obj);
4613 line->AddParam("trainer", MakeUnique<CLevelParserParam>(controllableObj->GetTrainer()));
4614 if (controllableObj->GetSelect())
4615 line->AddParam("select", MakeUnique<CLevelParserParam>(true));
4616 }
4617
4618 obj->Write(line);
4619
4620 if (obj->GetType() == OBJECT_BASE)
4621 line->AddParam("run", MakeUnique<CLevelParserParam>(3)); // stops and open (PARAM_FIXSCENE)
4622
4623
4624 if (obj->Implements(ObjectInterfaceType::ProgramStorage))
4625 {
4626 CProgramStorageObject* programStorage = dynamic_cast<CProgramStorageObject*>(obj);
4627 if (programStorage->GetProgramStorageIndex() >= 0)
4628 {
4629 programStorage->SaveAllProgramsForSavedScene(line, programDir);
4630 }
4631 else
4632 {
4633 // Probably an object created after the scene started, not loaded from level file
4634 // This means it doesn't normally store programs so it doesn't have program storage id assigned
4635 programStorage->SetProgramStorageIndex(999-objRank); // Set something that won't collide with normal programs
4636 programStorage->SaveAllProgramsForSavedScene(line, programDir);
4637 programStorage->SetProgramStorageIndex(-1); // Disable again
4638 }
4639
4640 if (obj->Implements(ObjectInterfaceType::Programmable))
4641 {
4642 int run = dynamic_cast<CProgramStorageObject&>(*obj).GetProgramIndex(dynamic_cast<CProgrammableObject&>(*obj).GetCurrentProgram());
4643 if (run != -1)
4644 {
4645 line->AddParam("run", MakeUnique<CLevelParserParam>(run+1));
4646 }
4647 }
4648 }
4649 }
4650
4651 //! Saves the current game
IOWriteScene(std::string filename,std::string filecbot,std::string filescreenshot,const std::string & info,bool emergencySave)4652 bool CRobotMain::IOWriteScene(std::string filename, std::string filecbot, std::string filescreenshot, const std::string& info, bool emergencySave)
4653 {
4654 if (!emergencySave)
4655 {
4656 // Render the indicator to show that we are working
4657 ShowSaveIndicator(true);
4658 m_app->Render(); // update
4659 }
4660
4661 std::string dirname = filename.substr(0, filename.find_last_of("/"));
4662
4663 CLevelParser levelParser(filename);
4664 CLevelParserLineUPtr line;
4665
4666 line = MakeUnique<CLevelParserLine>("Title");
4667 line->AddParam("text", MakeUnique<CLevelParserParam>(std::string(info)));
4668 levelParser.AddLine(std::move(line));
4669
4670
4671 //TODO: Do we need that? It's not used anyway
4672 line = MakeUnique<CLevelParserLine>("Version");
4673 line->AddParam("maj", MakeUnique<CLevelParserParam>(0));
4674 line->AddParam("min", MakeUnique<CLevelParserParam>(1));
4675 levelParser.AddLine(std::move(line));
4676
4677
4678 line = MakeUnique<CLevelParserLine>("Created");
4679 line->AddParam("date", MakeUnique<CLevelParserParam>(static_cast<int>(time(nullptr))));
4680 levelParser.AddLine(std::move(line));
4681
4682 line = MakeUnique<CLevelParserLine>("Mission");
4683 line->AddParam("base", MakeUnique<CLevelParserParam>(GetLevelCategoryDir(m_levelCategory)));
4684 if (m_levelCategory == LevelCategory::CustomLevels)
4685 line->AddParam("dir", MakeUnique<CLevelParserParam>(GetCustomLevelDir()));
4686 else
4687 line->AddParam("chap", MakeUnique<CLevelParserParam>(m_levelChap));
4688 line->AddParam("rank", MakeUnique<CLevelParserParam>(m_levelRank));
4689 line->AddParam("gametime", MakeUnique<CLevelParserParam>(GetGameTime()));
4690 levelParser.AddLine(std::move(line));
4691
4692 line = MakeUnique<CLevelParserLine>("Map");
4693 line->AddParam("zoom", MakeUnique<CLevelParserParam>(m_map->GetZoomMap()));
4694 levelParser.AddLine(std::move(line));
4695
4696 line = MakeUnique<CLevelParserLine>("DoneResearch");
4697 line->AddParam("bits", MakeUnique<CLevelParserParam>(static_cast<int>(m_researchDone[0])));
4698 levelParser.AddLine(std::move(line));
4699
4700 float sleep, delay, magnetic, progress;
4701 if (m_lightning->GetStatus(sleep, delay, magnetic, progress))
4702 {
4703 line = MakeUnique<CLevelParserLine>("BlitzMode");
4704 line->AddParam("sleep", MakeUnique<CLevelParserParam>(sleep));
4705 line->AddParam("delay", MakeUnique<CLevelParserParam>(delay));
4706 line->AddParam("magnetic", MakeUnique<CLevelParserParam>(magnetic/g_unit));
4707 line->AddParam("progress", MakeUnique<CLevelParserParam>(progress));
4708 levelParser.AddLine(std::move(line));
4709 }
4710
4711
4712 int objRank = 0;
4713 for (CObject* obj : m_objMan->GetAllObjects())
4714 {
4715 if (obj->GetType() == OBJECT_TOTO) continue;
4716 if (IsObjectBeingTransported(obj)) continue;
4717 if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast<CDestroyableObject&>(*obj).IsDying()) continue;
4718
4719 if (obj->Implements(ObjectInterfaceType::Carrier))
4720 {
4721 CObject* cargo = dynamic_cast<CCarrierObject&>(*obj).GetCargo();
4722 if (cargo != nullptr) // object transported?
4723 {
4724 line = MakeUnique<CLevelParserLine>("CreateFret");
4725 IOWriteObject(line.get(), cargo, dirname, objRank++);
4726 levelParser.AddLine(std::move(line));
4727 }
4728 }
4729
4730 if (obj->Implements(ObjectInterfaceType::Powered))
4731 {
4732 CObject* power = dynamic_cast<CPoweredObject&>(*obj).GetPower();
4733 if (power != nullptr) // battery transported?
4734 {
4735 line = MakeUnique<CLevelParserLine>("CreatePower");
4736 IOWriteObject(line.get(), power, dirname, objRank++);
4737 levelParser.AddLine(std::move(line));
4738 }
4739 }
4740
4741
4742 line = MakeUnique<CLevelParserLine>("CreateObject");
4743 IOWriteObject(line.get(), obj, dirname, objRank++);
4744 levelParser.AddLine(std::move(line));
4745 }
4746 try
4747 {
4748 levelParser.Save();
4749 }
4750 catch (CLevelParserException& e)
4751 {
4752 GetLogger()->Error("Failed to save level state - %s\n", e.what()); // TODO add visual error to notify user that save failed
4753 return false;
4754 }
4755
4756 // Writes the file of stacks of execution.
4757 COutputStream ostr(filecbot);
4758 if (!ostr.is_open()) return false;
4759
4760 bool bError = false;
4761 long version = 1;
4762 CBot::WriteLong(ostr, version); // version of COLOBOT
4763 version = CBot::CBotProgram::GetVersion();
4764 CBot::WriteLong(ostr, version); // version of CBOT
4765 CBot::WriteWord(ostr, 0); // TODO
4766
4767 for (CObject* obj : m_objMan->GetAllObjects())
4768 {
4769 if (obj->GetType() == OBJECT_TOTO) continue;
4770 if (IsObjectBeingTransported(obj)) continue;
4771 if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast<CDestroyableObject&>(*obj).IsDying()) continue;
4772
4773 if (!SaveFileStack(obj, ostr))
4774 {
4775 GetLogger()->Error("SaveFileStack failed at object id = %i\n", obj->GetID());
4776 bError = true;
4777 break;
4778 }
4779 }
4780
4781 if (!bError && !CBot::CBotClass::SaveStaticState(ostr))
4782 {
4783 GetLogger()->Error("CBotClass save static state failed\n");
4784 }
4785
4786 ostr.close();
4787
4788 if (!emergencySave)
4789 {
4790 ShowSaveIndicator(false); // force hide for screenshot
4791 MouseMode oldMouseMode = m_app->GetMouseMode();
4792 m_app->SetMouseMode(MOUSE_NONE); // disable the mouse
4793 m_displayText->HideText(true); // hide
4794 m_engine->SetScreenshotMode(true);
4795
4796 m_engine->Render(); // update (but don't show, we're not swapping buffers here!)
4797 m_engine->WriteScreenShot(filescreenshot);
4798 m_shotSaving++;
4799
4800 m_engine->SetScreenshotMode(false);
4801 m_displayText->HideText(false);
4802 m_app->SetMouseMode(oldMouseMode);
4803
4804 m_app->ResetTimeAfterLoading();
4805 }
4806 return true;
4807 }
4808
4809 //! Notifies the user that scene write is finished
IOWriteSceneFinished()4810 void CRobotMain::IOWriteSceneFinished()
4811 {
4812 m_displayText->DisplayError(INFO_WRITEOK, Math::Vector(0.0f,0.0f,0.0f));
4813 m_shotSaving--;
4814 }
4815
4816 //! Resumes the game
IOReadObject(CLevelParserLine * line,const std::string & programDir,const std::string & objCounterText,float objectProgress,int objRank)4817 CObject* CRobotMain::IOReadObject(CLevelParserLine *line, const std::string& programDir, const std::string& objCounterText, float objectProgress, int objRank)
4818 {
4819 ObjectCreateParams params = CObject::ReadCreateParams(line);
4820 params.power = -1.0f;
4821 params.id = line->GetParam("id")->AsInt();
4822
4823 std::string details = objCounterText;
4824 #if DEV_BUILD
4825 // Object categories may spoil the level a bit, so hide them in release builds
4826 details += ": "+CLevelParserParam::FromObjectType(params.type);
4827 #endif
4828 m_ui->GetLoadingScreen()->SetProgress(0.25f+objectProgress*0.7f, RT_LOADING_OBJECTS_SAVED, details);
4829
4830 CObject* obj = m_objMan->CreateObject(params);
4831
4832 if (obj->Implements(ObjectInterfaceType::Old))
4833 {
4834 COldObject* oldObj = dynamic_cast<COldObject*>(obj);
4835 oldObj->SetPosition(line->GetParam("pos")->AsPoint() * g_unit);
4836 oldObj->SetRotation(line->GetParam("angle")->AsPoint() * Math::DEG_TO_RAD);
4837 }
4838
4839 if (obj->GetType() == OBJECT_BASE) m_base = obj;
4840
4841 obj->Read(line);
4842
4843 int run = line->GetParam("run")->AsInt(-1);
4844 if (run != -1)
4845 {
4846 CAuto* automat = obj->GetAuto();
4847 if (automat != nullptr)
4848 automat->Start(run); // starts the film
4849 }
4850
4851 if (obj->Implements(ObjectInterfaceType::ProgramStorage))
4852 {
4853 CProgramStorageObject* programStorage = dynamic_cast<CProgramStorageObject*>(obj);
4854 if (!line->GetParam("programStorageIndex")->IsDefined()) // Backwards compatibility
4855 programStorage->SetProgramStorageIndex(objRank);
4856 programStorage->LoadAllProgramsForSavedScene(line, programDir);
4857 }
4858
4859 return obj;
4860 }
4861
4862 //! Resumes some part of the game
IOReadScene(std::string filename,std::string filecbot)4863 CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot)
4864 {
4865 std::string dirname = filename.substr(0, filename.find_last_of("/"));
4866
4867 CLevelParser levelParser(filename);
4868 levelParser.SetLevelPaths(m_levelCategory, m_levelChap, m_levelRank);
4869 levelParser.Load();
4870 int numObjects = levelParser.CountLines("CreateObject") + levelParser.CountLines("CreatePower") + levelParser.CountLines("CreateFret");
4871
4872 m_base = nullptr;
4873
4874 CObject* cargo = nullptr;
4875 CObject* power = nullptr;
4876 CObject* sel = nullptr;
4877 int objRank = 0;
4878 int objCounter = 0;
4879 for (auto& line : levelParser.GetLines())
4880 {
4881 if (line->GetCommand() == "Mission")
4882 m_gameTime = line->GetParam("gametime")->AsFloat(0.0f);
4883
4884 if (line->GetCommand() == "Map")
4885 m_map->ZoomMap(line->GetParam("zoom")->AsFloat());
4886
4887 if (line->GetCommand() == "DoneResearch")
4888 m_researchDone[0] = line->GetParam("bits")->AsInt();
4889
4890 if (line->GetCommand() == "BlitzMode")
4891 {
4892 float sleep = line->GetParam("sleep")->AsFloat();
4893 float delay = line->GetParam("delay")->AsFloat();
4894 float magnetic = line->GetParam("magnetic")->AsFloat()*g_unit;
4895 float progress = line->GetParam("progress")->AsFloat();
4896 m_lightning->SetStatus(sleep, delay, magnetic, progress);
4897 }
4898
4899 if (line->GetCommand() == "CreateFret")
4900 {
4901 cargo = IOReadObject(line.get(), dirname, StrUtils::ToString<int>(objCounter+1)+" / "+StrUtils::ToString<int>(numObjects), static_cast<float>(objCounter) / static_cast<float>(numObjects));
4902 objCounter++;
4903 }
4904
4905 if (line->GetCommand() == "CreatePower")
4906 {
4907 power = IOReadObject(line.get(), dirname, StrUtils::ToString<int>(objCounter+1)+" / "+StrUtils::ToString<int>(numObjects), static_cast<float>(objCounter) / static_cast<float>(numObjects));
4908 objCounter++;
4909 }
4910
4911 if (line->GetCommand() == "CreateObject")
4912 {
4913 CObject* obj = IOReadObject(line.get(), dirname, StrUtils::ToString<int>(objCounter+1)+" / "+StrUtils::ToString<int>(numObjects), static_cast<float>(objCounter) / static_cast<float>(numObjects), objRank++);
4914
4915 if (line->GetParam("select")->AsBool(false))
4916 sel = obj;
4917
4918 if (cargo != nullptr)
4919 {
4920 assert(obj->Implements(ObjectInterfaceType::Carrier)); // TODO: exception?
4921 assert(obj->Implements(ObjectInterfaceType::Old));
4922 dynamic_cast<CCarrierObject&>(*obj).SetCargo(cargo);
4923 auto task = MakeUnique<CTaskManip>(dynamic_cast<COldObject*>(obj));
4924 task->Start(TMO_AUTO, TMA_GRAB); // holds the object!
4925 }
4926
4927 if (power != nullptr)
4928 {
4929 assert(obj->Implements(ObjectInterfaceType::Powered));
4930 dynamic_cast<CPoweredObject&>(*obj).SetPower(power);
4931 assert(power->Implements(ObjectInterfaceType::Transportable));
4932 dynamic_cast<CTransportableObject&>(*power).SetTransporter(obj);
4933 }
4934 cargo = nullptr;
4935 power = nullptr;
4936
4937 objCounter++;
4938 }
4939 }
4940
4941 m_ui->GetLoadingScreen()->SetProgress(0.95f, RT_LOADING_CBOT_SAVE);
4942
4943 // Reads the file of stacks of execution.
4944 CInputStream istr(filecbot);
4945
4946 if (istr.is_open())
4947 {
4948 bool bError = false;
4949 long version = 0;
4950 CBot::ReadLong(istr, version); // version of COLOBOT
4951 if (version == 1)
4952 {
4953 CBot::ReadLong(istr, version); // version of CBOT
4954 if (version == CBot::CBotProgram::GetVersion())
4955 {
4956 unsigned short flag;
4957 CBot::ReadWord(istr, flag); // TODO
4958 bError = (flag != 0);
4959
4960 if (!bError) for (CObject* obj : m_objMan->GetAllObjects())
4961 {
4962 if (obj->GetType() == OBJECT_TOTO) continue;
4963 if (IsObjectBeingTransported(obj)) continue;
4964 if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast<CDestroyableObject&>(*obj).IsDying()) continue;
4965
4966 if (!ReadFileStack(obj, istr))
4967 {
4968 GetLogger()->Error("ReadFileStack failed at object id = %i\n", obj->GetID());
4969 bError = true;
4970 break;
4971 }
4972 }
4973
4974 if (!bError && !CBot::CBotClass::RestoreStaticState(istr))
4975 {
4976 GetLogger()->Error("CBotClass restore static state failed\n");
4977 bError = true;
4978 }
4979 }
4980 else
4981 GetLogger()->Error("cbot.run file is wrong version: %i\n", version);
4982 }
4983
4984 if (bError) GetLogger()->Error("Restoring CBOT state failed at stream position: %li\n", istr.tellg());
4985 istr.close();
4986 }
4987
4988 m_ui->GetLoadingScreen()->SetProgress(1.0f, RT_LOADING_FINISHED);
4989
4990 return sel;
4991 }
4992
4993
4994 //! Changes current player
SelectPlayer(std::string playerName)4995 void CRobotMain::SelectPlayer(std::string playerName)
4996 {
4997 assert(!playerName.empty());
4998
4999 m_playerProfile = MakeUnique<CPlayerProfile>(playerName);
5000 SetGlobalGamerName(playerName);
5001 }
5002
GetPlayerProfile()5003 CPlayerProfile* CRobotMain::GetPlayerProfile()
5004 {
5005 return m_playerProfile.get();
5006 }
5007
5008
5009 //! Resets all objects to their original position
ResetObject()5010 void CRobotMain::ResetObject()
5011 {
5012 // schedule reset during next frame
5013 m_resetCreate = true;
5014 }
5015
5016 //! Resets all objects to their original position
ResetCreate()5017 void CRobotMain::ResetCreate()
5018 {
5019 SaveAllScript();
5020
5021 // Removes all bullets in progress.
5022 m_particle->DeleteParticle(Gfx::PARTIGUN1);
5023 m_particle->DeleteParticle(Gfx::PARTIGUN2);
5024 m_particle->DeleteParticle(Gfx::PARTIGUN3);
5025 m_particle->DeleteParticle(Gfx::PARTIGUN4);
5026
5027 DeselectAll(); // removes the control buttons
5028 DeleteAllObjects(); // removes all the current 3D Scene
5029
5030 m_particle->FlushParticle();
5031 m_terrain->FlushBuildingLevel();
5032
5033 m_camera->SetType(Gfx::CAM_TYPE_NULL);
5034
5035 try
5036 {
5037 CreateScene(m_ui->GetSceneSoluce(), false, true);
5038
5039 for (CObject* obj : m_objMan->GetAllObjects())
5040 {
5041 if (obj->GetAnimateOnReset())
5042 {
5043 m_engine->GetPyroManager()->Create(Gfx::PT_RESET, obj);
5044 }
5045 }
5046 }
5047 catch (const std::runtime_error& e)
5048 {
5049 LevelLoadingError("An error occurred while trying to reset scene", e);
5050 }
5051 }
5052
5053 //! Updates the audiotracks
UpdateAudio(bool frame)5054 void CRobotMain::UpdateAudio(bool frame)
5055 {
5056 for(std::unique_ptr<CAudioChangeCondition>& audioChange : m_audioChange)
5057 {
5058 if (audioChange->changed) continue;
5059
5060 if (audioChange->Check())
5061 {
5062 GetLogger()->Info("Changing music to \"%s\"\n", audioChange->music.c_str());
5063 m_sound->PlayMusic(audioChange->music, audioChange->repeat);
5064 audioChange->changed = true;
5065 }
5066 }
5067 }
5068
5069 //! Set mission result from LevelController script
SetMissionResultFromScript(Error result,float delay)5070 void CRobotMain::SetMissionResultFromScript(Error result, float delay)
5071 {
5072 m_endTakeWinDelay = delay;
5073 m_endTakeLostDelay = delay;
5074 m_missionResult = result;
5075 m_missionResultFromScript = true;
5076 }
5077
ProcessEndMissionTakeForGroup(std::vector<CSceneEndCondition * > & endTakes)5078 Error CRobotMain::ProcessEndMissionTakeForGroup(std::vector<CSceneEndCondition*>& endTakes)
5079 {
5080 Error finalResult = ERR_OK;
5081 bool hasWinningConditions = false;
5082 for (CSceneEndCondition* endTake : endTakes)
5083 {
5084 Error result = endTake->GetMissionResult();
5085 if (endTake->lost < 0)
5086 hasWinningConditions = true;
5087
5088 if (result == ERR_OK && endTake->immediat)
5089 {
5090 hasWinningConditions = true;
5091 finalResult = result;
5092 break;
5093 }
5094
5095 if (result != ERR_OK)
5096 {
5097 finalResult = result;
5098 break;
5099 }
5100 }
5101 if (finalResult == ERR_OK && !hasWinningConditions) finalResult = ERR_MISSION_NOTERM; // Never end mission without ending conditions
5102 return finalResult;
5103 }
5104
5105 //! Process EndMissionTake commands, result is stored in m_missionResult
5106 //! If return value is different than ERR_MISSION_NOTERM, assume the mission is finished and pass on the result
ProcessEndMissionTake()5107 Error CRobotMain::ProcessEndMissionTake()
5108 {
5109 bool timeout = false;
5110 if (m_missionResult != INFO_LOST && m_missionResult != INFO_LOSTq)
5111 {
5112 if (m_endTakeTimeout >= 0.0f)
5113 {
5114 // Use the mission timer if available, or global mission time otherwise
5115 // Useful for exercises where the time starts when you start the program, not the mission itself
5116 float currentTime = m_missionTimerEnabled ? m_missionTimer : m_gameTime;
5117 if (currentTime > m_endTakeTimeout)
5118 {
5119 m_missionResult = INFO_LOST;
5120 timeout = true;
5121 }
5122 }
5123 }
5124
5125 // Sort end conditions by teams
5126 std::map<int, std::vector<CSceneEndCondition*>> teamsEndTake;
5127 for (std::unique_ptr<CSceneEndCondition>& endTake : m_endTake)
5128 teamsEndTake[endTake->winTeam].push_back(endTake.get());
5129
5130 // This is just a smart way to check if we have any map values other than 0 defined
5131 bool usesTeamConditions = teamsEndTake.size() > teamsEndTake.count(0);
5132
5133 if (!usesTeamConditions)
5134 {
5135 if (!timeout)
5136 m_missionResult = ProcessEndMissionTakeForGroup(teamsEndTake[0]);
5137
5138 if (m_missionResult != INFO_LOST && m_missionResult != INFO_LOSTq)
5139 {
5140 if (m_endTakeResearch != 0)
5141 {
5142 if (m_endTakeResearch != (m_endTakeResearch&m_researchDone[0]))
5143 {
5144 m_missionResult = ERR_MISSION_NOTERM;
5145 }
5146 }
5147 }
5148 }
5149 else
5150 {
5151 assert(m_endTakeResearch == 0); // TODO: Add support for per-team EndTakeResearch
5152
5153 // Special handling for teams
5154 m_missionResult = ERR_MISSION_NOTERM;
5155
5156 if (GetAllActiveTeams().empty() || timeout)
5157 {
5158 GetLogger()->Info("All teams died, mission ended\n");
5159 if (m_scoreboard)
5160 {
5161 std::string title, text, details_line;
5162 GetResource(RES_TEXT, RT_SCOREBOARD_RESULTS, title);
5163 if (m_missionTimerEnabled && m_missionTimerStarted)
5164 {
5165 GetResource(RES_TEXT, RT_SCOREBOARD_RESULTS_TIME, text);
5166 text = StrUtils::Format(text.c_str(), TimeFormat(m_missionTimer).c_str());
5167 }
5168 else
5169 {
5170 GetResource(RES_TEXT, RT_SCOREBOARD_RESULTS_TEXT, text);
5171 }
5172 GetResource(RES_TEXT, RT_SCOREBOARD_RESULTS_LINE, details_line);
5173 std::string details = "";
5174 for (std::pair<int, CScoreboard::Score> team : m_scoreboard->GetSortedScores())
5175 {
5176 if (!details.empty())
5177 details += ", ";
5178 details += StrUtils::Format(details_line.c_str(), GetTeamName(team.first).c_str(), team.second.points);
5179 }
5180 m_ui->GetDialog()->StartInformation(
5181 title,
5182 text,
5183 details,
5184 false, true,
5185 [&]()
5186 {
5187 ChangePhase(PHASE_WIN);
5188 }
5189 );
5190 m_endTakeWinDelay = 0.0f;
5191 m_missionResult = ERR_OK;
5192 }
5193 else
5194 {
5195 m_missionResult = INFO_LOST;
5196 }
5197 }
5198 else
5199 {
5200 for (auto it : teamsEndTake)
5201 {
5202 int team = it.first;
5203 if (team == 0) continue;
5204 if (m_teamFinished[team]) continue;
5205
5206 Error result = ProcessEndMissionTakeForGroup(it.second);
5207 if (result == INFO_LOST || result == INFO_LOSTq)
5208 {
5209 GetLogger()->Info("Team %d lost\n", team);
5210 std::string text;
5211 GetResource(RES_ERR, INFO_TEAM_DEAD, text);
5212 text = StrUtils::Format(text.c_str(), GetTeamName(team).c_str());
5213 m_displayText->DisplayText(text.c_str(), Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 10.0f, Ui::TT_ERROR);
5214
5215 m_displayText->SetEnable(false); // To prevent "bot destroyed" messages
5216 m_objMan->DestroyTeam(team);
5217 m_displayText->SetEnable(true);
5218
5219 m_teamFinished[team] = true;
5220 }
5221 else if (result == ERR_OK)
5222 {
5223 /*if (m_winDelay == 0.0f)
5224 {
5225 GetLogger()->Info("Team %d won\n", team);
5226
5227 m_displayText->DisplayText(("<<< Team "+boost::lexical_cast<std::string>(team)+" won the game >>>").c_str(), Math::Vector(0.0f,0.0f,0.0f));
5228 if (m_missionTimerEnabled && m_missionTimerStarted)
5229 {
5230 GetLogger()->Info("Mission time: %s\n", TimeFormat(m_missionTimer).c_str());
5231 m_displayText->DisplayText(("Time: " + TimeFormat(m_missionTimer)).c_str(), Math::Vector(0.0f,0.0f,0.0f));
5232 }
5233 m_missionTimerEnabled = m_missionTimerStarted = false;
5234 m_winDelay = m_endTakeWinDelay; // wins in two seconds
5235 m_lostDelay = 0.0f;
5236 m_displayText->SetEnable(false);
5237 }
5238 m_missionResult = ERR_OK;
5239 return ERR_OK;*/
5240 GetLogger()->Info("Team %d finished\n", team);
5241 std::string text;
5242 GetResource(RES_ERR, INFO_TEAM_FINISH, text);
5243 text = StrUtils::Format(text.c_str(), GetTeamName(team).c_str());
5244 m_displayText->DisplayText(text.c_str(), Math::Vector(0.0f,0.0f,0.0f));
5245 if (m_scoreboard)
5246 m_scoreboard->ProcessEndTake(team);
5247 m_objMan->DestroyTeam(team, DestructionType::Win);
5248 m_teamFinished[team] = true;
5249 if (m_endTakeTeamImmediateWin)
5250 {
5251 // All other teams fail
5252 for(int other_team : GetAllActiveTeams())
5253 {
5254 m_displayText->SetEnable(false); // To prevent "bot destroyed" messages
5255 m_objMan->DestroyTeam(other_team);
5256 m_displayText->SetEnable(true);
5257
5258 m_teamFinished[other_team] = true;
5259 }
5260 }
5261 }
5262 }
5263 }
5264 }
5265
5266 return ERR_MISSION_NOTERM;
5267 }
5268
5269 //! Checks if the mission is over
CheckEndMission(bool frame)5270 Error CRobotMain::CheckEndMission(bool frame)
5271 {
5272 // Process EndMissionTake, unless we are using LevelController script for processing ending conditions
5273 if (!m_missionResultFromScript)
5274 {
5275 Error result = ProcessEndMissionTake();
5276 if (result != ERR_MISSION_NOTERM) return result;
5277 }
5278 // Take action depending on m_missionResult
5279
5280 if (m_missionResult == INFO_LOSTq)
5281 {
5282 if (m_lostDelay == 0.0f)
5283 {
5284 m_lostDelay = 0.1f; // lost immediately
5285 m_winDelay = 0.0f;
5286 }
5287 m_missionTimerEnabled = m_missionTimerStarted = false;
5288 m_displayText->SetEnable(false);
5289 return INFO_LOSTq;
5290 }
5291
5292 if (m_missionResult == INFO_LOST)
5293 {
5294 if (m_lostDelay == 0.0f)
5295 {
5296 m_displayText->DisplayError(INFO_LOST, Math::Vector(0.0f,0.0f,0.0f));
5297 m_lostDelay = m_endTakeLostDelay; // lost in 6 seconds
5298 m_winDelay = 0.0f;
5299 }
5300 m_missionTimerEnabled = m_missionTimerStarted = false;
5301 m_displayText->SetEnable(false);
5302 return INFO_LOST;
5303 }
5304
5305 if (m_missionResult == ERR_OK)
5306 {
5307 if (m_endTakeWinDelay == -1.0f && m_winDelay == 0.0f)
5308 {
5309 m_winDelay = 1.0f; // wins in one second
5310 m_lostDelay = 0.0f;
5311 m_missionTimerEnabled = m_missionTimerStarted = false;
5312 m_displayText->SetEnable(false);
5313 return ERR_OK; // mission ended
5314 }
5315
5316 if (frame)
5317 {
5318 if (m_base != nullptr && !m_endTakeImmediat)
5319 {
5320 assert(m_base->Implements(ObjectInterfaceType::Controllable));
5321 if(dynamic_cast<CControllableObject&>(*m_base).GetSelectable())
5322 return ERR_MISSION_NOTERM;
5323 }
5324 }
5325
5326 if (m_winDelay == 0.0f)
5327 {
5328 m_displayText->DisplayError(INFO_WIN, Math::Vector(0.0f,0.0f,0.0f));
5329 if (m_missionTimerEnabled && m_missionTimerStarted)
5330 {
5331 GetLogger()->Info("Mission time: %s\n", TimeFormat(m_missionTimer).c_str());
5332 m_displayText->DisplayText(("Time: " + TimeFormat(m_missionTimer)).c_str(), Math::Vector(0.0f,0.0f,0.0f));
5333 }
5334 m_missionTimerEnabled = m_missionTimerStarted = false;
5335 m_winDelay = m_endTakeWinDelay; // wins in two seconds
5336 m_lostDelay = 0.0f;
5337 }
5338 m_displayText->SetEnable(false);
5339 return ERR_OK; // mission ended
5340 }
5341 else
5342 {
5343 m_displayText->SetEnable(true);
5344 return ERR_MISSION_NOTERM;
5345 }
5346 }
5347
5348
5349 //! Returns the list instructions required in CBot program in level
GetObligatoryTokenList()5350 const std::map<std::string, MinMax>& CRobotMain::GetObligatoryTokenList()
5351 {
5352 return m_obligatoryTokens;
5353 }
5354
5355 //! Indicates whether it is possible to control a driving robot
GetTrainerPilot()5356 bool CRobotMain::GetTrainerPilot()
5357 {
5358 return m_cheatTrainerPilot;
5359 }
5360
GetPlusTrainer()5361 bool CRobotMain::GetPlusTrainer()
5362 {
5363 return m_ui->GetPlusTrainer();
5364 }
5365
GetPlusExplorer()5366 bool CRobotMain::GetPlusExplorer()
5367 {
5368 return m_ui->GetPlusExplorer();
5369 }
5370
5371 //! Indicates whether the scene is fixed, without interaction
GetFixScene()5372 bool CRobotMain::GetFixScene()
5373 {
5374 return m_fixScene;
5375 }
5376
5377
GetScriptName()5378 const std::string& CRobotMain::GetScriptName()
5379 {
5380 return m_scriptName;
5381 }
5382
GetScriptFile()5383 const std::string& CRobotMain::GetScriptFile()
5384 {
5385 return m_scriptFile;
5386 }
5387
5388
GetShowSoluce()5389 bool CRobotMain::GetShowSoluce()
5390 {
5391 return m_cheatShowSoluce;
5392 }
5393
GetSceneSoluce()5394 bool CRobotMain::GetSceneSoluce()
5395 {
5396 if (m_infoFilename[SATCOM_SOLUCE][0] == 0) return false;
5397 return m_ui->GetSceneSoluce();
5398 }
5399
GetShowAll()5400 bool CRobotMain::GetShowAll()
5401 {
5402 return m_cheatAllMission;
5403 }
5404
GetRadar()5405 bool CRobotMain::GetRadar()
5406 {
5407 if (m_cheatRadar)
5408 return true;
5409
5410 for (CObject* obj : m_objMan->GetAllObjects())
5411 {
5412 ObjectType type = obj->GetType();
5413 if (type == OBJECT_RADAR && !obj->GetLock())
5414 return true;
5415 }
5416 return false;
5417 }
5418
GetMissionType()5419 MissionType CRobotMain::GetMissionType()
5420 {
5421 return m_missionType;
5422 }
5423
5424
5425 //! Returns the representation to use for the player
GetGamerFace()5426 int CRobotMain::GetGamerFace()
5427 {
5428 return m_playerProfile->GetApperance().face;
5429 }
5430
5431 //! Returns the representation to use for the player
GetGamerGlasses()5432 int CRobotMain::GetGamerGlasses()
5433 {
5434 return m_playerProfile->GetApperance().glasses;
5435 }
5436
5437 //! Returns the mode with just the head
GetGamerOnlyHead()5438 bool CRobotMain::GetGamerOnlyHead()
5439 {
5440 return m_ui->GetGamerOnlyHead();
5441 }
5442
5443 //! Returns the angle of presentation
GetPersoAngle()5444 float CRobotMain::GetPersoAngle()
5445 {
5446 return m_ui->GetPersoAngle();
5447 }
5448
SetLevel(LevelCategory cat,int chap,int rank)5449 void CRobotMain::SetLevel(LevelCategory cat, int chap, int rank)
5450 {
5451 GetLogger()->Debug("Change level to %s %d %d\n", GetLevelCategoryDir(cat).c_str(), chap, rank);
5452 m_levelCategory = cat;
5453 m_levelChap = chap;
5454 m_levelRank = rank;
5455 m_levelFile = CLevelParser::BuildScenePath(m_levelCategory, m_levelChap, m_levelRank);
5456 }
5457
GetLevelCategory()5458 LevelCategory CRobotMain::GetLevelCategory()
5459 {
5460 return m_levelCategory;
5461 }
5462
GetLevelChap()5463 int CRobotMain::GetLevelChap()
5464 {
5465 return m_levelChap;
5466 }
5467
GetLevelRank()5468 int CRobotMain::GetLevelRank()
5469 {
5470 return m_levelRank;
5471 }
5472
5473 //! Returns folder name of the scene that user selected to play.
GetCustomLevelDir()5474 std::string CRobotMain::GetCustomLevelDir()
5475 {
5476 assert(m_levelCategory == LevelCategory::CustomLevels);
5477 return m_ui->GetCustomLevelName(m_levelChap);
5478 }
5479
SetReadScene(std::string path)5480 void CRobotMain::SetReadScene(std::string path)
5481 {
5482 m_sceneReadPath = path;
5483 }
5484
UpdateChapterPassed()5485 void CRobotMain::UpdateChapterPassed()
5486 {
5487 return m_ui->UpdateChapterPassed();
5488 }
5489
5490 //! Changes game speed
SetSpeed(float speed)5491 void CRobotMain::SetSpeed(float speed)
5492 {
5493 speed = Math::Clamp(speed, MIN_SPEED, MAX_SPEED);
5494
5495 m_app->SetSimulationSpeed(speed);
5496 UpdateSpeedLabel();
5497 }
5498
GetSpeed()5499 float CRobotMain::GetSpeed()
5500 {
5501 return m_app->GetSimulationSpeed();
5502 }
5503
UpdateSpeedLabel()5504 void CRobotMain::UpdateSpeedLabel()
5505 {
5506 Ui::CButton* pb = static_cast<Ui::CButton*>(m_interface->SearchControl(EVENT_SPEED));
5507 float speed = m_app->GetSimulationSpeed();
5508
5509 if (pb != nullptr)
5510 {
5511 if (speed == 1.0f)
5512 {
5513 pb->ClearState(Ui::STATE_VISIBLE);
5514 }
5515 else
5516 {
5517 char text[10];
5518 sprintf(text, "x%.1f", speed);
5519 pb->SetName(text);
5520 pb->SetState(Ui::STATE_VISIBLE);
5521 }
5522 }
5523
5524 }
5525
5526
CreateShortcuts()5527 bool CRobotMain::CreateShortcuts()
5528 {
5529 if (m_phase != PHASE_SIMUL) return false;
5530 if (m_ui->GetLoadingScreen()->IsVisible()) return false;
5531 if (!m_shortCut) return false;
5532 return m_short->CreateShortcuts();
5533 }
5534
5535 //! Updates the map
UpdateMap()5536 void CRobotMain::UpdateMap()
5537 {
5538 m_map->UpdateMap();
5539 }
5540
5541 //! Indicates whether the mini-map is visible
GetShowMap()5542 bool CRobotMain::GetShowMap()
5543 {
5544 return m_mapShow;
5545 }
5546
5547
5548 //! Management of the lock mode for movies
SetMovieLock(bool lock)5549 void CRobotMain::SetMovieLock(bool lock)
5550 {
5551 m_movieLock = lock;
5552
5553 CreateShortcuts();
5554 m_map->ShowMap(!m_movieLock && m_mapShow && !m_ui->GetLoadingScreen()->IsVisible());
5555 if (m_movieLock) HiliteClear();
5556 }
5557
GetMovieLock()5558 bool CRobotMain::GetMovieLock()
5559 {
5560 return m_movieLock;
5561 }
5562
GetInfoLock()5563 bool CRobotMain::GetInfoLock()
5564 {
5565 return m_displayInfo != nullptr; // info in progress?
5566 }
5567
5568 //! Management of the blocking of the call of SatCom
SetSatComLock(bool lock)5569 void CRobotMain::SetSatComLock(bool lock)
5570 {
5571 m_satComLock = lock;
5572 }
5573
GetSatComLock()5574 bool CRobotMain::GetSatComLock()
5575 {
5576 return m_satComLock;
5577 }
5578
5579 //! Management of the lock mode for the edition
SetEditLock(bool lock,bool edit)5580 void CRobotMain::SetEditLock(bool lock, bool edit)
5581 {
5582 m_editLock = lock;
5583
5584 CreateShortcuts();
5585
5586 // Do not remove the card if it contains a still image.
5587 if (!lock || !m_map->GetFixImage())
5588 m_map->ShowMap(!m_editLock && m_mapShow);
5589
5590 m_displayText->HideText(lock);
5591 m_input->ResetKeyStates();
5592
5593 if (m_editLock)
5594 HiliteClear();
5595 else
5596 m_editFull = false;
5597 }
5598
GetEditLock()5599 bool CRobotMain::GetEditLock()
5600 {
5601 return m_editLock;
5602 }
5603
5604 //! Management of the fullscreen mode during editing
SetEditFull(bool full)5605 void CRobotMain::SetEditFull(bool full)
5606 {
5607 m_editFull = full;
5608 }
5609
GetEditFull()5610 bool CRobotMain::GetEditFull()
5611 {
5612 return m_editFull;
5613 }
5614
5615
5616 //! Indicates whether mouse is on an friend object, on which we should not shoot
SetFriendAim(bool friendAim)5617 void CRobotMain::SetFriendAim(bool friendAim)
5618 {
5619 m_friendAim = friendAim;
5620 }
5621
GetFriendAim()5622 bool CRobotMain::GetFriendAim()
5623 {
5624 return m_friendAim;
5625 }
5626
5627
5628 //! Starts music with a mission
StartMusic()5629 void CRobotMain::StartMusic()
5630 {
5631 GetLogger()->Debug("Starting music...\n");
5632 if (m_audioTrack != "")
5633 {
5634 m_sound->PlayMusic(m_audioTrack, m_audioRepeat, 0.0f);
5635 }
5636 }
5637
UpdatePause(PauseType pause)5638 void CRobotMain::UpdatePause(PauseType pause)
5639 {
5640 m_engine->SetPause(pause & PAUSE_ENGINE);
5641 m_camera->SetFreeze(pause & PAUSE_CAMERA);
5642 m_sound->MuteAll(pause & PAUSE_MUTE_SOUND);
5643 CreateShortcuts();
5644 if (pause != PAUSE_NONE) HiliteClear();
5645 }
5646
UpdatePauseMusic(PauseMusic music)5647 void CRobotMain::UpdatePauseMusic(PauseMusic music)
5648 {
5649 switch (music)
5650 {
5651 case PAUSE_MUSIC_NONE:
5652 m_sound->StopPauseMusic();
5653 break;
5654
5655 case PAUSE_MUSIC_EDITOR:
5656 if (m_editorTrack != "")
5657 m_sound->PlayPauseMusic(m_editorTrack, m_editorRepeat);
5658 break;
5659
5660 case PAUSE_MUSIC_SATCOM:
5661 if (m_satcomTrack != "")
5662 m_sound->PlayPauseMusic(m_satcomTrack, m_satcomRepeat);
5663 break;
5664 }
5665 }
5666
5667 //! Removes hilite and tooltip
ClearInterface()5668 void CRobotMain::ClearInterface()
5669 {
5670 HiliteClear(); // removes setting evidence
5671 m_tooltipName.clear(); // really removes the tooltip
5672 }
5673
DisplayError(Error err,CObject * pObj,float time)5674 void CRobotMain::DisplayError(Error err, CObject* pObj, float time)
5675 {
5676 m_displayText->DisplayError(err, pObj, time);
5677 }
5678
DisplayError(Error err,Math::Vector goal,float height,float dist,float time)5679 void CRobotMain::DisplayError(Error err, Math::Vector goal, float height, float dist, float time)
5680 {
5681 m_displayText->DisplayError(err, goal, height, dist, time);
5682 }
5683
UpdateCustomLevelList()5684 void CRobotMain::UpdateCustomLevelList()
5685 {
5686 m_ui->UpdateCustomLevelList();
5687 }
5688
GetCustomLevelName(int id)5689 std::string CRobotMain::GetCustomLevelName(int id)
5690 {
5691 return m_ui->GetCustomLevelName(id);
5692 }
5693
GetCustomLevelList()5694 const std::vector<std::string>& CRobotMain::GetCustomLevelList()
5695 {
5696 return m_ui->GetCustomLevelList();
5697 }
5698
IsLoading()5699 bool CRobotMain::IsLoading()
5700 {
5701 return m_ui->GetLoadingScreen()->IsVisible();
5702 }
5703
StartMissionTimer()5704 void CRobotMain::StartMissionTimer()
5705 {
5706 if (m_missionTimerEnabled && !m_missionTimerStarted)
5707 {
5708 GetLogger()->Info("Starting mission timer...\n");
5709 m_missionTimerStarted = true;
5710 }
5711 }
5712
SetAutosave(bool enable)5713 void CRobotMain::SetAutosave(bool enable)
5714 {
5715 if (m_autosave == enable) return;
5716
5717 m_autosave = enable;
5718 m_autosaveLast = m_gameTimeAbsolute;
5719 }
5720
GetAutosave()5721 bool CRobotMain::GetAutosave()
5722 {
5723 return m_autosave;
5724 }
5725
SetAutosaveInterval(int interval)5726 void CRobotMain::SetAutosaveInterval(int interval)
5727 {
5728 if (m_autosaveInterval == interval) return;
5729
5730 m_autosaveInterval = interval;
5731 m_autosaveLast = m_gameTimeAbsolute;
5732 }
5733
GetAutosaveInterval()5734 int CRobotMain::GetAutosaveInterval()
5735 {
5736 return m_autosaveInterval;
5737 }
5738
SetAutosaveSlots(int slots)5739 void CRobotMain::SetAutosaveSlots(int slots)
5740 {
5741 if (m_autosaveSlots == slots) return;
5742
5743 m_autosaveSlots = slots;
5744 }
5745
GetAutosaveSlots()5746 int CRobotMain::GetAutosaveSlots()
5747 {
5748 return m_autosaveSlots;
5749 }
5750
5751 // Remove oldest saves with autosave prefix
AutosaveRotate()5752 void CRobotMain::AutosaveRotate()
5753 {
5754 if (m_playerProfile == nullptr)
5755 return;
5756
5757 GetLogger()->Debug("Rotate autosaves...\n");
5758 auto saveDirs = CResourceManager::ListDirectories(m_playerProfile->GetSaveDir());
5759 const std::string autosavePrefix = "autosave";
5760 std::vector<std::string> autosaves;
5761 std::copy_if(saveDirs.begin(), saveDirs.end(), std::back_inserter(autosaves), [&](const std::string &save)
5762 {
5763 return save.substr(0, autosavePrefix.length()) == autosavePrefix;
5764 });
5765
5766 std::sort(autosaves.begin(), autosaves.end(), std::less<std::string>());
5767 for (int i = 0; i < static_cast<int>(autosaves.size()) - m_autosaveSlots + 1; i++)
5768 {
5769 CResourceManager::RemoveDirectory(m_playerProfile->GetSaveDir() + "/" + autosaves[i]);
5770 }
5771 }
5772
Autosave()5773 void CRobotMain::Autosave()
5774 {
5775 AutosaveRotate();
5776 GetLogger()->Info("Autosave!\n");
5777
5778 char timestr[100];
5779 char infostr[100];
5780 time_t now = time(nullptr);
5781 strftime(timestr, 99, "%y%m%d%H%M%S", localtime(&now));
5782 strftime(infostr, 99, "%y.%m.%d %H:%M", localtime(&now));
5783 std::string info = std::string("[AUTOSAVE] ") + infostr;
5784 std::string dir = m_playerProfile->GetSaveFile(std::string("autosave") + timestr);
5785
5786 m_playerProfile->SaveScene(dir, info);
5787 }
5788
QuickSave()5789 void CRobotMain::QuickSave()
5790 {
5791 GetLogger()->Info("Quicksave!\n");
5792
5793 char infostr[100];
5794 time_t now = time(nullptr);
5795 strftime(infostr, 99, "%y.%m.%d %H:%M", localtime(&now));
5796 std::string info = std::string("[QUICKSAVE]") + infostr;
5797 std::string dir = m_playerProfile->GetSaveFile(std::string("quicksave"));
5798
5799 m_playerProfile->SaveScene(dir, info);
5800 }
5801
QuickLoad()5802 void CRobotMain::QuickLoad()
5803 {
5804 std::string dir = m_playerProfile->GetSaveFile(std::string("quicksave"));
5805 if(!CResourceManager::Exists(dir))
5806 {
5807 m_displayText->DisplayError(ERR_NO_QUICK_SLOT, Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 1000.0f);
5808 GetLogger()->Debug("Quicksave slot not found\n");
5809 return;
5810 }
5811 m_playerProfile->LoadScene(dir);
5812 }
5813
SetExitAfterMission(bool exit)5814 void CRobotMain::SetExitAfterMission(bool exit)
5815 {
5816 m_exitAfterMission = exit;
5817 }
5818
CanPlayerInteract()5819 bool CRobotMain::CanPlayerInteract()
5820 {
5821 if(GetMissionType() == MISSION_CODE_BATTLE)
5822 {
5823 return !m_codeBattleStarted;
5824 }
5825 return true;
5826 }
5827
5828 const std::string NO_TEAM_NAME = "Team";
GetTeamName(int id)5829 const std::string& CRobotMain::GetTeamName(int id)
5830 {
5831 if(m_teamNames.count(id) == 0) return NO_TEAM_NAME;
5832 return m_teamNames[id];
5833 }
5834
IsTeamColorDefined(int id)5835 bool CRobotMain::IsTeamColorDefined(int id)
5836 {
5837 if(id == 0) return false; // Always use default for team 0
5838
5839 return m_colorNewBot.find(id) != m_colorNewBot.end();
5840 }
5841
5842
GetEnableBuild()5843 int CRobotMain::GetEnableBuild()
5844 {
5845 return m_build;
5846 }
5847
SetEnableBuild(int enableBuild)5848 void CRobotMain::SetEnableBuild(int enableBuild)
5849 {
5850 m_build = enableBuild;
5851 }
5852
GetEnableResearch()5853 int CRobotMain::GetEnableResearch()
5854 {
5855 return m_researchEnable;
5856 }
5857
SetEnableResearch(int enableResearch)5858 void CRobotMain::SetEnableResearch(int enableResearch)
5859 {
5860 m_researchEnable = enableResearch;
5861 }
5862
GetDoneResearch(int team)5863 int CRobotMain::GetDoneResearch(int team)
5864 {
5865 return m_researchDone[team];
5866 }
5867
SetDoneResearch(int doneResearch,int team)5868 void CRobotMain::SetDoneResearch(int doneResearch, int team)
5869 {
5870 m_researchDone[team] = doneResearch;
5871 }
5872
5873
IsBuildingEnabled(BuildType type)5874 bool CRobotMain::IsBuildingEnabled(BuildType type)
5875 {
5876 return (m_build & type) != 0;
5877 }
5878
IsBuildingEnabled(ObjectType type)5879 bool CRobotMain::IsBuildingEnabled(ObjectType type)
5880 {
5881 if(type == OBJECT_DERRICK) return IsBuildingEnabled(BUILD_DERRICK);
5882 if(type == OBJECT_FACTORY) return IsBuildingEnabled(BUILD_FACTORY);
5883 if(type == OBJECT_STATION) return IsBuildingEnabled(BUILD_STATION);
5884 if(type == OBJECT_CONVERT) return IsBuildingEnabled(BUILD_CONVERT);
5885 if(type == OBJECT_REPAIR) return IsBuildingEnabled(BUILD_REPAIR);
5886 if(type == OBJECT_TOWER) return IsBuildingEnabled(BUILD_TOWER);
5887 if(type == OBJECT_RESEARCH) return IsBuildingEnabled(BUILD_RESEARCH);
5888 if(type == OBJECT_RADAR) return IsBuildingEnabled(BUILD_RADAR);
5889 if(type == OBJECT_ENERGY) return IsBuildingEnabled(BUILD_ENERGY);
5890 if(type == OBJECT_LABO) return IsBuildingEnabled(BUILD_LABO);
5891 if(type == OBJECT_NUCLEAR) return IsBuildingEnabled(BUILD_NUCLEAR);
5892 if(type == OBJECT_INFO) return IsBuildingEnabled(BUILD_INFO);
5893 if(type == OBJECT_PARA) return IsBuildingEnabled(BUILD_PARA);
5894 if(type == OBJECT_SAFE) return IsBuildingEnabled(BUILD_SAFE);
5895 if(type == OBJECT_DESTROYER) return IsBuildingEnabled(BUILD_DESTROYER);
5896
5897 return false;
5898 }
5899
IsResearchEnabled(ResearchType type)5900 bool CRobotMain::IsResearchEnabled(ResearchType type)
5901 {
5902 return (m_researchEnable & type) != 0;
5903 }
5904
IsResearchDone(ResearchType type,int team)5905 bool CRobotMain::IsResearchDone(ResearchType type, int team)
5906 {
5907 if(team != 0 && m_researchDone.count(team) == 0)
5908 {
5909 // Initialize with defaults
5910 m_researchDone[team] = m_researchDone[0];
5911 }
5912
5913 return (m_researchDone[team] & type) != 0;
5914 }
5915
MarkResearchDone(ResearchType type,int team)5916 void CRobotMain::MarkResearchDone(ResearchType type, int team)
5917 {
5918 if(team != 0 && m_researchDone.count(team) == 0)
5919 {
5920 // Initialize with defaults
5921 m_researchDone[team] = m_researchDone[0];
5922 }
5923
5924 m_researchDone[team] |= type;
5925
5926 if(team == 0)
5927 {
5928 m_playerProfile->SetFreeGameResearchUnlock(m_playerProfile->GetFreeGameResearchUnlock() | m_researchDone[0]);
5929 }
5930 }
5931
CanBuildError(ObjectType type,int team)5932 Error CRobotMain::CanBuildError(ObjectType type, int team)
5933 {
5934 if(!IsBuildingEnabled(type)) return ERR_BUILD_DISABLED;
5935
5936 if(type == OBJECT_TOWER && !IsResearchDone(RESEARCH_TOWER, team)) return ERR_BUILD_RESEARCH;
5937 if(type == OBJECT_NUCLEAR && !IsResearchDone(RESEARCH_ATOMIC, team)) return ERR_BUILD_RESEARCH;
5938
5939 return ERR_OK;
5940 }
5941
CanFactoryError(ObjectType type,int team)5942 Error CRobotMain::CanFactoryError(ObjectType type, int team)
5943 {
5944 ToolType tool = GetToolFromObject(type);
5945 DriveType drive = GetDriveFromObject(type);
5946
5947 if (tool == ToolType::Sniffer && !IsResearchDone(RESEARCH_SNIFFER, team)) return ERR_BUILD_RESEARCH;
5948 if (tool == ToolType::Shooter && !IsResearchDone(RESEARCH_CANON, team)) return ERR_BUILD_RESEARCH;
5949 if (tool == ToolType::OrganicShooter && !IsResearchDone(RESEARCH_iGUN, team)) return ERR_BUILD_RESEARCH;
5950 if (tool == ToolType::Builder && !IsResearchDone(RESEARCH_BUILDER, team)) return ERR_BUILD_RESEARCH;
5951
5952 if (drive == DriveType::Tracked && !IsResearchDone(RESEARCH_TANK, team)) return ERR_BUILD_RESEARCH;
5953 if (drive == DriveType::Winged && !IsResearchDone(RESEARCH_FLY, team)) return ERR_BUILD_RESEARCH;
5954 if (drive == DriveType::Legged && !IsResearchDone(RESEARCH_iPAW, team)) return ERR_BUILD_RESEARCH;
5955 if (drive == DriveType::Heavy && !IsResearchDone(RESEARCH_TANK, team)) return ERR_BUILD_RESEARCH;
5956
5957 if (type == OBJECT_MOBILErt && !IsResearchDone(RESEARCH_THUMP, team)) return ERR_BUILD_RESEARCH;
5958 if (type == OBJECT_MOBILErc && !IsResearchDone(RESEARCH_PHAZER, team)) return ERR_BUILD_RESEARCH;
5959 if (type == OBJECT_MOBILErr && !IsResearchDone(RESEARCH_RECYCLER, team)) return ERR_BUILD_RESEARCH;
5960 if (type == OBJECT_MOBILErs && !IsResearchDone(RESEARCH_SHIELD, team)) return ERR_BUILD_RESEARCH;
5961 if (type == OBJECT_MOBILEsa && !IsResearchDone(RESEARCH_SUBM, team)) return ERR_BUILD_DISABLED; // Can be only researched manually in Scene file
5962 if (type == OBJECT_MOBILEst && !IsResearchDone(RESEARCH_SUBM, team)) return ERR_BUILD_DISABLED;
5963 if (type == OBJECT_MOBILEtg && !IsResearchDone(RESEARCH_TARGET, team)) return ERR_BUILD_RESEARCH;
5964
5965 if (tool == ToolType::Other && drive == DriveType::Other && type != OBJECT_MOBILEtg) return ERR_WRONG_OBJ;
5966
5967 return ERR_OK;
5968 }
5969
PushToSelectionHistory(CObject * obj)5970 void CRobotMain::PushToSelectionHistory(CObject* obj)
5971 {
5972 if (!m_selectionHistory.empty() && m_selectionHistory.back() == obj)
5973 return; // already in history
5974
5975 m_selectionHistory.push_back(obj);
5976
5977 if (m_selectionHistory.size() > 50) // to avoid infinite growth
5978 m_selectionHistory.pop_front();
5979 }
5980
PopFromSelectionHistory()5981 CObject* CRobotMain::PopFromSelectionHistory()
5982 {
5983 if (m_selectionHistory.empty())
5984 return nullptr;
5985
5986 CObject* obj = m_selectionHistory.back();
5987 m_selectionHistory.pop_back();
5988 return obj;
5989 }
5990
RemoveFromSelectionHistory(CObject * object)5991 void CRobotMain::RemoveFromSelectionHistory(CObject* object)
5992 {
5993 auto it = std::remove_if(m_selectionHistory.begin(), m_selectionHistory.end(),
5994 [object](const CObject* obj) { return obj == object; });
5995 m_selectionHistory.erase(it, m_selectionHistory.end());
5996 }
5997
GetGlobalMagnifyDamage()5998 float CRobotMain::GetGlobalMagnifyDamage()
5999 {
6000 return m_globalMagnifyDamage;
6001 }
6002
GetGlobalNuclearCapacity()6003 float CRobotMain::GetGlobalNuclearCapacity()
6004 {
6005 return m_globalNuclearCapacity;
6006 }
6007
GetGlobalCellCapacity()6008 float CRobotMain::GetGlobalCellCapacity()
6009 {
6010 return m_globalCellCapacity;
6011 }
6012
6013 // Beginning of the effect when the instruction "detect" is used.
6014
StartDetectEffect(COldObject * object,CObject * target)6015 void CRobotMain::StartDetectEffect(COldObject* object, CObject* target)
6016 {
6017 Math::Matrix* mat;
6018 Math::Vector pos, goal;
6019 Math::Point dim;
6020
6021 mat = object->GetWorldMatrix(0);
6022 pos = Math::Transform(*mat, Math::Vector(2.0f, 3.0f, 0.0f));
6023
6024 if ( target == nullptr )
6025 {
6026 goal = Math::Transform(*mat, Math::Vector(50.0f, 3.0f, 0.0f));
6027 }
6028 else
6029 {
6030 goal = target->GetPosition();
6031 goal.y += 3.0f;
6032 goal = Math::SegmentPoint(pos, goal, Math::Distance(pos, goal)-3.0f);
6033 }
6034
6035 dim.x = 3.0f;
6036 dim.y = dim.x;
6037 m_particle->CreateRay(pos, goal, Gfx::PARTIRAY2, dim, 0.2f);
6038
6039 if ( target != nullptr )
6040 {
6041 goal = target->GetPosition();
6042 goal.y += 3.0f;
6043 goal = Math::SegmentPoint(pos, goal, Math::Distance(pos, goal)-1.0f);
6044 dim.x = 6.0f;
6045 dim.y = dim.x;
6046 m_particle->CreateParticle(goal, Math::Vector(0.0f, 0.0f, 0.0f), dim,
6047 target != nullptr ? Gfx::PARTIGLINT : Gfx::PARTIGLINTr, 0.5f);
6048 }
6049
6050 m_sound->Play(target != nullptr ? SOUND_BUILD : SOUND_RECOVER);
6051 }
6052
CreateCodeBattleInterface()6053 void CRobotMain::CreateCodeBattleInterface()
6054 {
6055 if (m_phase == PHASE_SIMUL)
6056 {
6057 Math::Point pos, ddim;
6058 float offset = (ceil(m_viewpoints.size() / 2.0f) * 50);
6059
6060 int numTeams = m_scoreboard ? GetAllTeams().size() : 0;
6061 assert(numTeams < EVENT_SCOREBOARD_MAX-EVENT_SCOREBOARD+1);
6062 float textHeight = m_engine->GetText()->GetHeight(Gfx::FONT_COMMON, Gfx::FONT_SIZE_SMALL);
6063
6064 //window
6065 ddim.x = 100.0f/640.0f;
6066 ddim.y = (100.0f+offset)/480.0f + numTeams * textHeight;
6067 pos.x = 540.0f/640.0f;
6068 pos.y = 100.0f/480.0f;
6069 Ui::CWindow* pw = m_interface->CreateWindows(pos, ddim, 3, EVENT_WINDOW6);
6070
6071 //label text
6072 ddim.x = 100.0f/640.0f;
6073 ddim.y = 16.0f/480.0f;
6074 pos.x = 540.0f/640.0f;
6075 pos.y = (178.0f+offset)/480.0f + numTeams * textHeight;
6076 std::string text;
6077 GetResource(RES_EVENT, EVENT_LABEL_CODE_BATTLE, text);
6078 pw->CreateLabel(pos, ddim, 0, EVENT_LABEL_CODE_BATTLE, text);
6079
6080 //viewpoint selection section
6081 ddim.x = 40.0f/640.0f;
6082 ddim.y = 50.0f/640.0f;
6083 for(unsigned int i = 0; i < m_viewpoints.size(); i++)
6084 {
6085 //create button
6086 pos.x = (550.0f+40.0f*(i%2))/640.0f;
6087 pos.y = (130.0f+offset)/480.0f + numTeams * textHeight - 45.0f*(i/2)/480.0f;
6088 pw->CreateButton(pos, ddim, m_viewpoints[i].button, EventType(EVENT_VIEWPOINT0 + i));
6089 }
6090
6091 //start/camera button
6092 float titleBarSize = (11.0f/64.0f); // this is from the texture
6093 ddim.x = 80.0f/640.0f;
6094 ddim.y = ((1-titleBarSize)*100.0f-20.0f)/480.0f;
6095 pos.x = 550.0f/640.0f;
6096 pos.y = 110.0f/480.0f;
6097 if (!m_codeBattleStarted)
6098 {
6099 pw->CreateButton(pos, ddim, 21, EVENT_CODE_BATTLE_START);
6100 }
6101 else
6102 {
6103 pw->CreateButton(pos, ddim, 13, EVENT_CODE_BATTLE_SPECTATOR);
6104 }
6105
6106 if (!m_scoreboard) return;
6107 pos.y += ddim.y;
6108 ddim.y = textHeight;
6109 int i = 0;
6110 auto teams = GetAllTeams();
6111 for (auto it = teams.rbegin(); it != teams.rend(); ++it)
6112 {
6113 int team = *it;
6114 Ui::CControl* pl;
6115 ddim.x = 55.0f/640.0f;
6116 pl = m_codeBattleStarted
6117 ? static_cast<Ui::CControl*>(pw->CreateLabel(pos, ddim, 0, static_cast<EventType>(EVENT_SCOREBOARD+2*(numTeams-i-1)+0), "XXXXX"))
6118 : static_cast<Ui::CControl*>(pw->CreateEdit( pos, ddim, 0, static_cast<EventType>(EVENT_SCOREBOARD+2*(numTeams-i-1)+0)));
6119 pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);
6120 pl->SetFontSize(m_codeBattleStarted ? Gfx::FONT_SIZE_SMALL : Gfx::FONT_SIZE_SMALL*0.75f);
6121 m_codeBattleStarted ? pl->SetName(GetTeamName(team)) : static_cast<Ui::CEdit*>(pl)->SetText(GetTeamName(team));
6122 pos.x += 57.5f/640.0f;
6123 ddim.x = 22.5f/640.0f;
6124 pl = m_codeBattleStarted
6125 ? static_cast<Ui::CControl*>(pw->CreateLabel(pos, ddim, 0, static_cast<EventType>(EVENT_SCOREBOARD+2*(numTeams-i-1)+1), "???"))
6126 : static_cast<Ui::CControl*>(pw->CreateEdit( pos, ddim, 0, static_cast<EventType>(EVENT_SCOREBOARD+2*(numTeams-i-1)+1)));
6127 pl->SetTextAlign(Gfx::TEXT_ALIGN_RIGHT);
6128 pl->SetFontSize(m_codeBattleStarted ? Gfx::FONT_SIZE_SMALL : Gfx::FONT_SIZE_SMALL*0.75f);
6129 m_codeBattleStarted ? pl->SetName(StrUtils::ToString<int>(m_scoreboard->GetScore(team).points)) : static_cast<Ui::CEdit*>(pl)->SetText(StrUtils::ToString<int>(m_scoreboard->GetScore(team).points));
6130 pos.x -= 57.5f/640.0f;
6131 pos.y += ddim.y;
6132 i++;
6133 }
6134 }
6135 }
6136
ApplyCodeBattleInterface()6137 void CRobotMain::ApplyCodeBattleInterface()
6138 {
6139 assert(GetMissionType() == MISSION_CODE_BATTLE);
6140 if (!m_scoreboard) return;
6141
6142 Ui::CWindow* pw = static_cast<Ui::CWindow*>(m_interface->SearchControl(EVENT_WINDOW6));
6143 assert(pw != nullptr);
6144
6145 int i = 0;
6146 for (int team : GetAllTeams())
6147 {
6148 Ui::CEdit* pl;
6149
6150 pl = static_cast<Ui::CEdit*>(pw->SearchControl(static_cast<EventType>(EVENT_SCOREBOARD+2*i+0)));
6151 assert(pl != nullptr);
6152 m_teamNames[team] = pl->GetText(pl->GetTextLength());
6153
6154 pl = static_cast<Ui::CEdit*>(pw->SearchControl(static_cast<EventType>(EVENT_SCOREBOARD+2*i+1)));
6155 assert(pl != nullptr);
6156 m_scoreboard->SetScore(team, StrUtils::FromString<int>(pl->GetText(pl->GetTextLength())));
6157
6158 i++;
6159 }
6160 }
6161
UpdateCodeBattleInterface()6162 void CRobotMain::UpdateCodeBattleInterface()
6163 {
6164 assert(GetMissionType() == MISSION_CODE_BATTLE);
6165 if (!m_scoreboard) return;
6166
6167 Ui::CWindow* pw = static_cast<Ui::CWindow*>(m_interface->SearchControl(EVENT_WINDOW6));
6168 assert(pw != nullptr);
6169
6170 int i = 0;
6171 for (std::pair<int, CScoreboard::Score> team : m_scoreboard->GetSortedScores())
6172 {
6173 Ui::CControl* pl;
6174
6175 pl = pw->SearchControl(static_cast<EventType>(EVENT_SCOREBOARD+2*i+0));
6176 assert(pl != nullptr);
6177 pl->SetName(GetTeamName(team.first));
6178
6179 pl = pw->SearchControl(static_cast<EventType>(EVENT_SCOREBOARD+2*i+1));
6180 assert(pl != nullptr);
6181 pl->SetName(StrUtils::ToString<int>(team.second.points));
6182
6183 i++;
6184 }
6185 }
6186
DestroyCodeBattleInterface()6187 void CRobotMain::DestroyCodeBattleInterface()
6188 {
6189 m_viewpoints.clear();
6190 m_interface->DeleteControl(EVENT_WINDOW6);
6191 }
6192
SetCodeBattleSpectatorMode(bool mode)6193 void CRobotMain::SetCodeBattleSpectatorMode(bool mode)
6194 {
6195 // Deselect object, but keep camera attached to it
6196 CObject* obj = DeselectAll();
6197 if (m_codeBattleSpectator)
6198 obj = m_camera->GetControllingObject();
6199
6200 m_codeBattleSpectator = mode;
6201 SelectObject(obj, false); // this uses code battle selection mode already
6202 }
6203
UpdateDebugCrashSpheres()6204 void CRobotMain::UpdateDebugCrashSpheres()
6205 {
6206 if (m_debugCrashSpheres)
6207 {
6208 for (CObject* obj : m_objMan->GetAllObjects())
6209 {
6210 for (const auto& crashSphere : obj->GetAllCrashSpheres())
6211 {
6212 m_engine->RenderDebugSphere(crashSphere.sphere, Math::Matrix{}, Gfx::Color{0.0f, 0.0f, 1.0f, 1.0f});
6213 }
6214 }
6215 }
6216 }
6217
SetDebugCrashSpheres(bool draw)6218 void CRobotMain::SetDebugCrashSpheres(bool draw)
6219 {
6220 m_debugCrashSpheres = draw;
6221 }
6222
GetDebugCrashSpheres()6223 bool CRobotMain::GetDebugCrashSpheres()
6224 {
6225 return m_debugCrashSpheres;
6226 }
6227
PushToCommandHistory(std::string cmd)6228 void CRobotMain::PushToCommandHistory(std::string cmd)
6229 {
6230 if (!m_commandHistory.empty() && m_commandHistory.front() == cmd) // already in history
6231 return;
6232
6233 m_commandHistory.push_front(cmd);
6234
6235 if (m_commandHistory.size() > 50) // to avoid infinite growth
6236 m_commandHistory.pop_back();
6237 }
6238
GetNextFromCommandHistory()6239 std::string CRobotMain::GetNextFromCommandHistory()
6240 {
6241 if (m_commandHistory.empty() || static_cast<int>(m_commandHistory.size()) <= m_commandHistoryIndex + 1) // no next element
6242 return "";
6243 return m_commandHistory[++m_commandHistoryIndex];
6244 }
6245
GetPreviousFromCommandHistory()6246 std::string CRobotMain::GetPreviousFromCommandHistory()
6247 {
6248 if (m_commandHistory.empty() || m_commandHistoryIndex < 1) // first or none element selected
6249 return "";
6250 return m_commandHistory[--m_commandHistoryIndex];
6251 }
6252
GetScoreboard()6253 CScoreboard* CRobotMain::GetScoreboard()
6254 {
6255 return m_scoreboard.get();
6256 }
6257
GetAllTeams()6258 std::set<int> CRobotMain::GetAllTeams()
6259 {
6260 std::set<int> teams = GetAllActiveTeams();
6261 for(auto& it : m_teamFinished)
6262 {
6263 teams.insert(it.first);
6264 }
6265 return teams;
6266 }
6267
GetAllActiveTeams()6268 std::set<int> CRobotMain::GetAllActiveTeams()
6269 {
6270 std::set<int> teams;
6271 for (CObject* obj : m_objMan->GetAllObjects())
6272 {
6273 int team = obj->GetTeam();
6274 if (team == 0) continue;
6275 teams.insert(team);
6276 }
6277 return teams;
6278 }
6279