colobot/src/level/robotmain.cpp

5982 lines
190 KiB
C++

/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2016, 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/parser/parser.h"
#include "math/const.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 <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
// 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
template<> CRobotMain* CSingleton<CRobotMain>::m_instance = nullptr;
//! 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_selectInsect = false;
m_showSoluce = false;
m_codeBattleInit = false;
m_codeBattleStarted = false;
m_teamNames.clear();
#if DEV_BUILD
m_showAll = true; // for development
#else
m_showAll = false;
#endif
m_cheatRadar = false;
m_fixScene = false;
m_trainerPilot = false;
m_friendAim = false;
m_resetCreate = false;
m_shortCut = true;
m_movieInfoIndex = -1;
m_tooltipPos = Math::Point(0.0f, 0.0f);
m_tooltipName.clear();
m_tooltipTime = 0.0f;
m_endingWinRank = 0;
m_endingLostRank = 0;
m_winTerminate = false;
m_globalMagnifyDamage = 1.0f;
m_autosave = true;
m_autosaveInterval = 5;
m_autosaveSlots = 3;
m_autosaveLast = 0.0f;
m_shotSaving = 0;
m_cameraPan = 0.0f;
m_cameraZoom = 0.0f;
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();
FlushNewScriptName();
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_DIALOG);
m_movie->Flush();
m_movieInfoIndex = -1;
m_cameraPan = 0.0f;
m_cameraZoom = 0.0f;
m_shortCut = true;
}
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_endingWinRank == -1)
{
ChangePhase(PHASE_LEVEL_LIST);
}
else
{
m_winTerminate = (m_endingWinRank == 904);
SetLevel(LevelCategory::Win, 0, m_endingWinRank);
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_COLOBOT);
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_endingLostRank == -1)
{
ChangePhase(PHASE_LEVEL_LIST);
}
else
{
m_winTerminate = false;
SetLevel(LevelCategory::Lost, 0, m_endingLostRank);
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);
RemoteCamera(m_cameraPan, m_cameraZoom, event.rTime);
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();
}
}
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);
}
// 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;
}
return false;
}
if (IsPhaseWithWorld(m_phase))
{
if (data->key == KEY(F11))
{
m_debugMenu->ToggleInterface();
return false;
}
}
}
if (event.type == EVENT_KEY_DOWN &&
event.GetData<KeyEventData>()->key == KEY(RETURN) && m_cmdEdit)
{
char cmd[50];
Ui::CEdit* pe = static_cast<Ui::CEdit*>(m_interface->SearchControl(EVENT_CMD));
if (pe == nullptr) return false;
pe->GetText(cmd, 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);
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>();
KeyCamera(event.type, data->slot);
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)
{
SelectHuman();
}
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_SPEED05)
{
SetSpeed(0.5f);
}
if (data->slot == INPUT_SLOT_SPEED10)
{
SetSpeed(1.0f);
}
if (data->slot == INPUT_SLOT_SPEED15)
{
SetSpeed(1.5f);
}
if (data->slot == INPUT_SLOT_SPEED20)
{
SetSpeed(2.0f);
}
if (data->slot == INPUT_SLOT_SPEED30)
{
SetSpeed(3.0f);
}
if (data->slot == INPUT_SLOT_SPEED40)
{
SetSpeed(4.0f);
}
if (data->slot == INPUT_SLOT_SPEED60)
{
SetSpeed(6.0f);
}
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));
line.AddParam("dir", MakeUnique<CLevelParserParam>(obj->GetRotationY()));
std::stringstream ss;
ss << line;
SDL_SetClipboardText(ss.str().c_str());
}
}
break;
}
case EVENT_KEY_UP:
{
auto data = event.GetData<KeyEventData>();
KeyCamera(event.type, data->slot);
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_MOUSE_BUTTON_UP:
if (event.GetData<MouseButtonEventData>()->button != MOUSE_BUTTON_LEFT) // only left mouse button
break;
m_cameraPan = 0.0f;
m_cameraZoom = 0.0f;
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_CAMERAleft:
m_cameraPan = -1.0f;
break;
case EVENT_OBJECT_CAMERAright:
m_cameraPan = 1.0f;
break;
case EVENT_OBJECT_CAMERAnear:
m_cameraZoom = -1.0f;
break;
case EVENT_OBJECT_CAMERAaway:
m_cameraZoom = 1.0f;
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_trainerPilot = !m_trainerPilot;
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_selectInsect = !m_selectInsect;
return;
}
if (cmd == "showsoluce")
{
m_showSoluce = !m_showSoluce;
m_ui->ShowSoluceUpdate();
return;
}
if (cmd == "allmission")
{
m_showAll = !m_showAll;
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()
{
m_sound->MuteAll(true);
ClearInterface();
m_suspend = m_pause->ActivatePause(PAUSE_ENGINE|PAUSE_HIDE_SHORTCUTS|PAUSE_MUTE_SOUND);
m_engine->SetOverFront(false); // over flat behind
CreateShortcuts();
m_map->ShowMap(false);
m_infoObject = DeselectAll(); // removes the control buttons
m_displayText->HideText(true);
m_suspendInitCamera = m_camera->GetType();
m_camera->SetType(Gfx::CAM_TYPE_DIALOG);
}
//! End of dialogue during the game
void CRobotMain::StopSuspend()
{
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_camera->SetType(m_suspendInitCamera);
}
//! 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;
}
}
//! Updates all the shortcuts
void CRobotMain::UpdateShortcuts()
{
m_short->UpdateShortcuts();
}
//! Returns the object that default was select after the creation of a scene
CObject* CRobotMain::GetSelectObject()
{
if (m_selectObject != nullptr) return m_selectObject;
return SearchHuman();
}
//! Deselects everything, and returns the object that was selected
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());
m_camera->SetDist(dynamic_cast<CControllableObject*>(obj)->GetCameraDist());
}
else
{
m_camera->SetType(Gfx::CAM_TYPE_BACK);
}
CObject* toto = SearchToto();
if (toto != nullptr)
{
assert(toto->Implements(ObjectInterfaceType::Movable));
CMotionToto* mt = static_cast<CMotionToto*>(dynamic_cast<CMovableObject*>(toto)->GetMotion());
mt->SetLinkType(type);
}
}
//! Selects the object aimed by the mouse
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 && !IsSelectable(obj)) 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;
}
//! Deselects the selected object
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();
}
//! Selects the human
void CRobotMain::SelectHuman()
{
SelectObject(SearchHuman());
}
//! Returns the object human
CObject* CRobotMain::SearchHuman()
{
return m_objMan->FindNearest(nullptr, OBJECT_HUMAN);
}
//! Returns the object toto
CObject* CRobotMain::SearchToto()
{
return m_objMan->FindNearest(nullptr, OBJECT_TOTO);
}
//! Returns the nearest selectable object from a given position
CObject* CRobotMain::SearchNearest(Math::Vector pos, CObject* exclu)
{
float min = 100000.0f;
CObject* best = nullptr;
for (CObject* obj : m_objMan->GetAllObjects())
{
if (obj == exclu) continue;
if (!IsSelectable(obj)) continue;
ObjectType type = obj->GetType();
if (type == OBJECT_TOTO) continue;
Math::Vector oPos = obj->GetPosition();
float dist = Math::DistanceProjected(oPos, pos);
if (dist < min)
{
min = dist;
best = obj;
}
}
return best;
}
//! Returns the selected object
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;
}
//! Indicates whether an object is selectable
// TODO: Refactor this, calling CControllableObject::GetSelectable should always be enough
bool CRobotMain::IsSelectable(CObject* obj)
{
if (obj->GetType() == OBJECT_TOTO) return true;
if (!obj->Implements(ObjectInterfaceType::Controllable)) return false;
if (!m_selectInsect)
{
// TODO: Some function in CControllableObject
if ( obj->GetType() == OBJECT_MOTHER ||
obj->GetType() == OBJECT_ANT ||
obj->GetType() == OBJECT_SPIDER ||
obj->GetType() == OBJECT_BEE ||
obj->GetType() == OBJECT_WORM ||
obj->GetType() == OBJECT_MOBILEtg )
{
return false;
}
}
return dynamic_cast<CControllableObject*>(obj)->GetSelectable();
}
//! 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 (IsSelectable(obj))
{
assert(obj->Implements(ObjectInterfaceType::Controllable));
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_COLOBOT, 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_COLOBOT, 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);
}
//! Remote control the camera using the arrow keys
void CRobotMain::KeyCamera(EventType event, InputSlot key)
{
if (event == EVENT_KEY_UP)
{
if (key == INPUT_SLOT_LEFT)
{
m_cameraPan = 0.0f;
}
if (key == INPUT_SLOT_RIGHT)
{
m_cameraPan = 0.0f;
}
if (key == INPUT_SLOT_UP)
{
m_cameraZoom = 0.0f;
}
if (key == INPUT_SLOT_DOWN)
{
m_cameraZoom = 0.0f;
}
}
if (m_phase != PHASE_SIMUL) return;
if (m_editLock) return; // current edition?
if (m_trainerPilot) return;
CObject* obj = GetSelect();
if (obj == nullptr) return;
assert(obj->Implements(ObjectInterfaceType::Controllable));
if (!dynamic_cast<CControllableObject*>(obj)->GetTrainer()) return;
if (event == EVENT_KEY_DOWN)
{
if (key == INPUT_SLOT_LEFT)
{
m_cameraPan = -1.0f;
}
if (key == INPUT_SLOT_RIGHT)
{
m_cameraPan = 1.0f;
}
if (key == INPUT_SLOT_UP)
{
m_cameraZoom = -1.0f;
}
if (key == INPUT_SLOT_DOWN)
{
m_cameraZoom = 1.0f;
}
}
}
//! Panned with the camera if a button is pressed
void CRobotMain::RemoteCamera(float pan, float zoom, float rTime)
{
if (pan != 0.0f)
{
float value = m_camera->GetRemotePan();
value += pan*rTime*1.5f;
m_camera->SetRemotePan(value);
}
if (zoom != 0.0f)
{
float value = m_camera->GetRemoteZoom();
value += zoom*rTime*0.3f;
m_camera->SetRemoteZoom(value);
}
}
//! 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();
}
}
}
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)
{
// TODO: For some reason we're getting one big event with event.rTime > 0.1f after loading before the movie starts?
if (!m_immediatSatCom && !m_beginSatCom && !m_movieLock &&
m_gameTime > 0.1f && m_phase == PHASE_SIMUL)
{
m_displayText->DisplayError(INFO_BEGINSATCOM, Math::Vector(0.0f,0.0f,0.0f));
m_beginSatCom = true; // message appears
}
m_time += event.rTime;
if (!m_movieLock && !m_pause->IsPauseType(PAUSE_ENGINE))
{
m_gameTime += event.rTime;
m_gameTimeAbsolute += m_app->GetRealRelTime() / 1e9f;
}
if (!m_movieLock && !m_pause->IsPauseType(PAUSE_ENGINE) && m_missionTimerStarted)
m_missionTimer += event.rTime;
if (!m_pause->IsPauseType(PAUSE_ENGINE) && m_autosave && m_gameTimeAbsolute >= m_autosaveLast+(m_autosaveInterval*60) && m_phase == PHASE_SIMUL)
{
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();
}
}
}
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);
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)
{
CheckEndMission(true);
UpdateAudio(true);
}
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_winDelay = 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;
CreateCodeBattleInterface();
SetCodeBattleSpectatorMode(true);
m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE));
}
}
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;
}
//! Load the scene for the character
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();
SetLevel(LevelCategory::Perso, 0, 0);
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_endingWinRank = 0;
m_endingLostRank = 0;
m_audioChange.clear();
m_endTake.clear();
m_endTakeImmediat = false;
m_endTakeResearch = 0;
m_endTakeWinDelay = 2.0f;
m_endTakeLostDelay = 2.0f;
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;
try
{
m_ui->GetLoadingScreen()->SetProgress(0.05f, RT_LOADING_PROCESSING);
CLevelParser levelParser(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;
/*
* NOTE: Moving frequently used lines to the top
* may speed up loading
*/
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)
{
// NOTE: The old default was 0, but I think -1 is more correct - 0 means "ending file 000", while -1 means "no ending file"
m_endingWinRank = line->GetParam("win")->AsInt(-1);
m_endingLostRank = line->GetParam("lost")->AsInt(-1);
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));
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));
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);
m_camera->SetFixDirectionH(line->GetParam("fixDirection")->AsFloat(0.25f)*Math::PI);
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));
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() == "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)
{
AddNewScriptName(line->GetParam("type")->AsObjectType(OBJECT_NULL), const_cast<char*>(line->GetParam("name")->AsPath("ai").c_str()));
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()));
}
m_ui->GetLoadingScreen()->SetProgress(1.0f, RT_LOADING_FINISHED);
if (m_ui->GetLoadingScreen()->IsVisible())
{
// Force render of the "Loading finished" screen
// TODO: For some reason, rendering of the first frame after the simulation starts is very slow
// We're doing this because it looks weird when the progress bar is finished but it still says "Loading programs"
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;
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);
m_camera->FixCamera();
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
float CRobotMain::SearchNearestObject(Math::Vector center, CObject *exclu)
{
float min = 100000.0f;
for (CObject* obj : m_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 &center, 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(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(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 &center, 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(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(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(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);
}
//! Empty the list
void CRobotMain::FlushNewScriptName()
{
m_newScriptName.clear();
}
//! Adds a script name
void CRobotMain::AddNewScriptName(ObjectType type, const std::string& name)
{
NewScriptName newscript;
newscript.type = type;
newscript.name = name;
m_newScriptName.push_back(newscript);
}
//! Seeks a script name for a given type
std::string CRobotMain::GetNewScriptName(ObjectType type, int rank)
{
for (unsigned int i = 0; i < m_newScriptName.size(); i++)
{
if (m_newScriptName[i].type == type ||
m_newScriptName[i].type == OBJECT_NULL )
{
if (rank == 0) return m_newScriptName[i].name;
else rank --;
}
}
return "";
}
//! 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));
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() == "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_DIALOG);
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()
{
// Sort end conditions by teams
std::map<int, std::vector<CSceneEndCondition*>> teams;
for (std::unique_ptr<CSceneEndCondition>& endTake : m_endTake)
teams[endTake->winTeam].push_back(endTake.get());
int teamCount = 0;
bool usesTeamConditions = false;
for (auto it : teams)
{
int team = it.first;
if (team == 0) continue;
usesTeamConditions = true;
if (!m_objMan->TeamExists(team)) continue;
teamCount++;
}
if (!usesTeamConditions)
{
m_missionResult = ProcessEndMissionTakeForGroup(teams[0]);
}
else
{
// Special handling for teams
m_missionResult = ERR_MISSION_NOTERM;
if (teamCount == 0)
{
GetLogger()->Info("All teams died, mission ended with failure\n");
m_missionResult = INFO_LOST;
}
else
{
for (auto it : teams)
{
int team = it.first;
if (team == 0) continue;
if (!m_objMan->TeamExists(team)) continue;
Error result = ProcessEndMissionTakeForGroup(it.second);
if (result == INFO_LOST || result == INFO_LOSTq)
{
GetLogger()->Info("Team %d lost\n", team);
m_displayText->DisplayText(("<<< Team "+boost::lexical_cast<std::string>(team)+" lost! >>>").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);
}
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;
}
}
}
}
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;
}
}
}
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_trainerPilot;
}
//! 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_showSoluce;
}
bool CRobotMain::GetSceneSoluce()
{
if (m_infoFilename[SATCOM_SOLUCE][0] == 0) return false;
return m_ui->GetSceneSoluce();
}
bool CRobotMain::GetShowAll()
{
return m_showAll;
}
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)
{
m_levelCategory = cat;
m_levelChap = chap;
m_levelRank = rank;
}
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)
{
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);
}
}
}
//! Creates interface shortcuts to the units
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_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;
AutosaveRotate(false);
}
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;
AutosaveRotate(false);
}
int CRobotMain::GetAutosaveSlots()
{
return m_autosaveSlots;
}
int CRobotMain::AutosaveRotate(bool freeOne)
{
if (m_playerProfile == nullptr)
return 0;
GetLogger()->Debug("Rotate autosaves...\n");
// Find autosave dirs
auto saveDirs = CResourceManager::ListDirectories(m_playerProfile->GetSaveDir());
std::map<int, std::string> autosaveDirs;
for (auto& dir : saveDirs)
{
try
{
const std::string autosavePrefix = "autosave";
if (dir.substr(0, autosavePrefix.length()) == autosavePrefix)
{
int id = boost::lexical_cast<int>(dir.substr(autosavePrefix.length()));
autosaveDirs[id] = m_playerProfile->GetSaveFile(dir);
}
}
catch (...)
{
GetLogger()->Info("Bad autosave found: %s\n", dir.c_str());
// skip
}
}
if (autosaveDirs.size() == 0) return 1;
// Remove all but last m_autosaveSlots
std::map<int, std::string> autosavesToKeep;
int last_id = autosaveDirs.rbegin()->first;
int count = 0;
int to_keep = m_autosaveSlots-(freeOne ? 1 : 0);
int new_last_id = Math::Min(autosaveDirs.size(), to_keep);
bool rotate = false;
for (int i = last_id; i > 0; i--)
{
if (autosaveDirs.count(i) > 0)
{
count++;
if (count > m_autosaveSlots-(freeOne ? 1 : 0) || !m_autosave)
{
GetLogger()->Trace("Remove %s\n", autosaveDirs[i].c_str());
CResourceManager::RemoveDirectory(autosaveDirs[i]);
rotate = true;
}
else
{
GetLogger()->Trace("Keep %s\n", autosaveDirs[i].c_str());
autosavesToKeep[new_last_id-count+1] = autosaveDirs[i];
}
}
}
// Rename autosaves that we kept
if (rotate)
{
for (auto& save : autosavesToKeep)
{
std::string newDir = m_playerProfile->GetSaveFile("autosave" + boost::lexical_cast<std::string>(save.first));
GetLogger()->Trace("Rename %s -> %s\n", save.second.c_str(), newDir.c_str());
CResourceManager::Move(save.second, newDir);
}
}
return rotate ? count : count+1;
}
void CRobotMain::Autosave()
{
int id = AutosaveRotate(true);
GetLogger()->Info("Autosave!\n");
std::string dir = m_playerProfile->GetSaveFile("autosave" + boost::lexical_cast<std::string>(id));
char timestr[100];
time_t now = time(nullptr);
strftime(timestr, 99, "%x %X", localtime(&now));
std::string info = std::string("[AUTOSAVE] ")+timestr;
m_playerProfile->SaveScene(dir, info);
}
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 true;
}
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;
}
bool CRobotMain::CanBuild(ObjectType type, int team)
{
return CanBuildError(type, team) == 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;
}
bool CRobotMain::CanFactory(ObjectType type, int team)
{
return CanFactoryError(type, team) == 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;
ddim.x = 100.0f/640.0f;
ddim.y = 100.0f/480.0f;
pos.x = 540.0f/640.0f;
pos.y = 100.0f/480.0f;
Ui::CWindow* pw = m_interface->CreateWindows(pos, ddim, 3, EVENT_WINDOW6);
ddim.x = 100.0f/640.0f;
ddim.y = 16.0f/480.0f;
pos.x = 540.0f/640.0f;
pos.y = 178.0f/480.0f;
pw->CreateLabel(pos, ddim, 0, EVENT_LABEL0, "Code battle");
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);
}
}
}
void CRobotMain::DestroyCodeBattleInterface()
{
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
if (mode)
m_camera->SetFixDirectionV(-0.25f*Math::PI);
}
void CRobotMain::UpdateDebugCrashSpheres()
{
m_engine->ClearDisplayCrashSpheres();
if (m_debugCrashSpheres)
{
for (CObject* obj : m_objMan->GetAllObjects())
{
auto crashSpheres = obj->GetAllCrashSpheres();
std::vector<Math::Sphere> displaySpheres;
for (const auto& crashSphere : crashSpheres)
{
displaySpheres.push_back(crashSphere.sphere);
}
m_engine->AddDisplayCrashSpheres(displaySpheres);
}
}
}
void CRobotMain::SetDebugCrashSpheres(bool draw)
{
m_debugCrashSpheres = draw;
}
bool CRobotMain::GetDebugCrashSpheres()
{
return m_debugCrashSpheres;
}