colobot/src/level/robotmain.cpp

5884 lines
185 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.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/CBotDll.h"
#include "app/app.h"
#include "app/input.h"
#include "common/config_file.h"
#include "common/event.h"
#include "common/logger.h"
#include "common/make_unique.h"
#include "common/misc.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/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/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 <clipboard/clipboard.h>
#include <boost/lexical_cast.hpp>
// Global variables.
const float UNIT = 4.0f; // default for g_unit
float g_unit; // conversion factor
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_pause = CPauseManager::GetInstancePointer();
m_input = CInput::GetInstancePointer();
m_modelManager = MakeUnique<Gfx::CModelManager>();
m_settings = MakeUnique<CSettings>();
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_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_cameraRank = -1;
m_visitLast = EVENT_NULL;
m_visitObject = 0;
m_visitArrow = 0;
m_audioTrack = "";
m_audioRepeat = true;
m_satcomTrack = "";
m_satcomRepeat = true;
m_editorTrack = "";
m_editorRepeat = true;
m_selectObject = 0;
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_freePhoto = 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_suspend = 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_exitAfterMission = false;
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 = 0;
}
m_engine->SetTerrain(m_terrain.get());
m_app->SetMouseMode(MOUSE_ENGINE);
m_movie->Flush();
FlushDisplayInfo();
InitEye();
m_engine->SetTracePrecision(1.0f);
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();
}
void CRobotMain::ResetAfterDeviceChanged()
{
if (m_phase == PHASE_SETUPds ||
m_phase == PHASE_SETUPgs ||
m_phase == PHASE_SETUPps ||
m_phase == PHASE_SETUPcs ||
m_phase == PHASE_SETUPss ||
m_phase == PHASE_SIMUL ||
m_phase == PHASE_WIN ||
m_phase == PHASE_LOST)
ChangeColor();
UpdateMap();
}
//! Creates the file colobot.ini at the first time
void CRobotMain::CreateConfigFile()
{
m_settings->SaveSettings();
m_settings->SaveResolutionSettings(m_app->GetVideoConfig());
GetConfigFile().Save();
}
void CRobotMain::LoadConfigFile()
{
m_settings->LoadSettings();
}
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";
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)
{
return !IsPhaseWithWorld(phase);
}
//! Changes phase
void CRobotMain::ChangePhase(Phase phase)
{
bool resetWorld = false;
if ((IsPhaseWithWorld(m_phase) || IsPhaseWithWorld(phase)) && !IsInSimulationConfigPhase(m_phase) && !IsInSimulationConfigPhase(phase))
{
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(0);
if (m_gameTime > 10.0f) // did you play at least 10 seconds?
{
m_playerProfile->IncrementLevelTryCount(m_levelCategory, m_levelChap, m_levelRank);
}
}
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
}
m_app->SetLowCPU(true); // doesn't use much CPU in interface phases
DeleteAllObjects(); // removes all the current 3D Scene
}
m_phase = phase;
if (resetWorld)
{
m_winDelay = 0.0f;
m_lostDelay = 0.0f;
m_beginSatCom = false;
m_movieLock = false;
m_satComLock = false;
m_editLock = false;
m_freePhoto = false;
m_resetCreate = false;
m_infoObject = nullptr;
ChangePause(PAUSE_NONE);
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_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);
m_ui->ChangePhase(m_phase);
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)
{
m_app->SetLowCPU(false); // high CPU for simulation
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();
if (m_immediatSatCom && !loading &&
m_infoFilename[SATCOM_HUSTON][0] != 0)
StartDisplayInfo(SATCOM_HUSTON, false); // shows the instructions
m_sound->StopMusic(0.0f);
if (m_base == nullptr || loading) StartMusic();
}
catch (const std::runtime_error& e)
{
LevelLoadingError("An error occured 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 occured 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 occured 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 (event.type == EVENT_FRAME)
{
if (!m_movie->EventProcess(event)) // end of the movie?
{
MainMovieType type = m_movie->GetStopType();
if (type == MM_SATCOMopen)
{
ChangePause(PAUSE_NONE);
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);
m_interface->EventProcess(event);
if (m_displayInfo != nullptr) // current edition?
m_displayInfo->EventProcess(event);
UpdateInfoText();
return EventFrame(event);
}
if (event.type == EVENT_WRITE_SCENE_FINISHED)
{
IOWriteSceneFinished();
return false;
}
// Management of the console.
if (event.type == EVENT_KEY_DOWN &&
event.GetData<KeyEventData>()->key == KEY(BACKQUOTE)) // Pause ?
{
if (m_phase != PHASE_PLAYER_SELECT &&
!m_movie->IsExist() &&
!m_movieLock && !m_editLock)
{
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) ChangePause(PAUSE_CHEAT);
m_cmdEdit = true;
}
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);
if (m_phase == PHASE_SIMUL) ChangePause(PAUSE_NONE);
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 (event.type == EVENT_MOUSE_MOVE)
{
m_lastMousePos = event.mousePos;
HiliteObject(event.mousePos);
}
if (!m_displayText->EventProcess(event))
return false;
if (event.type == EVENT_MOUSE_MOVE)
{
m_lastMousePos = event.mousePos;
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();
return false;
}
CObject* obj;
// Simulation phase of the game
if (m_phase == PHASE_SIMUL)
{
if (!m_editFull)
m_camera->EventProcess(event);
switch (event.type)
{
case EVENT_KEY_DOWN:
{
auto data = event.GetData<KeyEventData>();
KeyCamera(event.type, data->slot);
HiliteClear();
if (data->key == KEY(F11))
{
m_particle->WriteWheelTrace("Savegame/t.png", 256, 256, Math::Vector(16.0f, 0.0f, -368.0f), Math::Vector(140.0f, 0.0f, -248.0f));
return false;
}
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_movieLock && !m_editLock && !m_cmdEdit &&
m_camera->GetType() != Gfx::CAM_TYPE_VISIT &&
!m_movie->IsExist())
{
ChangePause(m_pause->GetPause(PAUSE_USER) || m_pause->GetPause(PAUSE_CODE_BATTLE_LOCK) ? PAUSE_NONE : PAUSE_USER);
}
}
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;
widgetSetClipboardText(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, [&]() {
DeleteObject();
});
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(char *cmd)
{
if (cmd[0] == 0) return;
if (m_phase == PHASE_SIMUL)
{
if (strcmp(cmd, "winmission") == 0)
m_eventQueue->AddEvent(Event(EVENT_WIN));
if (strcmp(cmd, "lostmission") == 0)
m_eventQueue->AddEvent(Event(EVENT_LOST));
if (strcmp(cmd, "trainerpilot") == 0)
{
m_trainerPilot = !m_trainerPilot;
return;
}
if (strcmp(cmd, "fly") == 0)
{
m_researchDone[0] |= RESEARCH_FLY;
m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE));
return;
}
if (strcmp(cmd, "allresearch") == 0)
{
m_researchDone[0] = -1; // all research are done
m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE));
return;
}
if (strcmp(cmd, "allbuildings") == 0)
{
m_build = -1; // all buildings are available
m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE));
return;
}
if (strcmp(cmd, "all") == 0)
{
m_researchDone[0] = -1; // all research are done
m_build = -1; // all buildings are available
m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE));
return;
}
if (strcmp(cmd, "nolimit") == 0)
{
m_terrain->SetFlyingMaxHeight(280.0f);
return;
}
if (strcmp(cmd, "controller") == 0)
{
if (m_controller != nullptr)
{
// 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 (strcmp(cmd, "photo1") == 0)
{
m_freePhoto = !m_freePhoto;
if (m_freePhoto)
{
m_camera->SetType(Gfx::CAM_TYPE_FREE);
ChangePause(PAUSE_PHOTO);
}
else
{
m_camera->SetType(Gfx::CAM_TYPE_BACK);
ChangePause(PAUSE_NONE);
}
return;
}
if (strcmp(cmd, "photo2") == 0)
{
m_freePhoto = !m_freePhoto;
if (m_freePhoto)
{
m_camera->SetType(Gfx::CAM_TYPE_FREE);
DeselectAll(); // removes the control buttons
ChangePause(PAUSE_PHOTO);
m_map->ShowMap(false);
m_displayText->HideText(true);
}
else
{
m_camera->SetType(Gfx::CAM_TYPE_BACK);
ChangePause(PAUSE_NONE);
m_map->ShowMap(m_mapShow);
m_displayText->HideText(false);
}
return;
}
if (strcmp(cmd, "noclip") == 0)
{
CObject* object = GetSelect();
if (object != nullptr)
object->SetCollisions(false);
return;
}
if (strcmp(cmd, "clip") == 0)
{
CObject* object = GetSelect();
if (object != nullptr)
object->SetCollisions(true);
return;
}
if (strcmp(cmd, "addhusky") == 0)
{
CObject* object = GetSelect();
if (object != nullptr && object->Implements(ObjectInterfaceType::Shielded))
dynamic_cast<CShieldedObject*>(object)->SetMagnifyDamage(dynamic_cast<CShieldedObject*>(object)->GetMagnifyDamage()*0.1f);
return;
}
if (strcmp(cmd, "addfreezer") == 0)
{
CObject* object = GetSelect();
if (object != nullptr && object->Implements(ObjectInterfaceType::JetFlying))
dynamic_cast<CJetFlyingObject*>(object)->SetRange(dynamic_cast<CJetFlyingObject*>(object)->GetRange()*10.0f);
return;
}
if (strcmp(cmd, "\155\157\157") == 0)
{
// 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 (strcmp(cmd, "fullpower") == 0)
{
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 (strcmp(cmd, "fullenergy") == 0)
{
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 (strcmp(cmd, "fullshield") == 0)
{
CObject* object = GetSelect();
if (object != nullptr && object->Implements(ObjectInterfaceType::Shielded))
dynamic_cast<CShieldedObject*>(object)->SetShield(1.0f);
return;
}
if (strcmp(cmd, "fullrange") == 0)
{
CObject* object = GetSelect();
if (object != nullptr)
{
if (object->Implements(ObjectInterfaceType::JetFlying))
dynamic_cast<CJetFlyingObject*>(object)->SetReactorRange(1.0f);
}
return;
}
}
if (strcmp(cmd, "debugmode") == 0)
{
if (m_app->IsDebugModeActive(DEBUG_ALL))
{
m_app->SetDebugModeActive(DEBUG_ALL, false);
}
else
{
m_app->SetDebugModeActive(DEBUG_ALL, true);
}
return;
}
if (strcmp(cmd, "showstat") == 0)
{
m_engine->SetShowStats(!m_engine->GetShowStats());
return;
}
if (strcmp(cmd, "selectinsect") == 0)
{
m_selectInsect = !m_selectInsect;
return;
}
if (strcmp(cmd, "showsoluce") == 0)
{
m_showSoluce = !m_showSoluce;
m_ui->ShowSoluceUpdate();
return;
}
if (strcmp(cmd, "allmission") == 0)
{
m_showAll = !m_showAll;
m_ui->AllMissionUpdate();
return;
}
if (strcmp(cmd, "invradar") == 0)
{
m_cheatRadar = !m_cheatRadar;
return;
}
float speed;
if (sscanf(cmd, "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;
m_infoPos[i] = 0;
}
strcpy(m_infoFilename[SATCOM_OBJECT], "objects.txt");
m_infoIndex = 0;
}
//! 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);
ChangePause(PAUSE_SATCOMMOVIE);
m_infoObject = DeselectAll(); // removes the control buttons
m_displayText->HideText(true);
return;
}
}
if (m_movie->IsExist())
{
m_movie->Stop();
ChangePause(PAUSE_NONE);
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_infoIndex = index;
if (index != -1)
m_displayInfo->SetPosition(m_infoPos[index]);
}
//! 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);
if (m_infoIndex != -1)
m_infoPos[m_infoIndex] = m_displayInfo->GetPosition();
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];
}
//! Returns the name of the text display
int CRobotMain::GetDisplayInfoPosition(int index)
{
return m_infoPos[index];
}
//! Returns the name of the text display
void CRobotMain::SetDisplayInfoPosition(int index, int pos)
{
m_infoPos[index] = pos;
}
//! Beginning of a dialogue during the game
void CRobotMain::StartSuspend()
{
m_sound->MuteAll(true);
ClearInterface();
m_suspendInitPause = m_pause->GetPauseType();
m_pause->SetPause(PAUSE_DIALOG);
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);
m_suspend = true;
}
//! End of dialogue during the game
void CRobotMain::StopSuspend()
{
m_sound->MuteAll(false);
ClearInterface();
m_pause->SetPause(m_suspendInitPause);
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);
m_suspend = false;
}
//! 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;
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) == 0)
{
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 != 0)
{
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);
ChangePause(PAUSE_VISIT);
}
//! Move the arrow to visit
void CRobotMain::FrameVisit(float rTime)
{
if (m_visitArrow == 0) 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();
ChangePause(PAUSE_NONE);
if (m_visitObject != 0)
{
SelectObject(m_visitObject, false); // gives the command buttons
m_visitObject = 0;
}
}
//! 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;
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 = 0;
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;
}
CObject* CRobotMain::SearchObject(ObjectType type)
{
return m_objMan->FindNearest(nullptr, type);
}
//! Detects the object aimed by the mouse
CObject* CRobotMain::DetectObject(Math::Point pos)
{
int objRank = m_engine->DetectObject(pos);
for (CObject* obj : m_objMan->GetAllObjects())
{
if (!obj->GetActive()) continue;
CObject* transporter = nullptr;
if (obj->Implements(ObjectInterfaceType::Transportable))
transporter = dynamic_cast<CTransportableObject*>(obj)->GetTransporter();
if (transporter != nullptr && !transporter->GetActive()) continue;
if (obj->GetProxyActivate()) continue;
CObject* target = obj;
if (obj->GetType() == OBJECT_POWER || obj->GetType() == OBJECT_ATOMIC)
{
assert(obj->Implements(ObjectInterfaceType::Transportable));
target = dynamic_cast<CTransportableObject*>(obj)->GetTransporter(); // battery connected
if (target == nullptr) target = obj; // standalone battery
}
for (int j = 0; j < OBJECTMAXPART; j++)
{
int rank = obj->GetObjectRank(j);
if (rank == -1) continue;
if (rank != objRank) continue;
return target;
}
}
return 0;
}
//! Indicates whether an object is selectable
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::DeleteObject()
{
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(0);
m_short->SetHighlight(0);
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) 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;
const char* filename = GetHelpFilename(obj->GetType()).c_str();
if (filename[0] == 0) 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_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 type, InputSlot key)
{
if (type == 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 (type == 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 != 0)
automat->Abort();
}
}
}
//! 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->GetPause())
{
m_gameTime += event.rTime;
m_gameTimeAbsolute += m_app->GetRealRelTime() / 1e9f;
}
if (!m_movieLock && !m_pause->GetPause() && m_missionTimerStarted)
m_missionTimer += event.rTime;
if (!m_pause->GetPause() && 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_autosaveLast = m_gameTimeAbsolute;
Autosave();
}
}
}
m_water->EventProcess(event);
m_cloud->EventProcess(event);
m_lightning->EventProcess(event);
m_planet->EventProcess(event);
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_freePhoto)
{
// 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->GetPause()) // 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
ChangePause(PAUSE_CODE_BATTLE_LOCK);
m_sound->MuteAll(false); // Allow sound
m_codeBattleInit = true; // Will start on resume
}
if (!m_codeBattleStarted && !m_pause->GetPause())
{
m_codeBattleStarted = 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_freePhoto) 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;
}
//! Calculates the point of arrival of the camera
Math::Vector CRobotMain::LookatPoint(Math::Vector eye, float angleH, float angleV,
float length)
{
Math::Vector lookat = eye;
lookat.z += length;
RotatePoint(eye, angleH, angleV, lookat);
return lookat;
}
//! 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 occured 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;
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_endTakeResearch = 0;
m_endTakeWinDelay = 2.0f;
m_endTakeLostDelay = 2.0f;
m_globalMagnifyDamage = 1.0f;
m_obligatoryTotal = 0;
m_prohibitedTotal = 0;
m_mapShow = true;
m_mapImage = false;
m_mapFilename[0] = 0;
m_controller = nullptr;
m_colorRefBot.r = 10.0f/256.0f;
m_colorRefBot.g = 166.0f/256.0f;
m_colorRefBot.b = 254.0f/256.0f; // blue
m_colorRefBot.a = 0.0f;
m_colorNewBot.clear();
m_colorNewBot[0] = m_colorRefBot;
m_colorRefAlien.r = 135.0f/256.0f;
m_colorRefAlien.g = 170.0f/256.0f;
m_colorRefAlien.b = 13.0f/256.0f; // green
m_colorRefAlien.a = 0.0f;
m_colorNewAlien = m_colorRefAlien;
m_colorRefGreen.r = 135.0f/256.0f;
m_colorRefGreen.g = 170.0f/256.0f;
m_colorRefGreen.b = 13.0f/256.0f; // green
m_colorRefGreen.a = 0.0f;
m_colorNewGreen = m_colorRefGreen;
m_colorRefWater.r = 25.0f/256.0f;
m_colorRefWater.g = 255.0f/256.0f;
m_colorRefWater.b = 240.0f/256.0f; // cyan
m_colorRefWater.a = 0.0f;
m_colorNewWater = m_colorRefWater;
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("");
sprintf(m_title, "%s %d.%d", GetLevelCategoryDir(m_levelCategory).c_str(), m_levelChap, m_levelRank);
sprintf(m_resume, "%s %d.%d", GetLevelCategoryDir(m_levelCategory).c_str(), m_levelChap, m_levelRank);
std::string scriptNameStr;
GetResource(RES_TEXT, RT_SCRIPT_NEW, scriptNameStr);
strcpy(m_scriptName, scriptNameStr.c_str());
m_scriptFile[0] = 0;
m_missionType = MISSION_NORMAL;
m_codeBattleInit = false;
m_codeBattleStarted = false;
m_teamNames.clear();
m_missionResult = ERR_MISSION_NOTERM;
}
//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 = 0;
/*
* 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)
{
strcpy(m_scriptName, line->GetParam("text")->AsString().c_str());
continue;
}
if (line->GetCommand() == "ScriptFile" && !resetObject)
{
strcpy(m_scriptFile, line->GetParam("name")->AsString().c_str());
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 && m_controller == nullptr)
{
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 && m_controller == nullptr)
{
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, true);
m_engine->SetDeepView(line->GetParam("water")->AsFloat(100.0f)*g_unit, 1, true);
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("moxeX")->AsFloat(0.0f);
pos.y = line->GetParam("moxeY")->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(m_colorRefWater);
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())
{
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->SetFixDirection(line->GetParam("fixDirection")->AsFloat(0.25f)*Math::PI);
continue;
}
if (line->GetCommand() == "EndMissionTake" && !resetObject && m_controller == nullptr)
{
auto endTake = MakeUnique<CSceneEndCondition>();
endTake->Read(line.get());
m_endTake.push_back(std::move(endTake));
continue;
}
if (line->GetCommand() == "EndMissionDelay" && !resetObject && m_controller == nullptr)
{
m_endTakeWinDelay = line->GetParam("win")->AsFloat(2.0f);
m_endTakeLostDelay = line->GetParam("lost")->AsFloat(2.0f);
continue;
}
if (line->GetCommand() == "EndMissionResearch" && !resetObject && m_controller == nullptr) //TODO: Is this used anywhere?
{
m_endTakeResearch |= line->GetParam("type")->AsResearchFlag();
continue;
}
if (line->GetCommand() == "ObligatoryToken" && !resetObject) //NOTE: This was used only in CeeBot, maybe we should add this to some Colobot exercises?
{
int i = m_obligatoryTotal;
if (i < 100) //TODO: remove the limit
{
strcpy(m_obligatoryToken[i], line->GetParam("text")->AsString().c_str());
m_obligatoryTotal ++;
}
continue;
}
if (line->GetCommand() == "ProhibitedToken" && !resetObject) //NOTE: This was used only in CeeBot, maybe we should add this to some Colobot exercises?
{
int i = m_prohibitedTotal;
if (i < 100) //TODO: remove the limit
{
strcpy(m_prohibitedToken[i], line->GetParam("text")->AsString().c_str());
m_prohibitedTotal ++;
}
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 != 0) // 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);
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", m_colorRefBot, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, true);
m_engine->ChangeTextureColor("textures/objects/convert.png"+teamStr, "textures/objects/convert.png", m_colorRefBot, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, true);
m_engine->ChangeTextureColor("textures/objects/derrick.png"+teamStr, "textures/objects/derrick.png", m_colorRefBot, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, true);
m_engine->ChangeTextureColor("textures/objects/factory.png"+teamStr, "textures/objects/factory.png", m_colorRefBot, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, true);
m_engine->ChangeTextureColor("textures/objects/lemt.png"+teamStr, "textures/objects/lemt.png", m_colorRefBot, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, true);
m_engine->ChangeTextureColor("textures/objects/roller.png"+teamStr, "textures/objects/roller.png", m_colorRefBot, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, true);
m_engine->ChangeTextureColor("textures/objects/search.png"+teamStr, "textures/objects/search.png", m_colorRefBot, newColor, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 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", m_colorRefBot, 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", m_colorRefBot, 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", m_colorRefAlien, m_colorNewAlien, colorRef2, colorNew2, 0.50f, -1.0f, ts, ti, exclu);
m_engine->ChangeTextureColor("textures/objects/mother.png", m_colorRefAlien, m_colorNewAlien, colorRef2, colorNew2, 0.50f, -1.0f, ts, ti);
// GreeneryColor
m_engine->ChangeTextureColor("textures/objects/plant.png", m_colorRefGreen, 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", m_colorRefWater, m_colorNewWater, colorRef2, colorNew2, 0.20f, -1.0f, ts, ti, 0, m_colorShiftWater, true);
// PARTIFLIC :
ts = Math::Point(0.00f, 0.75f);
ti = Math::Point(0.25f, 1.00f);
m_engine->ChangeTextureColor("textures/effect02.png", m_colorRefWater, m_colorNewWater, colorRef2, colorNew2, 0.20f, -1.0f, ts, ti, 0, m_colorShiftWater, true);
// This loads the newly recolored textures to objects
m_engine->LoadAllTextures();
}
//! 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->GetActive()) 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->GetActive()) 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 = 0;
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
bool CRobotMain::FlushNewScriptName()
{
for (int i = 0; i < MAXNEWSCRIPTNAME; i++)
m_newScriptName[i].used = false;
return true;
}
//! Adds a script name
bool CRobotMain::AddNewScriptName(ObjectType type, char *name)
{
for (int i = 0; i < MAXNEWSCRIPTNAME; i++)
{
if (!m_newScriptName[i].used)
{
m_newScriptName[i].used = true;
m_newScriptName[i].type = type;
strcpy(m_newScriptName[i].name, name);
return true;
}
}
return false;
}
//! Seeks a script name for a given type
char* CRobotMain::GetNewScriptName(ObjectType type, int rank)
{
for (int i = 0; i < MAXNEWSCRIPTNAME; i++)
{
if (m_newScriptName[i].used &&
(m_newScriptName[i].type == type ||
m_newScriptName[i].type == OBJECT_NULL))
{
if (rank == 0) return m_newScriptName[i].name;
else rank --;
}
}
return nullptr;
}
//! Seeks if an object occupies in a spot, to prevent a backup of the game
bool CRobotMain::IOIsBusy()
{
if (CScriptFunctions::m_numberOfOpenFiles > 0) 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)
{
if (obj->GetType() == OBJECT_FIX) return;
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))
{
COldObject* oldObj = dynamic_cast<COldObject*>(obj);
for (int i = 1; i < OBJECTMAXPART; i++)
{
if (oldObj->GetObjectRank(i) == -1) continue;
Math::Vector pos = oldObj->GetPartPosition(i);
if (pos.x != 0.0f || pos.y != 0.0f || pos.z != 0.0f)
{
pos /= g_unit;
line->AddParam("p" + boost::lexical_cast<std::string>(i), MakeUnique<CLevelParserParam>(pos));
}
Math::Vector rot = oldObj->GetPartRotation(i);
if (rot.x != 0.0f || rot.y != 0.0f || rot.z != 0.0f)
{
rot /= (Math::PI/180.0f);
line->AddParam("a" + boost::lexical_cast<std::string>(i), MakeUnique<CLevelParserParam>(rot));
}
Math::Vector scale = oldObj->GetPartScale(i);
if (scale.x != 1.0f || scale.y != 1.0f || scale.z != 1.0f)
{
line->AddParam("z" + boost::lexical_cast<std::string>(i), MakeUnique<CLevelParserParam>(scale));
}
}
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, char *info)
{
// 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>(GetCurrentTimestamp()));
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 (obj->GetType() == OBJECT_FIX) continue;
if (IsObjectBeingTransported(obj)) continue;
if (obj->GetBurn()) continue;
if (obj->GetDead()) continue;
if (obj->IsExploding()) 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 = fOpen((CResourceManager::GetSaveLocation() + "/" + filecbot).c_str(), "wb");
if (file == nullptr) return false;
long version = 1;
fWrite(&version, sizeof(long), 1, file); // version of COLOBOT
version = CBotProgram::GetVersion();
fWrite(&version, sizeof(long), 1, file); // version of CBOT
objRank = 0;
for (CObject* obj : m_objMan->GetAllObjects())
{
if (obj->GetType() == OBJECT_TOTO) continue;
if (obj->GetType() == OBJECT_FIX) continue;
if (IsObjectBeingTransported(obj)) continue;
if (obj->GetBurn()) continue;
if (obj->GetDead()) continue;
if (!SaveFileStack(obj, file, objRank++)) break;
}
CBotClass::SaveStaticState(file);
fClose(file);
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);
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);
for (int i = 1; i < OBJECTMAXPART; i++)
{
if (oldObj->GetObjectRank(i) == -1) continue;
Math::Vector pos = line->GetParam(std::string("p")+boost::lexical_cast<std::string>(i))->AsPoint(Math::Vector());
if (pos.x != 0.0f || pos.y != 0.0f || pos.z != 0.0f)
{
oldObj->SetPartPosition(i, pos*g_unit);
}
Math::Vector dir = line->GetParam(std::string("a")+boost::lexical_cast<std::string>(i))->AsPoint(Math::Vector());
if (dir.x != 0.0f || dir.y != 0.0f || dir.z != 0.0f)
{
oldObj->SetPartRotation(i, dir*(Math::PI/180.0f));
}
Math::Vector zoom = line->GetParam(std::string("z")+boost::lexical_cast<std::string>(i))->AsPoint(Math::Vector());
if (zoom.x != 0.0f || zoom.y != 0.0f || zoom.z != 0.0f)
{
oldObj->SetPartScale(i, zoom);
}
}
}
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 combatibility
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.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 = fOpen((CResourceManager::GetSaveLocation() + "/" + filecbot).c_str(), "rb");
if (file != nullptr)
{
long version;
fRead(&version, sizeof(long), 1, file); // version of COLOBOT
if (version == 1)
{
fRead(&version, sizeof(long), 1, file); // version of CBOT
if (version == CBotProgram::GetVersion())
{
objRank = 0;
for (CObject* obj : m_objMan->GetAllObjects())
{
if (obj->GetType() == OBJECT_TOTO) continue;
if (obj->GetType() == OBJECT_FIX) continue;
if (IsObjectBeingTransported(obj)) continue;
if (obj->GetBurn()) continue;
if (obj->GetDead()) continue;
if (!ReadFileStack(obj, file, objRank++)) break;
}
}
}
CBotClass::RestoreStaticState(file);
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 occured 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;
}
}
}
void CRobotMain::SetEndMission(Error result, float delay)
{
if (m_controller != nullptr)
{
m_endTakeWinDelay = delay;
m_endTakeLostDelay = delay;
m_missionResult = result;
}
}
Error CRobotMain::CheckEndMissionForGroup(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;
}
//! Checks if the mission is over
Error CRobotMain::CheckEndMission(bool frame)
{
bool isImmediat = false;
// Process EndMissionTake, unless we are using MissionController
if (m_controller == nullptr)
{
// 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());
if(endTake->immediat)
isImmediat = true;
}
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 = CheckEndMissionForGroup(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 = CheckEndMissionForGroup(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;
if (m_exitAfterMission)
m_eventQueue->AddEvent(Event(EVENT_QUIT));
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;
}
}
}
}
// 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);
if (m_exitAfterMission)
m_eventQueue->AddEvent(Event(EVENT_QUIT));
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);
if (m_exitAfterMission)
m_eventQueue->AddEvent(Event(EVENT_QUIT));
return INFO_LOST;
}
if (m_missionResult == ERR_OK)
{
if (m_endTakeWinDelay == -1.0f)
{
m_winDelay = 1.0f; // wins in one second
m_lostDelay = 0.0f;
m_missionTimerEnabled = m_missionTimerStarted = false;
m_displayText->SetEnable(false);
if (m_exitAfterMission)
m_eventQueue->AddEvent(Event(EVENT_QUIT));
return ERR_OK; // mission ended
}
if (frame)
{
if(m_base != nullptr && !isImmediat)
{
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;
}
if (m_exitAfterMission)
m_eventQueue->AddEvent(Event(EVENT_QUIT));
m_displayText->SetEnable(false);
return ERR_OK; // mission ended
}
else
{
m_displayText->SetEnable(true);
return ERR_MISSION_NOTERM;
}
}
//! Returns the number of instructions required
int CRobotMain::GetObligatoryToken()
{
return m_obligatoryTotal;
}
//! Returns the name of a required instruction
char* CRobotMain::GetObligatoryToken(int i)
{
return m_obligatoryToken[i];
}
//! Checks if an instruction is part of the obligatory list
int CRobotMain::IsObligatoryToken(const char* token)
{
for (int i = 0; i < m_obligatoryTotal; i++)
{
if (strcmp(token, m_obligatoryToken[i]) == 0)
return i;
}
return -1;
}
//! Checks if an instruction is not part of the banned list
bool CRobotMain::IsProhibitedToken(const char* token)
{
for (int i = 0; i < m_prohibitedTotal; i++)
{
if (strcmp(token, m_prohibitedToken[i]) == 0)
return false;
}
return true;
}
//! 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;
}
char* CRobotMain::GetTitle()
{
return m_title;
}
char* CRobotMain::GetResume()
{
return m_resume;
}
char* CRobotMain::GetScriptName()
{
return m_scriptName;
}
char* CRobotMain::GetScriptFile()
{
return m_scriptFile;
}
bool CRobotMain::GetInterfaceGlint()
{
return m_settings->GetInterfaceGlint();
}
bool CRobotMain::GetSoluce4()
{
return m_settings->GetSoluce4();
}
bool CRobotMain::GetMovies()
{
return m_settings->GetMovies();
}
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 on the pause mode
void CRobotMain::ChangePause(PauseType pause)
{
if (pause != PAUSE_NONE)
m_pause->SetPause(pause);
else
m_pause->ClearPause();
m_sound->MuteAll(m_pause->GetPause());
CreateShortcuts();
if (m_pause->GetPause()) HiliteClear();
}
//! 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;
}
bool CRobotMain::GetFreePhoto()
{
return m_freePhoto;
}
//! 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;
}
//! Management of the precision of drawing the ground
void CRobotMain::SetTracePrecision(float factor)
{
m_engine->SetTracePrecision(factor);
}
float CRobotMain::GetTracePrecision()
{
return m_engine->GetTracePrecision();
}
//! 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);
}
}
//! Starts pause music
void CRobotMain::StartPauseMusic(PauseType pause)
{
switch(pause)
{
case PAUSE_EDITOR:
if (m_editorTrack != "")
m_sound->PlayPauseMusic(m_editorTrack, m_editorRepeat);
break;
case PAUSE_SATCOM:
if (m_satcomTrack != "")
m_sound->PlayPauseMusic(m_satcomTrack, m_satcomRepeat);
break;
default:
// Don't change music
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();
}
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)
{
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()) == "autosave")
{
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];
TimeToAscii(time(nullptr), timestr);
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_ATOMIC && !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 == 0 )
{
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);
}