Permalink
Cannot retrieve contributors at this time
6138 lines (5211 sloc)
200 KB
| /* | |
| * This file is part of the Colobot: Gold Edition source code | |
| * Copyright (C) 2001-2018, Daniel Roux, EPSITEC SA & TerranovaTeam | |
| * http://epsitec.ch; http://colobot.info; http://github.com/colobot | |
| * | |
| * This program is free software: you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation, either version 3 of the License, or | |
| * (at your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| * See the GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program. If not, see http://gnu.org/licenses | |
| */ | |
| #include "level/robotmain.h" | |
| #include "CBot/CBot.h" | |
| #include "app/app.h" | |
| #include "app/input.h" | |
| #include "app/pausemanager.h" | |
| #include "common/config_file.h" | |
| #include "common/event.h" | |
| #include "common/logger.h" | |
| #include "common/make_unique.h" | |
| #include "common/restext.h" | |
| #include "common/settings.h" | |
| #include "common/stringutils.h" | |
| #include "common/resources/inputstream.h" | |
| #include "common/resources/outputstream.h" | |
| #include "common/resources/resourcemanager.h" | |
| #include "graphics/engine/camera.h" | |
| #include "graphics/engine/cloud.h" | |
| #include "graphics/engine/engine.h" | |
| #include "graphics/engine/lightman.h" | |
| #include "graphics/engine/lightning.h" | |
| #include "graphics/engine/oldmodelmanager.h" | |
| #include "graphics/engine/particle.h" | |
| #include "graphics/engine/planet.h" | |
| #include "graphics/engine/pyro_manager.h" | |
| #include "graphics/engine/terrain.h" | |
| #include "graphics/engine/text.h" | |
| #include "graphics/engine/water.h" | |
| #include "graphics/model/model_manager.h" | |
| #include "level/mainmovie.h" | |
| #include "level/player_profile.h" | |
| #include "level/scene_conditions.h" | |
| #include "level/scoreboard.h" | |
| #include "level/parser/parser.h" | |
| #include "math/const.h" | |
| #include "math/func.h" | |
| #include "math/geometry.h" | |
| #include "object/object.h" | |
| #include "object/object_create_exception.h" | |
| #include "object/object_manager.h" | |
| #include "object/auto/auto.h" | |
| #include "object/motion/motion.h" | |
| #include "object/motion/motionhuman.h" | |
| #include "object/motion/motiontoto.h" | |
| #include "object/subclass/exchange_post.h" | |
| #include "object/task/task.h" | |
| #include "object/task/taskbuild.h" | |
| #include "object/task/taskmanip.h" | |
| #include "physics/physics.h" | |
| #include "script/cbottoken.h" | |
| #include "script/script.h" | |
| #include "script/scriptfunc.h" | |
| #include "sound/sound.h" | |
| #include "ui/debug_menu.h" | |
| #include "ui/displayinfo.h" | |
| #include "ui/displaytext.h" | |
| #include "ui/maindialog.h" | |
| #include "ui/mainmap.h" | |
| #include "ui/mainshort.h" | |
| #include "ui/mainui.h" | |
| #include "ui/controls/button.h" | |
| #include "ui/controls/edit.h" | |
| #include "ui/controls/group.h" | |
| #include "ui/controls/interface.h" | |
| #include "ui/controls/label.h" | |
| #include "ui/controls/map.h" | |
| #include "ui/controls/shortcut.h" | |
| #include "ui/controls/slider.h" | |
| #include "ui/controls/window.h" | |
| #include "ui/screen/screen_loading.h" | |
| #include <algorithm> | |
| #include <iomanip> | |
| #include <stdexcept> | |
| #include <ctime> | |
| #include <boost/lexical_cast.hpp> | |
| // Global variables. | |
| const float UNIT = 4.0f; // default for g_unit | |
| float g_unit; // conversion factor | |
| // Min/max values for the game speed. | |
| const float MIN_SPEED = 1/8.0f; | |
| const float MAX_SPEED = 256.0f; | |
| // Reference colors used when recoloring textures, see ChangeColor() | |
| const Gfx::Color COLOR_REF_BOT = Gfx::Color( 10.0f/256.0f, 166.0f/256.0f, 254.0f/256.0f); // blue | |
| const Gfx::Color COLOR_REF_ALIEN = Gfx::Color(135.0f/256.0f, 170.0f/256.0f, 13.0f/256.0f); // green | |
| const Gfx::Color COLOR_REF_GREEN = Gfx::Color(135.0f/256.0f, 170.0f/256.0f, 13.0f/256.0f); // green | |
| const Gfx::Color COLOR_REF_WATER = Gfx::Color( 25.0f/256.0f, 255.0f/256.0f, 240.0f/256.0f); // cyan | |
| //! Constructor of robot application | |
| CRobotMain::CRobotMain() | |
| { | |
| m_app = CApplication::GetInstancePointer(); | |
| m_eventQueue = m_app->GetEventQueue(); | |
| m_sound = m_app->GetSound(); | |
| m_engine = Gfx::CEngine::GetInstancePointer(); | |
| m_oldModelManager = m_engine->GetModelManager(); | |
| m_lightMan = m_engine->GetLightManager(); | |
| m_particle = m_engine->GetParticle(); | |
| m_water = m_engine->GetWater(); | |
| m_cloud = m_engine->GetCloud(); | |
| m_lightning = m_engine->GetLightning(); | |
| m_planet = m_engine->GetPlanet(); | |
| m_input = CInput::GetInstancePointer(); | |
| m_modelManager = MakeUnique<Gfx::CModelManager>(); | |
| m_settings = MakeUnique<CSettings>(); | |
| m_pause = MakeUnique<CPauseManager>(); | |
| m_interface = MakeUnique<Ui::CInterface>(); | |
| m_terrain = MakeUnique<Gfx::CTerrain>(); | |
| m_camera = MakeUnique<Gfx::CCamera>(); | |
| m_displayText = MakeUnique<Ui::CDisplayText>(); | |
| m_movie = MakeUnique<CMainMovie>(); | |
| m_ui = MakeUnique<Ui::CMainUserInterface>(); | |
| m_short = MakeUnique<Ui::CMainShort>(); | |
| m_map = MakeUnique<Ui::CMainMap>(); | |
| m_objMan = MakeUnique<CObjectManager>( | |
| m_engine, | |
| m_terrain.get(), | |
| m_oldModelManager, | |
| m_modelManager.get(), | |
| m_particle); | |
| m_debugMenu = MakeUnique<Ui::CDebugMenu>(this, m_engine, m_objMan.get(), m_sound); | |
| m_time = 0.0f; | |
| m_gameTime = 0.0f; | |
| m_gameTimeAbsolute = 0.0f; | |
| m_levelCategory = LevelCategory::Exercises; | |
| m_levelChap = 0; | |
| m_levelRank = 0; | |
| m_sceneReadPath = ""; | |
| m_missionTimerEnabled = false; | |
| m_missionTimerStarted = false; | |
| m_missionTimer = 0.0f; | |
| m_phase = PHASE_PLAYER_SELECT; | |
| m_visitLast = EVENT_NULL; | |
| m_visitObject = nullptr; | |
| m_visitArrow = nullptr; | |
| m_audioTrack = ""; | |
| m_audioRepeat = true; | |
| m_satcomTrack = ""; | |
| m_satcomRepeat = true; | |
| m_editorTrack = ""; | |
| m_editorRepeat = true; | |
| m_selectObject = nullptr; | |
| m_infoUsed = 0; | |
| m_controller = nullptr; | |
| m_missionType = MISSION_NORMAL; | |
| m_immediatSatCom = false; | |
| m_beginSatCom = false; | |
| m_lockedSatCom = false; | |
| m_movieLock = false; | |
| m_satComLock = false; | |
| m_editLock = false; | |
| m_editFull = false; | |
| m_hilite = false; | |
| m_cheatSelectInsect = false; | |
| m_cheatShowSoluce = false; | |
| m_codeBattleInit = false; | |
| m_codeBattleStarted = false; | |
| m_teamNames.clear(); | |
| #if DEV_BUILD | |
| m_cheatAllMission = true; // for development | |
| #else | |
| m_cheatAllMission = false; | |
| #endif | |
| m_cheatRadar = false; | |
| m_fixScene = false; | |
| m_cheatTrainerPilot = false; | |
| m_friendAim = false; | |
| m_resetCreate = false; | |
| m_shortCut = true; | |
| m_commandHistoryIndex = -1; | |
| m_movieInfoIndex = -1; | |
| m_tooltipPos = Math::Point(0.0f, 0.0f); | |
| m_tooltipName.clear(); | |
| m_tooltipTime = 0.0f; | |
| m_winTerminate = false; | |
| m_globalMagnifyDamage = 1.0f; | |
| m_autosave = true; | |
| m_autosaveInterval = 5; | |
| m_autosaveSlots = 3; | |
| m_autosaveLast = 0.0f; | |
| m_shotSaving = 0; | |
| m_build = 0; | |
| m_researchDone.clear(); // no research done | |
| m_researchDone[0] = 0; | |
| m_researchEnable = 0; | |
| g_unit = UNIT; | |
| for (int i = 0; i < MAXSHOWLIMIT; i++) | |
| { | |
| m_showLimit[i].used = false; | |
| m_showLimit[i].total = 0; | |
| m_showLimit[i].link = nullptr; | |
| } | |
| m_debugCrashSpheres = false; | |
| m_engine->SetTerrain(m_terrain.get()); | |
| m_app->SetMouseMode(MOUSE_ENGINE); | |
| m_movie->Flush(); | |
| FlushDisplayInfo(); | |
| InitEye(); | |
| m_engine->SetTracePrecision(1.0f); | |
| m_settings->LoadSettings(); | |
| m_settings->SaveSettings(); | |
| m_settings->SaveResolutionSettings(m_app->GetVideoConfig()); | |
| SelectPlayer(CPlayerProfile::GetLastName()); | |
| CScriptFunctions::Init(); | |
| } | |
| //! Destructor of robot application | |
| CRobotMain::~CRobotMain() | |
| { | |
| } | |
| Gfx::CCamera* CRobotMain::GetCamera() | |
| { | |
| return m_camera.get(); | |
| } | |
| Gfx::CTerrain* CRobotMain::GetTerrain() | |
| { | |
| return m_terrain.get(); | |
| } | |
| Ui::CInterface* CRobotMain::GetInterface() | |
| { | |
| return m_interface.get(); | |
| } | |
| Ui::CDisplayText* CRobotMain::GetDisplayText() | |
| { | |
| return m_displayText.get(); | |
| } | |
| CPauseManager* CRobotMain::GetPauseManager() | |
| { | |
| return m_pause.get(); | |
| } | |
| std::string PhaseToString(Phase phase) | |
| { | |
| if (phase == PHASE_WELCOME1) return "PHASE_WELCOME1"; | |
| if (phase == PHASE_WELCOME2) return "PHASE_WELCOME2"; | |
| if (phase == PHASE_WELCOME3) return "PHASE_WELCOME3"; | |
| if (phase == PHASE_PLAYER_SELECT) return "PHASE_PLAYER_SELECT"; | |
| if (phase == PHASE_APPERANCE) return "PHASE_APPERANCE"; | |
| if (phase == PHASE_MAIN_MENU) return "PHASE_MAIN_MENU"; | |
| if (phase == PHASE_LEVEL_LIST) return "PHASE_LEVEL_LIST"; | |
| if (phase == PHASE_SIMUL) return "PHASE_SIMUL"; | |
| if (phase == PHASE_SETUPd) return "PHASE_SETUPd"; | |
| if (phase == PHASE_SETUPg) return "PHASE_SETUPg"; | |
| if (phase == PHASE_SETUPp) return "PHASE_SETUPp"; | |
| if (phase == PHASE_SETUPc) return "PHASE_SETUPc"; | |
| if (phase == PHASE_SETUPs) return "PHASE_SETUPs"; | |
| if (phase == PHASE_SETUPds) return "PHASE_SETUPds"; | |
| if (phase == PHASE_SETUPgs) return "PHASE_SETUPgs"; | |
| if (phase == PHASE_SETUPps) return "PHASE_SETUPps"; | |
| if (phase == PHASE_SETUPcs) return "PHASE_SETUPcs"; | |
| if (phase == PHASE_SETUPss) return "PHASE_SETUPss"; | |
| if (phase == PHASE_WRITEs) return "PHASE_WRITEs"; | |
| if (phase == PHASE_READ) return "PHASE_READ"; | |
| if (phase == PHASE_READs) return "PHASE_READs"; | |
| if (phase == PHASE_WIN) return "PHASE_WIN"; | |
| if (phase == PHASE_LOST) return "PHASE_LOST"; | |
| if (phase == PHASE_QUIT_SCREEN) return "PHASE_QUIT_SCREEN"; | |
| if (phase == PHASE_SATCOM) return "PHASE_SATCOM"; | |
| return "(unknown)"; | |
| } | |
| bool IsInSimulationConfigPhase(Phase phase) | |
| { | |
| return (phase >= PHASE_SETUPds && phase <= PHASE_SETUPss) || phase == PHASE_READs || phase == PHASE_WRITEs; | |
| } | |
| bool IsPhaseWithWorld(Phase phase) | |
| { | |
| if (phase == PHASE_SIMUL ) return true; | |
| if (phase == PHASE_WIN ) return true; | |
| if (phase == PHASE_LOST ) return true; | |
| if (phase == PHASE_APPERANCE) return true; | |
| if (IsInSimulationConfigPhase(phase)) return true; | |
| return false; | |
| } | |
| bool IsMainMenuPhase(Phase phase) | |
| { | |
| if (phase == PHASE_APPERANCE) return true; | |
| return !IsPhaseWithWorld(phase); | |
| } | |
| //! Changes phase | |
| void CRobotMain::ChangePhase(Phase phase) | |
| { | |
| bool resetWorld = false; | |
| if ((IsPhaseWithWorld(m_phase) || IsPhaseWithWorld(phase)) && !IsInSimulationConfigPhase(m_phase) && !IsInSimulationConfigPhase(phase)) | |
| { | |
| if (IsPhaseWithWorld(m_phase) && !IsPhaseWithWorld(phase) && m_exitAfterMission) | |
| { | |
| GetLogger()->Info("Mission finished in single mission mode, exiting\n"); | |
| m_eventQueue->AddEvent(Event(EVENT_QUIT)); | |
| return; | |
| } | |
| GetLogger()->Info("Reseting world on phase change...\n"); | |
| resetWorld = true; | |
| } | |
| if (resetWorld) | |
| { | |
| m_missionTimerEnabled = m_missionTimerStarted = false; | |
| m_missionTimer = 0.0f; | |
| if (m_phase == PHASE_SIMUL) // ends a simulation? | |
| { | |
| SaveAllScript(); | |
| m_sound->StopMusic(0.0f); | |
| m_camera->SetControllingObject(nullptr); | |
| if (m_gameTime > 10.0f) // did you play at least 10 seconds? | |
| { | |
| m_playerProfile->IncrementLevelTryCount(m_levelCategory, m_levelChap, m_levelRank); | |
| } | |
| if (m_userPause != nullptr) | |
| { | |
| m_pause->DeactivatePause(m_userPause); | |
| m_userPause = nullptr; | |
| } | |
| } | |
| if (phase == PHASE_WIN) // wins a simulation? | |
| { | |
| m_playerProfile->SetLevelPassed(m_levelCategory, m_levelChap, m_levelRank, true); | |
| m_ui->NextMission(); // passes to the next mission | |
| } | |
| DeleteAllObjects(); // removes all the current 3D Scene | |
| } | |
| m_phase = phase; | |
| if (m_phase != PHASE_SIMUL) | |
| { | |
| Ui::CWindow* pw = static_cast<Ui::CWindow*>(m_interface->SearchControl(EVENT_WINDOW6)); | |
| if ( pw != nullptr ) pw->ClearState(Ui::STATE_VISIBLE | Ui::STATE_ENABLE); | |
| } | |
| if (resetWorld) | |
| { | |
| m_winDelay = 0.0f; | |
| m_lostDelay = 0.0f; | |
| m_beginSatCom = false; | |
| m_movieLock = false; | |
| m_satComLock = false; | |
| m_editLock = false; | |
| m_resetCreate = false; | |
| m_infoObject = nullptr; | |
| m_pause->FlushPause(); | |
| m_freePhotoPause = nullptr; | |
| m_userPause = nullptr; | |
| m_focusPause = nullptr; | |
| FlushDisplayInfo(); | |
| m_engine->SetRankView(0); | |
| m_terrain->FlushRelief(); | |
| m_engine->DeleteAllObjects(); | |
| m_oldModelManager->DeleteAllModelCopies(); | |
| m_engine->SetWaterAddColor(Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f)); | |
| m_engine->SetBackground(""); | |
| m_engine->SetBackForce(false); | |
| m_engine->SetForegroundName(""); | |
| m_engine->SetOverColor(); | |
| m_engine->DeleteGroundMark(0); | |
| SetSpeed(1.0f); | |
| m_terrain->SetWind(Math::Vector(0.0f, 0.0f, 0.0f)); | |
| m_terrain->FlushBuildingLevel(); | |
| m_terrain->FlushFlyingLimit(); | |
| m_lightMan->FlushLights(); | |
| m_particle->FlushParticle(); | |
| m_water->Flush(); | |
| m_cloud->Flush(); | |
| m_lightning->Flush(); | |
| m_planet->Flush(); | |
| m_interface->Flush(); | |
| m_newScriptName.clear(); | |
| m_sound->SetListener(Math::Vector(0.0f, 0.0f, 0.0f), Math::Vector(0.0f, 0.0f, 1.0f)); | |
| m_sound->StopAll(); | |
| m_camera->SetType(Gfx::CAM_TYPE_NULL); | |
| m_movie->Flush(); | |
| m_movieInfoIndex = -1; | |
| m_shortCut = true; | |
| m_viewpoints.clear(); | |
| } | |
| ClearInterface(); | |
| Math::Point dim, pos; | |
| // Creates and hide the command console. | |
| dim.x = 200.0f/640.0f; | |
| dim.y = 18.0f/480.0f; | |
| pos.x = 20.0f/640.0f; | |
| pos.y = 100.0f/480.0f; | |
| Ui::CEdit* pe = m_interface->CreateEdit(pos, dim, 0, EVENT_CMD); | |
| pe->ClearState(Ui::STATE_VISIBLE); | |
| pe->SetMaxChar(100); | |
| m_cmdEdit = false; // hidden for now | |
| // Creates the speedometer. | |
| dim.x = 30.0f/640.0f; | |
| dim.y = 20.0f/480.0f; | |
| pos.x = 4.0f/640.0f; | |
| pos.y = 426.0f/480.0f; | |
| // Creates the save indicator | |
| Ui::CButton* pb = m_interface->CreateButton(pos, dim, 0, EVENT_SPEED); | |
| pb->SetState(Ui::STATE_SIMPLY); | |
| pb->ClearState(Ui::STATE_VISIBLE); | |
| if (m_phase == PHASE_PLAYER_SELECT) | |
| { | |
| if (CResourceManager::DirectoryExists("crashsave")) | |
| { | |
| GetLogger()->Info("Pre-crash save found!\n"); | |
| m_ui->GetDialog()->StartQuestion( | |
| "Your game seems to have crashed. Do you want to restore pre-crash state?", false, false, false, | |
| [&]() | |
| { | |
| GetLogger()->Info("Trying to restore pre-crash state...\n"); | |
| assert(m_playerProfile != nullptr); | |
| m_playerProfile->LoadScene("../../crashsave"); | |
| CResourceManager::RemoveDirectory("crashsave"); | |
| }, | |
| [&]() | |
| { | |
| GetLogger()->Info("Not restoring pre-crash state\n"); | |
| CResourceManager::RemoveDirectory("crashsave"); | |
| } | |
| ); | |
| } | |
| } | |
| m_ui->ChangePhase(m_phase); | |
| if (m_phase == PHASE_SATCOM) | |
| { | |
| m_interface->DeleteControl(EVENT_WINDOW5); | |
| StartDisplayInfo(InjectLevelPathsForCurrentLevel("cbot.txt", "help/%lng%"), 0); | |
| } | |
| if (!resetWorld) return; | |
| dim.x = 32.0f/640.0f; | |
| dim.y = 32.0f/480.0f; | |
| float ox = 3.0f/640.0f; | |
| float oy = 3.0f/480.0f; | |
| float sx = (32.0f+2.0f)/640.0f; | |
| float sy = (32.0f+2.0f)/480.0f; | |
| if (m_phase != PHASE_APPERANCE) | |
| { | |
| m_engine->SetDrawWorld(true); | |
| m_engine->SetDrawFront(false); | |
| m_fixScene = false; | |
| } | |
| if (m_phase == PHASE_SIMUL) | |
| { | |
| bool loading = !m_sceneReadPath.empty(); | |
| m_ui->ShowLoadingScreen(true); | |
| m_ui->GetLoadingScreen()->SetProgress(0.0f, RT_LOADING_INIT); | |
| m_map->CreateMap(); | |
| m_map->ShowMap(false); | |
| try | |
| { | |
| CreateScene(m_ui->GetSceneSoluce(), false, false); // interactive scene | |
| if (m_mapImage) | |
| m_map->SetFixImage(m_mapFilename); | |
| m_app->ResetTimeAfterLoading(); | |
| m_sound->StopMusic(0.0f); | |
| if (m_base == nullptr || loading) StartMusic(); | |
| if (m_immediatSatCom && !loading && | |
| m_infoFilename[SATCOM_HUSTON][0] != 0) | |
| StartDisplayInfo(SATCOM_HUSTON, false); // shows the instructions | |
| } | |
| catch (const std::runtime_error& e) | |
| { | |
| LevelLoadingError("An error occurred while trying to load a level", e); | |
| } | |
| } | |
| if (m_phase == PHASE_WIN) | |
| { | |
| m_sound->StopAll(); | |
| if (m_endingWin.empty()) | |
| { | |
| ChangePhase(PHASE_LEVEL_LIST); | |
| } | |
| else | |
| { | |
| m_winTerminate = (m_endingWin.substr(m_endingWin.find_last_of("/")+1) == "win904.txt"); | |
| m_levelFile = m_endingWin; | |
| try | |
| { | |
| CreateScene(false, true, false); // sets scene | |
| pos.x = ox+sx*1; pos.y = oy+sy*1; | |
| Math::Point ddim; | |
| ddim.x = dim.x*2; ddim.y = dim.y*2; | |
| m_interface->CreateButton(pos, ddim, 16, EVENT_BUTTON_OK); | |
| if (m_winTerminate) | |
| { | |
| pos.x = ox+sx*3; pos.y = oy+sy*0.2f; | |
| ddim.x = dim.x*15; ddim.y = dim.y*3.0f; | |
| pe = m_interface->CreateEdit(pos, ddim, 0, EVENT_EDIT0); | |
| pe->SetGenericMode(true); | |
| pe->SetFontType(Gfx::FONT_COMMON); | |
| pe->SetEditCap(false); | |
| pe->SetHighlightCap(false); | |
| pe->ReadText(std::string("help/") + m_app->GetLanguageChar() + std::string("/win.txt")); | |
| } | |
| else | |
| { | |
| m_displayText->DisplayError(INFO_WIN, Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 1000.0f); | |
| } | |
| StartMusic(); | |
| } | |
| catch (const std::runtime_error& e) | |
| { | |
| LevelLoadingError("An error occurred while trying to load win scene", e); | |
| } | |
| } | |
| } | |
| if (m_phase == PHASE_LOST) | |
| { | |
| m_sound->StopAll(); | |
| if (m_endingLost.empty()) | |
| { | |
| ChangePhase(PHASE_LEVEL_LIST); | |
| } | |
| else | |
| { | |
| m_winTerminate = false; | |
| m_levelFile = m_endingLost; | |
| try | |
| { | |
| CreateScene(false, true, false); // sets scene | |
| pos.x = ox+sx*1; pos.y = oy+sy*1; | |
| Math::Point ddim; | |
| ddim.x = dim.x*2; ddim.y = dim.y*2; | |
| m_interface->CreateButton(pos, ddim, 16, EVENT_BUTTON_OK); | |
| m_displayText->DisplayError(INFO_LOST, Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 1000.0f); | |
| StartMusic(); | |
| } | |
| catch (const std::runtime_error& e) | |
| { | |
| LevelLoadingError("An error occurred while trying to load lost scene", e); | |
| } | |
| } | |
| } | |
| m_engine->LoadAllTextures(); | |
| } | |
| Phase CRobotMain::GetPhase() | |
| { | |
| return m_phase; | |
| } | |
| //! Processes an event | |
| bool CRobotMain::ProcessEvent(Event &event) | |
| { | |
| if (!m_ui->EventProcess(event)) return false; | |
| if (m_phase == PHASE_SIMUL) | |
| { | |
| if (!m_editFull) | |
| m_camera->EventProcess(event); | |
| } | |
| if (!m_debugMenu->EventProcess(event)) return false; | |
| if (event.type == EVENT_FRAME) | |
| { | |
| if (!m_movie->EventProcess(event)) // end of the movie? | |
| { | |
| MainMovieType type = m_movie->GetStopType(); | |
| if (type == MM_SATCOMopen) | |
| { | |
| m_pause->DeactivatePause(m_satcomMoviePause); | |
| m_satcomMoviePause = nullptr; | |
| SelectObject(m_infoObject, false); // hands over the command buttons | |
| m_map->ShowMap(m_mapShow); | |
| m_displayText->HideText(false); | |
| int i = m_movieInfoIndex; | |
| StartDisplayInfo(m_movieInfoIndex, false); | |
| m_movieInfoIndex = i; | |
| } | |
| } | |
| m_displayText->EventProcess(event); | |
| if (m_displayInfo != nullptr) // current edition? | |
| m_displayInfo->EventProcess(event); | |
| UpdateInfoText(); | |
| return EventFrame(event); | |
| } | |
| if (event.type == EVENT_RELOAD_TEXTURES) | |
| { | |
| if (IsPhaseWithWorld(m_phase)) | |
| { | |
| ChangeColor(); | |
| UpdateMap(); | |
| } | |
| m_engine->LoadAllTextures(); | |
| } | |
| if (event.type == EVENT_RESOLUTION_CHANGED) | |
| { | |
| // Recreate the interface (needed if the aspect ratio changes) | |
| // TODO: This can sometimes cause unwanted side effects, like hidden windows reappearing. To be fixed during CEGUI refactoring. | |
| m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE)); | |
| CreateShortcuts(); | |
| } | |
| if (event.type == EVENT_FOCUS_LOST) | |
| { | |
| GetLogger()->Trace("Window unfocused\n"); | |
| if (m_settings->GetFocusLostPause()) | |
| { | |
| m_focusPause = m_pause->ActivatePause(PAUSE_ENGINE); | |
| } | |
| return false; | |
| } | |
| if (event.type == EVENT_FOCUS_GAINED) | |
| { | |
| GetLogger()->Trace("Window focused\n"); | |
| if (m_focusPause != nullptr) | |
| { | |
| m_pause->DeactivatePause(m_focusPause); | |
| m_focusPause = nullptr; | |
| } | |
| return false; | |
| } | |
| if (event.type == EVENT_WRITE_SCENE_FINISHED) | |
| { | |
| IOWriteSceneFinished(); | |
| return false; | |
| } | |
| if (event.type == EVENT_UPDINTERFACE) | |
| { | |
| if (m_missionType == MISSION_CODE_BATTLE && !m_codeBattleStarted) | |
| { | |
| CreateCodeBattleInterface(); | |
| } | |
| } | |
| if (event.type == EVENT_CODE_BATTLE_START) | |
| { | |
| m_pause->DeactivatePause(m_userPause); | |
| m_userPause = nullptr; | |
| } | |
| if (event.type == EVENT_CODE_BATTLE_SPECTATOR) | |
| { | |
| SetCodeBattleSpectatorMode(!m_codeBattleSpectator); | |
| } | |
| if (event.type >= EVENT_VIEWPOINT0 && event.type <= EVENT_VIEWPOINT9) | |
| { | |
| m_camera->SetType(Gfx::CAM_TYPE_SCRIPT); | |
| m_camera->SetSmooth(Gfx::CAM_SMOOTH_HARD); | |
| m_camera->SetScriptCameraAnimate(m_viewpoints[event.type - EVENT_VIEWPOINT0].eye, m_viewpoints[event.type - EVENT_VIEWPOINT0].look); | |
| } | |
| // Management of the console. | |
| if (event.type == EVENT_KEY_DOWN) | |
| { | |
| auto data = event.GetData<KeyEventData>(); | |
| if (data->slot == INPUT_SLOT_CMDLINE) | |
| { | |
| if (m_phase != PHASE_PLAYER_SELECT && | |
| !m_movie->IsExist() && | |
| !m_movieLock && !m_editLock && !m_cmdEdit) | |
| { | |
| Ui::CEdit* pe = static_cast<Ui::CEdit*>(m_interface->SearchControl(EVENT_CMD)); | |
| if (pe == nullptr) return false; | |
| pe->SetState(Ui::STATE_VISIBLE); | |
| m_interface->SetFocus(pe); | |
| if (m_phase == PHASE_SIMUL) m_cmdEditPause = m_pause->ActivatePause(PAUSE_ENGINE); | |
| m_cmdEdit = true; | |
| m_commandHistoryIndex = -1; // no element selected in command history | |
| } | |
| return false; | |
| } | |
| if (IsPhaseWithWorld(m_phase)) | |
| { | |
| if (data->key == KEY(F10)) | |
| { | |
| m_debugMenu->ToggleInterface(); | |
| return false; | |
| } | |
| } | |
| } | |
| // Browse forward command history with UP key | |
| if (event.type == EVENT_KEY_DOWN && | |
| event.GetData<KeyEventData>()->key == KEY(UP) && m_cmdEdit) | |
| { | |
| Ui::CEdit* pe = static_cast<Ui::CEdit*>(m_interface->SearchControl(EVENT_CMD)); | |
| if (pe == nullptr) return false; | |
| std::string cmd = GetNextFromCommandHistory(); | |
| if (!cmd.empty()) pe->SetText(cmd); | |
| return false; | |
| } | |
| // Browse backward command history with DOWN key | |
| if (event.type == EVENT_KEY_DOWN && | |
| event.GetData<KeyEventData>()->key == KEY(DOWN) && m_cmdEdit) | |
| { | |
| Ui::CEdit* pe = static_cast<Ui::CEdit*>(m_interface->SearchControl(EVENT_CMD)); | |
| if (pe == nullptr) return false; | |
| std::string cmd = GetPreviousFromCommandHistory(); | |
| if (!cmd.empty()) pe->SetText(cmd); | |
| return false; | |
| } | |
| if (event.type == EVENT_KEY_DOWN && | |
| event.GetData<KeyEventData>()->key == KEY(RETURN) && m_cmdEdit) | |
| { | |
| std::string cmd; | |
| Ui::CEdit* pe = static_cast<Ui::CEdit*>(m_interface->SearchControl(EVENT_CMD)); | |
| if (pe == nullptr) return false; | |
| cmd = pe->GetText(50); | |
| pe->SetText(""); | |
| pe->ClearState(Ui::STATE_VISIBLE); | |
| m_interface->SetFocus(nullptr); | |
| if (m_phase == PHASE_SIMUL) | |
| { | |
| m_pause->DeactivatePause(m_cmdEditPause); | |
| m_cmdEditPause = nullptr; | |
| } | |
| ExecuteCmd(cmd); | |
| PushToCommandHistory(cmd); | |
| m_cmdEdit = false; | |
| return false; | |
| } | |
| if (event.type == EVENT_KEY_DOWN && m_cmdEdit) | |
| return false; // cheat console active, so ignore keys | |
| // Management of the speed change. | |
| if (event.type == EVENT_SPEED) | |
| SetSpeed(1.0f); | |
| if (!m_displayText->EventProcess(event)) | |
| return false; | |
| if (event.type == EVENT_MOUSE_MOVE) | |
| { | |
| HiliteObject(event.mousePos); | |
| } | |
| if (m_displayInfo != nullptr) // current info? | |
| { | |
| m_displayInfo->EventProcess(event); | |
| if (event.type == EVENT_KEY_DOWN) | |
| { | |
| auto data = event.GetData<KeyEventData>(); | |
| if (data->slot == INPUT_SLOT_HELP || | |
| data->slot == INPUT_SLOT_PROG || | |
| data->key == KEY(ESCAPE)) | |
| { | |
| StopDisplayInfo(); | |
| } | |
| } | |
| if (event.type == EVENT_OBJECT_INFOOK) | |
| StopDisplayInfo(); | |
| if (m_displayInfo == nullptr && m_phase == PHASE_SATCOM) | |
| ChangePhase(PHASE_MAIN_MENU); | |
| return false; | |
| } | |
| CObject* obj; | |
| // Simulation phase of the game | |
| if (m_phase == PHASE_SIMUL) | |
| { | |
| switch (event.type) | |
| { | |
| case EVENT_KEY_DOWN: | |
| { | |
| auto data = event.GetData<KeyEventData>(); | |
| HiliteClear(); | |
| if (m_editLock) // current edition? | |
| { | |
| if (data->slot == INPUT_SLOT_HELP) | |
| { | |
| StartDisplayInfo(SATCOM_HUSTON, false); | |
| return false; | |
| } | |
| if (data->slot == INPUT_SLOT_PROG) | |
| { | |
| StartDisplayInfo(SATCOM_PROG, false); | |
| return false; | |
| } | |
| break; | |
| } | |
| if (m_movieLock) // current movie? | |
| { | |
| if (data->slot == INPUT_SLOT_QUIT || | |
| data->key == KEY(ESCAPE)) | |
| { | |
| AbortMovie(); | |
| } | |
| return false; | |
| } | |
| if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT) | |
| { | |
| if (data->slot == INPUT_SLOT_VISIT) | |
| { | |
| StartDisplayVisit(EVENT_NULL); | |
| } | |
| if (data->slot == INPUT_SLOT_QUIT || | |
| data->key == KEY(ESCAPE)) | |
| { | |
| StopDisplayVisit(); | |
| } | |
| return false; | |
| } | |
| if (data->slot == INPUT_SLOT_QUIT) | |
| { | |
| if (m_movie->IsExist()) | |
| StartDisplayInfo(SATCOM_HUSTON, false); | |
| else if (m_winDelay > 0.0f) | |
| ChangePhase(PHASE_WIN); | |
| else if (m_lostDelay > 0.0f) | |
| ChangePhase(PHASE_LOST); | |
| else if (!m_cmdEdit) | |
| m_ui->GetDialog()->StartPauseMenu(); // do you want to leave? | |
| } | |
| if (data->slot == INPUT_SLOT_PAUSE) | |
| { | |
| if (m_userPause == nullptr) | |
| { | |
| if (!m_pause->IsPauseType(PAUSE_ENGINE)) | |
| { | |
| m_userPause = m_pause->ActivatePause(PAUSE_ENGINE); | |
| } | |
| } | |
| else | |
| { | |
| m_pause->DeactivatePause(m_userPause); | |
| m_userPause = nullptr; | |
| } | |
| } | |
| if (data->slot == INPUT_SLOT_CAMERA) | |
| { | |
| ChangeCamera(); | |
| } | |
| if (data->slot == INPUT_SLOT_DESEL) | |
| { | |
| if (m_shortCut) | |
| DeselectObject(); | |
| } | |
| if (data->slot == INPUT_SLOT_HUMAN) | |
| { | |
| SelectObject(SearchHuman()); | |
| } | |
| if (data->slot == INPUT_SLOT_NEXT && ((event.kmodState & KEY_MOD(CTRL)) != 0)) | |
| { | |
| m_short->SelectShortcut(EVENT_OBJECT_SHORTCUT_MODE); // switch bots <-> buildings | |
| return false; | |
| } | |
| if (data->slot == INPUT_SLOT_NEXT) | |
| { | |
| if (m_shortCut) | |
| m_short->SelectNext(); | |
| } | |
| if (data->slot == INPUT_SLOT_HELP) | |
| { | |
| StartDisplayInfo(SATCOM_HUSTON, true); | |
| } | |
| if (data->slot == INPUT_SLOT_PROG) | |
| { | |
| StartDisplayInfo(SATCOM_PROG, true); | |
| } | |
| if (data->slot == INPUT_SLOT_VISIT) | |
| { | |
| StartDisplayVisit(EVENT_NULL); | |
| } | |
| if (data->slot == INPUT_SLOT_SPEED_DEC) | |
| { | |
| SetSpeed(GetSpeed()*0.5f); | |
| } | |
| if (data->slot == INPUT_SLOT_SPEED_RESET) | |
| { | |
| SetSpeed(1.0f); | |
| } | |
| if (data->slot == INPUT_SLOT_SPEED_INC) | |
| { | |
| SetSpeed(GetSpeed()*2.0f); | |
| } | |
| if (data->slot == INPUT_SLOT_QUICKSAVE) | |
| { | |
| QuickSave(); | |
| } | |
| if (data->slot == INPUT_SLOT_QUICKLOAD) | |
| { | |
| QuickLoad(); | |
| } | |
| if (data->key == KEY(c) && ((event.kmodState & KEY_MOD(CTRL)) != 0) && m_engine->GetShowStats()) | |
| { | |
| CObject* obj = GetSelect(); | |
| if (obj != nullptr) | |
| { | |
| CLevelParserLine line("CreateObject"); | |
| line.AddParam("type", MakeUnique<CLevelParserParam>(obj->GetType())); | |
| Math::Vector pos = obj->GetPosition()/g_unit; | |
| pos.y = 0.0f; | |
| line.AddParam("pos", MakeUnique<CLevelParserParam>(pos)); | |
| float dir = Math::NormAngle(obj->GetRotationY()) / Math::PI; | |
| line.AddParam("dir", MakeUnique<CLevelParserParam>(dir)); | |
| std::stringstream ss; | |
| ss << line; | |
| SDL_SetClipboardText(ss.str().c_str()); | |
| } | |
| } | |
| break; | |
| } | |
| case EVENT_MOUSE_BUTTON_DOWN: | |
| { | |
| if (event.GetData<MouseButtonEventData>()->button != MOUSE_BUTTON_LEFT) // only left mouse button | |
| break; | |
| obj = DetectObject(event.mousePos); | |
| if (!m_shortCut) obj = nullptr; | |
| if (obj != nullptr && obj->GetType() == OBJECT_TOTO) | |
| { | |
| if (m_displayInfo != nullptr) // current info? | |
| { | |
| StopDisplayInfo(); | |
| } | |
| else | |
| { | |
| if (!m_editLock) | |
| StartDisplayInfo(SATCOM_HUSTON, true); | |
| } | |
| } | |
| else | |
| { | |
| SelectObject(obj); | |
| } | |
| break; | |
| } | |
| case EVENT_OBJECT_LIMIT: | |
| StartShowLimit(); | |
| break; | |
| case EVENT_OBJECT_DESELECT: | |
| if (m_shortCut) | |
| DeselectObject(); | |
| break; | |
| case EVENT_OBJECT_HELP: | |
| HelpObject(); | |
| break; | |
| case EVENT_OBJECT_CAMERA: | |
| ChangeCamera(); | |
| break; | |
| case EVENT_OBJECT_DELETE: | |
| m_ui->GetDialog()->StartQuestion( | |
| RT_DIALOG_DELOBJ, true, false, false, | |
| [&]() | |
| { | |
| DestroySelectedObject(); | |
| } | |
| ); | |
| break; | |
| case EVENT_OBJECT_BHELP: | |
| StartDisplayInfo(SATCOM_HUSTON, true); | |
| break; | |
| case EVENT_OBJECT_SOLUCE: | |
| StartDisplayInfo(SATCOM_SOLUCE, true); | |
| break; | |
| case EVENT_OBJECT_MAPZOOM: | |
| m_map->ZoomMap(); | |
| break; | |
| case EVENT_DT_VISIT0: | |
| case EVENT_DT_VISIT1: | |
| case EVENT_DT_VISIT2: | |
| case EVENT_DT_VISIT3: | |
| case EVENT_DT_VISIT4: | |
| StartDisplayVisit(event.type); | |
| break; | |
| case EVENT_DT_END: | |
| StopDisplayVisit(); | |
| break; | |
| case EVENT_OBJECT_MOVIELOCK: | |
| AbortMovie(); | |
| break; | |
| case EVENT_WIN: | |
| m_missionTimerEnabled = m_missionTimerStarted = false; | |
| ChangePhase(PHASE_WIN); | |
| break; | |
| case EVENT_LOST: | |
| m_missionTimerEnabled = m_missionTimerStarted = false; | |
| ChangePhase(PHASE_LOST); | |
| break; | |
| default: | |
| break; | |
| } | |
| if (event.type >= EVENT_OBJECT_SHORTCUT_MODE && event.type <= EVENT_OBJECT_SHORTCUT_MAX) | |
| { | |
| m_short->SelectShortcut(event.type); | |
| } | |
| EventObject(event); | |
| return false; | |
| } | |
| if (m_phase == PHASE_APPERANCE) | |
| EventObject(event); | |
| if (m_phase == PHASE_WIN || | |
| m_phase == PHASE_LOST) | |
| { | |
| EventObject(event); | |
| switch (event.type) | |
| { | |
| case EVENT_KEY_DOWN: | |
| { | |
| auto data = event.GetData<KeyEventData>(); | |
| if (data->key == KEY(ESCAPE) || | |
| data->key == KEY(RETURN)) | |
| { | |
| if (m_winTerminate) | |
| ChangePhase(PHASE_MAIN_MENU); | |
| else | |
| ChangePhase(PHASE_LEVEL_LIST); | |
| } | |
| break; | |
| } | |
| case EVENT_BUTTON_OK: | |
| if (m_winTerminate) | |
| ChangePhase(PHASE_MAIN_MENU); | |
| else | |
| ChangePhase(PHASE_LEVEL_LIST); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| return true; | |
| } | |
| //! Executes a command | |
| void CRobotMain::ExecuteCmd(const std::string& cmd) | |
| { | |
| if (cmd.empty()) return; | |
| if (m_phase == PHASE_SIMUL) | |
| { | |
| if (cmd == "winmission") | |
| m_eventQueue->AddEvent(Event(EVENT_WIN)); | |
| if (cmd == "lostmission") | |
| m_eventQueue->AddEvent(Event(EVENT_LOST)); | |
| if (cmd == "trainerpilot") | |
| { | |
| m_cheatTrainerPilot = !m_cheatTrainerPilot; | |
| return; | |
| } | |
| if (cmd == "fly") | |
| { | |
| m_researchDone[0] |= RESEARCH_FLY; | |
| m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE)); | |
| return; | |
| } | |
| if (cmd == "allresearch") | |
| { | |
| m_researchDone[0] = -1; // all research are done | |
| m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE)); | |
| return; | |
| } | |
| if (cmd == "allbuildings") | |
| { | |
| m_build = -1; // all buildings are available | |
| m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE)); | |
| return; | |
| } | |
| if (cmd == "all") | |
| { | |
| m_researchDone[0] = -1; // all research are done | |
| m_build = -1; // all buildings are available | |
| m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE)); | |
| return; | |
| } | |
| if (cmd == "nolimit") | |
| { | |
| m_terrain->SetFlyingMaxHeight(280.0f); | |
| return; | |
| } | |
| if (cmd == "controller") | |
| { | |
| if (m_controller == nullptr) | |
| { | |
| GetLogger()->Error("No LevelController on the map to select\n"); | |
| return; | |
| } | |
| // Don't use SelectObject because it checks if the object is selectable | |
| if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT) | |
| StopDisplayVisit(); | |
| CObject* prev = DeselectAll(); | |
| if (prev != nullptr && prev != m_controller) | |
| PushToSelectionHistory(prev); | |
| SelectOneObject(m_controller, true); | |
| m_short->UpdateShortcuts(); | |
| return; | |
| } | |
| if (cmd == "photo1") | |
| { | |
| if (m_freePhotoPause == nullptr) | |
| { | |
| m_camera->SetType(Gfx::CAM_TYPE_FREE); | |
| m_freePhotoPause = m_pause->ActivatePause(PAUSE_ENGINE|PAUSE_PHOTO|PAUSE_OBJECT_UPDATES); | |
| } | |
| else | |
| { | |
| m_camera->SetType(Gfx::CAM_TYPE_BACK); | |
| m_pause->DeactivatePause(m_freePhotoPause); | |
| m_freePhotoPause = nullptr; | |
| } | |
| return; | |
| } | |
| if (cmd == "photo2") | |
| { | |
| if (m_freePhotoPause == nullptr) | |
| { | |
| m_camera->SetType(Gfx::CAM_TYPE_FREE); | |
| DeselectAll(); // removes the control buttons | |
| m_freePhotoPause = m_pause->ActivatePause(PAUSE_ENGINE|PAUSE_PHOTO|PAUSE_OBJECT_UPDATES); | |
| m_map->ShowMap(false); | |
| m_displayText->HideText(true); | |
| } | |
| else | |
| { | |
| m_camera->SetType(Gfx::CAM_TYPE_BACK); | |
| m_pause->DeactivatePause(m_freePhotoPause); | |
| m_freePhotoPause = nullptr; | |
| m_map->ShowMap(m_mapShow); | |
| m_displayText->HideText(false); | |
| } | |
| return; | |
| } | |
| int camtype; | |
| if (sscanf(cmd.c_str(), "camtype %d", &camtype) > 0) | |
| { | |
| m_camera->SetType(static_cast<Gfx::CameraType>(camtype)); | |
| return; | |
| } | |
| float camspeed; | |
| if (sscanf(cmd.c_str(), "camspeed %f", &camspeed) > 0) | |
| { | |
| m_camera->SetCameraSpeed(camspeed); | |
| return; | |
| } | |
| if (cmd == "freecam") | |
| { | |
| m_camera->SetType(Gfx::CAM_TYPE_FREE); | |
| return; | |
| } | |
| if (cmd == "noclip") | |
| { | |
| CObject* object = GetSelect(); | |
| if (object != nullptr) | |
| object->SetCollisions(false); | |
| return; | |
| } | |
| if (cmd == "clip") | |
| { | |
| CObject* object = GetSelect(); | |
| if (object != nullptr) | |
| object->SetCollisions(true); | |
| return; | |
| } | |
| if (cmd == "addhusky") | |
| { | |
| CObject* object = GetSelect(); | |
| if (object != nullptr && object->Implements(ObjectInterfaceType::Shielded)) | |
| dynamic_cast<CShieldedObject*>(object)->SetMagnifyDamage(dynamic_cast<CShieldedObject*>(object)->GetMagnifyDamage()*0.1f); | |
| return; | |
| } | |
| if (cmd == "addfreezer") | |
| { | |
| CObject* object = GetSelect(); | |
| if (object != nullptr && object->Implements(ObjectInterfaceType::JetFlying)) | |
| dynamic_cast<CJetFlyingObject*>(object)->SetRange(dynamic_cast<CJetFlyingObject*>(object)->GetRange()*10.0f); | |
| return; | |
| } | |
| if (cmd == "\155\157\157") | |
| { | |
| // VGhpcyBpcyBlYXN0ZXItZWdnIGFuZCBzbyBpdCBzaG91bGQgYmUgb2JmdXNjYXRlZCEgRG8gbm90 | |
| // IGNsZWFuLXVwIHRoaXMgY29kZSEK | |
| GetLogger()->Info(" _________________________\n"); | |
| GetLogger()->Info("< \x50\x6F\x6C\x73\x6B\x69 \x50\x6F\x72\x74\x61\x6C C\x6F\x6C\x6F\x62\x6F\x74\x61! \x3E\n"); | |
| GetLogger()->Info(" -------------------------\n"); | |
| GetLogger()->Info(" \x5C\x20\x20\x20\x5E\x5F\x5F\x5E\n"); | |
| GetLogger()->Info(" \x20\x5C\x20\x20\x28\x6F\x6F\x29\x5C\x5F\x5F\x5F\x5F\x5F\x5F\x5F\n"); | |
| GetLogger()->Info(" \x28\x5F\x5F\x29\x5C \x20\x20\x20\x20\x29\x5C\x2F\x5C\n"); | |
| GetLogger()->Info(" \x20\x20\x20\x20\x7C|\x2D\x2D\x2D\x2D\x77\x20\x7C\n"); | |
| GetLogger()->Info(" \x20\x20 \x7C\x7C\x20\x20\x20\x20 ||\n"); | |
| } | |
| if (cmd == "fullpower") | |
| { | |
| CObject* object = GetSelect(); | |
| if (object != nullptr) | |
| { | |
| if (object->Implements(ObjectInterfaceType::Powered)) | |
| { | |
| CObject* power = dynamic_cast<CPoweredObject*>(object)->GetPower(); | |
| if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) | |
| dynamic_cast<CPowerContainerObject*>(power)->SetEnergyLevel(1.0f); | |
| } | |
| if (object->Implements(ObjectInterfaceType::Shielded)) | |
| dynamic_cast<CShieldedObject*>(object)->SetShield(1.0f); | |
| if (object->Implements(ObjectInterfaceType::JetFlying)) | |
| dynamic_cast<CJetFlyingObject*>(object)->SetReactorRange(1.0f); | |
| } | |
| return; | |
| } | |
| if (cmd == "fullenergy") | |
| { | |
| CObject* object = GetSelect(); | |
| if (object != nullptr) | |
| { | |
| if (object->Implements(ObjectInterfaceType::Powered)) | |
| { | |
| CObject* power = dynamic_cast<CPoweredObject*>(object)->GetPower(); | |
| if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) | |
| dynamic_cast<CPowerContainerObject*>(power)->SetEnergyLevel(1.0f); | |
| } | |
| } | |
| return; | |
| } | |
| if (cmd == "fullshield") | |
| { | |
| CObject* object = GetSelect(); | |
| if (object != nullptr && object->Implements(ObjectInterfaceType::Shielded)) | |
| dynamic_cast<CShieldedObject*>(object)->SetShield(1.0f); | |
| return; | |
| } | |
| if (cmd == "fullrange") | |
| { | |
| CObject* object = GetSelect(); | |
| if (object != nullptr) | |
| { | |
| if (object->Implements(ObjectInterfaceType::JetFlying)) | |
| dynamic_cast<CJetFlyingObject*>(object)->SetReactorRange(1.0f); | |
| } | |
| return; | |
| } | |
| } | |
| if (cmd == "debugmode") | |
| { | |
| if (m_app->IsDebugModeActive(DEBUG_ALL)) | |
| { | |
| m_app->SetDebugModeActive(DEBUG_ALL, false); | |
| } | |
| else | |
| { | |
| m_app->SetDebugModeActive(DEBUG_ALL, true); | |
| } | |
| return; | |
| } | |
| if (cmd == "showstat") | |
| { | |
| m_engine->SetShowStats(!m_engine->GetShowStats()); | |
| return; | |
| } | |
| if (cmd == "invui") | |
| { | |
| m_engine->SetRenderInterface(!m_engine->GetRenderInterface()); | |
| return; | |
| } | |
| if (cmd == "selectinsect") | |
| { | |
| m_cheatSelectInsect = !m_cheatSelectInsect; | |
| return; | |
| } | |
| if (cmd == "showsoluce") | |
| { | |
| m_cheatShowSoluce = !m_cheatShowSoluce; | |
| m_ui->ShowSoluceUpdate(); | |
| return; | |
| } | |
| if (cmd == "allmission") | |
| { | |
| m_cheatAllMission = !m_cheatAllMission; | |
| m_ui->AllMissionUpdate(); | |
| return; | |
| } | |
| if (cmd == "invradar") | |
| { | |
| m_cheatRadar = !m_cheatRadar; | |
| return; | |
| } | |
| float speed; | |
| if (sscanf(cmd.c_str(), "speed %f", &speed) > 0) | |
| { | |
| SetSpeed(speed); | |
| UpdateSpeedLabel(); | |
| return; | |
| } | |
| if (m_phase == PHASE_SIMUL) | |
| m_displayText->DisplayError(ERR_CMD, Math::Vector(0.0f,0.0f,0.0f)); | |
| } | |
| //! Returns the type of current movie | |
| MainMovieType CRobotMain::GetMainMovie() | |
| { | |
| return m_movie->GetType(); | |
| } | |
| //! Clears the display of instructions | |
| void CRobotMain::FlushDisplayInfo() | |
| { | |
| for (int i = 0; i < SATCOM_MAX; i++) | |
| { | |
| m_infoFilename[i][0] = 0; | |
| } | |
| strcpy(m_infoFilename[SATCOM_OBJECT], "objects.txt"); | |
| } | |
| //! Beginning of the displaying of instructions. | |
| //! index: SATCOM_* | |
| void CRobotMain::StartDisplayInfo(int index, bool movie) | |
| { | |
| if (m_cmdEdit || m_satComLock || m_lockedSatCom) return; | |
| CObject* obj = GetSelect(); | |
| bool human = obj != nullptr && obj->GetType() == OBJECT_HUMAN; | |
| if (!m_editLock && movie && !m_movie->IsExist() && human) | |
| { | |
| assert(obj->Implements(ObjectInterfaceType::Movable)); | |
| if (dynamic_cast<CMovableObject*>(obj)->GetMotion()->GetAction() == -1) | |
| { | |
| m_movieInfoIndex = index; | |
| m_movie->Start(MM_SATCOMopen, 2.5f); | |
| m_satcomMoviePause = m_pause->ActivatePause(PAUSE_ENGINE|PAUSE_HIDE_SHORTCUTS); | |
| m_infoObject = DeselectAll(); // removes the control buttons | |
| m_displayText->HideText(true); | |
| return; | |
| } | |
| } | |
| if (m_movie->IsExist()) | |
| { | |
| m_movie->Stop(); | |
| m_pause->DeactivatePause(m_satcomMoviePause); | |
| m_satcomMoviePause = nullptr; | |
| SelectObject(m_infoObject, false); // hands over the command buttons | |
| m_displayText->HideText(false); | |
| } | |
| StartDisplayInfo(m_infoFilename[index], index); | |
| } | |
| //! Beginning of the displaying of instructions | |
| void CRobotMain::StartDisplayInfo(const std::string& filename, int index) | |
| { | |
| if (m_cmdEdit) return; | |
| m_movieInfoIndex = -1; | |
| ClearInterface(); // removes setting evidence and tooltip | |
| if (!m_editLock) | |
| { | |
| m_infoObject = DeselectAll(); // removes the control buttons | |
| m_displayText->HideText(true); | |
| m_sound->MuteAll(true); | |
| } | |
| bool soluce = m_ui->GetSceneSoluce(); | |
| m_displayInfo = MakeUnique<Ui::CDisplayInfo>(); | |
| m_displayInfo->StartDisplayInfo(filename, index, soluce); | |
| m_displayInfo->SetPosition(0); | |
| } | |
| //! End of displaying of instructions | |
| void CRobotMain::StopDisplayInfo() | |
| { | |
| if (m_cmdEdit) return; | |
| if (m_movieInfoIndex != -1) // film to read the SatCom? | |
| m_movie->Start(MM_SATCOMclose, 2.0f); | |
| m_displayInfo->StopDisplayInfo(); | |
| m_displayInfo.reset(); | |
| if (!m_editLock) | |
| { | |
| SelectObject(m_infoObject, false); // gives the command buttons | |
| m_displayText->HideText(false); | |
| m_sound->MuteAll(false); | |
| } | |
| if (m_infoUsed == 0) | |
| m_displayText->ClearText(); // removes message "see SatCom ..." | |
| m_infoUsed ++; | |
| } | |
| //! Returns the name of the text display | |
| char* CRobotMain::GetDisplayInfoName(int index) | |
| { | |
| return m_infoFilename[index]; | |
| } | |
| //! Beginning of a dialogue during the game | |
| void CRobotMain::StartSuspend() | |
| { | |
| if (m_suspend != nullptr) return; // already suspended | |
| if (!IsPhaseWithWorld(m_phase)) return; | |
| GetLogger()->Info("Start suspend\n"); | |
| m_sound->MuteAll(true); | |
| ClearInterface(); | |
| m_suspend = m_pause->ActivatePause(PAUSE_ENGINE | PAUSE_HIDE_SHORTCUTS | PAUSE_MUTE_SOUND | PAUSE_CAMERA); | |
| m_engine->SetOverFront(false); // over flat behind | |
| CreateShortcuts(); | |
| m_map->ShowMap(false); | |
| m_infoObject = DeselectAll(); // removes the control buttons | |
| m_displayText->HideText(true); | |
| m_engine->EnablePauseBlur(); | |
| } | |
| //! End of dialogue during the game | |
| void CRobotMain::StopSuspend() | |
| { | |
| if (m_suspend == nullptr) return; // not suspended | |
| GetLogger()->Info("Stop suspend\n"); | |
| m_sound->MuteAll(false); | |
| ClearInterface(); | |
| m_pause->DeactivatePause(m_suspend); | |
| m_suspend = nullptr; | |
| m_engine->SetOverFront(true); // over flat front | |
| CreateShortcuts(); | |
| if (m_infoObject != nullptr) | |
| SelectObject(m_infoObject, false); // gives the command buttons | |
| m_map->ShowMap(m_mapShow); | |
| m_displayText->HideText(false); | |
| m_engine->DisablePauseBlur(); | |
| } | |
| //! Returns the absolute time of the game | |
| float CRobotMain::GetGameTime() | |
| { | |
| return m_gameTime; | |
| } | |
| //! Start of the visit instead of an error | |
| void CRobotMain::StartDisplayVisit(EventType event) | |
| { | |
| if (m_editLock) return; | |
| if (m_visitPause) | |
| { | |
| m_pause->DeactivatePause(m_visitPause); | |
| m_visitPause = nullptr; | |
| } | |
| Ui::CWindow* pw = static_cast<Ui::CWindow*>(m_interface->SearchControl(EVENT_WINDOW2)); | |
| if (pw == nullptr) return; | |
| if (event == EVENT_NULL) // visit by keyboard shortcut? | |
| { | |
| int i; | |
| if (m_visitLast != EVENT_NULL) // already a current visit? | |
| i = m_visitLast-EVENT_DT_VISIT0; | |
| else | |
| i = Ui::MAXDTLINE; | |
| // Seeks the last. | |
| for (int j = 0; j < Ui::MAXDTLINE; j++) | |
| { | |
| i --; | |
| if (i < 0) i = Ui::MAXDTLINE-1; | |
| Ui::CButton* button = static_cast<Ui::CButton*>(pw->SearchControl(static_cast<EventType>(EVENT_DT_VISIT0+i))); | |
| if (button == nullptr || !button->TestState(Ui::STATE_ENABLE)) continue; | |
| Ui::CGroup* group = static_cast<Ui::CGroup*>(pw->SearchControl(static_cast<EventType>(EVENT_DT_GROUP0+i))); | |
| if (group != nullptr) | |
| { | |
| event = static_cast<EventType>(EVENT_DT_VISIT0+i); | |
| break; | |
| } | |
| } | |
| } | |
| if (event == EVENT_NULL) | |
| { | |
| m_sound->Play(SOUND_TZOING); // nothing to do! | |
| return; | |
| } | |
| m_visitLast = event; | |
| ClearInterface(); // removes setting evidence and tooltip | |
| if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT) // already a current visit? | |
| { | |
| m_camera->StopVisit(); | |
| m_displayText->ClearVisit(); | |
| } | |
| else | |
| { | |
| m_visitObject = DeselectAll(); // removes the control buttons | |
| } | |
| // Creates the "continue" button. | |
| if (m_interface->SearchControl(EVENT_DT_END) == nullptr) | |
| { | |
| Math::Point pos, dim; | |
| pos.x = 10.0f/640.0f; | |
| pos.y = 10.0f/480.0f; | |
| dim.x = 50.0f/640.0f; | |
| dim.y = 50.0f/480.0f; | |
| m_interface->CreateButton(pos, dim, 16, EVENT_DT_END); | |
| } | |
| // Creates the arrow to show the place. | |
| if (m_visitArrow != nullptr) | |
| { | |
| CObjectManager::GetInstancePointer()->DeleteObject(m_visitArrow); | |
| m_visitArrow = nullptr; | |
| } | |
| ObjectCreateParams params; | |
| params.pos = m_displayText->GetVisitGoal(event); | |
| params.type = OBJECT_SHOW; | |
| params.height = 10.0f; | |
| m_visitArrow = m_objMan->CreateObject(params); | |
| m_visitPos = m_visitArrow->GetPosition(); | |
| m_visitPosArrow = m_visitPos; | |
| m_visitPosArrow.y += m_displayText->GetVisitHeight(event); | |
| m_visitArrow->SetPosition(m_visitPosArrow); | |
| m_visitTime = 0.0; | |
| m_visitParticle = 0.0f; | |
| m_particle->DeleteParticle(Gfx::PARTISHOW); | |
| m_camera->StartVisit(m_displayText->GetVisitGoal(event), | |
| m_displayText->GetVisitDist(event)); | |
| m_displayText->SetVisit(event); | |
| m_visitPause = m_pause->ActivatePause(PAUSE_ENGINE); | |
| } | |
| //! Move the arrow to visit | |
| void CRobotMain::FrameVisit(float rTime) | |
| { | |
| if (m_visitArrow == nullptr) return; | |
| // Moves the arrow. | |
| m_visitTime += rTime; | |
| Math::Vector pos = m_visitPosArrow; | |
| pos.y += 1.5f+sinf(m_visitTime*4.0f)*4.0f; | |
| m_visitArrow->SetPosition(pos); | |
| m_visitArrow->SetRotationY(m_visitTime*2.0f); | |
| // Manages the particles "arrows". | |
| m_visitParticle -= rTime; | |
| if (m_visitParticle <= 0.0f) | |
| { | |
| m_visitParticle = 1.5f; | |
| pos = m_visitPos; | |
| float level = m_terrain->GetFloorLevel(pos)+2.0f; | |
| if (pos.y < level) pos.y = level; // not below the ground | |
| Math::Vector speed(0.0f, 0.0f, 0.0f); | |
| Math::Point dim; | |
| dim.x = 30.0f; | |
| dim.y = dim.x; | |
| m_particle->CreateParticle(pos, speed, dim, Gfx::PARTISHOW, 2.0f); | |
| } | |
| } | |
| //! End of the visit instead of an error | |
| void CRobotMain::StopDisplayVisit() | |
| { | |
| m_visitLast = EVENT_NULL; | |
| // Removes the button. | |
| m_interface->DeleteControl(EVENT_DT_END); | |
| // Removes the arrow. | |
| if (m_visitArrow != nullptr) | |
| { | |
| CObjectManager::GetInstancePointer()->DeleteObject(m_visitArrow); | |
| m_visitArrow = nullptr; | |
| } | |
| // Removes particles "arrows". | |
| m_particle->DeleteParticle(Gfx::PARTISHOW); | |
| m_camera->StopVisit(); | |
| m_displayText->ClearVisit(); | |
| m_pause->DeactivatePause(m_visitPause); | |
| m_visitPause = nullptr; | |
| if (m_visitObject != nullptr) | |
| { | |
| SelectObject(m_visitObject, false); // gives the command buttons | |
| m_visitObject = nullptr; | |
| } | |
| } | |
| void CRobotMain::UpdateShortcuts() | |
| { | |
| m_short->UpdateShortcuts(); | |
| } | |
| CObject* CRobotMain::GetSelectObject() | |
| { | |
| if (m_selectObject != nullptr) return m_selectObject; | |
| return SearchHuman(); | |
| } | |
| CObject* CRobotMain::DeselectAll() | |
| { | |
| CObject* prev = nullptr; | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (!obj->Implements(ObjectInterfaceType::Controllable)) continue; | |
| auto controllableObj = dynamic_cast<CControllableObject*>(obj); | |
| if (controllableObj->GetSelect()) prev = obj; | |
| controllableObj->SetSelect(false); | |
| } | |
| return prev; | |
| } | |
| //! Selects an object, without attending to deselect the rest | |
| void CRobotMain::SelectOneObject(CObject* obj, bool displayError) | |
| { | |
| assert(obj->Implements(ObjectInterfaceType::Controllable)); | |
| dynamic_cast<CControllableObject*>(obj)->SetSelect(true, displayError); | |
| m_camera->SetControllingObject(obj); | |
| ObjectType type = obj->GetType(); | |
| if ( type == OBJECT_HUMAN || | |
| type == OBJECT_MOBILEfa || | |
| type == OBJECT_MOBILEta || | |
| type == OBJECT_MOBILEwa || | |
| type == OBJECT_MOBILEia || | |
| type == OBJECT_MOBILEfc || | |
| type == OBJECT_MOBILEtc || | |
| type == OBJECT_MOBILEwc || | |
| type == OBJECT_MOBILEic || | |
| type == OBJECT_MOBILEfi || | |
| type == OBJECT_MOBILEti || | |
| type == OBJECT_MOBILEwi || | |
| type == OBJECT_MOBILEii || | |
| type == OBJECT_MOBILEfs || | |
| type == OBJECT_MOBILEts || | |
| type == OBJECT_MOBILEws || | |
| type == OBJECT_MOBILEis || | |
| type == OBJECT_MOBILErt || | |
| type == OBJECT_MOBILErc || | |
| type == OBJECT_MOBILErr || | |
| type == OBJECT_MOBILErs || | |
| type == OBJECT_MOBILEsa || | |
| type == OBJECT_MOBILEft || | |
| type == OBJECT_MOBILEtt || | |
| type == OBJECT_MOBILEwt || | |
| type == OBJECT_MOBILEit || | |
| type == OBJECT_MOBILEdr || | |
| type == OBJECT_APOLLO2 ) | |
| { | |
| m_camera->SetType(dynamic_cast<CControllableObject*>(obj)->GetCameraType()); | |
| } | |
| else | |
| { | |
| m_camera->SetType(Gfx::CAM_TYPE_BACK); | |
| } | |
| } | |
| bool CRobotMain::SelectObject(CObject* obj, bool displayError) | |
| { | |
| if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT) | |
| StopDisplayVisit(); | |
| if (m_movieLock || m_editLock) return false; | |
| if (m_movie->IsExist()) return false; | |
| if (obj != nullptr && | |
| (!obj->Implements(ObjectInterfaceType::Controllable) || !(dynamic_cast<CControllableObject*>(obj)->GetSelectable() || m_cheatSelectInsect))) return false; | |
| if (m_missionType == MISSION_CODE_BATTLE && m_codeBattleStarted && m_codeBattleSpectator) | |
| { | |
| DeselectAll(); | |
| // During code battles, only change camera | |
| m_camera->SetControllingObject(obj); | |
| if (obj != nullptr) | |
| { | |
| m_camera->SetType(Gfx::CAM_TYPE_PLANE); | |
| } | |
| else | |
| { | |
| m_camera->SetType(Gfx::CAM_TYPE_FREE); | |
| } | |
| } | |
| else | |
| { | |
| if (obj == nullptr) return false; | |
| CObject* prev = DeselectAll(); | |
| if (prev != nullptr && prev != obj) | |
| PushToSelectionHistory(prev); | |
| SelectOneObject(obj, displayError); | |
| } | |
| m_short->UpdateShortcuts(); | |
| return true; | |
| } | |
| bool CRobotMain::DeselectObject() | |
| { | |
| DeselectAll(); | |
| CObject* obj = PopFromSelectionHistory(); | |
| if (obj == nullptr) | |
| obj = SearchHuman(); | |
| if (obj != nullptr) | |
| SelectOneObject(obj); | |
| else | |
| m_camera->SetType(Gfx::CAM_TYPE_FREE); | |
| m_short->UpdateShortcuts(); | |
| return true; | |
| } | |
| //! Quickly removes all objects | |
| void CRobotMain::DeleteAllObjects() | |
| { | |
| m_engine->GetPyroManager()->DeleteAll(); | |
| // Removes the arrow. | |
| if (m_visitArrow != nullptr) | |
| { | |
| CObjectManager::GetInstancePointer()->DeleteObject(m_visitArrow); | |
| m_visitArrow = nullptr; | |
| } | |
| for (int i = 0; i < MAXSHOWLIMIT; i++) | |
| FlushShowLimit(i); | |
| m_objMan->DeleteAllObjects(); | |
| } | |
| CObject* CRobotMain::SearchHuman() | |
| { | |
| return m_objMan->FindNearest(nullptr, OBJECT_HUMAN); | |
| } | |
| CObject* CRobotMain::GetSelect() | |
| { | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (!obj->Implements(ObjectInterfaceType::Controllable)) continue; | |
| if (dynamic_cast<CControllableObject*>(obj)->GetSelect()) | |
| return obj; | |
| } | |
| return nullptr; | |
| } | |
| //! Detects the object aimed by the mouse | |
| CObject* CRobotMain::DetectObject(Math::Point pos) | |
| { | |
| Math::Vector p; | |
| int objRank = m_engine->DetectObject(pos, p); | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (!obj->GetDetectable()) continue; | |
| CObject* transporter = nullptr; | |
| if (obj->Implements(ObjectInterfaceType::Transportable)) | |
| transporter = dynamic_cast<CTransportableObject*>(obj)->GetTransporter(); | |
| if (transporter != nullptr && !transporter->GetDetectable()) continue; | |
| if (obj->GetProxyActivate()) continue; | |
| CObject* target = obj; | |
| if (obj->Implements(ObjectInterfaceType::PowerContainer) && obj->Implements(ObjectInterfaceType::Transportable)) | |
| { | |
| target = dynamic_cast<CTransportableObject*>(obj)->GetTransporter(); // battery connected | |
| if (target == nullptr) | |
| { | |
| target = obj; // standalone battery | |
| } | |
| else | |
| { | |
| if (!target->Implements(ObjectInterfaceType::Powered) || dynamic_cast<CPoweredObject*>(target)->GetPower() != obj) | |
| { | |
| // transported, but not in the power slot | |
| target = obj; | |
| } | |
| } | |
| } | |
| if (!obj->Implements(ObjectInterfaceType::Old)) continue; | |
| for (int j = 0; j < OBJECTMAXPART; j++) | |
| { | |
| int rank = obj->GetObjectRank(j); | |
| if (rank == -1) continue; | |
| if (rank != objRank) continue; | |
| return target; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| //! Deletes the selected object | |
| bool CRobotMain::DestroySelectedObject() | |
| { | |
| CObject* obj = GetSelect(); | |
| if (obj == nullptr) return false; | |
| assert(obj->Implements(ObjectInterfaceType::Controllable)); | |
| m_engine->GetPyroManager()->Create(Gfx::PT_FRAGT, obj); | |
| dynamic_cast<CControllableObject*>(obj)->SetSelect(false); // deselects the object | |
| m_camera->SetType(Gfx::CAM_TYPE_EXPLO); | |
| DeselectAll(); | |
| RemoveFromSelectionHistory(obj); | |
| return true; | |
| } | |
| //! Removes setting evidence of the object with the mouse hovers over | |
| void CRobotMain::HiliteClear() | |
| { | |
| ClearTooltip(); | |
| m_tooltipName.clear(); // really removes the tooltip | |
| if (!m_hilite) return; | |
| int rank = -1; | |
| m_engine->SetHighlightRank(&rank); // nothing more selected | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (!obj->Implements(ObjectInterfaceType::Controllable)) continue; | |
| dynamic_cast<CControllableObject*>(obj)->SetHighlight(false); | |
| } | |
| m_map->SetHighlight(nullptr); | |
| m_short->SetHighlight(nullptr); | |
| m_hilite = false; | |
| } | |
| //! Highlights the object with the mouse hovers over | |
| void CRobotMain::HiliteObject(Math::Point pos) | |
| { | |
| if (m_fixScene && m_phase != PHASE_APPERANCE) return; | |
| if (m_movieLock) return; | |
| if (m_movie->IsExist()) return; | |
| if (m_app->GetMouseMode() == MOUSE_NONE) return; | |
| ClearInterface(); // removes setting evidence and tooltip | |
| CObject* obj = m_short->DetectShort(pos); | |
| std::string interfaceTooltipName; | |
| if (m_settings->GetTooltips() && m_interface->GetTooltip(pos, interfaceTooltipName)) | |
| { | |
| m_tooltipPos = pos; | |
| m_tooltipName = interfaceTooltipName; | |
| m_tooltipTime = 0.0f; | |
| if (obj == nullptr) return; | |
| } | |
| if (m_suspend != nullptr) return; | |
| if (obj == nullptr) | |
| { | |
| bool inMap = false; | |
| obj = m_map->DetectMap(pos, inMap); | |
| if (obj == nullptr) | |
| { | |
| if (inMap) return; | |
| obj = DetectObject(pos); | |
| if ((m_camera->GetType() == Gfx::CAM_TYPE_ONBOARD) && | |
| (m_camera->GetControllingObject() == obj)) | |
| return; | |
| } | |
| } | |
| if (obj != nullptr) | |
| { | |
| if (m_settings->GetTooltips()) | |
| { | |
| std::string objectTooltipName = obj->GetTooltipText(); | |
| if (!objectTooltipName.empty()) | |
| { | |
| m_tooltipPos = pos; | |
| m_tooltipName = objectTooltipName; | |
| m_tooltipTime = 0.0f; | |
| } | |
| } | |
| if (obj->Implements(ObjectInterfaceType::Controllable) && (dynamic_cast<CControllableObject*>(obj)->GetSelectable() || m_cheatSelectInsect)) | |
| { | |
| if (dynamic_cast<CControllableObject*>(obj)->GetSelectable()) | |
| { | |
| // Don't highlight objects that would not be selectable without selectinsect | |
| dynamic_cast<CControllableObject*>(obj)->SetHighlight(true); | |
| } | |
| m_map->SetHighlight(obj); | |
| m_short->SetHighlight(obj); | |
| m_hilite = true; | |
| } | |
| } | |
| } | |
| //! Highlights the object with the mouse hovers over | |
| void CRobotMain::HiliteFrame(float rTime) | |
| { | |
| if (m_fixScene && m_phase != PHASE_APPERANCE) return; | |
| if (m_movieLock) return; | |
| if (m_movie->IsExist()) return; | |
| m_tooltipTime += rTime; | |
| ClearTooltip(); | |
| if (m_tooltipTime >= 0.2f && !m_tooltipName.empty()) | |
| { | |
| CreateTooltip(m_tooltipPos, m_tooltipName); | |
| } | |
| } | |
| //! Creates a tooltip | |
| void CRobotMain::CreateTooltip(Math::Point pos, const std::string& text) | |
| { | |
| Math::Point corner; | |
| corner.x = pos.x+0.022f; | |
| corner.y = pos.y-0.052f; | |
| Math::Point start, end; | |
| m_engine->GetText()->SizeText(text, Gfx::FONT_COMMON, Gfx::FONT_SIZE_SMALL, | |
| corner, Gfx::TEXT_ALIGN_LEFT, | |
| start, end); | |
| start.x -= 0.010f; | |
| start.y -= 0.006f; | |
| end.x += 0.010f; | |
| end.y += 0.008f; // small'ish margin | |
| pos.x = start.x; | |
| pos.y = start.y; | |
| Math::Point dim; | |
| dim.x = end.x-start.x; | |
| dim.y = end.y-start.y; | |
| Math::Point offset; | |
| offset.x = 0.0f; | |
| offset.y = 0.0f; | |
| if (pos.x+dim.x > 1.0f) offset.x = 1.0f-(pos.x+dim.x); | |
| if (pos.y < 0.0f) offset.y = -pos.y; | |
| corner.x += offset.x; | |
| corner.y += offset.y; | |
| pos.x += offset.x; | |
| pos.y += offset.y; | |
| m_interface->CreateWindows(pos, dim, 1, EVENT_TOOLTIP); | |
| Ui::CWindow* pw = static_cast<Ui::CWindow*>(m_interface->SearchControl(EVENT_TOOLTIP)); | |
| if (pw != nullptr) | |
| { | |
| pw->SetState(Ui::STATE_SHADOW); | |
| pw->SetTrashEvent(false); | |
| pos.y -= m_engine->GetText()->GetHeight(Gfx::FONT_COMMON, Gfx::FONT_SIZE_SMALL) / 2.0f; | |
| pw->CreateLabel(pos, dim, -1, EVENT_LABEL2, text); | |
| } | |
| } | |
| //! Clears the previous tooltip | |
| void CRobotMain::ClearTooltip() | |
| { | |
| m_interface->DeleteControl(EVENT_TOOLTIP); | |
| } | |
| //! Displays help for an object | |
| void CRobotMain::HelpObject() | |
| { | |
| CObject* obj = GetSelect(); | |
| if (obj == nullptr) return; | |
| std::string filename = GetHelpFilename(obj->GetType()); | |
| if (filename.empty()) return; | |
| StartDisplayInfo(filename, -1); | |
| } | |
| //! Change the mode of the camera | |
| void CRobotMain::ChangeCamera() | |
| { | |
| CObject* obj = GetSelect(); | |
| if (obj == nullptr) return; | |
| assert(obj->Implements(ObjectInterfaceType::Controllable)); | |
| auto controllableObj = dynamic_cast<CControllableObject*>(obj); | |
| if (controllableObj->GetCameraLock()) return; | |
| ObjectType oType = obj->GetType(); | |
| Gfx::CameraType type = controllableObj->GetCameraType(); | |
| if ( oType != OBJECT_HUMAN && | |
| oType != OBJECT_TECH && | |
| oType != OBJECT_MOBILEfa && | |
| oType != OBJECT_MOBILEta && | |
| oType != OBJECT_MOBILEwa && | |
| oType != OBJECT_MOBILEia && | |
| oType != OBJECT_MOBILEfc && | |
| oType != OBJECT_MOBILEtc && | |
| oType != OBJECT_MOBILEwc && | |
| oType != OBJECT_MOBILEic && | |
| oType != OBJECT_MOBILEfi && | |
| oType != OBJECT_MOBILEti && | |
| oType != OBJECT_MOBILEwi && | |
| oType != OBJECT_MOBILEii && | |
| oType != OBJECT_MOBILEfs && | |
| oType != OBJECT_MOBILEts && | |
| oType != OBJECT_MOBILEws && | |
| oType != OBJECT_MOBILEis && | |
| oType != OBJECT_MOBILErt && | |
| oType != OBJECT_MOBILErc && | |
| oType != OBJECT_MOBILErr && | |
| oType != OBJECT_MOBILErs && | |
| oType != OBJECT_MOBILEsa && | |
| oType != OBJECT_MOBILEtg && | |
| oType != OBJECT_MOBILEft && | |
| oType != OBJECT_MOBILEtt && | |
| oType != OBJECT_MOBILEwt && | |
| oType != OBJECT_MOBILEit && | |
| oType != OBJECT_MOBILEdr && | |
| oType != OBJECT_APOLLO2 ) return; | |
| if (oType == OBJECT_MOBILEdr) // designer? | |
| { | |
| if (type == Gfx::CAM_TYPE_PLANE ) type = Gfx::CAM_TYPE_BACK; | |
| else if (type == Gfx::CAM_TYPE_BACK ) type = Gfx::CAM_TYPE_PLANE; | |
| } | |
| else if (controllableObj->GetTrainer()) // trainer? | |
| { | |
| if (type == Gfx::CAM_TYPE_ONBOARD) type = Gfx::CAM_TYPE_FIX; | |
| else if (type == Gfx::CAM_TYPE_FIX ) type = Gfx::CAM_TYPE_PLANE; | |
| else if (type == Gfx::CAM_TYPE_PLANE ) type = Gfx::CAM_TYPE_BACK; | |
| else if (type == Gfx::CAM_TYPE_BACK ) type = Gfx::CAM_TYPE_ONBOARD; | |
| } | |
| else | |
| { | |
| if (type == Gfx::CAM_TYPE_ONBOARD) type = Gfx::CAM_TYPE_BACK; | |
| else if (type == Gfx::CAM_TYPE_BACK ) type = Gfx::CAM_TYPE_ONBOARD; | |
| } | |
| controllableObj->SetCameraType(type); | |
| m_camera->SetType(type); | |
| } | |
| //! Cancels the current movie | |
| void CRobotMain::AbortMovie() | |
| { | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (obj->Implements(ObjectInterfaceType::Old)) | |
| { | |
| CAuto* automat = obj->GetAuto(); | |
| if (automat != nullptr) | |
| automat->Abort(); | |
| } | |
| } | |
| } | |
| static std::string TimeFormat(float time) | |
| { | |
| int minutes = static_cast<int>(floor(time/60)); | |
| double time2 = fmod(time, 60); | |
| double seconds; | |
| double fraction = modf(time2, &seconds)*100; | |
| std::ostringstream sstream; | |
| sstream << std::setfill('0') << std::setw(2) << minutes << ":" << std::setfill('0') << std::setw(2) << floor(seconds) << "." << std::setfill('0') << std::setw(2) << floor(fraction); | |
| return sstream.str(); | |
| } | |
| //! Updates the text information | |
| void CRobotMain::UpdateInfoText() | |
| { | |
| if (m_phase == PHASE_SIMUL) | |
| { | |
| CObject* obj = GetSelect(); | |
| if (obj != nullptr) | |
| { | |
| Math::Vector pos = obj->GetPosition(); | |
| m_engine->SetStatisticPos(pos / g_unit); | |
| } | |
| } | |
| m_engine->SetTimerDisplay(m_missionTimerEnabled && m_missionTimerStarted ? TimeFormat(m_missionTimer) : ""); | |
| } | |
| //! Initializes the view | |
| void CRobotMain::InitEye() | |
| { | |
| if (m_phase == PHASE_SIMUL) | |
| m_camera->Init(Math::Vector( 0.0f, 10.0f, 0.0f), | |
| Math::Vector(10.0f, 5.0f, 0.0f), 0.0f); | |
| } | |
| //! Advances the entire scene | |
| bool CRobotMain::EventFrame(const Event &event) | |
| { | |
| m_time += event.rTime; | |
| m_water->EventProcess(event); | |
| m_cloud->EventProcess(event); | |
| m_lightning->EventProcess(event); | |
| m_planet->EventProcess(event); | |
| UpdateDebugCrashSpheres(); | |
| Ui::CMap* pm = nullptr; | |
| Ui::CWindow* pw = static_cast<Ui::CWindow*>(m_interface->SearchControl(EVENT_WINDOW1)); | |
| if (pw == nullptr) | |
| { | |
| pm = nullptr; | |
| } | |
| else | |
| { | |
| pm = static_cast<Ui::CMap*>(pw->SearchControl(EVENT_OBJECT_MAP)); | |
| if (pm != nullptr) pm->FlushObject(); | |
| } | |
| CObject* toto = nullptr; | |
| if (!m_pause->IsPauseType(PAUSE_OBJECT_UPDATES)) | |
| { | |
| // Advances all the robots, but not toto. | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (pm != nullptr) | |
| pm->UpdateObject(obj); | |
| if (IsObjectBeingTransported(obj)) | |
| continue; | |
| if (obj->GetType() == OBJECT_TOTO) | |
| toto = obj; | |
| else if (obj->Implements(ObjectInterfaceType::Interactive)) | |
| dynamic_cast<CInteractiveObject*>(obj)->EventProcess(event); | |
| if ( obj->GetProxyActivate() ) // active if it is near? | |
| { | |
| Math::Vector eye = m_engine->GetLookatPt(); | |
| float dist = Math::Distance(eye, obj->GetPosition()); | |
| if ( dist < obj->GetProxyDistance() ) | |
| { | |
| obj->SetProxyActivate(false); | |
| CreateShortcuts(); | |
| m_sound->Play(SOUND_FINDING); | |
| m_engine->GetPyroManager()->Create(Gfx::PT_FINDING, obj, 0.0f); | |
| DisplayError(INFO_FINDING, obj); | |
| } | |
| } | |
| } | |
| // Advances all objects transported by robots. | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (! IsObjectBeingTransported(obj)) | |
| continue; | |
| if (obj->Implements(ObjectInterfaceType::Interactive)) | |
| dynamic_cast<CInteractiveObject*>(obj)->EventProcess(event); | |
| } | |
| m_engine->GetPyroManager()->EventProcess(event); | |
| } | |
| // The camera follows the object, because its position | |
| // may depend on the selected object (Gfx::CAM_TYPE_ONBOARD or Gfx::CAM_TYPE_BACK). | |
| if (m_phase == PHASE_SIMUL && !m_editFull) | |
| { | |
| m_camera->EventProcess(event); | |
| if (m_engine->GetFog()) | |
| m_camera->SetOverBaseColor(m_particle->GetFogColor(m_engine->GetEyePt())); | |
| } | |
| if (m_phase == PHASE_APPERANCE || | |
| m_phase == PHASE_WIN || | |
| m_phase == PHASE_LOST) | |
| { | |
| m_camera->EventProcess(event); | |
| } | |
| // Advances toto following the camera, because its position depends on the camera. | |
| if (toto != nullptr) | |
| dynamic_cast<CInteractiveObject*>(toto)->EventProcess(event); | |
| // NOTE: m_movieLock is set only after the first update of CAutoBase finishes | |
| if (m_phase == PHASE_SIMUL) | |
| { | |
| if (!m_immediatSatCom && !m_beginSatCom && !m_movieLock) | |
| { | |
| m_displayText->DisplayError(INFO_BEGINSATCOM, Math::Vector(0.0f, 0.0f, 0.0f)); | |
| m_beginSatCom = true; // message appears | |
| } | |
| if (!m_pause->IsPauseType(PAUSE_ENGINE) && !m_movieLock) | |
| { | |
| m_gameTime += event.rTime; | |
| m_gameTimeAbsolute += m_app->GetRealRelTime() / 1e9f; | |
| if (m_missionTimerStarted) | |
| m_missionTimer += event.rTime; | |
| if (m_autosave && m_gameTimeAbsolute >= m_autosaveLast + (m_autosaveInterval * 60)) | |
| { | |
| if (m_levelCategory == LevelCategory::Missions || | |
| m_levelCategory == LevelCategory::FreeGame || | |
| m_levelCategory == LevelCategory::CustomLevels) | |
| { | |
| if (!IOIsBusy() && m_missionType != MISSION_CODE_BATTLE) | |
| { | |
| m_autosaveLast = m_gameTimeAbsolute; | |
| Autosave(); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| HiliteFrame(event.rTime); | |
| // Moves the film indicator. | |
| if (m_movieLock && !m_editLock) // movie in progress? | |
| { | |
| Ui::CControl* pc = m_interface->SearchControl(EVENT_OBJECT_MOVIELOCK); | |
| if (pc != nullptr) | |
| { | |
| Math::Point pos, dim; | |
| dim.x = 32.0f/640.0f; | |
| dim.y = 32.0f/480.0f; | |
| pos.x = 20.0f/640.0f; | |
| pos.y = (480.0f-24.0f)/480.0f; | |
| float zoom = 1.0f+sinf(m_time*6.0f)*0.1f; // 0.9 .. 1.1 | |
| dim.x *= zoom; | |
| dim.y *= zoom; | |
| pos.x -= dim.x/2.0f; | |
| pos.y -= dim.y/2.0f; | |
| pc->SetPos(pos); | |
| pc->SetDim(dim); | |
| } | |
| } | |
| // Moves edition indicator. | |
| if (m_editLock || m_pause->IsPauseType(PAUSE_ENGINE)) // edition in progress? | |
| { | |
| Ui::CControl* pc = m_interface->SearchControl(EVENT_OBJECT_EDITLOCK); | |
| if (pc != nullptr) | |
| { | |
| Math::Point pos, dim; | |
| if (m_editFull || m_editLock) | |
| { | |
| dim.x = 10.0f/640.0f; | |
| dim.y = 10.0f/480.0f; | |
| pos.x = -20.0f/640.0f; | |
| pos.y = -20.0f/480.0f; // invisible! | |
| } | |
| else | |
| { | |
| dim.x = 32.0f/640.0f; | |
| dim.y = 32.0f/480.0f; | |
| pos.x = (640.0f-24.0f)/640.0f; | |
| pos.y = (480.0f-24.0f)/480.0f; | |
| float zoom = 1.0f+sinf(m_time*6.0f)*0.1f; // 0.9 .. 1.1 | |
| dim.x *= zoom; | |
| dim.y *= zoom; | |
| pos.x -= dim.x/2.0f; | |
| pos.y -= dim.y/2.0f; | |
| } | |
| pc->SetPos(pos); | |
| pc->SetDim(dim); | |
| } | |
| } | |
| Ui::CControl* pc = m_interface->SearchControl(EVENT_OBJECT_SAVING); | |
| if (pc != nullptr) | |
| { | |
| Math::Point pos, dim; | |
| if (m_shotSaving <= 0) | |
| { | |
| dim.x = 10.0f/640.0f; | |
| dim.y = 10.0f/480.0f; | |
| pos.x = -20.0f/640.0f; | |
| pos.y = -20.0f/480.0f; // invisible! | |
| } | |
| else | |
| { | |
| dim.x = 32.0f/640.0f; | |
| dim.y = 32.0f/480.0f; | |
| pos.x = (640.0f-24.0f)/640.0f; | |
| pos.y = (480.0f-24.0f)/480.0f; | |
| float zoom = 1.0f+sinf(m_time*6.0f)*0.1f; // 0.9 .. 1.1 | |
| dim.x *= zoom; | |
| dim.y *= zoom; | |
| pos.x -= dim.x/2.0f; | |
| pos.y -= dim.y/2.0f; | |
| } | |
| pc->SetPos(pos); | |
| pc->SetDim(dim); | |
| } | |
| // Will move the arrow to visit. | |
| if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT) | |
| FrameVisit(event.rTime); | |
| // Moves the boundaries. | |
| FrameShowLimit(event.rTime); | |
| if (m_phase == PHASE_SIMUL) | |
| { | |
| if (!m_editLock && !m_engine->GetPause()) | |
| { | |
| CheckEndMission(true); | |
| UpdateAudio(true); | |
| if (m_scoreboard) | |
| m_scoreboard->UpdateObjectCount(); | |
| } | |
| if (m_winDelay > 0.0f && !m_editLock) | |
| { | |
| m_winDelay -= event.rTime; | |
| if (m_winDelay <= 0.0f) | |
| { | |
| if (m_movieLock) | |
| m_winDelay = 1.0f; | |
| else | |
| m_eventQueue->AddEvent(Event(EVENT_WIN)); | |
| } | |
| } | |
| if (m_lostDelay > 0.0f && !m_editLock) | |
| { | |
| m_lostDelay -= event.rTime; | |
| if (m_lostDelay <= 0.0f) | |
| { | |
| if (m_movieLock) | |
| m_lostDelay = 1.0f; | |
| else | |
| m_eventQueue->AddEvent(Event(EVENT_LOST)); | |
| } | |
| } | |
| if (GetMissionType() == MISSION_CODE_BATTLE) | |
| { | |
| if (!m_codeBattleInit) | |
| { | |
| // NOTE: It's important to do this AFTER the first update event finished processing | |
| // because otherwise all robot parts are misplaced | |
| m_userPause = m_pause->ActivatePause(PAUSE_ENGINE); | |
| m_codeBattleInit = true; // Will start on resume | |
| } | |
| if (!m_codeBattleStarted && m_userPause == nullptr) | |
| { | |
| m_codeBattleStarted = true; | |
| ApplyCodeBattleInterface(); | |
| CreateCodeBattleInterface(); | |
| SetCodeBattleSpectatorMode(true); | |
| m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE)); | |
| } | |
| UpdateCodeBattleInterface(); | |
| } | |
| } | |
| return true; | |
| } | |
| void CRobotMain::ShowSaveIndicator(bool show) | |
| { | |
| Ui::CControl* pc = m_interface->SearchControl(EVENT_OBJECT_SAVING); | |
| if (pc != nullptr) | |
| { | |
| Math::Point pos, dim; | |
| if (!show) | |
| { | |
| dim.x = 10.0f/640.0f; | |
| dim.y = 10.0f/480.0f; | |
| pos.x = -20.0f/640.0f; | |
| pos.y = -20.0f/480.0f; // invisible! | |
| } | |
| else | |
| { | |
| dim.x = 32.0f/640.0f; | |
| dim.y = 32.0f/480.0f; | |
| pos.x = (640.0f-24.0f)/640.0f; | |
| pos.y = (480.0f-24.0f)/480.0f; | |
| pos.x -= dim.x/2.0f; | |
| pos.y -= dim.y/2.0f; | |
| } | |
| pc->SetPos(pos); | |
| pc->SetDim(dim); | |
| } | |
| } | |
| //! Makes the event for all robots | |
| bool CRobotMain::EventObject(const Event &event) | |
| { | |
| if (m_pause->IsPauseType(PAUSE_OBJECT_UPDATES)) return true; | |
| m_resetCreate = false; | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (obj->Implements(ObjectInterfaceType::Interactive)) | |
| { | |
| dynamic_cast<CInteractiveObject*>(obj)->EventProcess(event); | |
| } | |
| } | |
| if (m_resetCreate) | |
| ResetCreate(); | |
| return true; | |
| } | |
| void CRobotMain::ScenePerso() | |
| { | |
| DeleteAllObjects(); // removes all the current 3D Scene | |
| m_terrain->FlushRelief(); | |
| m_engine->DeleteAllObjects(); | |
| m_oldModelManager->DeleteAllModelCopies(); | |
| m_terrain->FlushBuildingLevel(); | |
| m_terrain->FlushFlyingLimit(); | |
| m_lightMan->FlushLights(); | |
| m_particle->FlushParticle(); | |
| m_levelFile = "levels/other/perso.txt"; | |
| try | |
| { | |
| CreateScene(false, true, false); // sets scene | |
| } | |
| catch (const std::runtime_error& e) | |
| { | |
| LevelLoadingError("An error occurred while trying to load apperance scene", e, PHASE_PLAYER_SELECT); | |
| } | |
| m_engine->SetDrawWorld(false); // does not draw anything on the interface | |
| m_engine->SetDrawFront(true); // draws on the human interface | |
| CObject* obj = SearchHuman(); | |
| if (obj != nullptr) | |
| { | |
| obj->SetDrawFront(true); // draws the interface | |
| assert(obj->Implements(ObjectInterfaceType::Movable)); | |
| CMotionHuman* mh = static_cast<CMotionHuman*>(dynamic_cast<CMovableObject*>(obj)->GetMotion()); | |
| mh->StartDisplayPerso(); | |
| } | |
| } | |
| //! Creates the whole scene | |
| void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject) | |
| { | |
| m_fixScene = fixScene; | |
| m_base = nullptr; | |
| if (!resetObject) | |
| { | |
| m_build = 0; | |
| m_researchDone.clear(); // no research done | |
| m_researchDone[0] = 0; | |
| m_researchEnable = 0; | |
| g_unit = UNIT; | |
| FlushDisplayInfo(); | |
| m_terrain->FlushMaterials(); | |
| m_audioTrack = ""; | |
| m_audioRepeat = true; | |
| m_satcomTrack = ""; | |
| m_satcomRepeat = true; | |
| m_editorTrack = ""; | |
| m_editorRepeat = true; | |
| m_displayText->SetDelay(1.0f); | |
| m_displayText->SetEnable(true); | |
| m_immediatSatCom = false; | |
| m_lockedSatCom = false; | |
| m_endingWin = ""; | |
| m_endingLost = ""; | |
| m_audioChange.clear(); | |
| m_endTake.clear(); | |
| m_endTakeImmediat = false; | |
| m_endTakeResearch = 0; | |
| m_endTakeTimeout = -1.0f; | |
| m_endTakeTeamImmediateWin = false; | |
| m_endTakeWinDelay = 2.0f; | |
| m_endTakeLostDelay = 2.0f; | |
| m_teamFinished.clear(); | |
| m_scoreboard.reset(); | |
| m_globalMagnifyDamage = 1.0f; | |
| m_obligatoryTokens.clear(); | |
| m_mapShow = true; | |
| m_mapImage = false; | |
| m_mapFilename[0] = 0; | |
| m_controller = nullptr; | |
| m_colorNewBot.clear(); | |
| m_colorNewBot[0] = COLOR_REF_BOT; | |
| m_colorNewAlien = COLOR_REF_ALIEN; | |
| m_colorNewGreen = COLOR_REF_GREEN; | |
| m_colorNewWater = COLOR_REF_WATER; | |
| m_engine->SetAmbientColor(Gfx::Color(0.5f, 0.5f, 0.5f, 0.5f), 0); | |
| m_engine->SetAmbientColor(Gfx::Color(0.5f, 0.5f, 0.5f, 0.5f), 1); | |
| m_engine->SetFogColor(Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f), 0); | |
| m_engine->SetFogColor(Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f), 1); | |
| m_engine->SetDeepView(1000.0f, 0); | |
| m_engine->SetDeepView(1000.0f, 1); | |
| m_engine->SetFogStart(0.75f, 0); | |
| m_engine->SetFogStart(0.75f, 1); | |
| m_engine->SetSecondTexture(""); | |
| m_engine->SetForegroundName(""); | |
| GetResource(RES_TEXT, RT_SCRIPT_NEW, m_scriptName); | |
| m_scriptFile = ""; | |
| m_missionType = MISSION_NORMAL; | |
| m_codeBattleInit = false; | |
| m_codeBattleStarted = false; | |
| m_teamNames.clear(); | |
| m_missionResult = ERR_MISSION_NOTERM; | |
| m_missionResultFromScript = false; | |
| } | |
| // NOTE: Reset timer always, even when only resetting object positions | |
| m_missionTimerEnabled = false; | |
| m_missionTimerStarted = false; | |
| m_missionTimer = 0.0f; | |
| std::string backgroundPath = ""; | |
| Gfx::Color backgroundUp = Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f); | |
| Gfx::Color backgroundDown = Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f); | |
| Gfx::Color backgroundCloudUp = Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f); | |
| Gfx::Color backgroundCloudDown = Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f); | |
| bool backgroundFull = false; | |
| auto LoadingWarning = [&](const std::string& message) | |
| { | |
| GetLogger()->Warn("%s\n", message.c_str()); | |
| m_ui->GetDialog()->StartInformation("Level loading warning", "This level contains problems. It may stop working in future versions of the game.", message); | |
| }; | |
| try | |
| { | |
| m_ui->GetLoadingScreen()->SetProgress(0.05f, RT_LOADING_PROCESSING); | |
| GetLogger()->Info("Loading level: %s\n", m_levelFile.c_str()); | |
| CLevelParser levelParser(m_levelFile); | |
| levelParser.SetLevelPaths(m_levelCategory, m_levelChap, m_levelRank); | |
| levelParser.Load(); | |
| int numObjects = levelParser.CountLines("CreateObject"); | |
| m_ui->GetLoadingScreen()->SetProgress(0.1f, RT_LOADING_LEVEL_SETTINGS); | |
| int rankObj = 0; | |
| CObject* sel = nullptr; | |
| for (auto& line : levelParser.GetLines()) | |
| { | |
| if (line->GetCommand() == "Title" && !resetObject) | |
| { | |
| //strcpy(m_title, line->GetParam("text")->AsString().c_str()); | |
| continue; | |
| } | |
| if (line->GetCommand() == "Resume" && !resetObject) | |
| { | |
| //strcpy(m_resume, line->GetParam("text")->AsString().c_str()); | |
| continue; | |
| } | |
| if (line->GetCommand() == "ScriptName" && !resetObject) | |
| { | |
| m_scriptName = line->GetParam("text")->AsString(); | |
| continue; | |
| } | |
| if (line->GetCommand() == "ScriptFile" && !resetObject) | |
| { | |
| m_scriptFile = line->GetParam("name")->AsString(); | |
| continue; | |
| } | |
| if (line->GetCommand() == "Instructions" && !resetObject) | |
| { | |
| strcpy(m_infoFilename[SATCOM_HUSTON], line->GetParam("name")->AsPath("help/%lng%").c_str()); | |
| m_immediatSatCom = line->GetParam("immediat")->AsBool(false); | |
| m_beginSatCom = m_lockedSatCom = line->GetParam("lock")->AsBool(false); | |
| if (m_app->GetSceneTestMode()) m_immediatSatCom = false; | |
| continue; | |
| } | |
| if (line->GetCommand() == "Satellite" && !resetObject) | |
| { | |
| strcpy(m_infoFilename[SATCOM_SAT], line->GetParam("name")->AsPath("help/%lng%").c_str()); | |
| continue; | |
| } | |
| if (line->GetCommand() == "Loading" && !resetObject) | |
| { | |
| strcpy(m_infoFilename[SATCOM_LOADING], line->GetParam("name")->AsPath("help/%lng%").c_str()); | |
| continue; | |
| } | |
| if (line->GetCommand() == "HelpFile" && !resetObject) | |
| { | |
| strcpy(m_infoFilename[SATCOM_PROG], line->GetParam("name")->AsPath("help/%lng%").c_str()); | |
| continue; | |
| } | |
| if (line->GetCommand() == "SoluceFile" && !resetObject) | |
| { | |
| strcpy(m_infoFilename[SATCOM_SOLUCE], line->GetParam("name")->AsPath("help/%lng%").c_str()); | |
| continue; | |
| } | |
| if (line->GetCommand() == "EndingFile" && !resetObject) | |
| { | |
| auto Process = [&](const std::string& type) -> std::string | |
| { | |
| if (line->GetParam(type)->IsDefined()) | |
| { | |
| try | |
| { | |
| int rank = boost::lexical_cast<int>(line->GetParam(type)->GetValue()); | |
| if (rank >= 0) | |
| { | |
| // TODO: Fix default levels and add a future removal warning | |
| 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); | |
| std::stringstream ss; | |
| ss << "levels/other/" << type << std::setfill('0') << std::setw(3) << rank << ".txt"; | |
| return ss.str(); | |
| } | |
| else | |
| { | |
| // TODO: Fix default levels and add a future removal warning | |
| 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()); | |
| return ""; | |
| } | |
| } | |
| catch (boost::bad_lexical_cast &e) | |
| { | |
| return line->GetParam(type)->AsPath("levels"); | |
| } | |
| } | |
| return ""; | |
| }; | |
| m_endingWin = Process("win"); | |
| m_endingLost = Process("lost"); | |
| continue; | |
| } | |
| if (line->GetCommand() == "MessageDelay" && !resetObject) | |
| { | |
| m_displayText->SetDelay(line->GetParam("factor")->AsFloat()); | |
| continue; | |
| } | |
| if (line->GetCommand() == "MissionTimer") | |
| { | |
| m_missionTimerEnabled = line->GetParam("enabled")->AsBool(); | |
| if (!line->GetParam("program")->AsBool(false)) | |
| { | |
| m_missionTimerStarted = true; | |
| } | |
| continue; | |
| } | |
| if (line->GetCommand() == "TeamName") | |
| { | |
| int team = line->GetParam("team")->AsInt(); | |
| std::string name = line->GetParam("name")->AsString(); | |
| m_teamNames[team] = name; | |
| continue; | |
| } | |
| if (line->GetCommand() == "CacheAudio" && !resetObject) | |
| { | |
| std::string filename = line->GetParam("filename")->AsPath("music"); | |
| m_ui->GetLoadingScreen()->SetProgress(0.15f, RT_LOADING_MUSIC, filename); | |
| m_sound->CacheMusic(filename); | |
| continue; | |
| } | |
| if (line->GetCommand() == "AudioChange" && !resetObject) | |
| { | |
| auto audioChange = MakeUnique<CAudioChangeCondition>(); | |
| audioChange->Read(line.get()); | |
| m_ui->GetLoadingScreen()->SetProgress(0.15f, RT_LOADING_MUSIC, audioChange->music); | |
| m_sound->CacheMusic(audioChange->music); | |
| m_audioChange.push_back(std::move(audioChange)); | |
| if (!line->GetParam("pos")->IsDefined() || !line->GetParam("dist")->IsDefined()) | |
| { | |
| LoadingWarning("The defaults for pos= and dist= are going to change, specify them explicitly. See issue #759 (https://git.io/vVBzH)"); | |
| } | |
| continue; | |
| } | |
| if (line->GetCommand() == "Audio" && !resetObject) | |
| { | |
| if (line->GetParam("track")->IsDefined()) | |
| { | |
| if (line->GetParam("filename")->IsDefined()) | |
| throw CLevelParserException("You can't use track and filename at the same time"); | |
| GetLogger()->Warn("Using track= is deprecated. Please replace this with filename=\n"); | |
| int trackid = line->GetParam("track")->AsInt(); | |
| if (trackid != 0) | |
| { | |
| std::stringstream filenameStr; | |
| filenameStr << "music/music" << std::setfill('0') << std::setw(3) << trackid << ".ogg"; | |
| m_audioTrack = filenameStr.str(); | |
| } | |
| else | |
| { | |
| m_audioTrack = ""; | |
| } | |
| } | |
| else | |
| { | |
| if (line->GetParam("filename")->IsDefined()) | |
| { | |
| m_audioTrack = line->GetParam("filename")->AsPath("music"); | |
| } | |
| else | |
| { | |
| m_audioTrack = ""; | |
| } | |
| } | |
| if (!m_audioTrack.empty()) | |
| { | |
| m_audioRepeat = line->GetParam("repeat")->AsBool(true); | |
| } | |
| if (line->GetParam("satcom")->IsDefined()) | |
| { | |
| m_satcomTrack = line->GetParam("satcom")->AsPath("music"); | |
| m_satcomRepeat = line->GetParam("satcomRepeat")->AsBool(true); | |
| } | |
| else | |
| { | |
| m_satcomTrack = ""; | |
| } | |
| if (line->GetParam("editor")->IsDefined()) | |
| { | |
| m_editorTrack = line->GetParam("editor")->AsPath("music"); | |
| m_editorRepeat = line->GetParam("editorRepeat")->AsBool(true); | |
| } | |
| else | |
| { | |
| m_editorTrack = ""; | |
| } | |
| if (!m_audioTrack.empty()) | |
| { | |
| m_ui->GetLoadingScreen()->SetProgress(0.15f, RT_LOADING_MUSIC, m_audioTrack); | |
| m_sound->CacheMusic(m_audioTrack); | |
| } | |
| if (!m_satcomTrack.empty()) | |
| { | |
| m_ui->GetLoadingScreen()->SetProgress(0.15f, RT_LOADING_MUSIC, m_satcomTrack); | |
| m_sound->CacheMusic(m_satcomTrack); | |
| } | |
| if (!m_editorTrack.empty()) | |
| { | |
| m_ui->GetLoadingScreen()->SetProgress(0.15f, RT_LOADING_MUSIC, m_editorTrack); | |
| m_sound->CacheMusic(m_editorTrack); | |
| } | |
| continue; | |
| } | |
| if (line->GetCommand() == "AmbientColor" && !resetObject) | |
| { | |
| m_engine->SetAmbientColor(line->GetParam("air")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)), 0); | |
| m_engine->SetAmbientColor(line->GetParam("water")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)), 1); | |
| continue; | |
| } | |
| if (line->GetCommand() == "FogColor" && !resetObject) | |
| { | |
| m_engine->SetFogColor(line->GetParam("air")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)), 0); | |
| m_engine->SetFogColor(line->GetParam("water")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)), 1); | |
| continue; | |
| } | |
| if (line->GetCommand() == "VehicleColor" && !resetObject) | |
| { | |
| m_colorNewBot[line->GetParam("team")->AsInt(0)] = line->GetParam("color")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)); | |
| continue; | |
| } | |
| if (line->GetCommand() == "InsectColor" && !resetObject) | |
| { | |
| m_colorNewAlien = line->GetParam("color")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)); | |
| continue; | |
| } | |
| if (line->GetCommand() == "GreeneryColor" && !resetObject) | |
| { | |
| m_colorNewGreen = line->GetParam("color")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)); | |
| continue; | |
| } | |
| if (line->GetCommand() == "DeepView" && !resetObject) | |
| { | |
| m_engine->SetDeepView(line->GetParam("air")->AsFloat(500.0f)*g_unit, 0, false); | |
| m_engine->SetDeepView(line->GetParam("water")->AsFloat(100.0f)*g_unit, 1, false); | |
| continue; | |
| } | |
| if (line->GetCommand() == "FogStart" && !resetObject) | |
| { | |
| m_engine->SetFogStart(line->GetParam("air")->AsFloat(0.5f), 0); | |
| m_engine->SetFogStart(line->GetParam("water")->AsFloat(0.5f), 1); | |
| continue; | |
| } | |
| if (line->GetCommand() == "SecondTexture" && !resetObject) | |
| { | |
| if (line->GetParam("rank")->IsDefined()) | |
| { | |
| char tex[20] = { 0 }; | |
| sprintf(tex, "dirty%.2d.png", line->GetParam("rank")->AsInt()); | |
| m_engine->SetSecondTexture(tex); | |
| } | |
| else | |
| { | |
| m_engine->SetSecondTexture("../" + line->GetParam("texture")->AsPath("textures")); | |
| } | |
| continue; | |
| } | |
| if (line->GetCommand() == "Background" && !resetObject) | |
| { | |
| if (line->GetParam("image")->IsDefined()) | |
| backgroundPath = line->GetParam("image")->AsPath("textures"); | |
| backgroundUp = line->GetParam("up")->AsColor(backgroundUp); | |
| backgroundDown = line->GetParam("down")->AsColor(backgroundDown); | |
| backgroundCloudUp = line->GetParam("cloudUp")->AsColor(backgroundCloudUp); | |
| backgroundCloudDown = line->GetParam("cloudDown")->AsColor(backgroundCloudDown); | |
| backgroundFull = line->GetParam("full")->AsBool(backgroundFull); | |
| continue; | |
| } | |
| if (line->GetCommand() == "Planet" && !resetObject) | |
| { | |
| Math::Vector ppos, uv1, uv2; | |
| ppos = line->GetParam("pos")->AsPoint(); | |
| uv1 = line->GetParam("uv1")->AsPoint(); | |
| uv2 = line->GetParam("uv2")->AsPoint(); | |
| m_planet->Create(line->GetParam("mode")->AsPlanetType(), | |
| Math::Point(ppos.x, ppos.z), | |
| line->GetParam("dim")->AsFloat(0.2f), | |
| line->GetParam("speed")->AsFloat(0.0f), | |
| line->GetParam("dir")->AsFloat(0.0f), | |
| line->GetParam("image")->AsPath("textures"), | |
| Math::Point(uv1.x, uv1.z), | |
| Math::Point(uv2.x, uv2.z), | |
| line->GetParam("image")->AsPath("textures").find("planet") != std::string::npos // TODO: add transparent op or modify textures | |
| ); | |
| continue; | |
| } | |
| if (line->GetCommand() == "ForegroundName" && !resetObject) | |
| { | |
| m_engine->SetForegroundName(line->GetParam("image")->AsPath("textures")); | |
| continue; | |
| } | |
| if (line->GetCommand() == "Level" && !resetObject) | |
| { | |
| g_unit = line->GetParam("unitScale")->AsFloat(4.0f); | |
| m_engine->SetTracePrecision(line->GetParam("traceQuality")->AsFloat(1.0f)); | |
| m_shortCut = line->GetParam("shortcut")->AsBool(true); | |
| m_missionType = line->GetParam("type")->AsMissionType(MISSION_NORMAL); | |
| m_globalMagnifyDamage = line->GetParam("magnifyDamage")->AsFloat(1.0f); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainGenerate" && !resetObject) | |
| { | |
| m_ui->GetLoadingScreen()->SetProgress(0.2f, RT_LOADING_TERRAIN); | |
| m_terrain->Generate(line->GetParam("mosaic")->AsInt(20), | |
| line->GetParam("brick")->AsInt(3), | |
| line->GetParam("size")->AsFloat(20.0f), | |
| line->GetParam("vision")->AsFloat(500.0f)*g_unit, | |
| line->GetParam("depth")->AsInt(2), | |
| line->GetParam("hard")->AsFloat(0.5f)); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainWind" && !resetObject) | |
| { | |
| m_terrain->SetWind(line->GetParam("speed")->AsPoint()); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainRelief" && !resetObject) | |
| { | |
| m_ui->GetLoadingScreen()->SetProgress(0.2f+(1.f/5.f)*0.05f, RT_LOADING_TERRAIN, RT_LOADING_TERRAIN_RELIEF); | |
| m_terrain->LoadRelief( | |
| line->GetParam("image")->AsPath("textures"), | |
| line->GetParam("factor")->AsFloat(1.0f), | |
| line->GetParam("border")->AsBool(true)); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainRandomRelief" && !resetObject) | |
| { | |
| m_ui->GetLoadingScreen()->SetProgress(0.2f+(1.f/5.f)*0.05f, RT_LOADING_TERRAIN, RT_LOADING_TERRAIN_RELIEF); | |
| m_terrain->RandomizeRelief(); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainResource" && !resetObject) | |
| { | |
| m_ui->GetLoadingScreen()->SetProgress(0.2f+(2.f/5.f)*0.05f, RT_LOADING_TERRAIN, RT_LOADING_TERRAIN_RES); | |
| m_terrain->LoadResources(line->GetParam("image")->AsPath("textures")); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainWater" && !resetObject) | |
| { | |
| Math::Vector pos; | |
| pos.x = line->GetParam("moveX")->AsFloat(0.0f); | |
| pos.y = line->GetParam("moveY")->AsFloat(0.0f); | |
| pos.z = pos.x; | |
| m_water->Create(line->GetParam("air")->AsWaterType(Gfx::WATER_TT), | |
| line->GetParam("water")->AsWaterType(Gfx::WATER_TT), | |
| line->GetParam("image")->AsPath("textures"), | |
| line->GetParam("diffuse")->AsColor(Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f)), | |
| line->GetParam("ambient")->AsColor(Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f)), | |
| line->GetParam("level")->AsFloat(100.0f)*g_unit, | |
| line->GetParam("glint")->AsFloat(1.0f), | |
| pos); | |
| m_colorNewWater = line->GetParam("color")->AsColor(COLOR_REF_WATER); | |
| m_colorShiftWater = line->GetParam("brightness")->AsFloat(0.0f); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainLava" && !resetObject) | |
| { | |
| m_water->SetLava(line->GetParam("mode")->AsBool()); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainCloud" && !resetObject) | |
| { | |
| std::string path = ""; | |
| if (line->GetParam("image")->IsDefined()) | |
| path = line->GetParam("image")->AsPath("textures"); | |
| m_cloud->Create(path, | |
| line->GetParam("diffuse")->AsColor(Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f)), | |
| line->GetParam("ambient")->AsColor(Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f)), | |
| line->GetParam("level")->AsFloat(500.0f)*g_unit); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainBlitz" && !resetObject) | |
| { | |
| m_lightning->Create(line->GetParam("sleep")->AsFloat(0.0f), | |
| line->GetParam("delay")->AsFloat(3.0f), | |
| line->GetParam("magnetic")->AsFloat(50.0f)*g_unit); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainInitTextures" && !resetObject) | |
| { | |
| m_ui->GetLoadingScreen()->SetProgress(0.2f+(3.f/5.f)*0.05f, RT_LOADING_TERRAIN, RT_LOADING_TERRAIN_TEX); | |
| std::string name = "../" + line->GetParam("image")->AsPath("textures"); | |
| if (name.find(".") == std::string::npos) | |
| name += ".png"; | |
| unsigned int dx = line->GetParam("dx")->AsInt(1); | |
| unsigned int dy = line->GetParam("dy")->AsInt(1); | |
| int tt[100]; //TODO: I have no idea how TerrainInitTextures works, but maybe we shuld remove the limit to 100? | |
| if (dx*dy > 100) | |
| throw CLevelParserException("In TerrainInitTextures: dx*dy must be <100"); | |
| if (line->GetParam("table")->IsDefined()) | |
| { | |
| auto& table = line->GetParam("table")->AsArray(); | |
| if (table.size() > dx*dy) | |
| throw CLevelParserException("In TerrainInitTextures: table size must be dx*dy"); | |
| for (unsigned int i = 0; i < dx*dy; i++) | |
| { | |
| if (i >= table.size()) | |
| { | |
| tt[i] = 0; | |
| } | |
| else | |
| { | |
| tt[i] = table[i]->AsInt(); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| for (unsigned int i = 0; i < dx*dy; i++) | |
| { | |
| tt[i] = 0; | |
| } | |
| } | |
| m_terrain->InitTextures(name.c_str(), tt, dx, dy); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainInit" && !resetObject) | |
| { | |
| m_terrain->InitMaterials(line->GetParam("id")->AsInt(1)); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainMaterial" && !resetObject) | |
| { | |
| std::string name = line->GetParam("image")->AsPath("textures"); | |
| if (name.find(".") == std::string::npos) | |
| name += ".png"; | |
| name = "../" + name; | |
| m_terrain->AddMaterial(line->GetParam("id")->AsInt(0), | |
| name.c_str(), | |
| Math::Point(line->GetParam("u")->AsFloat(), | |
| line->GetParam("v")->AsFloat()), | |
| line->GetParam("up")->AsInt(), | |
| line->GetParam("right")->AsInt(), | |
| line->GetParam("down")->AsInt(), | |
| line->GetParam("left")->AsInt(), | |
| line->GetParam("hard")->AsFloat(0.5f)); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainLevel" && !resetObject) | |
| { | |
| m_ui->GetLoadingScreen()->SetProgress(0.2f+(3.f/5.f)*0.05f, RT_LOADING_TERRAIN, RT_LOADING_TERRAIN_TEX); | |
| int id[50]; //TODO: I have no idea how TerrainLevel works, but maybe we should remove the limit to 50? | |
| if (line->GetParam("id")->IsDefined()) | |
| { | |
| auto& idArray = line->GetParam("id")->AsArray(); | |
| if (idArray.size() > 50) | |
| throw CLevelParserException("In TerrainLevel: id array size must be < 50"); | |
| unsigned int i = 0; | |
| while (i < 50) | |
| { | |
| id[i] = idArray[i]->AsInt(); | |
| i++; | |
| if (i >= idArray.size()) break; | |
| } | |
| id[i] = 0; | |
| } | |
| m_terrain->GenerateMaterials(id, | |
| line->GetParam("min")->AsFloat(0.0f)*g_unit, | |
| line->GetParam("max")->AsFloat(100.0f)*g_unit, | |
| line->GetParam("slope")->AsFloat(5.0f), | |
| line->GetParam("freq")->AsFloat(100.0f), | |
| line->GetParam("center")->AsPoint(Math::Vector(0.0f, 0.0f, 0.0f))*g_unit, | |
| line->GetParam("radius")->AsFloat(0.0f)*g_unit); | |
| continue; | |
| } | |
| if (line->GetCommand() == "TerrainCreate" && !resetObject) | |
| { | |
| m_ui->GetLoadingScreen()->SetProgress(0.2f+(4.f/5.f)*0.05f, RT_LOADING_TERRAIN, RT_LOADING_TERRAIN_GEN); | |
| m_terrain->CreateObjects(); | |
| continue; | |
| } | |
| if (line->GetCommand() == "BeginObject") | |
| { | |
| InitEye(); | |
| SetMovieLock(false); | |
| if (!resetObject) | |
| ChangeColor(); // changes the colors of texture | |
| if (!m_sceneReadPath.empty()) // loading file ? | |
| { | |
| m_ui->GetLoadingScreen()->SetProgress(0.25f, RT_LOADING_OBJECTS_SAVED); | |
| sel = IOReadScene(m_sceneReadPath + "/data.sav", m_sceneReadPath + "/cbot.run"); | |
| } | |
| else | |
| { | |
| m_ui->GetLoadingScreen()->SetProgress(0.25f, RT_LOADING_OBJECTS); | |
| } | |
| continue; | |
| } | |
| if (line->GetCommand() == "LevelController" && m_sceneReadPath.empty()) | |
| { | |
| if (m_controller != nullptr) | |
| { | |
| throw CLevelParserException("There can be only one LevelController in the level"); | |
| } | |
| m_controller = m_objMan->CreateObject(Math::Vector(0.0f, 0.0f, 0.0f), 0.0f, OBJECT_CONTROLLER); | |
| assert(m_controller->Implements(ObjectInterfaceType::Programmable)); | |
| assert(m_controller->Implements(ObjectInterfaceType::ProgramStorage)); | |
| assert(m_controller->Implements(ObjectInterfaceType::Old)); | |
| dynamic_cast<COldObject*>(m_controller)->SetCheckToken(false); | |
| if (line->GetParam("script")->IsDefined()) | |
| { | |
| CProgramStorageObject* programStorage = dynamic_cast<CProgramStorageObject*>(m_controller); | |
| Program* program = programStorage->AddProgram(); | |
| programStorage->ReadProgram(program, line->GetParam("script")->AsPath("ai")); | |
| program->readOnly = true; | |
| dynamic_cast<CProgrammableObject*>(m_controller)->RunProgram(program); | |
| } | |
| continue; | |
| } | |
| if (line->GetCommand() == "CreateObject" && m_sceneReadPath.empty()) | |
| { | |
| ObjectCreateParams params = CObject::ReadCreateParams(line.get()); | |
| float objectProgress = static_cast<float>(rankObj) / static_cast<float>(numObjects); | |
| std::string details = StrUtils::ToString<int>(rankObj+1)+" / "+StrUtils::ToString<int>(numObjects); | |
| #if DEV_BUILD | |
| // Object categories may spoil the level a bit, so hide them in release builds | |
| details += ": "+CLevelParserParam::FromObjectType(params.type); | |
| #endif | |
| m_ui->GetLoadingScreen()->SetProgress(0.25f+objectProgress*0.75f, RT_LOADING_OBJECTS, details); | |
| try | |
| { | |
| CObject* obj = m_objMan->CreateObject(params); | |
| obj->Read(line.get()); | |
| if (m_fixScene && obj->GetType() == OBJECT_HUMAN) | |
| { | |
| assert(obj->Implements(ObjectInterfaceType::Movable)); | |
| CMotion* motion = dynamic_cast<CMovableObject*>(obj)->GetMotion(); | |
| if (m_phase == PHASE_WIN ) motion->SetAction(MHS_WIN, 0.4f); | |
| if (m_phase == PHASE_LOST) motion->SetAction(MHS_LOST, 0.5f); | |
| } | |
| if (obj->Implements(ObjectInterfaceType::Controllable) && line->GetParam("select")->AsBool(false)) | |
| sel = obj; | |
| if (obj->GetType() == OBJECT_BASE) | |
| m_base = obj; | |
| if (obj->Implements(ObjectInterfaceType::ProgramStorage)) | |
| { | |
| CProgramStorageObject* programStorage = dynamic_cast<CProgramStorageObject*>(obj); | |
| if (obj->Implements(ObjectInterfaceType::Controllable) && dynamic_cast<CControllableObject*>(obj)->GetSelectable() && obj->GetType() != OBJECT_HUMAN) | |
| { | |
| programStorage->SetProgramStorageIndex(rankObj); | |
| } | |
| char categoryChar = GetLevelCategoryDir(m_levelCategory)[0]; | |
| programStorage->LoadAllProgramsForLevel( | |
| line.get(), | |
| m_playerProfile->GetSaveFile(StrUtils::Format("%c%.3d%.3d", categoryChar, m_levelChap, m_levelRank)), | |
| soluce | |
| ); | |
| } | |
| } | |
| catch (const CObjectCreateException& e) | |
| { | |
| GetLogger()->Error("Error loading level object: %s\n", e.what()); | |
| throw; | |
| } | |
| rankObj ++; | |
| continue; | |
| } | |
| if (line->GetCommand() == "CreateFog" && !resetObject) | |
| { | |
| Gfx::ParticleType type = static_cast<Gfx::ParticleType>(Gfx::PARTIFOG0+(line->GetParam("type")->AsInt())); | |
| Math::Vector pos = line->GetParam("pos")->AsPoint()*g_unit; | |
| float height = line->GetParam("height")->AsFloat(1.0f)*g_unit; | |
| float ddim = line->GetParam("dim")->AsFloat(50.0f)*g_unit; | |
| float delay = line->GetParam("delay")->AsFloat(2.0f); | |
| m_terrain->AdjustToFloor(pos); | |
| pos.y += height; | |
| Math::Point dim; | |
| dim.x = ddim; | |
| dim.y = dim.x; | |
| m_particle->CreateParticle(pos, Math::Vector(0.0f, 0.0f, 0.0f), dim, type, delay, 0.0f, 0.0f); | |
| continue; | |
| } | |
| if (line->GetCommand() == "CreateLight" && !resetObject) | |
| { | |
| Gfx::EngineObjectType type; | |
| int lightRank = CreateLight(line->GetParam("dir")->AsPoint(), | |
| line->GetParam("color")->AsColor(Gfx::Color(0.5f, 0.5f, 0.5f, 1.0f))); | |
| type = line->GetParam("type")->AsTerrainType(Gfx::ENG_OBJTYPE_NULL); | |
| if (type == Gfx::ENG_OBJTYPE_TERRAIN) | |
| { | |
| m_lightMan->SetLightPriority(lightRank, Gfx::LIGHT_PRI_HIGHEST); | |
| m_lightMan->SetLightIncludeType(lightRank, Gfx::ENG_OBJTYPE_TERRAIN); | |
| } | |
| if (type == Gfx::ENG_OBJTYPE_QUARTZ) | |
| m_lightMan->SetLightIncludeType(lightRank, Gfx::ENG_OBJTYPE_QUARTZ); | |
| if (type == Gfx::ENG_OBJTYPE_METAL) | |
| m_lightMan->SetLightIncludeType(lightRank, Gfx::ENG_OBJTYPE_METAL); | |
| if (type == Gfx::ENG_OBJTYPE_FIX) | |
| m_lightMan->SetLightExcludeType(lightRank, Gfx::ENG_OBJTYPE_TERRAIN); | |
| continue; | |
| } | |
| if (line->GetCommand() == "CreateSpot" && !resetObject) | |
| { | |
| Gfx::EngineObjectType type; | |
| int rankLight = CreateSpot(line->GetParam("pos")->AsPoint()*g_unit, | |
| line->GetParam("color")->AsColor(Gfx::Color(0.5f, 0.5f, 0.5f, 1.0f))); | |
| type = line->GetParam("type")->AsTerrainType(Gfx::ENG_OBJTYPE_NULL); | |
| if (type == Gfx::ENG_OBJTYPE_TERRAIN) | |
| m_lightMan->SetLightIncludeType(rankLight, Gfx::ENG_OBJTYPE_TERRAIN); | |
| if (type == Gfx::ENG_OBJTYPE_QUARTZ) | |
| m_lightMan->SetLightIncludeType(rankLight, Gfx::ENG_OBJTYPE_QUARTZ); | |
| if (type == Gfx::ENG_OBJTYPE_METAL) | |
| m_lightMan->SetLightIncludeType(rankLight, Gfx::ENG_OBJTYPE_METAL); | |
| if (type == Gfx::ENG_OBJTYPE_FIX) | |
| m_lightMan->SetLightExcludeType(rankLight, Gfx::ENG_OBJTYPE_TERRAIN); | |
| continue; | |
| } | |
| if (line->GetCommand() == "GroundSpot" && !resetObject) | |
| { | |
| int rank = m_engine->CreateGroundSpot(); | |
| if (rank != -1) | |
| { | |
| m_engine->SetObjectGroundSpotPos(rank, line->GetParam("pos")->AsPoint(Math::Vector(0.0f, 0.0f, 0.0f))*g_unit); | |
| m_engine->SetObjectGroundSpotRadius(rank, line->GetParam("radius")->AsFloat(10.0f)*g_unit); | |
| m_engine->SetObjectGroundSpotColor(rank, line->GetParam("color")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f))); | |
| m_engine->SetObjectGroundSpotSmooth(rank, line->GetParam("smooth")->AsFloat(1.0f)); | |
| m_engine->SetObjectGroundSpotMinMax(rank, line->GetParam("min")->AsFloat(0.0f)*g_unit, | |
| line->GetParam("max")->AsFloat(0.0f)*g_unit); | |
| } | |
| continue; | |
| } | |
| if (line->GetCommand() == "WaterColor" && !resetObject) | |
| { | |
| m_engine->SetWaterAddColor(line->GetParam("color")->AsColor()); | |
| continue; | |
| } | |
| if (line->GetCommand() == "MapColor" && !resetObject) | |
| { | |
| m_map->FloorColorMap(line->GetParam("floor")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f)), | |
| line->GetParam("water")->AsColor(Gfx::Color(0.533f, 0.533f, 0.533f, 0.533f))); | |
| m_mapShow = line->GetParam("show")->AsBool(true); | |
| m_map->SetToy(line->GetParam("toyIcon")->AsBool(false)); | |
| m_mapImage = line->GetParam("image")->AsBool(false); | |
| if (m_mapImage) | |
| { | |
| Math::Vector offset; | |
| strcpy(m_mapFilename, line->GetParam("filename")->AsPath("textures").c_str()); | |
| offset = line->GetParam("offset")->AsPoint(Math::Vector(0.0f, 0.0f, 0.0f)); | |
| m_map->SetFixParam(line->GetParam("zoom")->AsFloat(1.0f), | |
| offset.x, offset.z, | |
| line->GetParam("angle")->AsFloat(0.0f)*Math::PI/180.0f, | |
| line->GetParam("mode")->AsInt(0), | |
| line->GetParam("debug")->AsBool(false)); | |
| } | |
| continue; | |
| } | |
| if (line->GetCommand() == "MapZoom" && !resetObject) | |
| { | |
| m_map->ZoomMap(line->GetParam("factor")->AsFloat(2.0f)); | |
| m_map->MapEnable(line->GetParam("enable")->AsBool(true)); | |
| continue; | |
| } | |
| if (line->GetCommand() == "MaxFlyingHeight" && !resetObject) | |
| { | |
| m_terrain->SetFlyingMaxHeight(line->GetParam("max")->AsFloat(280.0f)*g_unit); | |
| continue; | |
| } | |
| if (line->GetCommand() == "AddFlyingHeight" && !resetObject) | |
| { | |
| m_terrain->AddFlyingLimit(line->GetParam("center")->AsPoint()*g_unit, | |
| line->GetParam("extRadius")->AsFloat(20.0f)*g_unit, | |
| line->GetParam("intRadius")->AsFloat(10.0f)*g_unit, | |
| line->GetParam("maxHeight")->AsFloat(200.0f)); | |
| continue; | |
| } | |
| if (line->GetCommand() == "Camera") | |
| { | |
| m_camera->Init(line->GetParam("eye")->AsPoint(Math::Vector(0.0f, 0.0f, 0.0f))*g_unit, | |
| line->GetParam("lookat")->AsPoint(Math::Vector(0.0f, 0.0f, 0.0f))*g_unit, | |
| resetObject ? 0.0f : line->GetParam("delay")->AsFloat(0.0f)); | |
| if (line->GetParam("fadeIn")->AsBool(false)) | |
| m_camera->StartOver(Gfx::CAM_OVER_EFFECT_FADEIN_WHITE, Math::Vector(0.0f, 0.0f, 0.0f), 1.0f); | |
| continue; | |
| } | |
| //! Note: This feature may be changed in next releases, | |
| //! Places new viewpoint, which can be selected later in (currently only in Code Battle) UI. | |
| //! Usage: View eye=x; y; z lookat=x; y; z | |
| if (line->GetCommand() == "View") | |
| { | |
| if(m_viewpoints.size() == 10) | |
| { | |
| GetLogger()->Warn("Reached limit of 10 viewpoints, next ones will be ommited.\n"); | |
| continue; | |
| } | |
| Viewpoint tmp; | |
| tmp.eye = line->GetParam("eye")->AsPoint()*g_unit; | |
| tmp.look = line->GetParam("lookat")->AsPoint()*g_unit; | |
| tmp.button = line->GetParam("button")->AsInt(13); // 13 is the camera button | |
| m_viewpoints.push_back(tmp); | |
| continue; | |
| } | |
| if (line->GetCommand() == "EndMissionTake" && !resetObject) | |
| { | |
| auto endTake = MakeUnique<CSceneEndCondition>(); | |
| endTake->Read(line.get()); | |
| if (endTake->immediat) | |
| m_endTakeImmediat = true; | |
| m_endTake.push_back(std::move(endTake)); | |
| if (!line->GetParam("pos")->IsDefined() || !line->GetParam("dist")->IsDefined()) | |
| { | |
| LoadingWarning("The defaults for pos= and dist= are going to change, specify them explicitly. See issue #759 (https://git.io/vVBzH)"); | |
| } | |
| continue; | |
| } | |
| if (line->GetCommand() == "EndMissionTeams" && !resetObject) | |
| { | |
| m_endTakeTeamImmediateWin = line->GetParam("immediateWin")->AsBool(false); // false = finishing removes the team that finished, true = finishing for one team ends the whole game | |
| continue; | |
| } | |
| if (line->GetCommand() == "EndMissionDelay" && !resetObject) | |
| { | |
| m_endTakeWinDelay = line->GetParam("win")->AsFloat(2.0f); | |
| m_endTakeLostDelay = line->GetParam("lost")->AsFloat(2.0f); | |
| continue; | |
| } | |
| if (line->GetCommand() == "EndMissionResearch" && !resetObject) // This is not used in any original Colobot levels, but we'll keep it for userlevel creators | |
| { | |
| m_endTakeResearch |= line->GetParam("type")->AsResearchFlag(); | |
| continue; | |
| } | |
| if (line->GetCommand() == "EndMissionTimeout" && !resetObject) | |
| { | |
| m_endTakeTimeout = line->GetParam("time")->AsFloat(); | |
| continue; | |
| } | |
| if (line->GetCommand() == "Scoreboard" && !resetObject) | |
| { | |
| if (line->GetParam("enable")->AsBool(false)) | |
| { | |
| // Create the scoreboard | |
| m_scoreboard = MakeUnique<CScoreboard>(); | |
| m_scoreboard->SetSortType(line->GetParam("sort")->AsSortType(CScoreboard::SortType::SORT_ID)); | |
| } | |
| continue; | |
| } | |
| if (line->GetCommand() == "ScoreboardKillRule" && !resetObject) | |
| { | |
| if (!m_scoreboard) | |
| throw CLevelParserException("ScoreboardKillRule encountered but scoreboard is not enabled"); | |
| auto rule = MakeUnique<CScoreboard::CScoreboardKillRule>(); | |
| rule->Read(line.get()); | |
| m_scoreboard->AddKillRule(std::move(rule)); | |
| continue; | |
| } | |
| if (line->GetCommand() == "ScoreboardObjectRule" && !resetObject) | |
| { | |
| if (!m_scoreboard) | |
| throw CLevelParserException("ScoreboardObjectRule encountered but scoreboard is not enabled"); | |
| auto rule = MakeUnique<CScoreboard::CScoreboardObjectRule>(); | |
| rule->Read(line.get()); | |
| m_scoreboard->AddObjectRule(std::move(rule)); | |
| continue; | |
| } | |
| if (line->GetCommand() == "ScoreboardEndTakeRule" && !resetObject) | |
| { | |
| if (!m_scoreboard) | |
| throw CLevelParserException("ScoreboardEndTakeRule encountered but scoreboard is not enabled"); | |
| auto rule = MakeUnique<CScoreboard::CScoreboardEndTakeRule>(); | |
| rule->Read(line.get()); | |
| m_scoreboard->AddEndTakeRule(std::move(rule)); | |
| continue; | |
| } | |
| if (line->GetCommand() == "ObligatoryToken" && !resetObject) | |
| { | |
| std::string token = line->GetParam("text")->AsString(); | |
| if (!line->GetParam("min")->IsDefined() && !line->GetParam("max")->IsDefined()) | |
| 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()); | |
| if (m_obligatoryTokens.count(token)) | |
| throw CLevelParserException("Incorrect ObligatoryToken specification - you cannot define a token twice"); | |
| 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 | |
| m_obligatoryTokens[token].max = line->GetParam("max")->AsInt(-1); | |
| if (m_obligatoryTokens[token].min >= 0 && m_obligatoryTokens[token].max >= 0 && m_obligatoryTokens[token].min > m_obligatoryTokens[token].max) | |
| { | |
| throw CLevelParserException("Incorrect ObligatoryToken specification - min cannot be greater than max"); | |
| } | |
| continue; | |
| } | |
| if (line->GetCommand() == "ProhibitedToken" && !resetObject) // NOTE: Kept only for backwards compatibility | |
| { | |
| std::string token = line->GetParam("text")->AsString(); | |
| GetLogger()->Warn("ProhibitedToken is only provided for backwards compatibility - instead, do this: ObligatoryToken text=\"%s\" max=0\n", token.c_str()); | |
| if (m_obligatoryTokens.count(token)) | |
| throw CLevelParserException("Incorrect ObligatoryToken specification - you cannot define a token twice"); | |
| m_obligatoryTokens[token].min = -1; | |
| m_obligatoryTokens[token].max = 0; | |
| continue; | |
| } | |
| if (line->GetCommand() == "EnableBuild" && !resetObject) | |
| { | |
| m_build |= line->GetParam("type")->AsBuildFlag(); | |
| continue; | |
| } | |
| if (line->GetCommand() == "EnableResearch" && !resetObject) | |
| { | |
| m_researchEnable |= line->GetParam("type")->AsResearchFlag(); | |
| continue; | |
| } | |
| if (line->GetCommand() == "DoneResearch" && m_sceneReadPath.empty() && !resetObject) // not loading file? | |
| { | |
| m_researchDone[0] |= line->GetParam("type")->AsResearchFlag(); | |
| continue; | |
| } | |
| if (line->GetCommand() == "NewScript" && !resetObject) | |
| { | |
| m_newScriptName.push_back(NewScriptName(line->GetParam("type")->AsObjectType(OBJECT_NULL), line->GetParam("name")->AsString(""))); | |
| continue; | |
| } | |
| 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) | |
| if (resetObject) continue; // ignore when reseting just objects (TODO: see above) | |
| throw CLevelParserException("Unknown command: '" + line->GetCommand() + "' in " + line->GetLevelFilename() + ":" + boost::lexical_cast<std::string>(line->GetLineNumber())); | |
| } | |
| // Do this here to prevent the first frame from taking a long time to render | |
| m_engine->UpdateGroundSpotTextures(); | |
| m_ui->GetLoadingScreen()->SetProgress(1.0f, RT_LOADING_FINISHED); | |
| if (m_ui->GetLoadingScreen()->IsVisible()) | |
| { | |
| // Force render of the "Loading finished" screen because it looks weird when the progress bar disappears in the middle | |
| m_app->Render(); | |
| } | |
| if (!resetObject) | |
| { | |
| m_engine->SetBackground(backgroundPath, | |
| backgroundUp, | |
| backgroundDown, | |
| backgroundCloudUp, | |
| backgroundCloudDown, | |
| backgroundFull); | |
| } | |
| if (m_levelCategory == LevelCategory::Missions && !resetObject) // mission? | |
| { | |
| m_playerProfile->SetFreeGameResearchUnlock(m_playerProfile->GetFreeGameResearchUnlock() | m_researchDone[0]); | |
| m_playerProfile->SetFreeGameBuildUnlock(m_playerProfile->GetFreeGameBuildUnlock() | m_build); | |
| } | |
| if (m_levelCategory == LevelCategory::FreeGame && !resetObject) // free play? | |
| { | |
| m_researchDone[0] = m_playerProfile->GetFreeGameResearchUnlock(); | |
| m_build = m_playerProfile->GetFreeGameBuildUnlock(); | |
| m_build &= ~BUILD_RESEARCH; | |
| m_build &= ~BUILD_LABO; | |
| m_build |= BUILD_FACTORY; | |
| m_build |= BUILD_GFLAT; | |
| m_build |= BUILD_FLAG; | |
| } | |
| if (!resetObject) | |
| { | |
| m_short->SetMode(false); // vehicles? | |
| } | |
| m_map->ShowMap(m_mapShow); | |
| m_map->UpdateMap(); | |
| // TODO: m_engine->TimeInit(); ?? | |
| m_input->ResetKeyStates(); | |
| m_time = 0.0f; | |
| if (m_sceneReadPath.empty()) m_gameTime = 0.0f; | |
| m_gameTimeAbsolute = 0.0f; | |
| m_autosaveLast = 0.0f; | |
| m_infoUsed = 0; | |
| m_selectObject = sel; | |
| if (m_base == nullptr && // no main base? | |
| !m_fixScene) // interractive scene? | |
| { | |
| CObject* obj = sel; | |
| if (sel == nullptr) | |
| obj = SearchHuman(); | |
| if (obj != nullptr) | |
| { | |
| assert(obj->Implements(ObjectInterfaceType::Controllable)); | |
| SelectObject(obj); | |
| m_camera->SetControllingObject(obj); | |
| m_camera->SetType(dynamic_cast<CControllableObject*>(obj)->GetCameraType()); | |
| } | |
| } | |
| if (m_fixScene) | |
| m_camera->SetType(Gfx::CAM_TYPE_SCRIPT); | |
| if (!m_sceneReadPath.empty() && sel != nullptr) // loading file? | |
| { | |
| Math::Vector pos = sel->GetPosition(); | |
| m_camera->Init(pos, pos, 0.0f); | |
| SelectObject(sel); | |
| m_camera->SetControllingObject(sel); | |
| m_beginSatCom = true; // message already displayed | |
| } | |
| } | |
| catch (...) | |
| { | |
| m_sceneReadPath = ""; | |
| throw; | |
| } | |
| m_sceneReadPath = ""; | |
| if (m_app->GetSceneTestMode()) | |
| m_eventQueue->AddEvent(Event(EVENT_QUIT)); | |
| m_ui->ShowLoadingScreen(false); | |
| if (m_missionType == MISSION_CODE_BATTLE) | |
| { | |
| CreateCodeBattleInterface(); | |
| } | |
| CreateShortcuts(); | |
| } | |
| void CRobotMain::LevelLoadingError(const std::string& error, const std::runtime_error& exception, Phase exitPhase) | |
| { | |
| m_ui->ShowLoadingScreen(false); | |
| GetLogger()->Error("%s\n", error.c_str()); | |
| GetLogger()->Error("%s\n", exception.what()); | |
| ChangePhase(exitPhase); | |
| m_ui->GetDialog()->StartInformation("Loading error", error, exception.what(), true, false); | |
| } | |
| //! Creates a directional light | |
| int CRobotMain::CreateLight(Math::Vector direction, Gfx::Color color) | |
| { | |
| if (direction.x == 0.0f && | |
| direction.y == 0.0f && | |
| direction.z == 0.0f) | |
| { | |
| direction.y = -1.0f; | |
| } | |
| Gfx::Light light; | |
| light.type = Gfx::LIGHT_DIRECTIONAL; | |
| light.diffuse = color; | |
| light.ambient = color * 0.1f; | |
| light.direction = direction; | |
| int obj = m_lightMan->CreateLight(Gfx::LIGHT_PRI_HIGH); | |
| m_lightMan->SetLight(obj, light); | |
| return obj; | |
| } | |
| //! Creates a light spot | |
| int CRobotMain::CreateSpot(Math::Vector pos, Gfx::Color color) | |
| { | |
| if (!m_engine->GetLightMode()) return -1; | |
| pos.y += m_terrain->GetFloorLevel(pos); | |
| Gfx::Light light; | |
| light.type = Gfx::LIGHT_SPOT; | |
| light.diffuse = color; | |
| light.ambient = color * 0.1f; | |
| light.position = pos; | |
| light.direction = Math::Vector(0.0f, -1.0f, 0.0f); | |
| light.spotIntensity = 1.0f; | |
| light.spotAngle = 90.0f*Math::PI/180.0f; | |
| light.attenuation0 = 2.0f; | |
| light.attenuation1 = 0.0f; | |
| light.attenuation2 = 0.0f; | |
| int obj = m_lightMan->CreateLight(Gfx::LIGHT_PRI_HIGH); | |
| m_lightMan->SetLight(obj, light); | |
| return obj; | |
| } | |
| //! Change the colors and textures | |
| void CRobotMain::ChangeColor() | |
| { | |
| if (m_phase != PHASE_SIMUL && | |
| m_phase != PHASE_SETUPds && | |
| m_phase != PHASE_SETUPgs && | |
| m_phase != PHASE_SETUPps && | |
| m_phase != PHASE_SETUPcs && | |
| m_phase != PHASE_SETUPss && | |
| m_phase != PHASE_WIN && | |
| m_phase != PHASE_LOST && | |
| m_phase != PHASE_APPERANCE ) return; | |
| // Player texture | |
| Math::Point ts = Math::Point(0.0f, 0.0f); | |
| Math::Point ti = Math::Point(1.0f, 1.0f); // the entire image | |
| Gfx::Color colorRef1, colorNew1, colorRef2, colorNew2; | |
| colorRef1.a = 0.0f; | |
| colorRef2.a = 0.0f; | |
| colorRef1.r = 206.0f/256.0f; | |
| colorRef1.g = 206.0f/256.0f; | |
| colorRef1.b = 204.0f/256.0f; // ~white | |
| colorNew1 = m_playerProfile->GetApperance().colorCombi; | |
| colorRef2.r = 255.0f/256.0f; | |
| colorRef2.g = 132.0f/256.0f; | |
| colorRef2.b = 1.0f/256.0f; // orange | |
| colorNew2 = m_playerProfile->GetApperance().colorBand; | |
| Math::Point exclu[6]; | |
| exclu[0] = Math::Point(192.0f/256.0f, 0.0f/256.0f); | |
| exclu[1] = Math::Point(256.0f/256.0f, 64.0f/256.0f); // crystals + cylinders | |
| exclu[2] = Math::Point(208.0f/256.0f, 224.0f/256.0f); | |
| exclu[3] = Math::Point(256.0f/256.0f, 256.0f/256.0f); // SatCom screen | |
| exclu[4] = Math::Point(0.0f, 0.0f); | |
| exclu[5] = Math::Point(0.0f, 0.0f); // terminator | |
| m_engine->ChangeTextureColor("textures/objects/human.png", colorRef1, colorNew1, colorRef2, colorNew2, 0.30f, 0.01f, ts, ti, exclu); | |
| float tolerance; | |
| int face = GetGamerFace(); | |
| if (face == 0) // normal? | |
| { | |
| colorRef1.r = 90.0f/256.0f; | |
| colorRef1.g = 95.0f/256.0f; | |
| colorRef1.b = 85.0f/256.0f; // black | |
| tolerance = 0.15f; | |
| } | |
| if (face == 1) // bald? | |
| { | |
| colorRef1.r = 74.0f/256.0f; | |
| colorRef1.g = 58.0f/256.0f; | |
| colorRef1.b = 46.0f/256.0f; // brown | |
| tolerance = 0.20f; | |
| } | |
| if (face == 2) // carlos? | |
| { | |
| colorRef1.r = 70.0f/256.0f; | |
| colorRef1.g = 40.0f/256.0f; | |
| colorRef1.b = 8.0f/256.0f; // brown | |
| tolerance = 0.30f; | |
| } | |
| if (face == 3) // blonde? | |
| { | |
| colorRef1.r = 74.0f/256.0f; | |
| colorRef1.g = 16.0f/256.0f; | |
| colorRef1.b = 0.0f/256.0f; // yellow | |
| tolerance = 0.20f; | |
| } | |
| colorNew1 = m_playerProfile->GetApperance().colorHair; | |
| colorRef2.r = 0.0f; | |
| colorRef2.g = 0.0f; | |
| colorRef2.b = 0.0f; | |
| colorNew2.r = 0.0f; | |
| colorNew2.g = 0.0f; | |
| colorNew2.b = 0.0f; | |
| char name[100]; | |
| sprintf(name, "textures/objects/face%.2d.png", face+1); | |
| exclu[0] = Math::Point(105.0f/256.0f, 47.0f/166.0f); | |
| exclu[1] = Math::Point(153.0f/256.0f, 79.0f/166.0f); // blue canister | |
| exclu[2] = Math::Point(0.0f, 0.0f); | |
| exclu[3] = Math::Point(0.0f, 0.0f); // terminator | |
| m_engine->ChangeTextureColor(name, colorRef1, colorNew1, colorRef2, colorNew2, tolerance, 0.00f, ts, ti, exclu); | |
| colorRef2.r = 0.0f; | |
| colorRef2.g = 0.0f; | |
| colorRef2.b = 0.0f; | |
| colorNew2.r = 0.0f; | |
| colorNew2.g = 0.0f; | |
| colorNew2.b = 0.0f; | |
| // VehicleColor | |
| for (auto it : m_colorNewBot) | |
| { | |
| int team = it.first; | |
| Gfx::Color newColor = it.second; | |
| std::string teamStr = StrUtils::ToString<int>(team); | |
| if(team == 0) teamStr = ""; | |
| 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); | |
| 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); | |
| 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); | |
| 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); | |
| 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); | |
| 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); | |
| 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); | |
| exclu[0] = Math::Point( 0.0f/256.0f, 160.0f/256.0f); | |
| exclu[1] = Math::Point(256.0f/256.0f, 256.0f/256.0f); // pencils | |
| exclu[2] = Math::Point(0.0f, 0.0f); | |
| exclu[3] = Math::Point(0.0f, 0.0f); // terminator | |
| 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); | |
| exclu[0] = Math::Point(237.0f/256.0f, 176.0f/256.0f); | |
| exclu[1] = Math::Point(256.0f/256.0f, 220.0f/256.0f); // blue canister | |
| exclu[2] = Math::Point(106.0f/256.0f, 150.0f/256.0f); | |
| exclu[3] = Math::Point(130.0f/256.0f, 214.0f/256.0f); // safe location | |
| exclu[4] = Math::Point(0.0f, 0.0f); | |
| exclu[5] = Math::Point(0.0f, 0.0f); // terminator | |
| 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); | |
| } | |
| // AlienColor | |
| exclu[0] = Math::Point(128.0f/256.0f, 160.0f/256.0f); | |
| exclu[1] = Math::Point(256.0f/256.0f, 256.0f/256.0f); // SatCom | |
| exclu[2] = Math::Point(0.0f, 0.0f); | |
| exclu[3] = Math::Point(0.0f, 0.0f); // terminator | |
| m_engine->ChangeTextureColor("textures/objects/ant.png", COLOR_REF_ALIEN, m_colorNewAlien, colorRef2, colorNew2, 0.50f, -1.0f, ts, ti, exclu); | |
| m_engine->ChangeTextureColor("textures/objects/mother.png", COLOR_REF_ALIEN, m_colorNewAlien, colorRef2, colorNew2, 0.50f, -1.0f, ts, ti); | |
| // GreeneryColor | |
| m_engine->ChangeTextureColor("textures/objects/plant.png", COLOR_REF_GREEN, m_colorNewGreen, colorRef2, colorNew2, 0.50f, -1.0f, ts, ti); | |
| // water color | |
| // PARTIPLOUF0 and PARTIDROP : | |
| ts = Math::Point(0.500f, 0.500f); | |
| ti = Math::Point(0.875f, 0.750f); | |
| m_engine->ChangeTextureColor("textures/effect00.png", COLOR_REF_WATER, m_colorNewWater, colorRef2, colorNew2, 0.20f, -1.0f, ts, ti, nullptr, m_colorShiftWater, true); | |
| // PARTIFLIC : | |
| ts = Math::Point(0.00f, 0.75f); | |
| ti = Math::Point(0.25f, 1.00f); | |
| m_engine->ChangeTextureColor("textures/effect02.png", COLOR_REF_WATER, m_colorNewWater, colorRef2, colorNew2, 0.20f, -1.0f, ts, ti, nullptr, m_colorShiftWater, true); | |
| } | |
| //! Calculates the distance to the nearest object | |
| namespace | |
| { | |
| float SearchNearestObject(CObjectManager* objMan, Math::Vector center, CObject* exclu) | |
| { | |
| float min = 100000.0f; | |
| for (CObject* obj : objMan->GetAllObjects()) | |
| { | |
| if (!obj->GetDetectable()) continue; // inactive? | |
| if (IsObjectBeingTransported(obj)) continue; | |
| if (obj == exclu) continue; | |
| ObjectType type = obj->GetType(); | |
| if (type == OBJECT_BASE) | |
| { | |
| Math::Vector oPos = obj->GetPosition(); | |
| if (oPos.x != center.x || | |
| oPos.z != center.z) | |
| { | |
| float dist = Math::Distance(center, oPos) - 80.0f; | |
| if (dist < 0.0f) dist = 0.0f; | |
| min = Math::Min(min, dist); | |
| continue; | |
| } | |
| } | |
| if (type == OBJECT_STATION || | |
| type == OBJECT_REPAIR || | |
| type == OBJECT_DESTROYER) | |
| { | |
| Math::Vector oPos = obj->GetPosition(); | |
| float dist = Math::Distance(center, oPos) - 8.0f; | |
| if (dist < 0.0f) dist = 0.0f; | |
| min = Math::Min(min, dist); | |
| } | |
| for (const auto &crashSphere : obj->GetAllCrashSpheres()) | |
| { | |
| Math::Vector oPos = crashSphere.sphere.pos; | |
| float oRadius = crashSphere.sphere.radius; | |
| float dist = Math::Distance(center, oPos) - oRadius; | |
| if (dist < 0.0f) dist = 0.0f; | |
| min = Math::Min(min, dist); | |
| } | |
| } | |
| return min; | |
| } | |
| } | |
| //! Calculates a free space | |
| bool CRobotMain::FreeSpace(Math::Vector ¢er, float minRadius, float maxRadius, | |
| float space, CObject *exclu) | |
| { | |
| if (minRadius < maxRadius) // from internal to external? | |
| { | |
| for (float radius = minRadius; radius <= maxRadius; radius += space) | |
| { | |
| float ia = space/radius; | |
| for (float angle = 0.0f; angle < Math::PI*2.0f; angle += ia) | |
| { | |
| Math::Point p; | |
| p.x = center.x+radius; | |
| p.y = center.z; | |
| p = Math::RotatePoint(Math::Point(center.x, center.z), angle, p); | |
| Math::Vector pos; | |
| pos.x = p.x; | |
| pos.z = p.y; | |
| pos.y = 0.0f; | |
| m_terrain->AdjustToFloor(pos, true); | |
| float dist = SearchNearestObject(m_objMan.get(), pos, exclu); | |
| if (dist >= space) | |
| { | |
| float flat = m_terrain->GetFlatZoneRadius(pos, dist/2.0f); | |
| if (flat >= dist/2.0f) | |
| { | |
| center = pos; | |
| return true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| else // from external to internal? | |
| { | |
| for (float radius=maxRadius; radius >= minRadius; radius -= space) | |
| { | |
| float ia = space/radius; | |
| for (float angle=0.0f ; angle<Math::PI*2.0f ; angle+=ia ) | |
| { | |
| Math::Point p; | |
| p.x = center.x+radius; | |
| p.y = center.z; | |
| p = Math::RotatePoint(Math::Point(center.x, center.z), angle, p); | |
| Math::Vector pos; | |
| pos.x = p.x; | |
| pos.z = p.y; | |
| pos.y = 0.0f; | |
| m_terrain->AdjustToFloor(pos, true); | |
| float dist = SearchNearestObject(m_objMan.get(), pos, exclu); | |
| if (dist >= space) | |
| { | |
| float flat = m_terrain->GetFlatZoneRadius(pos, dist/2.0f); | |
| if (flat >= dist/2.0f) | |
| { | |
| center = pos; | |
| return true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| //! Calculates a flat free space | |
| bool CRobotMain::FlatFreeSpace(Math::Vector ¢er, float minFlat, float minRadius, float maxRadius, | |
| float space, CObject *exclu) | |
| { | |
| if (minRadius < maxRadius) // from internal to external? | |
| { | |
| for (float radius = minRadius; radius <= maxRadius; radius += space) | |
| { | |
| float ia = space/radius; | |
| for (float angle = 0.0f; angle < Math::PI*2.0f; angle += ia) | |
| { | |
| Math::Point p; | |
| p.x = center.x+radius; | |
| p.y = center.z; | |
| p = Math::RotatePoint(Math::Point(center.x, center.z), angle, p); | |
| Math::Vector pos; | |
| pos.x = p.x; | |
| pos.z = p.y; | |
| pos.y = 0.0f; | |
| m_terrain->AdjustToFloor(pos, true); | |
| float dist = SearchNearestObject(m_objMan.get(), pos, exclu); | |
| if (dist >= space) | |
| { | |
| float flat = m_terrain->GetFlatZoneRadius(pos, dist/2.0f); | |
| if (flat >= dist/2.0f) | |
| { | |
| flat = m_terrain->GetFlatZoneRadius(pos, minFlat); | |
| if(flat >= minFlat) | |
| { | |
| center = pos; | |
| return true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| else // from external to internal? | |
| { | |
| for (float radius=maxRadius; radius >= minRadius; radius -= space) | |
| { | |
| float ia = space/radius; | |
| for (float angle=0.0f ; angle<Math::PI*2.0f ; angle+=ia ) | |
| { | |
| Math::Point p; | |
| p.x = center.x+radius; | |
| p.y = center.z; | |
| p = Math::RotatePoint(Math::Point(center.x, center.z), angle, p); | |
| Math::Vector pos; | |
| pos.x = p.x; | |
| pos.z = p.y; | |
| pos.y = 0.0f; | |
| m_terrain->AdjustToFloor(pos, true); | |
| float dist = SearchNearestObject(m_objMan.get(), pos, exclu); | |
| if (dist >= space) | |
| { | |
| float flat = m_terrain->GetFlatZoneRadius(pos, dist/2.0f); | |
| if (flat >= dist/2.0f) | |
| { | |
| flat = m_terrain->GetFlatZoneRadius(pos, minFlat); | |
| if(flat >= minFlat) | |
| { | |
| center = pos; | |
| return true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| //! Calculates the maximum radius of a free space | |
| float CRobotMain::GetFlatZoneRadius(Math::Vector center, float maxRadius, | |
| CObject *exclu) | |
| { | |
| float dist = SearchNearestObject(m_objMan.get(), center, exclu); | |
| if (dist == 0.0f) return 0.0f; | |
| if (dist < maxRadius) | |
| maxRadius = dist; | |
| return m_terrain->GetFlatZoneRadius(center, maxRadius); | |
| } | |
| //! Hides buildable area when a cube of metal is taken up | |
| void CRobotMain::HideDropZone(CObject* metal) | |
| { | |
| if (m_showLimit[1].used && | |
| m_showLimit[1].link == metal) | |
| { | |
| FlushShowLimit(1); | |
| } | |
| if (m_showLimit[2].used && | |
| m_showLimit[2].link == metal) | |
| { | |
| FlushShowLimit(2); | |
| } | |
| } | |
| //! Shows the buildable area when a cube of metal is deposited | |
| void CRobotMain::ShowDropZone(CObject* metal, CObject* transporter) | |
| { | |
| if (metal == nullptr) return; | |
| Math::Vector center = metal->GetPosition(); | |
| // Calculates the maximum radius possible depending on other items. | |
| float oMax = 30.0f; // radius to build the biggest building | |
| float tMax; | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (!obj->GetDetectable()) continue; // inactive? | |
| if (IsObjectBeingTransported(obj)) continue; | |
| if (obj == metal) continue; | |
| if (obj == transporter) continue; | |
| Math::Vector oPos; | |
| ObjectType type = obj->GetType(); | |
| if (type == OBJECT_BASE) | |
| { | |
| oPos = obj->GetPosition(); | |
| float dist = Math::Distance(center, oPos)-80.0f; | |
| oMax = Math::Min(oMax, dist); | |
| } | |
| else | |
| { | |
| for (const auto& crashSphere : obj->GetAllCrashSpheres()) | |
| { | |
| float dist = Math::Distance(center, crashSphere.sphere.pos)-crashSphere.sphere.radius; | |
| oMax = Math::Min(oMax, dist); | |
| } | |
| } | |
| if ( type == OBJECT_DERRICK || | |
| type == OBJECT_FACTORY || | |
| type == OBJECT_STATION || | |
| type == OBJECT_CONVERT || | |
| type == OBJECT_REPAIR || | |
| type == OBJECT_DESTROYER|| | |
| type == OBJECT_TOWER || | |
| type == OBJECT_RESEARCH || | |
| type == OBJECT_RADAR || | |
| type == OBJECT_ENERGY || | |
| type == OBJECT_LABO || | |
| type == OBJECT_NUCLEAR || | |
| type == OBJECT_START || | |
| type == OBJECT_END || | |
| type == OBJECT_INFO || | |
| type == OBJECT_PARA || | |
| type == OBJECT_SAFE || | |
| type == OBJECT_HUSTON ) // building? | |
| { | |
| for (const auto& crashSphere : obj->GetAllCrashSpheres()) | |
| { | |
| float dist = Math::Distance(center, crashSphere.sphere.pos)-crashSphere.sphere.radius-BUILDMARGIN; | |
| oMax = Math::Min(oMax, dist); | |
| } | |
| } | |
| } | |
| // Calculates the maximum possible radius depending on terrain. | |
| if (oMax >= 2.0f) | |
| tMax = m_terrain->GetFlatZoneRadius(center, 30.0f); | |
| else | |
| tMax = 0.0f; | |
| float radius = Math::Min(oMax, tMax); | |
| if (radius >= 2.0f) | |
| SetShowLimit(1, Gfx::PARTILIMIT2, metal, center, radius, 10.0f); | |
| } | |
| //! Erases the boundaries shown | |
| void CRobotMain::FlushShowLimit(int i) | |
| { | |
| for (int j = 0; j < m_showLimit[i].total; j++) | |
| { | |
| if (m_showLimit[i].parti[j] == 0) continue; | |
| m_particle->DeleteParticle(m_showLimit[i].parti[j]); | |
| m_showLimit[i].parti[j] = 0; | |
| } | |
| m_showLimit[i].total = 0; | |
| m_showLimit[i].link = nullptr; | |
| m_showLimit[i].used = false; | |
| } | |
| //! Specifies the boundaries to show | |
| void CRobotMain::SetShowLimit(int i, Gfx::ParticleType parti, CObject *obj, | |
| Math::Vector pos, float radius, float duration) | |
| { | |
| FlushShowLimit(i); // erases the current boundaries | |
| if (radius <= 0.0f) return; | |
| Math::Point dim; | |
| float dist; | |
| if (radius <= 50.0f) | |
| { | |
| dim = Math::Point(0.3f, 0.3f); | |
| dist = 2.5f; | |
| } | |
| else | |
| { | |
| dim = Math::Point(1.5f, 1.5f); | |
| dist = 10.0f; | |
| } | |
| m_showLimit[i].used = true; | |
| m_showLimit[i].link = obj; | |
| m_showLimit[i].pos = pos; | |
| m_showLimit[i].radius = radius; | |
| m_showLimit[i].duration = duration; | |
| m_showLimit[i].total = static_cast<int>((radius*2.0f*Math::PI)/dist); | |
| if (m_showLimit[i].total > MAXSHOWPARTI) m_showLimit[i].total = MAXSHOWPARTI; | |
| m_showLimit[i].time = 0.0f; | |
| for (int j = 0; j < m_showLimit[i].total; j++) | |
| { | |
| m_showLimit[i].parti[j] = m_particle->CreateParticle(pos, Math::Vector(0.0f, 0.0f, 0.0f), dim, parti, duration); | |
| } | |
| } | |
| //! Mount the boundaries of the selected object | |
| void CRobotMain::StartShowLimit() | |
| { | |
| CObject* obj = GetSelect(); | |
| if (obj == nullptr) return; | |
| if (!obj->Implements(ObjectInterfaceType::Ranged)) return; | |
| float range = dynamic_cast<CRangedObject*>(obj)->GetShowLimitRadius(); | |
| if (range == 0.0f) return; | |
| SetShowLimit(0, Gfx::PARTILIMIT1, obj, obj->GetPosition(), range); | |
| } | |
| //! Advances the boundaries shown | |
| void CRobotMain::FrameShowLimit(float rTime) | |
| { | |
| if (m_engine->GetPause()) return; | |
| for (int i = 0; i < MAXSHOWLIMIT; i++) | |
| { | |
| if (!m_showLimit[i].used) continue; | |
| m_showLimit[i].time += rTime; | |
| if (m_showLimit[i].time >= m_showLimit[i].duration) | |
| { | |
| FlushShowLimit(i); | |
| continue; | |
| } | |
| float factor; | |
| if (m_showLimit[i].time < 1.0f) | |
| factor = m_showLimit[i].time; | |
| else if (m_showLimit[i].time > m_showLimit[i].duration-1.0f) | |
| factor = m_showLimit[i].duration-m_showLimit[i].time; | |
| else | |
| factor = 1.0f; | |
| float speed = 0.4f-m_showLimit[i].radius*0.001f; | |
| if (speed < 0.1f) speed = 0.1f; | |
| float angle = m_showLimit[i].time*speed; | |
| if (m_showLimit[i].link != nullptr) | |
| { | |
| m_showLimit[i].pos = m_showLimit[i].link->GetPosition(); | |
| } | |
| for (int j = 0; j < m_showLimit[i].total; j++) | |
| { | |
| if (m_showLimit[i].parti[j] == 0) continue; | |
| Math::Point center; | |
| center.x = m_showLimit[i].pos.x; | |
| center.y = m_showLimit[i].pos.z; | |
| Math::Point rotate; | |
| rotate.x = center.x+m_showLimit[i].radius*factor; | |
| rotate.y = center.y; | |
| rotate = Math::RotatePoint(center, angle, rotate); | |
| Math::Vector pos; | |
| pos.x = rotate.x; | |
| pos.z = rotate.y; | |
| pos.y = 0.0f; | |
| m_terrain->AdjustToFloor(pos, true); | |
| if (m_showLimit[i].radius <= 50.0f) pos.y += 0.5f; | |
| else pos.y += 2.0f; | |
| m_particle->SetPosition(m_showLimit[i].parti[j], pos); | |
| angle += (2.0f*Math::PI)/m_showLimit[i].total; | |
| } | |
| } | |
| } | |
| //! Saves all programs of all the robots | |
| void CRobotMain::SaveAllScript() | |
| { | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| SaveOneScript(obj); | |
| } | |
| } | |
| //! Saves all programs of the robot. | |
| void CRobotMain::SaveOneScript(CObject *obj) | |
| { | |
| if (! obj->Implements(ObjectInterfaceType::ProgramStorage)) return; | |
| CProgramStorageObject* programStorage = dynamic_cast<CProgramStorageObject*>(obj); | |
| char categoryChar = GetLevelCategoryDir(m_levelCategory)[0]; | |
| programStorage->SaveAllUserPrograms(m_playerProfile->GetSaveFile(StrUtils::Format("%c%.3d%.3d", categoryChar, m_levelChap, m_levelRank))); | |
| } | |
| //! Saves the stack of the program in execution of a robot | |
| bool CRobotMain::SaveFileStack(CObject *obj, FILE *file, int objRank) | |
| { | |
| if (objRank == -1) return true; | |
| if (! obj->Implements(ObjectInterfaceType::Programmable)) return true; | |
| CProgrammableObject* programmable = dynamic_cast<CProgrammableObject*>(obj); | |
| ObjectType type = obj->GetType(); | |
| if (type == OBJECT_HUMAN) return true; | |
| return programmable->WriteStack(file); | |
| } | |
| //! Resumes the execution stack of the program in a robot | |
| bool CRobotMain::ReadFileStack(CObject *obj, FILE *file, int objRank) | |
| { | |
| if (objRank == -1) return true; | |
| if (! obj->Implements(ObjectInterfaceType::Programmable)) return true; | |
| CProgrammableObject* programmable = dynamic_cast<CProgrammableObject*>(obj); | |
| ObjectType type = obj->GetType(); | |
| if (type == OBJECT_HUMAN) return true; | |
| return programmable->ReadStack(file); | |
| } | |
| std::vector<std::string> CRobotMain::GetNewScriptNames(ObjectType type) | |
| { | |
| std::vector<std::string> names; | |
| for (const auto& newScript : m_newScriptName) | |
| { | |
| if (newScript.type == type || | |
| newScript.type == OBJECT_NULL ) | |
| { | |
| names.push_back(newScript.name); | |
| } | |
| } | |
| return names; | |
| } | |
| //! Seeks if an object occupies in a spot, to prevent a backup of the game | |
| bool CRobotMain::IOIsBusy() | |
| { | |
| if (CScriptFunctions::CheckOpenFiles()) return true; | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (! obj->Implements(ObjectInterfaceType::TaskExecutor)) continue; | |
| 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 | |
| if (dynamic_cast<CTaskExecutorObject*>(obj)->IsForegroundTask()) return true; | |
| } | |
| return false; | |
| } | |
| //! Writes an object into the backup file | |
| void CRobotMain::IOWriteObject(CLevelParserLine* line, CObject* obj, const std::string& programDir, int objRank) | |
| { | |
| line->AddParam("type", MakeUnique<CLevelParserParam>(obj->GetType())); | |
| line->AddParam("id", MakeUnique<CLevelParserParam>(obj->GetID())); | |
| line->AddParam("pos", MakeUnique<CLevelParserParam>(obj->GetPosition()/g_unit)); | |
| line->AddParam("angle", MakeUnique<CLevelParserParam>(obj->GetRotation() * Math::RAD_TO_DEG)); | |
| line->AddParam("zoom", MakeUnique<CLevelParserParam>(obj->GetScale())); | |
| if (obj->Implements(ObjectInterfaceType::Old)) | |
| { | |
| line->AddParam("option", MakeUnique<CLevelParserParam>(obj->GetOption())); | |
| } | |
| if (obj->Implements(ObjectInterfaceType::Controllable)) | |
| { | |
| auto controllableObj = dynamic_cast<CControllableObject*>(obj); | |
| line->AddParam("trainer", MakeUnique<CLevelParserParam>(controllableObj->GetTrainer())); | |
| if (controllableObj->GetSelect()) | |
| line->AddParam("select", MakeUnique<CLevelParserParam>(true)); | |
| } | |
| obj->Write(line); | |
| if (obj->GetType() == OBJECT_BASE) | |
| line->AddParam("run", MakeUnique<CLevelParserParam>(3)); // stops and open (PARAM_FIXSCENE) | |
| if (obj->Implements(ObjectInterfaceType::ProgramStorage)) | |
| { | |
| CProgramStorageObject* programStorage = dynamic_cast<CProgramStorageObject*>(obj); | |
| if (programStorage->GetProgramStorageIndex() >= 0) | |
| { | |
| programStorage->SaveAllProgramsForSavedScene(line, programDir); | |
| } | |
| else | |
| { | |
| // Probably an object created after the scene started, not loaded from level file | |
| // This means it doesn't normally store programs so it doesn't have program storage id assigned | |
| programStorage->SetProgramStorageIndex(999-objRank); // Set something that won't collide with normal programs | |
| programStorage->SaveAllProgramsForSavedScene(line, programDir); | |
| programStorage->SetProgramStorageIndex(-1); // Disable again | |
| } | |
| if (obj->Implements(ObjectInterfaceType::Programmable)) | |
| { | |
| int run = dynamic_cast<CProgramStorageObject*>(obj)->GetProgramIndex(dynamic_cast<CProgrammableObject*>(obj)->GetCurrentProgram()); | |
| if (run != -1) | |
| { | |
| line->AddParam("run", MakeUnique<CLevelParserParam>(run+1)); | |
| } | |
| } | |
| } | |
| } | |
| //! Saves the current game | |
| bool CRobotMain::IOWriteScene(std::string filename, std::string filecbot, std::string filescreenshot, const std::string& info, bool emergencySave) | |
| { | |
| if (!emergencySave) | |
| { | |
| // Render the indicator to show that we are working | |
| ShowSaveIndicator(true); | |
| m_app->Render(); // update | |
| } | |
| std::string dirname = filename.substr(0, filename.find_last_of("/")); | |
| CLevelParser levelParser(filename); | |
| CLevelParserLineUPtr line; | |
| line = MakeUnique<CLevelParserLine>("Title"); | |
| line->AddParam("text", MakeUnique<CLevelParserParam>(std::string(info))); | |
| levelParser.AddLine(std::move(line)); | |
| //TODO: Do we need that? It's not used anyway | |
| line = MakeUnique<CLevelParserLine>("Version"); | |
| line->AddParam("maj", MakeUnique<CLevelParserParam>(0)); | |
| line->AddParam("min", MakeUnique<CLevelParserParam>(1)); | |
| levelParser.AddLine(std::move(line)); | |
| line = MakeUnique<CLevelParserLine>("Created"); | |
| line->AddParam("date", MakeUnique<CLevelParserParam>(static_cast<int>(time(nullptr)))); | |
| levelParser.AddLine(std::move(line)); | |
| line = MakeUnique<CLevelParserLine>("Mission"); | |
| line->AddParam("base", MakeUnique<CLevelParserParam>(GetLevelCategoryDir(m_levelCategory))); | |
| if (m_levelCategory == LevelCategory::CustomLevels) | |
| line->AddParam("dir", MakeUnique<CLevelParserParam>(GetCustomLevelDir())); | |
| else | |
| line->AddParam("chap", MakeUnique<CLevelParserParam>(m_levelChap)); | |
| line->AddParam("rank", MakeUnique<CLevelParserParam>(m_levelRank)); | |
| line->AddParam("gametime", MakeUnique<CLevelParserParam>(GetGameTime())); | |
| levelParser.AddLine(std::move(line)); | |
| line = MakeUnique<CLevelParserLine>("Map"); | |
| line->AddParam("zoom", MakeUnique<CLevelParserParam>(m_map->GetZoomMap())); | |
| levelParser.AddLine(std::move(line)); | |
| line = MakeUnique<CLevelParserLine>("DoneResearch"); | |
| line->AddParam("bits", MakeUnique<CLevelParserParam>(static_cast<int>(m_researchDone[0]))); | |
| levelParser.AddLine(std::move(line)); | |
| float sleep, delay, magnetic, progress; | |
| if (m_lightning->GetStatus(sleep, delay, magnetic, progress)) | |
| { | |
| line = MakeUnique<CLevelParserLine>("BlitzMode"); | |
| line->AddParam("sleep", MakeUnique<CLevelParserParam>(sleep)); | |
| line->AddParam("delay", MakeUnique<CLevelParserParam>(delay)); | |
| line->AddParam("magnetic", MakeUnique<CLevelParserParam>(magnetic/g_unit)); | |
| line->AddParam("progress", MakeUnique<CLevelParserParam>(progress)); | |
| levelParser.AddLine(std::move(line)); | |
| } | |
| int objRank = 0; | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (obj->GetType() == OBJECT_TOTO) continue; | |
| if (IsObjectBeingTransported(obj)) continue; | |
| if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast<CDestroyableObject*>(obj)->IsDying()) continue; | |
| if (obj->Implements(ObjectInterfaceType::Carrier)) | |
| { | |
| CObject* cargo = dynamic_cast<CCarrierObject*>(obj)->GetCargo(); | |
| if (cargo != nullptr) // object transported? | |
| { | |
| line = MakeUnique<CLevelParserLine>("CreateFret"); | |
| IOWriteObject(line.get(), cargo, dirname, objRank++); | |
| levelParser.AddLine(std::move(line)); | |
| } | |
| } | |
| if (obj->Implements(ObjectInterfaceType::Powered)) | |
| { | |
| CObject* power = dynamic_cast<CPoweredObject*>(obj)->GetPower(); | |
| if (power != nullptr) // battery transported? | |
| { | |
| line = MakeUnique<CLevelParserLine>("CreatePower"); | |
| IOWriteObject(line.get(), power, dirname, objRank++); | |
| levelParser.AddLine(std::move(line)); | |
| } | |
| } | |
| line = MakeUnique<CLevelParserLine>("CreateObject"); | |
| IOWriteObject(line.get(), obj, dirname, objRank++); | |
| levelParser.AddLine(std::move(line)); | |
| } | |
| try | |
| { | |
| levelParser.Save(); | |
| } | |
| catch (CLevelParserException& e) | |
| { | |
| GetLogger()->Error("Failed to save level state - %s\n", e.what()); | |
| return false; | |
| } | |
| // Writes the file of stacks of execution. | |
| FILE* file = CBot::fOpen((CResourceManager::GetSaveLocation() + "/" + filecbot).c_str(), "wb"); | |
| if (file == nullptr) return false; | |
| long version = 1; | |
| CBot::fWrite(&version, sizeof(long), 1, file); // version of COLOBOT | |
| version = CBot::CBotProgram::GetVersion(); | |
| CBot::fWrite(&version, sizeof(long), 1, file); // version of CBOT | |
| objRank = 0; | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (obj->GetType() == OBJECT_TOTO) continue; | |
| if (IsObjectBeingTransported(obj)) continue; | |
| if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast<CDestroyableObject*>(obj)->IsDying()) continue; | |
| if (!SaveFileStack(obj, file, objRank++)) break; | |
| } | |
| CBot::CBotClass::SaveStaticState(file); | |
| CBot::fClose(file); | |
| if (!emergencySave) | |
| { | |
| ShowSaveIndicator(false); // force hide for screenshot | |
| MouseMode oldMouseMode = m_app->GetMouseMode(); | |
| m_app->SetMouseMode(MOUSE_NONE); // disable the mouse | |
| m_displayText->HideText(true); // hide | |
| m_engine->SetScreenshotMode(true); | |
| m_engine->Render(); // update (but don't show, we're not swapping buffers here!) | |
| m_engine->WriteScreenShot(CResourceManager::GetSaveLocation() + "/" + filescreenshot); //TODO: Use PHYSFS? | |
| m_shotSaving++; | |
| m_engine->SetScreenshotMode(false); | |
| m_displayText->HideText(false); | |
| m_app->SetMouseMode(oldMouseMode); | |
| m_app->ResetTimeAfterLoading(); | |
| } | |
| return true; | |
| } | |
| //! Notifies the user that scene write is finished | |
| void CRobotMain::IOWriteSceneFinished() | |
| { | |
| m_displayText->DisplayError(INFO_WRITEOK, Math::Vector(0.0f,0.0f,0.0f)); | |
| m_shotSaving--; | |
| } | |
| //! Resumes the game | |
| CObject* CRobotMain::IOReadObject(CLevelParserLine *line, const std::string& programDir, const std::string& objCounterText, float objectProgress, int objRank) | |
| { | |
| ObjectCreateParams params = CObject::ReadCreateParams(line); | |
| params.power = -1.0f; | |
| params.id = line->GetParam("id")->AsInt(); | |
| std::string details = objCounterText; | |
| #if DEV_BUILD | |
| // Object categories may spoil the level a bit, so hide them in release builds | |
| details += ": "+CLevelParserParam::FromObjectType(params.type); | |
| #endif | |
| m_ui->GetLoadingScreen()->SetProgress(0.25f+objectProgress*0.7f, RT_LOADING_OBJECTS_SAVED, details); | |
| CObject* obj = m_objMan->CreateObject(params); | |
| if (obj->Implements(ObjectInterfaceType::Old)) | |
| { | |
| COldObject* oldObj = dynamic_cast<COldObject*>(obj); | |
| oldObj->SetPosition(line->GetParam("pos")->AsPoint() * g_unit); | |
| oldObj->SetRotation(line->GetParam("angle")->AsPoint() * Math::DEG_TO_RAD); | |
| } | |
| if (obj->GetType() == OBJECT_BASE) m_base = obj; | |
| obj->Read(line); | |
| int run = line->GetParam("run")->AsInt(-1); | |
| if (run != -1) | |
| { | |
| CAuto* automat = obj->GetAuto(); | |
| if (automat != nullptr) | |
| automat->Start(run); // starts the film | |
| } | |
| if (obj->Implements(ObjectInterfaceType::ProgramStorage)) | |
| { | |
| CProgramStorageObject* programStorage = dynamic_cast<CProgramStorageObject*>(obj); | |
| if (!line->GetParam("programStorageIndex")->IsDefined()) // Backwards compatibility | |
| programStorage->SetProgramStorageIndex(objRank); | |
| programStorage->LoadAllProgramsForSavedScene(line, programDir); | |
| } | |
| return obj; | |
| } | |
| //! Resumes some part of the game | |
| CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) | |
| { | |
| std::string dirname = filename.substr(0, filename.find_last_of("/")); | |
| CLevelParser levelParser(filename); | |
| levelParser.SetLevelPaths(m_levelCategory, m_levelChap, m_levelRank); | |
| levelParser.Load(); | |
| int numObjects = levelParser.CountLines("CreateObject") + levelParser.CountLines("CreatePower") + levelParser.CountLines("CreateFret"); | |
| m_base = nullptr; | |
| CObject* cargo = nullptr; | |
| CObject* power = nullptr; | |
| CObject* sel = nullptr; | |
| int objRank = 0; | |
| int objCounter = 0; | |
| for (auto& line : levelParser.GetLines()) | |
| { | |
| if (line->GetCommand() == "Mission") | |
| m_gameTime = line->GetParam("gametime")->AsFloat(0.0f); | |
| if (line->GetCommand() == "Map") | |
| m_map->ZoomMap(line->GetParam("zoom")->AsFloat()); | |
| if (line->GetCommand() == "DoneResearch") | |
| m_researchDone[0] = line->GetParam("bits")->AsInt(); | |
| if (line->GetCommand() == "BlitzMode") | |
| { | |
| float sleep = line->GetParam("sleep")->AsFloat(); | |
| float delay = line->GetParam("delay")->AsFloat(); | |
| float magnetic = line->GetParam("magnetic")->AsFloat()*g_unit; | |
| float progress = line->GetParam("progress")->AsFloat(); | |
| m_lightning->SetStatus(sleep, delay, magnetic, progress); | |
| } | |
| if (line->GetCommand() == "CreateFret") | |
| { | |
| cargo = IOReadObject(line.get(), dirname, StrUtils::ToString<int>(objCounter+1)+" / "+StrUtils::ToString<int>(numObjects), static_cast<float>(objCounter) / static_cast<float>(numObjects)); | |
| objCounter++; | |
| } | |
| if (line->GetCommand() == "CreatePower") | |
| { | |
| power = IOReadObject(line.get(), dirname, StrUtils::ToString<int>(objCounter+1)+" / "+StrUtils::ToString<int>(numObjects), static_cast<float>(objCounter) / static_cast<float>(numObjects)); | |
| objCounter++; | |
| } | |
| if (line->GetCommand() == "CreateObject") | |
| { | |
| CObject* obj = IOReadObject(line.get(), dirname, StrUtils::ToString<int>(objCounter+1)+" / "+StrUtils::ToString<int>(numObjects), static_cast<float>(objCounter) / static_cast<float>(numObjects), objRank++); | |
| if (line->GetParam("select")->AsBool(false)) | |
| sel = obj; | |
| if (cargo != nullptr) | |
| { | |
| assert(obj->Implements(ObjectInterfaceType::Carrier)); // TODO: exception? | |
| assert(obj->Implements(ObjectInterfaceType::Old)); | |
| dynamic_cast<CCarrierObject*>(obj)->SetCargo(cargo); | |
| auto task = MakeUnique<CTaskManip>(dynamic_cast<COldObject*>(obj)); | |
| task->Start(TMO_AUTO, TMA_GRAB); // holds the object! | |
| } | |
| if (power != nullptr) | |
| { | |
| assert(obj->Implements(ObjectInterfaceType::Powered)); | |
| dynamic_cast<CPoweredObject*>(obj)->SetPower(power); | |
| assert(power->Implements(ObjectInterfaceType::Transportable)); | |
| dynamic_cast<CTransportableObject*>(power)->SetTransporter(obj); | |
| } | |
| cargo = nullptr; | |
| power = nullptr; | |
| objCounter++; | |
| } | |
| } | |
| m_ui->GetLoadingScreen()->SetProgress(0.95f, RT_LOADING_CBOT_SAVE); | |
| // Reads the file of stacks of execution. | |
| FILE* file = CBot::fOpen((CResourceManager::GetSaveLocation() + "/" + filecbot).c_str(), "rb"); | |
| if (file != nullptr) | |
| { | |
| long version; | |
| CBot::fRead(&version, sizeof(long), 1, file); // version of COLOBOT | |
| if (version == 1) | |
| { | |
| CBot::fRead(&version, sizeof(long), 1, file); // version of CBOT | |
| if (version == CBot::CBotProgram::GetVersion()) | |
| { | |
| objRank = 0; | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (obj->GetType() == OBJECT_TOTO) continue; | |
| if (IsObjectBeingTransported(obj)) continue; | |
| if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast<CDestroyableObject*>(obj)->IsDying()) continue; | |
| if (!ReadFileStack(obj, file, objRank++)) break; | |
| } | |
| } | |
| } | |
| CBot::CBotClass::RestoreStaticState(file); | |
| CBot::fClose(file); | |
| } | |
| m_ui->GetLoadingScreen()->SetProgress(1.0f, RT_LOADING_FINISHED); | |
| return sel; | |
| } | |
| //! Changes current player | |
| void CRobotMain::SelectPlayer(std::string playerName) | |
| { | |
| assert(!playerName.empty()); | |
| m_playerProfile = MakeUnique<CPlayerProfile>(playerName); | |
| SetGlobalGamerName(playerName); | |
| } | |
| CPlayerProfile* CRobotMain::GetPlayerProfile() | |
| { | |
| return m_playerProfile.get(); | |
| } | |
| //! Resets all objects to their original position | |
| void CRobotMain::ResetObject() | |
| { | |
| // schedule reset during next frame | |
| m_resetCreate = true; | |
| } | |
| //! Resets all objects to their original position | |
| void CRobotMain::ResetCreate() | |
| { | |
| SaveAllScript(); | |
| // Removes all bullets in progress. | |
| m_particle->DeleteParticle(Gfx::PARTIGUN1); | |
| m_particle->DeleteParticle(Gfx::PARTIGUN2); | |
| m_particle->DeleteParticle(Gfx::PARTIGUN3); | |
| m_particle->DeleteParticle(Gfx::PARTIGUN4); | |
| DeselectAll(); // removes the control buttons | |
| DeleteAllObjects(); // removes all the current 3D Scene | |
| m_particle->FlushParticle(); | |
| m_terrain->FlushBuildingLevel(); | |
| m_camera->SetType(Gfx::CAM_TYPE_NULL); | |
| try | |
| { | |
| CreateScene(m_ui->GetSceneSoluce(), false, true); | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| if (obj->GetAnimateOnReset()) | |
| { | |
| m_engine->GetPyroManager()->Create(Gfx::PT_RESET, obj); | |
| } | |
| } | |
| } | |
| catch (const std::runtime_error& e) | |
| { | |
| LevelLoadingError("An error occurred while trying to reset scene", e); | |
| } | |
| } | |
| //! Updates the audiotracks | |
| void CRobotMain::UpdateAudio(bool frame) | |
| { | |
| for(std::unique_ptr<CAudioChangeCondition>& audioChange : m_audioChange) | |
| { | |
| if (audioChange->changed) continue; | |
| if (audioChange->Check()) | |
| { | |
| GetLogger()->Info("Changing music to \"%s\"\n", audioChange->music.c_str()); | |
| m_sound->PlayMusic(audioChange->music, audioChange->repeat); | |
| audioChange->changed = true; | |
| } | |
| } | |
| } | |
| //! Set mission result from LevelController script | |
| void CRobotMain::SetMissionResultFromScript(Error result, float delay) | |
| { | |
| m_endTakeWinDelay = delay; | |
| m_endTakeLostDelay = delay; | |
| m_missionResult = result; | |
| m_missionResultFromScript = true; | |
| } | |
| Error CRobotMain::ProcessEndMissionTakeForGroup(std::vector<CSceneEndCondition*>& endTakes) | |
| { | |
| Error finalResult = ERR_OK; | |
| bool hasWinningConditions = false; | |
| for (CSceneEndCondition* endTake : endTakes) | |
| { | |
| Error result = endTake->GetMissionResult(); | |
| if (endTake->lost < 0) | |
| hasWinningConditions = true; | |
| if (result == ERR_OK && endTake->immediat) | |
| { | |
| hasWinningConditions = true; | |
| finalResult = result; | |
| break; | |
| } | |
| if (result != ERR_OK) | |
| { | |
| finalResult = result; | |
| break; | |
| } | |
| } | |
| if (finalResult == ERR_OK && !hasWinningConditions) finalResult = ERR_MISSION_NOTERM; // Never end mission without ending conditions | |
| return finalResult; | |
| } | |
| //! Process EndMissionTake commands, result is stored in m_missionResult | |
| //! If return value is different than ERR_MISSION_NOTERM, assume the mission is finished and pass on the result | |
| Error CRobotMain::ProcessEndMissionTake() | |
| { | |
| bool timeout = false; | |
| if (m_missionResult != INFO_LOST && m_missionResult != INFO_LOSTq) | |
| { | |
| if (m_endTakeTimeout >= 0.0f) | |
| { | |
| // Use the mission timer if available, or global mission time otherwise | |
| // Useful for exercises where the time starts when you start the program, not the mission itself | |
| float currentTime = m_missionTimerEnabled ? m_missionTimer : m_gameTime; | |
| if (currentTime > m_endTakeTimeout) | |
| { | |
| m_missionResult = INFO_LOST; | |
| timeout = true; | |
| } | |
| } | |
| } | |
| // Sort end conditions by teams | |
| std::map<int, std::vector<CSceneEndCondition*>> teamsEndTake; | |
| for (std::unique_ptr<CSceneEndCondition>& endTake : m_endTake) | |
| teamsEndTake[endTake->winTeam].push_back(endTake.get()); | |
| // This is just a smart way to check if we have any map values other than 0 defined | |
| bool usesTeamConditions = teamsEndTake.size() > teamsEndTake.count(0); | |
| if (!usesTeamConditions) | |
| { | |
| if (!timeout) | |
| m_missionResult = ProcessEndMissionTakeForGroup(teamsEndTake[0]); | |
| if (m_missionResult != INFO_LOST && m_missionResult != INFO_LOSTq) | |
| { | |
| if (m_endTakeResearch != 0) | |
| { | |
| if (m_endTakeResearch != (m_endTakeResearch&m_researchDone[0])) | |
| { | |
| m_missionResult = ERR_MISSION_NOTERM; | |
| } | |
| } | |
| } | |
| } | |
| else | |
| { | |
| assert(m_endTakeResearch == 0); // TODO: Add support for per-team EndTakeResearch | |
| // Special handling for teams | |
| m_missionResult = ERR_MISSION_NOTERM; | |
| if (GetAllActiveTeams().empty() || timeout) | |
| { | |
| GetLogger()->Info("All teams died, mission ended\n"); | |
| if (m_scoreboard) | |
| { | |
| std::string title, text, details_line; | |
| GetResource(RES_TEXT, RT_SCOREBOARD_RESULTS, title); | |
| if (m_missionTimerEnabled && m_missionTimerStarted) | |
| { | |
| GetResource(RES_TEXT, RT_SCOREBOARD_RESULTS_TIME, text); | |
| text = StrUtils::Format(text.c_str(), TimeFormat(m_missionTimer).c_str()); | |
| } | |
| else | |
| { | |
| GetResource(RES_TEXT, RT_SCOREBOARD_RESULTS_TEXT, text); | |
| } | |
| GetResource(RES_TEXT, RT_SCOREBOARD_RESULTS_LINE, details_line); | |
| std::string details = ""; | |
| for (std::pair<int, CScoreboard::Score> team : m_scoreboard->GetSortedScores()) | |
| { | |
| if (!details.empty()) | |
| details += ", "; | |
| details += StrUtils::Format(details_line.c_str(), GetTeamName(team.first).c_str(), team.second.points); | |
| } | |
| m_ui->GetDialog()->StartInformation( | |
| title, | |
| text, | |
| details, | |
| false, true, | |
| [&]() | |
| { | |
| ChangePhase(PHASE_WIN); | |
| } | |
| ); | |
| m_endTakeWinDelay = 0.0f; | |
| m_missionResult = ERR_OK; | |
| } | |
| else | |
| { | |
| m_missionResult = INFO_LOST; | |
| } | |
| } | |
| else | |
| { | |
| for (auto it : teamsEndTake) | |
| { | |
| int team = it.first; | |
| if (team == 0) continue; | |
| if (m_teamFinished[team]) continue; | |
| Error result = ProcessEndMissionTakeForGroup(it.second); | |
| if (result == INFO_LOST || result == INFO_LOSTq) | |
| { | |
| GetLogger()->Info("Team %d lost\n", team); | |
| std::string text; | |
| GetResource(RES_ERR, INFO_TEAM_DEAD, text); | |
| text = StrUtils::Format(text.c_str(), GetTeamName(team).c_str()); | |
| m_displayText->DisplayText(text.c_str(), Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 10.0f, Ui::TT_ERROR); | |
| m_displayText->SetEnable(false); // To prevent "bot destroyed" messages | |
| m_objMan->DestroyTeam(team); | |
| m_displayText->SetEnable(true); | |
| m_teamFinished[team] = true; | |
| } | |
| else if (result == ERR_OK) | |
| { | |
| /*if (m_winDelay == 0.0f) | |
| { | |
| GetLogger()->Info("Team %d won\n", team); | |
| m_displayText->DisplayText(("<<< Team "+boost::lexical_cast<std::string>(team)+" won the game >>>").c_str(), Math::Vector(0.0f,0.0f,0.0f)); | |
| if (m_missionTimerEnabled && m_missionTimerStarted) | |
| { | |
| GetLogger()->Info("Mission time: %s\n", TimeFormat(m_missionTimer).c_str()); | |
| m_displayText->DisplayText(("Time: " + TimeFormat(m_missionTimer)).c_str(), Math::Vector(0.0f,0.0f,0.0f)); | |
| } | |
| m_missionTimerEnabled = m_missionTimerStarted = false; | |
| m_winDelay = m_endTakeWinDelay; // wins in two seconds | |
| m_lostDelay = 0.0f; | |
| m_displayText->SetEnable(false); | |
| } | |
| m_missionResult = ERR_OK; | |
| return ERR_OK;*/ | |
| GetLogger()->Info("Team %d finished\n", team); | |
| std::string text; | |
| GetResource(RES_ERR, INFO_TEAM_FINISH, text); | |
| text = StrUtils::Format(text.c_str(), GetTeamName(team).c_str()); | |
| m_displayText->DisplayText(text.c_str(), Math::Vector(0.0f,0.0f,0.0f)); | |
| if (m_scoreboard) | |
| m_scoreboard->ProcessEndTake(team); | |
| m_objMan->DestroyTeam(team, DestructionType::Win); | |
| m_teamFinished[team] = true; | |
| if (m_endTakeTeamImmediateWin) | |
| { | |
| // All other teams fail | |
| for(int other_team : GetAllActiveTeams()) | |
| { | |
| m_displayText->SetEnable(false); // To prevent "bot destroyed" messages | |
| m_objMan->DestroyTeam(other_team); | |
| m_displayText->SetEnable(true); | |
| m_teamFinished[other_team] = true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return ERR_MISSION_NOTERM; | |
| } | |
| //! Checks if the mission is over | |
| Error CRobotMain::CheckEndMission(bool frame) | |
| { | |
| // Process EndMissionTake, unless we are using LevelController script for processing ending conditions | |
| if (!m_missionResultFromScript) | |
| { | |
| Error result = ProcessEndMissionTake(); | |
| if (result != ERR_MISSION_NOTERM) return result; | |
| } | |
| // Take action depending on m_missionResult | |
| if (m_missionResult == INFO_LOSTq) | |
| { | |
| if (m_lostDelay == 0.0f) | |
| { | |
| m_lostDelay = 0.1f; // lost immediately | |
| m_winDelay = 0.0f; | |
| } | |
| m_missionTimerEnabled = m_missionTimerStarted = false; | |
| m_displayText->SetEnable(false); | |
| return INFO_LOSTq; | |
| } | |
| if (m_missionResult == INFO_LOST) | |
| { | |
| if (m_lostDelay == 0.0f) | |
| { | |
| m_displayText->DisplayError(INFO_LOST, Math::Vector(0.0f,0.0f,0.0f)); | |
| m_lostDelay = m_endTakeLostDelay; // lost in 6 seconds | |
| m_winDelay = 0.0f; | |
| } | |
| m_missionTimerEnabled = m_missionTimerStarted = false; | |
| m_displayText->SetEnable(false); | |
| return INFO_LOST; | |
| } | |
| if (m_missionResult == ERR_OK) | |
| { | |
| if (m_endTakeWinDelay == -1.0f && m_winDelay == 0.0f) | |
| { | |
| m_winDelay = 1.0f; // wins in one second | |
| m_lostDelay = 0.0f; | |
| m_missionTimerEnabled = m_missionTimerStarted = false; | |
| m_displayText->SetEnable(false); | |
| return ERR_OK; // mission ended | |
| } | |
| if (frame) | |
| { | |
| if (m_base != nullptr && !m_endTakeImmediat) | |
| { | |
| assert(m_base->Implements(ObjectInterfaceType::Controllable)); | |
| if(dynamic_cast<CControllableObject*>(m_base)->GetSelectable()) | |
| return ERR_MISSION_NOTERM; | |
| } | |
| } | |
| if (m_winDelay == 0.0f) | |
| { | |
| m_displayText->DisplayError(INFO_WIN, Math::Vector(0.0f,0.0f,0.0f)); | |
| if (m_missionTimerEnabled && m_missionTimerStarted) | |
| { | |
| GetLogger()->Info("Mission time: %s\n", TimeFormat(m_missionTimer).c_str()); | |
| m_displayText->DisplayText(("Time: " + TimeFormat(m_missionTimer)).c_str(), Math::Vector(0.0f,0.0f,0.0f)); | |
| } | |
| m_missionTimerEnabled = m_missionTimerStarted = false; | |
| m_winDelay = m_endTakeWinDelay; // wins in two seconds | |
| m_lostDelay = 0.0f; | |
| } | |
| m_displayText->SetEnable(false); | |
| return ERR_OK; // mission ended | |
| } | |
| else | |
| { | |
| m_displayText->SetEnable(true); | |
| return ERR_MISSION_NOTERM; | |
| } | |
| } | |
| //! Returns the list instructions required in CBot program in level | |
| const std::map<std::string, MinMax>& CRobotMain::GetObligatoryTokenList() | |
| { | |
| return m_obligatoryTokens; | |
| } | |
| //! Indicates whether it is possible to control a driving robot | |
| bool CRobotMain::GetTrainerPilot() | |
| { | |
| return m_cheatTrainerPilot; | |
| } | |
| //! Indicates whether the scene is fixed, without interaction | |
| bool CRobotMain::GetFixScene() | |
| { | |
| return m_fixScene; | |
| } | |
| const std::string& CRobotMain::GetScriptName() | |
| { | |
| return m_scriptName; | |
| } | |
| const std::string& CRobotMain::GetScriptFile() | |
| { | |
| return m_scriptFile; | |
| } | |
| bool CRobotMain::GetShowSoluce() | |
| { | |
| return m_cheatShowSoluce; | |
| } | |
| bool CRobotMain::GetSceneSoluce() | |
| { | |
| if (m_infoFilename[SATCOM_SOLUCE][0] == 0) return false; | |
| return m_ui->GetSceneSoluce(); | |
| } | |
| bool CRobotMain::GetShowAll() | |
| { | |
| return m_cheatAllMission; | |
| } | |
| bool CRobotMain::GetRadar() | |
| { | |
| if (m_cheatRadar) | |
| return true; | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| ObjectType type = obj->GetType(); | |
| if (type == OBJECT_RADAR && !obj->GetLock()) | |
| return true; | |
| } | |
| return false; | |
| } | |
| MissionType CRobotMain::GetMissionType() | |
| { | |
| return m_missionType; | |
| } | |
| //! Returns the representation to use for the player | |
| int CRobotMain::GetGamerFace() | |
| { | |
| return m_playerProfile->GetApperance().face; | |
| } | |
| //! Returns the representation to use for the player | |
| int CRobotMain::GetGamerGlasses() | |
| { | |
| return m_playerProfile->GetApperance().glasses; | |
| } | |
| //! Returns the mode with just the head | |
| bool CRobotMain::GetGamerOnlyHead() | |
| { | |
| return m_ui->GetGamerOnlyHead(); | |
| } | |
| //! Returns the angle of presentation | |
| float CRobotMain::GetPersoAngle() | |
| { | |
| return m_ui->GetPersoAngle(); | |
| } | |
| void CRobotMain::SetLevel(LevelCategory cat, int chap, int rank) | |
| { | |
| GetLogger()->Debug("Change level to %s %d %d\n", GetLevelCategoryDir(cat).c_str(), chap, rank); | |
| m_levelCategory = cat; | |
| m_levelChap = chap; | |
| m_levelRank = rank; | |
| m_levelFile = CLevelParser::BuildScenePath(m_levelCategory, m_levelChap, m_levelRank); | |
| } | |
| LevelCategory CRobotMain::GetLevelCategory() | |
| { | |
| return m_levelCategory; | |
| } | |
| int CRobotMain::GetLevelChap() | |
| { | |
| return m_levelChap; | |
| } | |
| int CRobotMain::GetLevelRank() | |
| { | |
| return m_levelRank; | |
| } | |
| //! Returns folder name of the scene that user selected to play. | |
| std::string CRobotMain::GetCustomLevelDir() | |
| { | |
| assert(m_levelCategory == LevelCategory::CustomLevels); | |
| return m_ui->GetCustomLevelName(m_levelChap); | |
| } | |
| void CRobotMain::SetReadScene(std::string path) | |
| { | |
| m_sceneReadPath = path; | |
| } | |
| void CRobotMain::UpdateChapterPassed() | |
| { | |
| return m_ui->UpdateChapterPassed(); | |
| } | |
| //! Changes game speed | |
| void CRobotMain::SetSpeed(float speed) | |
| { | |
| speed = Math::Clamp(speed, MIN_SPEED, MAX_SPEED); | |
| m_app->SetSimulationSpeed(speed); | |
| UpdateSpeedLabel(); | |
| } | |
| float CRobotMain::GetSpeed() | |
| { | |
| return m_app->GetSimulationSpeed(); | |
| } | |
| void CRobotMain::UpdateSpeedLabel() | |
| { | |
| Ui::CButton* pb = static_cast<Ui::CButton*>(m_interface->SearchControl(EVENT_SPEED)); | |
| float speed = m_app->GetSimulationSpeed(); | |
| if (pb != nullptr) | |
| { | |
| if (speed == 1.0f) | |
| { | |
| pb->ClearState(Ui::STATE_VISIBLE); | |
| } | |
| else | |
| { | |
| char text[10]; | |
| sprintf(text, "x%.1f", speed); | |
| pb->SetName(text); | |
| pb->SetState(Ui::STATE_VISIBLE); | |
| } | |
| } | |
| } | |
| bool CRobotMain::CreateShortcuts() | |
| { | |
| if (m_phase != PHASE_SIMUL) return false; | |
| if (m_ui->GetLoadingScreen()->IsVisible()) return false; | |
| if (!m_shortCut) return false; | |
| return m_short->CreateShortcuts(); | |
| } | |
| //! Updates the map | |
| void CRobotMain::UpdateMap() | |
| { | |
| m_map->UpdateMap(); | |
| } | |
| //! Indicates whether the mini-map is visible | |
| bool CRobotMain::GetShowMap() | |
| { | |
| return m_mapShow; | |
| } | |
| //! Management of the lock mode for movies | |
| void CRobotMain::SetMovieLock(bool lock) | |
| { | |
| m_movieLock = lock; | |
| CreateShortcuts(); | |
| m_map->ShowMap(!m_movieLock && m_mapShow && !m_ui->GetLoadingScreen()->IsVisible()); | |
| if (m_movieLock) HiliteClear(); | |
| } | |
| bool CRobotMain::GetMovieLock() | |
| { | |
| return m_movieLock; | |
| } | |
| bool CRobotMain::GetInfoLock() | |
| { | |
| return m_displayInfo != nullptr; // info in progress? | |
| } | |
| //! Management of the blocking of the call of SatCom | |
| void CRobotMain::SetSatComLock(bool lock) | |
| { | |
| m_satComLock = lock; | |
| } | |
| bool CRobotMain::GetSatComLock() | |
| { | |
| return m_satComLock; | |
| } | |
| //! Management of the lock mode for the edition | |
| void CRobotMain::SetEditLock(bool lock, bool edit) | |
| { | |
| m_editLock = lock; | |
| CreateShortcuts(); | |
| // Do not remove the card if it contains a still image. | |
| if (!lock || !m_map->GetFixImage()) | |
| m_map->ShowMap(!m_editLock && m_mapShow); | |
| m_displayText->HideText(lock); | |
| m_input->ResetKeyStates(); | |
| if (m_editLock) | |
| HiliteClear(); | |
| else | |
| m_editFull = false; | |
| } | |
| bool CRobotMain::GetEditLock() | |
| { | |
| return m_editLock; | |
| } | |
| //! Management of the fullscreen mode during editing | |
| void CRobotMain::SetEditFull(bool full) | |
| { | |
| m_editFull = full; | |
| } | |
| bool CRobotMain::GetEditFull() | |
| { | |
| return m_editFull; | |
| } | |
| //! Indicates whether mouse is on an friend object, on which we should not shoot | |
| void CRobotMain::SetFriendAim(bool friendAim) | |
| { | |
| m_friendAim = friendAim; | |
| } | |
| bool CRobotMain::GetFriendAim() | |
| { | |
| return m_friendAim; | |
| } | |
| //! Starts music with a mission | |
| void CRobotMain::StartMusic() | |
| { | |
| GetLogger()->Debug("Starting music...\n"); | |
| if (m_audioTrack != "") | |
| { | |
| m_sound->PlayMusic(m_audioTrack, m_audioRepeat, 0.0f); | |
| } | |
| } | |
| void CRobotMain::UpdatePause(PauseType pause) | |
| { | |
| m_engine->SetPause(pause & PAUSE_ENGINE); | |
| m_camera->SetFreeze(pause & PAUSE_CAMERA); | |
| m_sound->MuteAll(pause & PAUSE_MUTE_SOUND); | |
| CreateShortcuts(); | |
| if (pause != PAUSE_NONE) HiliteClear(); | |
| } | |
| void CRobotMain::UpdatePauseMusic(PauseMusic music) | |
| { | |
| switch (music) | |
| { | |
| case PAUSE_MUSIC_NONE: | |
| m_sound->StopPauseMusic(); | |
| break; | |
| case PAUSE_MUSIC_EDITOR: | |
| if (m_editorTrack != "") | |
| m_sound->PlayPauseMusic(m_editorTrack, m_editorRepeat); | |
| break; | |
| case PAUSE_MUSIC_SATCOM: | |
| if (m_satcomTrack != "") | |
| m_sound->PlayPauseMusic(m_satcomTrack, m_satcomRepeat); | |
| break; | |
| } | |
| } | |
| //! Removes hilite and tooltip | |
| void CRobotMain::ClearInterface() | |
| { | |
| HiliteClear(); // removes setting evidence | |
| m_tooltipName.clear(); // really removes the tooltip | |
| } | |
| void CRobotMain::DisplayError(Error err, CObject* pObj, float time) | |
| { | |
| m_displayText->DisplayError(err, pObj, time); | |
| } | |
| void CRobotMain::DisplayError(Error err, Math::Vector goal, float height, float dist, float time) | |
| { | |
| m_displayText->DisplayError(err, goal, height, dist, time); | |
| } | |
| void CRobotMain::UpdateCustomLevelList() | |
| { | |
| m_ui->UpdateCustomLevelList(); | |
| } | |
| std::string CRobotMain::GetCustomLevelName(int id) | |
| { | |
| return m_ui->GetCustomLevelName(id); | |
| } | |
| const std::vector<std::string>& CRobotMain::GetCustomLevelList() | |
| { | |
| return m_ui->GetCustomLevelList(); | |
| } | |
| bool CRobotMain::IsLoading() | |
| { | |
| return m_ui->GetLoadingScreen()->IsVisible(); | |
| } | |
| void CRobotMain::StartMissionTimer() | |
| { | |
| if (m_missionTimerEnabled && !m_missionTimerStarted) | |
| { | |
| GetLogger()->Info("Starting mission timer...\n"); | |
| m_missionTimerStarted = true; | |
| } | |
| } | |
| void CRobotMain::SetAutosave(bool enable) | |
| { | |
| if (m_autosave == enable) return; | |
| m_autosave = enable; | |
| m_autosaveLast = m_gameTimeAbsolute; | |
| } | |
| bool CRobotMain::GetAutosave() | |
| { | |
| return m_autosave; | |
| } | |
| void CRobotMain::SetAutosaveInterval(int interval) | |
| { | |
| if (m_autosaveInterval == interval) return; | |
| m_autosaveInterval = interval; | |
| m_autosaveLast = m_gameTimeAbsolute; | |
| } | |
| int CRobotMain::GetAutosaveInterval() | |
| { | |
| return m_autosaveInterval; | |
| } | |
| void CRobotMain::SetAutosaveSlots(int slots) | |
| { | |
| if (m_autosaveSlots == slots) return; | |
| m_autosaveSlots = slots; | |
| } | |
| int CRobotMain::GetAutosaveSlots() | |
| { | |
| return m_autosaveSlots; | |
| } | |
| // Remove oldest saves with autosave prefix | |
| void CRobotMain::AutosaveRotate() | |
| { | |
| if (m_playerProfile == nullptr) | |
| return; | |
| GetLogger()->Debug("Rotate autosaves...\n"); | |
| auto saveDirs = CResourceManager::ListDirectories(m_playerProfile->GetSaveDir()); | |
| const std::string autosavePrefix = "autosave"; | |
| std::vector<std::string> autosaves; | |
| std::copy_if(saveDirs.begin(), saveDirs.end(), std::back_inserter(autosaves), [&](const std::string &save) | |
| { | |
| return save.substr(0, autosavePrefix.length()) == autosavePrefix; | |
| }); | |
| std::sort(autosaves.begin(), autosaves.end(), std::less<std::string>()); | |
| for (int i = 0; i < static_cast<int>(autosaves.size()) - m_autosaveSlots + 1; i++) | |
| { | |
| CResourceManager::RemoveDirectory(m_playerProfile->GetSaveDir() + "/" + autosaves[i]); | |
| } | |
| } | |
| void CRobotMain::Autosave() | |
| { | |
| AutosaveRotate(); | |
| GetLogger()->Info("Autosave!\n"); | |
| char timestr[100]; | |
| char infostr[100]; | |
| time_t now = time(nullptr); | |
| strftime(timestr, 99, "%y%m%d%H%M%S", localtime(&now)); | |
| strftime(infostr, 99, "%y.%m.%d %H:%M", localtime(&now)); | |
| std::string info = std::string("[AUTOSAVE] ") + infostr; | |
| std::string dir = m_playerProfile->GetSaveFile(std::string("autosave") + timestr); | |
| m_playerProfile->SaveScene(dir, info); | |
| } | |
| void CRobotMain::QuickSave() | |
| { | |
| GetLogger()->Info("Quicksave!\n"); | |
| char infostr[100]; | |
| time_t now = time(nullptr); | |
| strftime(infostr, 99, "%y.%m.%d %H:%M", localtime(&now)); | |
| std::string info = std::string("[QUICKSAVE]") + infostr; | |
| std::string dir = m_playerProfile->GetSaveFile(std::string("quicksave")); | |
| m_playerProfile->SaveScene(dir, info); | |
| } | |
| void CRobotMain::QuickLoad() | |
| { | |
| std::string dir = m_playerProfile->GetSaveFile(std::string("quicksave")); | |
| if(!CResourceManager::Exists(dir)) | |
| { | |
| m_displayText->DisplayError(ERR_NO_QUICK_SLOT, Math::Vector(0.0f,0.0f,0.0f), 15.0f, 60.0f, 1000.0f); | |
| GetLogger()->Debug("Quicksave slot not found\n"); | |
| return; | |
| } | |
| m_playerProfile->LoadScene(dir); | |
| } | |
| void CRobotMain::SetExitAfterMission(bool exit) | |
| { | |
| m_exitAfterMission = exit; | |
| } | |
| bool CRobotMain::CanPlayerInteract() | |
| { | |
| if(GetMissionType() == MISSION_CODE_BATTLE) | |
| { | |
| return !m_codeBattleStarted; | |
| } | |
| return true; | |
| } | |
| const std::string NO_TEAM_NAME = "Team"; | |
| const std::string& CRobotMain::GetTeamName(int id) | |
| { | |
| if(m_teamNames.count(id) == 0) return NO_TEAM_NAME; | |
| return m_teamNames[id]; | |
| } | |
| bool CRobotMain::IsTeamColorDefined(int id) | |
| { | |
| if(id == 0) return false; // Always use default for team 0 | |
| return m_colorNewBot.find(id) != m_colorNewBot.end(); | |
| } | |
| int CRobotMain::GetEnableBuild() | |
| { | |
| return m_build; | |
| } | |
| void CRobotMain::SetEnableBuild(int enableBuild) | |
| { | |
| m_build = enableBuild; | |
| } | |
| int CRobotMain::GetEnableResearch() | |
| { | |
| return m_researchEnable; | |
| } | |
| void CRobotMain::SetEnableResearch(int enableResearch) | |
| { | |
| m_researchEnable = enableResearch; | |
| } | |
| int CRobotMain::GetDoneResearch(int team) | |
| { | |
| return m_researchDone[team]; | |
| } | |
| void CRobotMain::SetDoneResearch(int doneResearch, int team) | |
| { | |
| m_researchDone[team] = doneResearch; | |
| } | |
| bool CRobotMain::IsBuildingEnabled(BuildType type) | |
| { | |
| return (m_build & type) != 0; | |
| } | |
| bool CRobotMain::IsBuildingEnabled(ObjectType type) | |
| { | |
| if(type == OBJECT_DERRICK) return IsBuildingEnabled(BUILD_DERRICK); | |
| if(type == OBJECT_FACTORY) return IsBuildingEnabled(BUILD_FACTORY); | |
| if(type == OBJECT_STATION) return IsBuildingEnabled(BUILD_STATION); | |
| if(type == OBJECT_CONVERT) return IsBuildingEnabled(BUILD_CONVERT); | |
| if(type == OBJECT_REPAIR) return IsBuildingEnabled(BUILD_REPAIR); | |
| if(type == OBJECT_TOWER) return IsBuildingEnabled(BUILD_TOWER); | |
| if(type == OBJECT_RESEARCH) return IsBuildingEnabled(BUILD_RESEARCH); | |
| if(type == OBJECT_RADAR) return IsBuildingEnabled(BUILD_RADAR); | |
| if(type == OBJECT_ENERGY) return IsBuildingEnabled(BUILD_ENERGY); | |
| if(type == OBJECT_LABO) return IsBuildingEnabled(BUILD_LABO); | |
| if(type == OBJECT_NUCLEAR) return IsBuildingEnabled(BUILD_NUCLEAR); | |
| if(type == OBJECT_INFO) return IsBuildingEnabled(BUILD_INFO); | |
| if(type == OBJECT_PARA) return IsBuildingEnabled(BUILD_PARA); | |
| if(type == OBJECT_DESTROYER) return IsBuildingEnabled(BUILD_DESTROYER); | |
| return false; | |
| } | |
| bool CRobotMain::IsResearchEnabled(ResearchType type) | |
| { | |
| return (m_researchEnable & type) != 0; | |
| } | |
| bool CRobotMain::IsResearchDone(ResearchType type, int team) | |
| { | |
| if(team != 0 && m_researchDone.count(team) == 0) | |
| { | |
| // Initialize with defaults | |
| m_researchDone[team] = m_researchDone[0]; | |
| } | |
| return (m_researchDone[team] & type) != 0; | |
| } | |
| void CRobotMain::MarkResearchDone(ResearchType type, int team) | |
| { | |
| if(team != 0 && m_researchDone.count(team) == 0) | |
| { | |
| // Initialize with defaults | |
| m_researchDone[team] = m_researchDone[0]; | |
| } | |
| m_researchDone[team] |= type; | |
| if(team == 0) | |
| { | |
| m_playerProfile->SetFreeGameResearchUnlock(m_playerProfile->GetFreeGameResearchUnlock() | m_researchDone[0]); | |
| } | |
| } | |
| Error CRobotMain::CanBuildError(ObjectType type, int team) | |
| { | |
| if(!IsBuildingEnabled(type)) return ERR_BUILD_DISABLED; | |
| if(type == OBJECT_TOWER && !IsResearchDone(RESEARCH_TOWER, team)) return ERR_BUILD_RESEARCH; | |
| if(type == OBJECT_NUCLEAR && !IsResearchDone(RESEARCH_ATOMIC, team)) return ERR_BUILD_RESEARCH; | |
| return ERR_OK; | |
| } | |
| Error CRobotMain::CanFactoryError(ObjectType type, int team) | |
| { | |
| ToolType tool = GetToolFromObject(type); | |
| DriveType drive = GetDriveFromObject(type); | |
| if (tool == ToolType::Sniffer && !IsResearchDone(RESEARCH_SNIFFER, team)) return ERR_BUILD_RESEARCH; | |
| if (tool == ToolType::Shooter && !IsResearchDone(RESEARCH_CANON, team)) return ERR_BUILD_RESEARCH; | |
| if (tool == ToolType::OrganicShooter && !IsResearchDone(RESEARCH_iGUN, team)) return ERR_BUILD_RESEARCH; | |
| if (drive == DriveType::Tracked && !IsResearchDone(RESEARCH_TANK, team)) return ERR_BUILD_RESEARCH; | |
| if (drive == DriveType::Winged && !IsResearchDone(RESEARCH_FLY, team)) return ERR_BUILD_RESEARCH; | |
| if (drive == DriveType::Legged && !IsResearchDone(RESEARCH_iPAW, team)) return ERR_BUILD_RESEARCH; | |
| if (drive == DriveType::BigTracked && !IsResearchDone(RESEARCH_TANK, team)) return ERR_BUILD_RESEARCH; // NOTE: Subber is not BigTracked! It currently counts as Other | |
| if (type == OBJECT_MOBILErt && !IsResearchDone(RESEARCH_THUMP, team)) return ERR_BUILD_RESEARCH; | |
| if (type == OBJECT_MOBILErc && !IsResearchDone(RESEARCH_PHAZER, team)) return ERR_BUILD_RESEARCH; | |
| if (type == OBJECT_MOBILErr && !IsResearchDone(RESEARCH_RECYCLER, team)) return ERR_BUILD_RESEARCH; | |
| if (type == OBJECT_MOBILErs && !IsResearchDone(RESEARCH_SHIELD, team)) return ERR_BUILD_RESEARCH; | |
| if (type == OBJECT_MOBILEsa && !IsResearchDone(RESEARCH_SUBM, team)) return ERR_BUILD_DISABLED; // Can be only researched manually in Scene file | |
| return ERR_OK; | |
| } | |
| void CRobotMain::PushToSelectionHistory(CObject* obj) | |
| { | |
| if (!m_selectionHistory.empty() && m_selectionHistory.back() == obj) | |
| return; // already in history | |
| m_selectionHistory.push_back(obj); | |
| if (m_selectionHistory.size() > 50) // to avoid infinite growth | |
| m_selectionHistory.pop_front(); | |
| } | |
| CObject* CRobotMain::PopFromSelectionHistory() | |
| { | |
| if (m_selectionHistory.empty()) | |
| return nullptr; | |
| CObject* obj = m_selectionHistory.back(); | |
| m_selectionHistory.pop_back(); | |
| return obj; | |
| } | |
| void CRobotMain::RemoveFromSelectionHistory(CObject* object) | |
| { | |
| auto it = std::remove_if(m_selectionHistory.begin(), m_selectionHistory.end(), | |
| [object](const CObject* obj) { return obj == object; }); | |
| m_selectionHistory.erase(it, m_selectionHistory.end()); | |
| } | |
| float CRobotMain::GetGlobalMagnifyDamage() | |
| { | |
| return m_globalMagnifyDamage; | |
| } | |
| // Beginning of the effect when the instruction "detect" is used. | |
| void CRobotMain::StartDetectEffect(COldObject* object, CObject* target) | |
| { | |
| Math::Matrix* mat; | |
| Math::Vector pos, goal; | |
| Math::Point dim; | |
| mat = object->GetWorldMatrix(0); | |
| pos = Math::Transform(*mat, Math::Vector(2.0f, 3.0f, 0.0f)); | |
| if ( target == nullptr ) | |
| { | |
| goal = Math::Transform(*mat, Math::Vector(50.0f, 3.0f, 0.0f)); | |
| } | |
| else | |
| { | |
| goal = target->GetPosition(); | |
| goal.y += 3.0f; | |
| goal = Math::SegmentPoint(pos, goal, Math::Distance(pos, goal)-3.0f); | |
| } | |
| dim.x = 3.0f; | |
| dim.y = dim.x; | |
| m_particle->CreateRay(pos, goal, Gfx::PARTIRAY2, dim, 0.2f); | |
| if ( target != nullptr ) | |
| { | |
| goal = target->GetPosition(); | |
| goal.y += 3.0f; | |
| goal = Math::SegmentPoint(pos, goal, Math::Distance(pos, goal)-1.0f); | |
| dim.x = 6.0f; | |
| dim.y = dim.x; | |
| m_particle->CreateParticle(goal, Math::Vector(0.0f, 0.0f, 0.0f), dim, | |
| target != nullptr ? Gfx::PARTIGLINT : Gfx::PARTIGLINTr, 0.5f); | |
| } | |
| m_sound->Play(target != nullptr ? SOUND_BUILD : SOUND_RECOVER); | |
| } | |
| void CRobotMain::CreateCodeBattleInterface() | |
| { | |
| if (m_phase == PHASE_SIMUL) | |
| { | |
| Math::Point pos, ddim; | |
| float offset = (ceil(m_viewpoints.size() / 2.0f) * 50); | |
| int numTeams = m_scoreboard ? GetAllTeams().size() : 0; | |
| assert(numTeams < EVENT_SCOREBOARD_MAX-EVENT_SCOREBOARD+1); | |
| float textHeight = m_engine->GetText()->GetHeight(Gfx::FONT_COMMON, Gfx::FONT_SIZE_SMALL); | |
| //window | |
| ddim.x = 100.0f/640.0f; | |
| ddim.y = (100.0f+offset)/480.0f + numTeams * textHeight; | |
| pos.x = 540.0f/640.0f; | |
| pos.y = 100.0f/480.0f; | |
| Ui::CWindow* pw = m_interface->CreateWindows(pos, ddim, 3, EVENT_WINDOW6); | |
| //label text | |
| ddim.x = 100.0f/640.0f; | |
| ddim.y = 16.0f/480.0f; | |
| pos.x = 540.0f/640.0f; | |
| pos.y = (178.0f+offset)/480.0f + numTeams * textHeight; | |
| std::string text; | |
| GetResource(RES_EVENT, EVENT_LABEL_CODE_BATTLE, text); | |
| pw->CreateLabel(pos, ddim, 0, EVENT_LABEL_CODE_BATTLE, text); | |
| //viewpoint selection section | |
| ddim.x = 40.0f/640.0f; | |
| ddim.y = 50.0f/640.0f; | |
| for(unsigned int i = 0; i < m_viewpoints.size(); i++) | |
| { | |
| //create button | |
| pos.x = (550.0f+40.0f*(i%2))/640.0f; | |
| pos.y = (130.0f+offset)/480.0f + numTeams * textHeight - 45.0f*(i/2)/480.0f; | |
| pw->CreateButton(pos, ddim, m_viewpoints[i].button, EventType(EVENT_VIEWPOINT0 + i)); | |
| } | |
| //start/camera button | |
| float titleBarSize = (11.0f/64.0f); // this is from the texture | |
| ddim.x = 80.0f/640.0f; | |
| ddim.y = ((1-titleBarSize)*100.0f-20.0f)/480.0f; | |
| pos.x = 550.0f/640.0f; | |
| pos.y = 110.0f/480.0f; | |
| if (!m_codeBattleStarted) | |
| { | |
| pw->CreateButton(pos, ddim, 21, EVENT_CODE_BATTLE_START); | |
| } | |
| else | |
| { | |
| pw->CreateButton(pos, ddim, 13, EVENT_CODE_BATTLE_SPECTATOR); | |
| } | |
| if (!m_scoreboard) return; | |
| pos.y += ddim.y; | |
| ddim.y = textHeight; | |
| int i = 0; | |
| auto teams = GetAllTeams(); | |
| for (auto it = teams.rbegin(); it != teams.rend(); ++it) | |
| { | |
| int team = *it; | |
| Ui::CControl* pl; | |
| ddim.x = 55.0f/640.0f; | |
| pl = m_codeBattleStarted | |
| ? static_cast<Ui::CControl*>(pw->CreateLabel(pos, ddim, 0, static_cast<EventType>(EVENT_SCOREBOARD+2*(numTeams-i-1)+0), "XXXXX")) | |
| : static_cast<Ui::CControl*>(pw->CreateEdit( pos, ddim, 0, static_cast<EventType>(EVENT_SCOREBOARD+2*(numTeams-i-1)+0))); | |
| pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT); | |
| pl->SetFontSize(m_codeBattleStarted ? Gfx::FONT_SIZE_SMALL : Gfx::FONT_SIZE_SMALL*0.75f); | |
| m_codeBattleStarted ? pl->SetName(GetTeamName(team)) : static_cast<Ui::CEdit*>(pl)->SetText(GetTeamName(team)); | |
| pos.x += 57.5f/640.0f; | |
| ddim.x = 22.5f/640.0f; | |
| pl = m_codeBattleStarted | |
| ? static_cast<Ui::CControl*>(pw->CreateLabel(pos, ddim, 0, static_cast<EventType>(EVENT_SCOREBOARD+2*(numTeams-i-1)+1), "???")) | |
| : static_cast<Ui::CControl*>(pw->CreateEdit( pos, ddim, 0, static_cast<EventType>(EVENT_SCOREBOARD+2*(numTeams-i-1)+1))); | |
| pl->SetTextAlign(Gfx::TEXT_ALIGN_RIGHT); | |
| pl->SetFontSize(m_codeBattleStarted ? Gfx::FONT_SIZE_SMALL : Gfx::FONT_SIZE_SMALL*0.75f); | |
| 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)); | |
| pos.x -= 57.5f/640.0f; | |
| pos.y += ddim.y; | |
| i++; | |
| } | |
| } | |
| } | |
| void CRobotMain::ApplyCodeBattleInterface() | |
| { | |
| assert(GetMissionType() == MISSION_CODE_BATTLE); | |
| if (!m_scoreboard) return; | |
| Ui::CWindow* pw = static_cast<Ui::CWindow*>(m_interface->SearchControl(EVENT_WINDOW6)); | |
| assert(pw != nullptr); | |
| int i = 0; | |
| for (int team : GetAllTeams()) | |
| { | |
| Ui::CEdit* pl; | |
| pl = static_cast<Ui::CEdit*>(pw->SearchControl(static_cast<EventType>(EVENT_SCOREBOARD+2*i+0))); | |
| assert(pl != nullptr); | |
| m_teamNames[team] = pl->GetText(pl->GetTextLength()); | |
| pl = static_cast<Ui::CEdit*>(pw->SearchControl(static_cast<EventType>(EVENT_SCOREBOARD+2*i+1))); | |
| assert(pl != nullptr); | |
| m_scoreboard->SetScore(team, StrUtils::FromString<int>(pl->GetText(pl->GetTextLength()))); | |
| i++; | |
| } | |
| } | |
| void CRobotMain::UpdateCodeBattleInterface() | |
| { | |
| assert(GetMissionType() == MISSION_CODE_BATTLE); | |
| if (!m_scoreboard) return; | |
| Ui::CWindow* pw = static_cast<Ui::CWindow*>(m_interface->SearchControl(EVENT_WINDOW6)); | |
| assert(pw != nullptr); | |
| int i = 0; | |
| for (std::pair<int, CScoreboard::Score> team : m_scoreboard->GetSortedScores()) | |
| { | |
| Ui::CControl* pl; | |
| pl = pw->SearchControl(static_cast<EventType>(EVENT_SCOREBOARD+2*i+0)); | |
| assert(pl != nullptr); | |
| pl->SetName(GetTeamName(team.first)); | |
| pl = pw->SearchControl(static_cast<EventType>(EVENT_SCOREBOARD+2*i+1)); | |
| assert(pl != nullptr); | |
| pl->SetName(StrUtils::ToString<int>(team.second.points)); | |
| i++; | |
| } | |
| } | |
| void CRobotMain::DestroyCodeBattleInterface() | |
| { | |
| m_viewpoints.clear(); | |
| m_interface->DeleteControl(EVENT_WINDOW6); | |
| } | |
| void CRobotMain::SetCodeBattleSpectatorMode(bool mode) | |
| { | |
| // Deselect object, but keep camera attached to it | |
| CObject* obj = DeselectAll(); | |
| if (m_codeBattleSpectator) | |
| obj = m_camera->GetControllingObject(); | |
| m_codeBattleSpectator = mode; | |
| SelectObject(obj, false); // this uses code battle selection mode already | |
| } | |
| void CRobotMain::UpdateDebugCrashSpheres() | |
| { | |
| if (m_debugCrashSpheres) | |
| { | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| for (const auto& crashSphere : obj->GetAllCrashSpheres()) | |
| { | |
| m_engine->RenderDebugSphere(crashSphere.sphere, Math::Matrix{}, Gfx::Color{0.0f, 0.0f, 1.0f, 1.0f}); | |
| } | |
| } | |
| } | |
| } | |
| void CRobotMain::SetDebugCrashSpheres(bool draw) | |
| { | |
| m_debugCrashSpheres = draw; | |
| } | |
| bool CRobotMain::GetDebugCrashSpheres() | |
| { | |
| return m_debugCrashSpheres; | |
| } | |
| void CRobotMain::PushToCommandHistory(std::string cmd) | |
| { | |
| if (!m_commandHistory.empty() && m_commandHistory.front() == cmd) // already in history | |
| return; | |
| m_commandHistory.push_front(cmd); | |
| if (m_commandHistory.size() > 50) // to avoid infinite growth | |
| m_commandHistory.pop_back(); | |
| } | |
| std::string CRobotMain::GetNextFromCommandHistory() | |
| { | |
| if (m_commandHistory.empty() || static_cast<int>(m_commandHistory.size()) <= m_commandHistoryIndex + 1) // no next element | |
| return ""; | |
| return m_commandHistory[++m_commandHistoryIndex]; | |
| } | |
| std::string CRobotMain::GetPreviousFromCommandHistory() | |
| { | |
| if (m_commandHistory.empty() || m_commandHistoryIndex < 1) // first or none element selected | |
| return ""; | |
| return m_commandHistory[--m_commandHistoryIndex]; | |
| } | |
| CScoreboard* CRobotMain::GetScoreboard() | |
| { | |
| return m_scoreboard.get(); | |
| } | |
| std::set<int> CRobotMain::GetAllTeams() | |
| { | |
| std::set<int> teams = GetAllActiveTeams(); | |
| for(auto& it : m_teamFinished) | |
| { | |
| teams.insert(it.first); | |
| } | |
| return teams; | |
| } | |
| std::set<int> CRobotMain::GetAllActiveTeams() | |
| { | |
| std::set<int> teams; | |
| for (CObject* obj : m_objMan->GetAllObjects()) | |
| { | |
| int team = obj->GetTeam(); | |
| if (team == 0) continue; | |
| teams.insert(team); | |
| } | |
| return teams; | |
| } |