colobot/src/object/robotmain.cpp

6291 lines
195 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 "object/robotmain.h"
#include "CBot/CBotDll.h"
#include "app/app.h"
#include "app/controller.h"
#include "app/input.h"
#include "clipboard/clipboard.h"
#include "common/event.h"
#include "common/global.h"
#include "common/logger.h"
#include "common/misc.h"
#include "common/profile.h"
#include "common/restext.h"
#include "common/resources/resourcemanager.h"
#include "common/resources/inputstream.h"
#include "common/resources/outputstream.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 "math/const.h"
#include "math/geometry.h"
#include "object/auto/auto.h"
#include "object/auto/autobase.h"
#include "object/brain.h"
#include "object/mainmovie.h"
#include "object/motion/motion.h"
#include "object/motion/motionhuman.h"
#include "object/motion/motiontoto.h"
#include "object/object.h"
#include "object/object_create_exception.h"
#include "object/object_manager.h"
#include "object/scene_conditions.h"
#include "object/task/task.h"
#include "object/task/taskbuild.h"
#include "object/task/taskmanip.h"
#include "object/level/parser.h"
#include "object/subclass/exchange_post.h"
#include "physics/physics.h"
#include "script/cbottoken.h"
#include "script/script.h"
#include "script/scriptfunc.h"
#include "sound/sound.h"
#include "ui/button.h"
#include "ui/displayinfo.h"
#include "ui/displaytext.h"
#include "ui/edit.h"
#include "ui/interface.h"
#include "ui/label.h"
#include "ui/maindialog.h"
#include "ui/mainmap.h"
#include "ui/mainshort.h"
#include "ui/map.h"
#include "ui/shortcut.h"
#include "ui/slider.h"
#include "ui/window.h"
#include <iomanip>
#include <boost/lexical_cast.hpp>
template<> CRobotMain* CSingleton<CRobotMain>::m_instance = nullptr;
// TODO: remove once using std::string
const int MAX_FNAME = 255;
const float UNIT = 4.0f;
// Global variables.
float g_unit; // conversion factor
//! Constructor of robot application
CRobotMain::CRobotMain(CController* controller)
{
m_ctrl = controller;
m_app = nullptr;
m_objMan = nullptr;
m_eventQueue = nullptr;
m_sound = nullptr;
m_engine = nullptr;
m_oldModelManager = nullptr;
m_modelManager = std::unique_ptr<Gfx::CModelManager>(new Gfx::CModelManager());
m_lightMan = nullptr;
m_particle = nullptr;
m_water = nullptr;
m_cloud = nullptr;
m_lightning = nullptr;
m_planet = nullptr;
m_pause = nullptr;
m_input = nullptr;
m_interface = nullptr;
m_terrain = nullptr;
m_camera = nullptr;
m_displayText = nullptr;
m_movie = nullptr;
m_dialog = nullptr;
m_short = nullptr;
m_map = nullptr;
m_displayInfo = nullptr;
m_time = 0.0f;
m_gameTime = 0.0f;
m_gameTimeAbsolute = 0.0f;
m_missionTimerEnabled = false;
m_missionTimerStarted = false;
m_missionTimer = 0.0f;
m_phase = PHASE_NAME;
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_delayWriteMessage = 0;
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_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;
m_gamerName = "";
for (int i = 0; i < MAXSHOWLIMIT; i++)
{
m_showLimit[i].used = false;
m_showLimit[i].total = 0;
m_showLimit[i].link = 0;
}
}
void CRobotMain::Create(bool loadProfile)
{
m_app = m_ctrl->GetApplication();
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_interface = new Ui::CInterface();
m_terrain = new Gfx::CTerrain();
m_camera = new Gfx::CCamera();
m_displayText = new Ui::CDisplayText();
m_movie = new CMainMovie();
m_dialog = m_ctrl->GetMainDialog();
m_short = new Ui::CMainShort();
m_map = new Ui::CMainMap();
m_displayInfo = nullptr;
m_objMan = new CObjectManager(m_engine,
m_terrain,
m_oldModelManager,
m_modelManager.get(),
m_particle);
m_engine->SetTerrain(m_terrain);
m_engine->SetMovieLock(m_movieLock);
m_app->SetMouseMode(MOUSE_ENGINE);
m_movie->Flush();
FlushDisplayInfo();
m_fontSize = 19.0f;
m_windowPos = Math::Point(0.15f, 0.17f);
m_windowDim = Math::Point(0.70f, 0.66f);
float fValue;
int iValue;
if (loadProfile)
{
if (GetProfile().GetFloatProperty("Edit", "FontSize", fValue)) m_fontSize = fValue;
if (GetProfile().GetFloatProperty("Edit", "WindowPosX", fValue)) m_windowPos.x = fValue;
if (GetProfile().GetFloatProperty("Edit", "WindowPosY", fValue)) m_windowPos.y = fValue;
if (GetProfile().GetFloatProperty("Edit", "WindowDimX", fValue)) m_windowDim.x = fValue;
if (GetProfile().GetFloatProperty("Edit", "WindowDimY", fValue)) m_windowDim.y = fValue;
}
m_IOPublic = false;
m_IODim = Math::Point(320.0f/640.0f, (121.0f+18.0f*8)/480.0f);
m_IOPos.x = (1.0f-m_IODim.x)/2.0f; // in the middle
m_IOPos.y = (1.0f-m_IODim.y)/2.0f;
if (loadProfile)
{
if (GetProfile().GetIntProperty ("Edit", "IOPublic", iValue)) m_IOPublic = iValue;
if (GetProfile().GetFloatProperty("Edit", "IOPosX", fValue)) m_IOPos.x = fValue;
if (GetProfile().GetFloatProperty("Edit", "IOPosY", fValue)) m_IOPos.y = fValue;
if (GetProfile().GetFloatProperty("Edit", "IODimX", fValue)) m_IODim.x = fValue;
if (GetProfile().GetFloatProperty("Edit", "IODimY", fValue)) m_IODim.y = fValue;
}
InitEye();
m_engine->SetTracePrecision(1.0f);
if (loadProfile) GetProfile().GetStringProperty("Gamer", "LastName", m_gamerName);
SetGlobalGamerName(m_gamerName);
ReadFreeParam();
CScriptFunctions::m_filesDir = CResourceManager::GetSaveLocation() + "/" + m_dialog->GetFilesDir(); //TODO: Refactor to PHYSFS while rewriting CBot engine
CScriptFunctions::Init();
}
//! Destructor of robot application
CRobotMain::~CRobotMain()
{
delete m_displayText;
m_displayText = nullptr;
delete m_interface;
m_interface = nullptr;
delete m_terrain;
m_terrain = nullptr;
delete m_camera;
m_camera = nullptr;
delete m_displayText;
m_displayText = nullptr;
delete m_movie;
m_movie = nullptr;
delete m_short;
m_short = nullptr;
delete m_map;
m_map = nullptr;
delete m_objMan;
m_objMan = nullptr;
m_dialog = nullptr;
m_input = nullptr;
m_pause = nullptr;
m_app = nullptr;
m_ctrl = nullptr;
}
Gfx::CCamera* CRobotMain::GetCamera()
{
return m_camera;
}
Gfx::CTerrain* CRobotMain::GetTerrain()
{
return m_terrain;
}
Ui::CInterface* CRobotMain::GetInterface()
{
return m_interface;
}
Ui::CDisplayText* CRobotMain::GetDisplayText()
{
return m_displayText;
}
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::CreateIni()
{
m_dialog->SetupMemorize();
GetProfile().SetFloatProperty("Edit", "FontSize", m_fontSize);
GetProfile().SetFloatProperty("Edit", "WindowPosX", m_windowPos.x);
GetProfile().SetFloatProperty("Edit", "WindowPosY", m_windowPos.y);
GetProfile().SetFloatProperty("Edit", "WindowDimX", m_windowDim.x);
GetProfile().SetFloatProperty("Edit", "WindowDimY", m_windowDim.y);
GetProfile().SetIntProperty("Edit", "IOPublic", m_IOPublic);
GetProfile().SetFloatProperty("Edit", "IOPosX", m_IOPos.x);
GetProfile().SetFloatProperty("Edit", "IOPosY", m_IOPos.y);
GetProfile().SetFloatProperty("Edit", "IODimX", m_IODim.x);
GetProfile().SetFloatProperty("Edit", "IODimY", m_IODim.y);
GetProfile().Save();
}
void CRobotMain::LoadIni()
{
m_dialog->SetupRecall();
}
//! Changes phase
void CRobotMain::ChangePhase(Phase phase)
{
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?
{
int rank = m_dialog->GetSceneRank();
int numTry = m_dialog->GetGamerInfoTry(rank);
m_dialog->SetGamerInfoTry(rank, numTry+1);
m_dialog->WriteGamerInfo();
}
}
if (phase == PHASE_WIN) // wins a simulation?
{
int rank = m_dialog->GetSceneRank();
m_dialog->SetGamerInfoPassed(rank, true);
m_dialog->NextMission(); // passes to the next mission
m_dialog->WriteGamerInfo();
}
m_app->SetLowCPU(true); // doesn't use much CPU in interface phases
DeleteAllObjects(); // removes all the current 3D Scene
m_phase = phase;
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_engine->SetMovieLock(m_movieLock);
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();
ClearInterface();
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;
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 = static_cast<Ui::CEdit*>(m_interface->CreateEdit(pos, dim, 0, EVENT_CMD));
if (pe == nullptr) return;
pe->ClearState(Ui::STATE_VISIBLE);
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;
Ui::CButton* pb = m_interface->CreateButton(pos, dim, 0, EVENT_SPEED);
if (pb == nullptr) return;
pb->SetState(Ui::STATE_SIMPLY);
pb->ClearState(Ui::STATE_VISIBLE);
m_dialog->ChangePhase(m_phase);
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_PERSO)
{
m_engine->SetDrawWorld(true);
m_engine->SetDrawFront(false);
m_fixScene = false;
}
if (m_phase == PHASE_INIT)
{
m_engine->DeleteTexture("generic.png");
}
if (m_phase == PHASE_SIMUL)
{
m_engine->DeleteTexture("interface.png");
m_app->SetLowCPU(false); // high CPU for simulation
bool loading = (m_dialog->GetSceneRead()[0] != 0);
m_map->CreateMap();
try
{
CreateScene(m_dialog->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)
{
CLogger::GetInstancePointer()->Error("An error occured while trying to load a level\n");
CLogger::GetInstancePointer()->Error("%s\n", e.what());
ChangePhase(PHASE_INIT);
}
}
if (m_phase == PHASE_WIN)
{
m_sound->StopAll();
if (m_endingWinRank == -1)
{
ChangePhase(PHASE_TERM);
}
else
{
m_winTerminate = (m_endingWinRank == 904);
m_dialog->SetSceneName("win");
m_dialog->SetSceneRank(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)
{
CLogger::GetInstancePointer()->Error("An error occured while trying to load win scene\n");
CLogger::GetInstancePointer()->Error("%s\n", e.what());
ChangePhase(PHASE_TERM);
}
}
}
if (m_phase == PHASE_LOST)
{
m_sound->StopAll();
if (m_endingLostRank == -1)
{
ChangePhase(PHASE_TERM);
}
else
{
m_winTerminate = false;
m_dialog->SetSceneName("lost");
m_dialog->SetSceneRank(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)
{
CLogger::GetInstancePointer()->Error("An error occured while trying to load lost scene\n");
CLogger::GetInstancePointer()->Error("%s\n", e.what());
ChangePhase(PHASE_TERM);
}
}
}
m_engine->LoadAllTextures();
}
//! Processes an event
bool CRobotMain::ProcessEvent(Event &event)
{
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);
}
// Management of the console.
if (event.type == EVENT_KEY_DOWN &&
event.key.key == KEY(BACKQUOTE)) // Pause ?
{
if (m_phase != PHASE_NAME &&
!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.key.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)
{
if (event.key.slot == INPUT_SLOT_HELP ||
event.key.slot == INPUT_SLOT_PROG ||
event.key.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:
KeyCamera(event.type, event.key.slot);
HiliteClear();
if (event.key.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 (event.key.slot == INPUT_SLOT_HELP)
{
StartDisplayInfo(SATCOM_HUSTON, false);
return false;
}
if (event.key.slot == INPUT_SLOT_PROG)
{
StartDisplayInfo(SATCOM_PROG, false);
return false;
}
break;
}
if (m_movieLock) // current movie?
{
if (event.key.slot == INPUT_SLOT_QUIT ||
event.key.key == KEY(ESCAPE))
{
AbortMovie();
}
return false;
}
if (m_camera->GetType() == Gfx::CAM_TYPE_VISIT)
{
if (event.key.slot == INPUT_SLOT_VISIT)
{
StartDisplayVisit(EVENT_NULL);
}
if (event.key.slot == INPUT_SLOT_QUIT ||
event.key.key == KEY(ESCAPE))
{
StopDisplayVisit();
}
return false;
}
if (event.key.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_dialog->StartAbort(); // do you want to leave?
}
if (event.key.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) ? PAUSE_NONE : PAUSE_USER);
}
}
if (event.key.slot == INPUT_SLOT_CAMERA)
{
ChangeCamera();
}
if (event.key.slot == INPUT_SLOT_DESEL)
{
if (m_shortCut)
DeselectObject();
}
if (event.key.slot == INPUT_SLOT_HUMAN)
{
SelectHuman();
}
if (event.key.slot == INPUT_SLOT_NEXT && ((event.kmodState & KEY_MOD(CTRL)) != 0))
{
m_short->SelectShortcut(EVENT_OBJECT_SHORTCUT_MODE); // switch bots <-> buildings
return false;
}
if (event.key.slot == INPUT_SLOT_NEXT)
{
if (m_shortCut)
m_short->SelectNext();
}
if (event.key.slot == INPUT_SLOT_HELP)
{
StartDisplayInfo(SATCOM_HUSTON, true);
}
if (event.key.slot == INPUT_SLOT_PROG)
{
StartDisplayInfo(SATCOM_PROG, true);
}
if (event.key.slot == INPUT_SLOT_VISIT)
{
StartDisplayVisit(EVENT_NULL);
}
if (event.key.slot == INPUT_SLOT_SPEED05)
{
SetSpeed(0.5f);
}
if (event.key.slot == INPUT_SLOT_SPEED10)
{
SetSpeed(1.0f);
}
if (event.key.slot == INPUT_SLOT_SPEED15)
{
SetSpeed(1.5f);
}
if (event.key.slot == INPUT_SLOT_SPEED20)
{
SetSpeed(2.0f);
}
if (event.key.slot == INPUT_SLOT_SPEED30)
{
SetSpeed(3.0f);
}
if (event.key.slot == INPUT_SLOT_SPEED40)
{
SetSpeed(4.0f);
}
if (event.key.slot == INPUT_SLOT_SPEED60)
{
SetSpeed(6.0f);
}
if (event.key.key == KEY(c) && ((event.kmodState & KEY_MOD(CTRL)) != 0) && m_engine->GetShowStats())
{
CObject* obj = GetSelect();
if (obj != nullptr)
{
CLevelParserLine* line = new CLevelParserLine("CreateObject");
line->AddParam("type", CLevelParserParamUPtr{new CLevelParserParam(obj->GetType())});
line->AddParam("pos", CLevelParserParamUPtr{new CLevelParserParam(obj->GetPosition())});
line->AddParam("dir", CLevelParserParamUPtr{new CLevelParserParam(obj->GetRotationZ()/(Math::PI/180.0f))});
std::stringstream ss;
ss << *line;
widgetSetClipboardText(ss.str().c_str());
}
}
break;
case EVENT_KEY_UP:
KeyCamera(event.type, event.key.slot);
break;
case EVENT_MOUSE_BUTTON_DOWN:
if (event.mouseButton.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.mouseButton.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_dialog->StartDeleteObject(); // do you want to destroy it?
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_PERSO)
EventObject(event);
if (m_phase == PHASE_WIN ||
m_phase == PHASE_LOST)
{
EventObject(event);
switch (event.type)
{
case EVENT_KEY_DOWN:
if (event.key.key == KEY(ESCAPE) ||
event.key.key == KEY(RETURN))
{
if (m_winTerminate)
ChangePhase(PHASE_INIT);
else
ChangePhase(PHASE_TERM);
}
break;
case EVENT_BUTTON_OK:
if (m_winTerminate)
ChangePhase(PHASE_INIT);
else
ChangePhase(PHASE_TERM);
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->SetClip(false);
return;
}
if (strcmp(cmd, "clip") == 0)
{
CObject* object = GetSelect();
if (object != nullptr)
object->SetClip(true);
return;
}
if (strcmp(cmd, "addhusky") == 0)
{
CObject* object = GetSelect();
if (object != nullptr)
object->SetMagnifyDamage(object->GetMagnifyDamage()*0.1f);
return;
}
if (strcmp(cmd, "addfreezer") == 0)
{
CObject* object = GetSelect();
if (object != nullptr)
object->SetRange(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->SetEnergy(1.0f);
}
object->SetShield(1.0f);
CPhysics* physics = object->GetPhysics();
if (physics != nullptr)
physics->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->SetEnergy(1.0f);
}
}
return;
}
if (strcmp(cmd, "fullshield") == 0)
{
CObject* object = GetSelect();
if (object != nullptr)
object->SetShield(1.0f);
return;
}
if (strcmp(cmd, "fullrange") == 0)
{
CObject* object = GetSelect();
if (object != nullptr)
{
CPhysics* physics = object->GetPhysics();
if (physics != nullptr)
physics->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, "invshadow") == 0)
{
m_engine->SetShadow(!m_engine->GetShadow());
return;
}
if (strcmp(cmd, "invdirty") == 0)
{
m_engine->SetDirty(!m_engine->GetDirty());
return;
}
if (strcmp(cmd, "invfog") == 0)
{
m_engine->SetFog(!m_engine->GetFog());
return;
}
if (strcmp(cmd, "invlens") == 0)
{
m_engine->SetLensMode(!m_engine->GetLensMode());
return;
}
if (strcmp(cmd, "invwater") == 0)
{
m_engine->SetWaterMode(!m_engine->GetWaterMode());
return;
}
if (strcmp(cmd, "invsky") == 0)
{
m_engine->SetSkyMode(!m_engine->GetSkyMode());
return;
}
if (strcmp(cmd, "invplanet") == 0)
{
m_engine->SetPlanetMode(!m_engine->GetPlanetMode());
return;
}
if (strcmp(cmd, "selectinsect") == 0)
{
m_selectInsect = !m_selectInsect;
return;
}
if (strcmp(cmd, "showsoluce") == 0)
{
m_showSoluce = !m_showSoluce;
m_dialog->ShowSoluceUpdate();
return;
}
if (strcmp(cmd, "allmission") == 0)
{
m_showAll = !m_showAll;
m_dialog->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)
{
CMotion* motion = obj->GetMotion();
if (motion != nullptr && motion->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 char *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_dialog->GetSceneSoluce();
m_displayInfo = new 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();
delete m_displayInfo;
m_displayInfo = nullptr;
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_map->ShowMap(false);
m_infoObject = DeselectAll(); // removes the control buttons
m_displayText->HideText(true);
m_suspend = true;
}
//! End of dialogue during the game
void CRobotMain::StopSuspend()
{
SelectObject(m_infoObject, false); // gives the command buttons
m_map->ShowMap(m_mapShow);
m_displayText->HideText(false);
m_suspend = false;
}
//! Returns the absolute time of the game
float CRobotMain::GetGameTime()
{
return m_gameTime;
}
//! Managing the size of the default fonts
void CRobotMain::SetFontSize(float size)
{
m_fontSize = size;
GetProfile().SetFloatProperty("Edit", "FontSize", m_fontSize);
}
float CRobotMain::GetFontSize()
{
return m_fontSize;
}
//! Managing the size of the default window
void CRobotMain::SetWindowPos(Math::Point pos)
{
m_windowPos = pos;
GetProfile().SetFloatProperty("Edit", "WindowPosX", m_windowPos.x);
GetProfile().SetFloatProperty("Edit", "WindowPosY", m_windowPos.y);
}
Math::Point CRobotMain::GetWindowPos()
{
return m_windowPos;
}
void CRobotMain::SetWindowDim(Math::Point dim)
{
m_windowDim = dim;
GetProfile().SetFloatProperty("Edit", "WindowDimX", m_windowDim.x);
GetProfile().SetFloatProperty("Edit", "WindowDimY", m_windowDim.y);
}
Math::Point CRobotMain::GetWindowDim()
{
return m_windowDim;
}
//! Managing windows open/save
void CRobotMain::SetIOPublic(bool mode)
{
m_IOPublic = mode;
GetProfile().SetIntProperty("Edit", "IOPublic", m_IOPublic);
}
bool CRobotMain::GetIOPublic()
{
return m_IOPublic;
}
void CRobotMain::SetIOPos(Math::Point pos)
{
m_IOPos = pos;
GetProfile().SetFloatProperty("Edit", "IOPosX", m_IOPos.x);
GetProfile().SetFloatProperty("Edit", "IOPosY", m_IOPos.y);
}
Math::Point CRobotMain::GetIOPos()
{
return m_IOPos;
}
void CRobotMain::SetIODim(Math::Point dim)
{
m_IODim = dim;
GetProfile().SetFloatProperty("Edit", "IODimX", m_IODim.x);
GetProfile().SetFloatProperty("Edit", "IODimY", m_IODim.y);
}
Math::Point CRobotMain::GetIODim()
{
return m_IODim;
}
//! 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;
}
Math::Vector goal = m_displayText->GetVisitGoal(event);
m_visitArrow = m_objMan->CreateObject(goal, 0.0f, OBJECT_SHOW, -1.0f, 1.0f, 10.0f);
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->GetSelect()) prev = obj;
obj->SetSelect(false);
}
return prev;
}
//! Selects an object, without attending to deselect the rest
void CRobotMain::SelectOneObject(CObject* obj, bool displayError)
{
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(obj->GetCameraType());
m_camera->SetDist(obj->GetCameraDist());
}
else
{
m_camera->SetType(Gfx::CAM_TYPE_BACK);
}
CObject* toto = SearchToto();
if (toto != nullptr)
{
CMotionToto* mt = static_cast<CMotionToto*>(toto->GetMotion());
if (mt != nullptr)
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->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 = nullptr;
ObjectType type = obj->GetType();
if ( type == OBJECT_PORTICO ||
type == OBJECT_BASE ||
type == OBJECT_DERRICK ||
type == OBJECT_FACTORY ||
type == OBJECT_REPAIR ||
type == OBJECT_DESTROYER ||
type == OBJECT_STATION ||
type == OBJECT_CONVERT ||
type == OBJECT_TOWER ||
type == OBJECT_RESEARCH ||
type == OBJECT_RADAR ||
type == OBJECT_INFO ||
type == OBJECT_ENERGY ||
type == OBJECT_LABO ||
type == OBJECT_NUCLEAR ||
type == OBJECT_PARA ||
type == OBJECT_SAFE ||
type == OBJECT_HUSTON ||
type == OBJECT_TARGET1 ||
type == OBJECT_TARGET2 ||
type == OBJECT_START ||
type == OBJECT_END ||
type == OBJECT_STONE ||
type == OBJECT_URANIUM ||
type == OBJECT_BULLET ||
type == OBJECT_METAL ||
type == OBJECT_BBOX ||
type == OBJECT_KEYa ||
type == OBJECT_KEYb ||
type == OBJECT_KEYc ||
type == OBJECT_KEYd ||
type == OBJECT_TNT ||
type == OBJECT_SCRAP1 ||
type == OBJECT_SCRAP2 ||
type == OBJECT_SCRAP3 ||
type == OBJECT_SCRAP4 ||
type == OBJECT_SCRAP5 ||
type == OBJECT_BOMB ||
type == OBJECT_BAG ||
type == OBJECT_WAYPOINT ||
type == OBJECT_FLAGb ||
type == OBJECT_FLAGr ||
type == OBJECT_FLAGg ||
type == OBJECT_FLAGy ||
type == OBJECT_FLAGv ||
type == OBJECT_MARKPOWER ||
type == OBJECT_MARKSTONE ||
type == OBJECT_MARKURANIUM ||
type == OBJECT_MARKKEYa ||
type == OBJECT_MARKKEYb ||
type == OBJECT_MARKKEYc ||
type == OBJECT_MARKKEYd ||
type == OBJECT_HUMAN ||
type == OBJECT_TECH ||
type == OBJECT_TOTO ||
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_MOBILEtg ||
type == OBJECT_MOBILEft ||
type == OBJECT_MOBILEtt ||
type == OBJECT_MOBILEwt ||
type == OBJECT_MOBILEit ||
type == OBJECT_MOBILEdr ||
type == OBJECT_MOTHER ||
type == OBJECT_ANT ||
type == OBJECT_SPIDER ||
type == OBJECT_BEE ||
type == OBJECT_WORM ||
type == OBJECT_EGG ||
type == OBJECT_RUINmobilew1 ||
type == OBJECT_RUINmobilew2 ||
type == OBJECT_RUINmobilet1 ||
type == OBJECT_RUINmobilet2 ||
type == OBJECT_RUINmobiler1 ||
type == OBJECT_RUINmobiler2 ||
type == OBJECT_RUINfactory ||
type == OBJECT_RUINdoor ||
type == OBJECT_RUINsupport ||
type == OBJECT_RUINradar ||
type == OBJECT_RUINconvert ||
type == OBJECT_RUINbase ||
type == OBJECT_RUINhead ||
type == OBJECT_APOLLO1 ||
type == OBJECT_APOLLO2 ||
type == OBJECT_APOLLO3 ||
type == OBJECT_APOLLO4 ||
type == OBJECT_APOLLO5 )
{
target = obj;
}
else if (type == OBJECT_POWER || type == OBJECT_ATOMIC)
{
target = dynamic_cast<CTransportableObject*>(obj)->GetTransporter(); // battery connected
if (!target) 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->GetSelectable()) return false;
ObjectType type = obj->GetType();
if ( type == OBJECT_HUMAN ||
type == OBJECT_TOTO ||
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 ||
type == OBJECT_BASE ||
type == OBJECT_DERRICK ||
type == OBJECT_FACTORY ||
type == OBJECT_REPAIR ||
type == OBJECT_DESTROYER||
type == OBJECT_STATION ||
type == OBJECT_CONVERT ||
type == OBJECT_TOWER ||
type == OBJECT_RESEARCH ||
type == OBJECT_RADAR ||
type == OBJECT_INFO ||
type == OBJECT_ENERGY ||
type == OBJECT_LABO ||
type == OBJECT_NUCLEAR ||
type == OBJECT_PARA ||
type == OBJECT_SAFE ||
type == OBJECT_HUSTON )
{
return true;
}
if (m_selectInsect)
{
if ( type == OBJECT_MOTHER ||
type == OBJECT_ANT ||
type == OBJECT_SPIDER ||
type == OBJECT_BEE ||
type == OBJECT_WORM ||
type == OBJECT_MOBILEtg )
{
return true;
}
}
return false;
}
//! Deletes the selected object
bool CRobotMain::DeleteObject()
{
CObject* obj = GetSelect();
if (obj == nullptr) return false;
m_engine->GetPyroManager()->Create(Gfx::PT_FRAGT, obj);
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())
{
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_PERSO) 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_dialog->GetTooltip() && 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)
{
std::string objectTooltipName;
if (m_dialog->GetTooltip() && obj->GetTooltipName(objectTooltipName))
{
m_tooltipPos = pos;
m_tooltipName = objectTooltipName;
m_tooltipTime = 0.0f;
}
if (IsSelectable(obj))
{
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_PERSO) 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()
{
for (CObject* obj : m_objMan->GetAllObjects())
{
if (obj->GetSelect())
{
if (obj->GetCameraLock()) return;
ObjectType oType = obj->GetType();
Gfx::CameraType type = obj->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 (obj->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;
}
obj->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;
if (!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);
}
}
m_engine->SetTimerDisplay(m_missionTimerEnabled && m_missionTimerStarted ? TimeFormat(m_missionTimer) : "");
}
//! Initializes the view
void CRobotMain::InitEye()
{
if (m_phase == PHASE_SIMUL)
m_camera->Init(Math::Vector( 0.0f, 10.0f, 0.0f),
Math::Vector(10.0f, 5.0f, 0.0f), 0.0f);
}
//! Advances the entire scene
bool CRobotMain::EventFrame(const Event &event)
{
m_time += event.rTime;
if (!m_movieLock && m_pause->GetPause() == PAUSE_NONE) {
m_gameTime += event.rTime;
m_gameTimeAbsolute += m_app->GetRealRelTime() / 1e9f;
}
if (!m_immediatSatCom && !m_beginSatCom &&
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
}
if (!m_movieLock && m_pause->GetPause() == PAUSE_NONE && m_missionTimerStarted)
m_missionTimer += event.rTime;
if (m_pause->GetPause() == PAUSE_NONE && m_autosave && m_gameTimeAbsolute >= m_autosaveLast+(m_autosaveInterval*60) && m_phase == PHASE_SIMUL)
{
std::string base = m_dialog->GetSceneName();
if (base == "missions" || base == "freemissions" || base == "custom")
{
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);
}
// 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_PERSO ||
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);
}
}
// 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 (m_delayWriteMessage > 0)
{
m_delayWriteMessage --;
if (m_delayWriteMessage == 0)
{
m_displayText->DisplayError(INFO_WRITEOK, Math::Vector(0.0f,0.0f,0.0f));
}
}
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_USER);
m_codeBattleInit = true; // Will start on resume
}
if (!m_codeBattleStarted && m_pause->GetPause() == PAUSE_NONE)
{
m_codeBattleStarted = true;
m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE));
}
}
return true;
}
//! 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();
ChangeColor();
m_dialog->SetSceneName("perso");
m_dialog->SetSceneRank(0);
try
{
CreateScene(false, true, false); // sets scene
}
catch (const std::runtime_error& e)
{
CLogger::GetInstancePointer()->Error("An error occured while trying to load apperance scene\n");
CLogger::GetInstancePointer()->Error("%s\n", e.what());
}
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
CMotionHuman* mh = static_cast<CMotionHuman*>(obj->GetMotion());
if (mh != nullptr)
mh->StartDisplayPerso();
}
}
//! Creates the whole scene
void CRobotMain::CreateScene(bool soluce, bool fixScene, bool resetObject)
{
char* base = m_dialog->GetSceneName();
int rank = m_dialog->GetSceneRank();
const char* read = m_dialog->GetSceneRead().c_str();
const char* stack = m_dialog->GetStackRead().c_str();
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 = 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("");
m_dialog->BuildResumeName(m_title, base, rank);
m_dialog->BuildResumeName(m_resume, base, rank);
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;
try
{
CLevelParser levelParser(base, rank/100, rank%100);
levelParser.Load();
int rankObj = 0;
int rankGadget = 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)
{
m_sound->CacheMusic(std::string("../")+line->GetParam("filename")->AsPath("music"));
continue;
}
if (line->GetCommand() == "AudioChange" && !resetObject && m_controller == nullptr)
{
auto audioChange = std::unique_ptr<CAudioChangeCondition>{new CAudioChangeCondition()};
audioChange->Read(line.get());
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");
CLogger::GetInstancePointer()->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" << std::setfill('0') << std::setw(3) << trackid << ".ogg";
m_audioTrack = filenameStr.str();
}
else
{
m_audioTrack = "";
}
}
else
{
if (line->GetParam("filename")->IsDefined())
{
m_audioTrack = std::string("../")+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 = std::string("../")+line->GetParam("satcom")->AsPath("music");
m_satcomRepeat = line->GetParam("satcomRepeat")->AsBool(true);
}
else
{
m_satcomTrack = "";
}
if (line->GetParam("editor")->IsDefined())
{
m_editorTrack = std::string("../")+line->GetParam("editor")->AsPath("music");
m_editorRepeat = line->GetParam("editorRepeat")->AsBool(true);
}
else
{
m_editorTrack = "";
}
if (!m_audioTrack.empty()) m_sound->CacheMusic(m_audioTrack);
if (!m_satcomTrack.empty()) m_sound->CacheMusic(m_satcomTrack);
if (!m_editorTrack.empty()) 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("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)
{
std::string path = "";
if (line->GetParam("image")->IsDefined())
path = "../" + line->GetParam("image")->AsPath("textures");
m_engine->SetBackground(path.c_str(),
line->GetParam("up")->AsColor(Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f)),
line->GetParam("down")->AsColor(Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f)),
line->GetParam("cloudUp")->AsColor(Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f)),
line->GetParam("cloudDown")->AsColor(Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f)),
line->GetParam("full")->AsBool(false));
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")->AsInt(0),
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_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_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_terrain->RandomizeRelief();
continue;
}
if (line->GetCommand() == "TerrainResource" && !resetObject)
{
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)
{
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)
{
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_terrain->CreateObjects();
continue;
}
if (line->GetCommand() == "BeginObject")
{
InitEye();
SetMovieLock(false);
if (read[0] != 0) // loading file ?
sel = IOReadScene(read, stack);
continue;
}
if (line->GetCommand() == "LevelController" && read[0] == 0)
{
m_controller = m_objMan->CreateObject(Math::Vector(0.0f, 0.0f, 0.0f), 0.0f, OBJECT_CONTROLLER, 100.0f);
m_controller->SetMagnifyDamage(100.0f);
m_controller->SetIgnoreBuildCheck(true);
if (m_controller->Implements(ObjectInterfaceType::Programmable))
{
CBrain* brain = dynamic_cast<CProgrammableObject*>(m_controller)->GetBrain();
if (line->GetParam("script")->IsDefined())
{
Program* program = brain->AddProgram();
program->filename = "../" + line->GetParam("script")->AsPath("ai");
program->readOnly = true;
brain->SetScriptRun(program);
}
}
continue;
}
if (line->GetCommand() == "CreateObject" && read[0] == 0)
{
ObjectType type = line->GetParam("type")->AsObjectType();
int gadget = line->GetParam("gadget")->AsInt(-1);
if ( gadget == -1 )
{
gadget = 0;
if ( type == OBJECT_TECH ||
(type >= OBJECT_PLANT0 &&
type <= OBJECT_PLANT19 ) ||
(type >= OBJECT_TREE0 &&
type <= OBJECT_TREE5 ) ||
(type >= OBJECT_QUARTZ0 &&
type <= OBJECT_QUARTZ3 ) ||
(type >= OBJECT_ROOT0 &&
type <= OBJECT_ROOT4 ) ) // not ROOT5!
{
gadget = 1;
}
}
if (gadget != 0) // is this a gadget?
{
if (!TestGadgetQuantity(rankGadget++)) continue;
}
Math::Vector pos = line->GetParam("pos")->AsPoint()*g_unit;
float dirAngle = line->GetParam("dir")->AsFloat(0.0f)*Math::PI;
bool trainer;
CObject* obj = nullptr;
try
{
obj = m_objMan->CreateObject(
pos, dirAngle,
type,
line->GetParam("power")->AsFloat(1.0f),
line->GetParam("z")->AsFloat(1.0f),
line->GetParam("h")->AsFloat(0.0f),
trainer = line->GetParam("trainer")->AsBool(false),
line->GetParam("toy")->AsBool(false),
line->GetParam("option")->AsInt(0)
);
}
catch (const CObjectCreateException& e)
{
if (gadget)
{
GetLogger()->Warn("Error loading decorative object: %s\n", e.what());
continue;
}
else
{
GetLogger()->Error("Error loading level object: %s\n", e.what());
throw;
}
}
if (m_fixScene && type == OBJECT_HUMAN)
{
CMotion* motion = 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::Old)) // TODO: temporary hack
{
COldObject* oldObj = dynamic_cast<COldObject*>(obj);
oldObj->SetDefRank(rankObj); // TODO: do we really need this?
if (type == OBJECT_BASE)
m_base = oldObj;
if (line->GetParam("select")->AsBool(false))
sel = oldObj;
// TODO: everything below should go to CObject::Read() function
// In fact, we could give CLevelParserLine as parameter to CreateObject() in the first place
Gfx::CameraType cType = line->GetParam("camera")->AsCameraType(Gfx::CAM_TYPE_NULL);
if (cType != Gfx::CAM_TYPE_NULL)
oldObj->SetCameraType(cType);
oldObj->SetCameraDist(line->GetParam("cameraDist")->AsFloat(50.0f));
oldObj->SetCameraLock(line->GetParam("cameraLock")->AsBool(false));
Gfx::PyroType pType = line->GetParam("pyro")->AsPyroType(Gfx::PT_NULL);
if (pType != Gfx::PT_NULL)
{
m_engine->GetPyroManager()->Create(pType, oldObj);
}
if (type == OBJECT_INFO)
{
CExchangePost* exchangePost = static_cast<CExchangePost*>(oldObj);
exchangePost->ReadInfo(line.get());
}
// Sets the parameters of the command line.
if (line->GetParam("cmdline")->IsDefined())
{
const auto& cmdline = line->GetParam("cmdline")->AsArray();
for (unsigned int i = 0; i < cmdline.size(); i++)
{
oldObj->SetCmdLine(i, cmdline[i]->AsFloat());
}
}
bool selectable = line->GetParam("selectable")->AsBool(true);
oldObj->SetSelectable(selectable);
oldObj->SetIgnoreBuildCheck(line->GetParam("ignoreBuildCheck")->AsBool(false));
oldObj->SetEnable(line->GetParam("enable")->AsBool(true));
oldObj->SetProxyActivate(line->GetParam("proxyActivate")->AsBool(false));
oldObj->SetProxyDistance(line->GetParam("proxyDistance")->AsFloat(15.0f)*g_unit);
oldObj->SetRange(line->GetParam("range")->AsFloat(30.0f));
oldObj->SetShield(line->GetParam("shield")->AsFloat(1.0f));
oldObj->SetMagnifyDamage(line->GetParam("magnifyDamage")->AsFloat(1.0f));
oldObj->SetTeam(line->GetParam("team")->AsInt(0));
oldObj->SetClip(line->GetParam("clip")->AsBool(true));
oldObj->SetCheckToken(!line->GetParam("checkToken")->IsDefined() ? trainer || !selectable : line->GetParam("checkToken")->AsBool(true));
// SetManual will affect bot speed
if (type == OBJECT_MOBILEdr)
{
oldObj->SetManual(!trainer);
}
Math::Vector zoom = line->GetParam("zoom")->AsPoint(Math::Vector(0.0f, 0.0f, 0.0f));
if (zoom.x != 0.0f || zoom.y != 0.0f || zoom.z != 0.0f)
oldObj->SetScale(zoom);
// only used in AlienWorm lines
if (type == OBJECT_WORM)
{
CMotion* motion = oldObj->GetMotion();
if (motion != nullptr && line->GetParam("param")->IsDefined())
{
const auto& p = line->GetParam("param")->AsArray();
for (unsigned int i = 0; i < 10 && i < p.size(); i++)
{
motion->SetParam(i, p[i]->AsFloat());
}
}
}
int run = -1;
std::map<int, Program*> loadedPrograms;
if (oldObj->Implements(ObjectInterfaceType::Programmable))
{
CBrain* brain = dynamic_cast<CProgrammableObject*>(oldObj)->GetBrain();
bool allFilled = true;
for (int i = 0; i < 10 || allFilled; i++)
{
std::string op = "script" + boost::lexical_cast<std::string>(i+1); // script1..script10
std::string opReadOnly = "scriptReadOnly" + boost::lexical_cast<std::string>(i+1); // scriptReadOnly1..scriptReadOnly10
std::string opRunnable = "scriptRunnable" + boost::lexical_cast<std::string>(i+1); // scriptRunnable1..scriptRunnable10
if (line->GetParam(op)->IsDefined())
{
Program* program = brain->AddProgram();
program->filename = "../" + line->GetParam(op)->AsPath("ai");
program->readOnly = line->GetParam(opReadOnly)->AsBool(true);
program->runnable = line->GetParam(opRunnable)->AsBool(true);
loadedPrograms[i] = program;
}
else
{
allFilled = false;
}
}
int i = line->GetParam("run")->AsInt(0);
if (i != 0)
{
run = i-1;
brain->SetScriptRun(loadedPrograms[run]);
}
}
CAuto* automat = oldObj->GetAuto();
if (automat != nullptr)
{
type = line->GetParam("autoType")->AsObjectType(OBJECT_NULL);
automat->SetType(type);
for (int i = 0; i < 5; i++)
{
std::string op = "autoValue" + boost::lexical_cast<std::string>(i+1); // autoValue1..autoValue5
automat->SetValue(i, line->GetParam(op)->AsFloat(0.0f));
}
automat->SetString(const_cast<char*>(line->GetParam("autoString")->AsPath("ai", "").c_str()));
int i = line->GetParam("run")->AsInt(-1);
if (i != -1)
{
if (i != PARAM_FIXSCENE &&
!m_dialog->GetMovies()) i = 0;
automat->Start(i); // starts the film
}
}
if (soluce && oldObj->Implements(ObjectInterfaceType::Programmable) && line->GetParam("soluce")->IsDefined())
dynamic_cast<CProgrammableObject*>(oldObj)->GetBrain()
->SetSoluceName(const_cast<char*>(line->GetParam("soluce")->AsPath("ai").c_str()));
if (line->GetParam("reset")->AsBool(false))
oldObj->SetAnimateOnReset(true);
}
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)
{
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->ShowMap(m_mapShow);
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 = std::unique_ptr<CSceneEndCondition>{new 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" && read[0] == 0 && !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 (read[0] != 0) 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->GetLevel()->GetFilename() + ":" + boost::lexical_cast<std::string>(line->GetLineNumber()));
}
if (read[0] == 0)
CompileScript(soluce); // compiles all scripts
if (strcmp(base, "missions") == 0 && !resetObject) // mission?
WriteFreeParam();
if (strcmp(base, "freemissions") == 0 && !resetObject) // free play?
{
m_researchDone[0] = m_freeResearch;
m_build = m_freeBuild;
m_build &= ~BUILD_RESEARCH;
m_build &= ~BUILD_LABO;
m_build |= BUILD_FACTORY;
m_build |= BUILD_GFLAT;
m_build |= BUILD_FLAG;
}
if (!resetObject && !fixScene)
{
ChangeColor(); // changes the colors of texture
m_short->SetMode(false); // vehicles?
}
CreateShortcuts();
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;
if (sel == nullptr)
obj = SearchHuman();
else
obj = sel;
if (obj != nullptr)
{
SelectObject(obj);
m_camera->SetControllingObject(obj);
m_camera->SetType(obj->GetCameraType());
}
}
if (m_fixScene)
m_camera->SetType(Gfx::CAM_TYPE_SCRIPT);
if (read[0] != 0 && 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_dialog->SetSceneRead("");
m_dialog->SetStackRead("");
throw;
}
m_dialog->SetSceneRead("");
m_dialog->SetStackRead("");
if (m_app->GetSceneTestMode())
m_eventQueue->AddEvent(Event(EVENT_QUIT));
}
//! 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()
{
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_dialog->GetGamerColorCombi();
colorRef2.r = 255.0f/256.0f;
colorRef2.g = 132.0f/256.0f;
colorRef2.b = 1.0f/256.0f; // orange
colorNew2 = m_dialog->GetGamerColorBand();
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_dialog->GetGamerColorHair();
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;
m_engine->ChangeTextureColor("textures/objects/base1.png", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, true);
m_engine->ChangeTextureColor("textures/objects/convert.png", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, true);
m_engine->ChangeTextureColor("textures/objects/derrick.png", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, true);
m_engine->ChangeTextureColor("textures/objects/factory.png", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, true);
m_engine->ChangeTextureColor("textures/objects/lemt.png", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, true);
m_engine->ChangeTextureColor("textures/objects/roller.png", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, 0, 0, true);
m_engine->ChangeTextureColor("textures/objects/search.png", m_colorRefBot, m_colorNewBot, 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", m_colorRefBot, m_colorNewBot, 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", m_colorRefBot, m_colorNewBot, colorRef2, colorNew2, 0.10f, -1.0f, ts, ti, exclu, 0, true);
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);
m_engine->ChangeTextureColor("textures/objects/plant.png", m_colorRefGreen, m_colorNewGreen, colorRef2, colorNew2, 0.50f, -1.0f, ts, ti);
// 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);
}
//! Updates the number of unnecessary objects
bool CRobotMain::TestGadgetQuantity(int rank)
{
static int table10[10] = {0,1,0,0,0,0,0,0,0,0};
static int table20[10] = {0,1,0,0,0,1,0,0,0,0};
static int table30[10] = {0,1,0,1,0,1,0,0,0,0};
static int table40[10] = {0,1,0,1,0,1,0,1,0,0};
static int table50[10] = {0,1,0,1,0,1,0,1,0,1};
static int table60[10] = {0,1,0,1,1,1,0,1,0,1};
static int table70[10] = {0,1,0,1,1,1,0,1,1,1};
static int table80[10] = {0,1,1,1,1,1,0,1,1,1};
static int table90[10] = {0,1,1,1,1,1,1,1,1,1};
float percent = m_engine->GetGadgetQuantity();
if (percent == 0.0f) return false;
if (percent == 1.0f) return true;
int *table;
if (percent <= 0.15f) table = table10;
else if (percent <= 0.25f) table = table20;
else if (percent <= 0.35f) table = table30;
else if (percent <= 0.45f) table = table40;
else if (percent <= 0.55f) table = table50;
else if (percent <= 0.65f) table = table60;
else if (percent <= 0.75f) table = table70;
else if (percent <= 0.85f) table = table80;
else table = table90;
return table[rank%10];
}
//! 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 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)
{
if (m_showLimit[i].link != 0)
{
m_showLimit[i].link->StopShowLimit();
}
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);
}
}
//! Adjusts the boundaries to show
void CRobotMain::AdjustShowLimit(int i, Math::Vector pos)
{
m_showLimit[i].pos = pos;
}
//! Mount the boundaries of the selected object
void CRobotMain::StartShowLimit()
{
CObject* obj = GetSelect();
if (obj == nullptr) return;
obj->StartShowLimit();
}
//! 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;
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;
}
}
}
//! Compiles all scripts of robots
void CRobotMain::CompileScript(bool soluce)
{
int nbError = 0;
int lastError = 0;
do
{
lastError = nbError;
nbError = 0;
for (CObject* obj : m_objMan->GetAllObjects())
{
if (! obj->Implements(ObjectInterfaceType::Programmable)) continue;
CBrain* brain = dynamic_cast<CProgrammableObject*>(obj)->GetBrain();
for (Program* program : brain->GetPrograms())
{
//? if (brain->GetCompile(j)) continue;
if (program->filename.empty()) continue;
std::string name = "ai/" + program->filename;
if (! brain->ReadProgram(program, const_cast<char*>(name.c_str())))
{
CLogger::GetInstancePointer()->Error("Unable to read script from file \"%s\"\n", name.c_str());
}
if (!brain->GetCompile(program)) nbError++;
}
LoadOneScript(obj, nbError);
}
}
while (nbError > 0 && nbError != lastError);
// Load all solutions.
if (soluce)
{
for (CObject* obj : m_objMan->GetAllObjects())
{
if (! obj->Implements(ObjectInterfaceType::Programmable)) continue;
CBrain* brain = dynamic_cast<CProgrammableObject*>(obj)->GetBrain();
char* name = brain->GetSoluceName();
if (name[0] != 0)
{
brain->ReadSoluce(name); // load solution
}
}
}
// Start all programs according to the command "run".
for (CObject* obj : m_objMan->GetAllObjects())
{
if (! obj->Implements(ObjectInterfaceType::Programmable)) continue;
CBrain* brain = dynamic_cast<CProgrammableObject*>(obj)->GetBrain();
Program* program = brain->GetScriptRun();
if (program != nullptr)
{
brain->RunProgram(program); // starts the program
}
}
}
//! Load all programs of the robot
void CRobotMain::LoadOneScript(CObject *obj, int &nbError)
{
if (! obj->Implements(ObjectInterfaceType::Programmable)) return;
CBrain* brain = dynamic_cast<CProgrammableObject*>(obj)->GetBrain();
if (!IsSelectable(obj)) return;
ObjectType type = obj->GetType();
if (type == OBJECT_HUMAN) return;
int objRank = obj->GetDefRank();
if (objRank == -1) return;
char* name = m_dialog->GetSceneName();
int rank = m_dialog->GetSceneRank();
for (unsigned int i = 0; i <= 999; i++)
{
//? if (brain->GetCompile(i)) continue;
char filename[MAX_FNAME];
sprintf(filename, "%s/%s/%c%.3d%.3d%.3d.txt",
GetSavegameDir(), m_gamerName.c_str(), name[0], rank, objRank, i);
if (CResourceManager::Exists(filename))
{
Program* program = brain->GetOrAddProgram(i);
brain->ReadProgram(program, filename);
if (!brain->GetCompile(program)) nbError++;
}
}
}
//! Load all programs of the robot
void CRobotMain::LoadFileScript(CObject *obj, const char* filename, int objRank,
int &nbError)
{
if (objRank == -1) return;
if (! obj->Implements(ObjectInterfaceType::Programmable)) return;
CBrain* brain = dynamic_cast<CProgrammableObject*>(obj)->GetBrain();
ObjectType type = obj->GetType();
if (type == OBJECT_HUMAN) return;
std::string dirname = filename;
dirname = dirname.substr(0, dirname.find_last_of("/"));
char fn[MAX_FNAME]; //TODO: Refactor to std::string
for (unsigned int i = 0; i <= 999; i++)
{
sprintf(fn, "%s/prog%.3d%.3d.txt", dirname.c_str(), objRank, i);
if (CResourceManager::Exists(fn))
{
Program* program = brain->GetOrAddProgram(i);
brain->ReadProgram(program, fn);
if (!brain->GetCompile(program)) nbError++;
}
}
}
//! Saves all programs of all the robots
void CRobotMain::SaveAllScript()
{
for (CObject* obj : m_objMan->GetAllObjects())
{
SaveOneScript(obj);
}
}
//! Saves all programs of the robot.
//! If a program does not exist, the corresponding file is destroyed.
void CRobotMain::SaveOneScript(CObject *obj)
{
if (! obj->Implements(ObjectInterfaceType::Programmable)) return;
CBrain* brain = dynamic_cast<CProgrammableObject*>(obj)->GetBrain();
if (!IsSelectable(obj)) return;
ObjectType type = obj->GetType();
if (type == OBJECT_HUMAN) return;
int objRank = obj->GetDefRank();
if (objRank == -1) return;
char* name = m_dialog->GetSceneName();
int rank = m_dialog->GetSceneRank();
auto programs = brain->GetPrograms();
// TODO: Find a better way to do that
for (unsigned int i = 0; i <= 999; i++)
{
char filename[MAX_FNAME];
sprintf(filename, "%s/%s/%c%.3d%.3d%.3d.txt",
GetSavegameDir(), m_gamerName.c_str(), name[0], rank, objRank, i);
if (i < programs.size())
{
brain->WriteProgram(programs[i], filename);
}
else
{
CResourceManager::Remove(filename);
}
}
}
//! Saves all programs of the robot.
//! If a program does not exist, the corresponding file is destroyed.
void CRobotMain::SaveFileScript(CObject *obj, const char* filename, int objRank)
{
if (objRank == -1) return;
if (! obj->Implements(ObjectInterfaceType::Programmable)) return;
CBrain* brain = dynamic_cast<CProgrammableObject*>(obj)->GetBrain();
ObjectType type = obj->GetType();
if (type == OBJECT_HUMAN) return;
std::string dirname = filename;
dirname = dirname.substr(0, dirname.find_last_of("/"));
char fn[MAX_FNAME]; //TODO: Refactor to std::string
auto programs = brain->GetPrograms();
// TODO: Find a better way to do that
for (unsigned int i = 0; i <= 999; i++)
{
sprintf(fn, "%s/prog%.3d%.3d.txt", dirname.c_str(), objRank, i);
if (i < programs.size())
{
brain->WriteProgram(programs[i], fn);
}
else
{
CResourceManager::Remove(fn);
}
}
}
//! 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;
CBrain* brain = dynamic_cast<CProgrammableObject*>(obj)->GetBrain();
ObjectType type = obj->GetType();
if (type == OBJECT_HUMAN) return true;
return brain->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;
CBrain* brain = dynamic_cast<CProgrammableObject*>(obj)->GetBrain();
ObjectType type = obj->GetType();
if (type == OBJECT_HUMAN) return true;
return brain->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::IsBusy()
{
if (CScriptFunctions::m_numberOfOpenFiles > 0) return true;
for (CObject* obj : m_objMan->GetAllObjects())
{
if (! obj->Implements(ObjectInterfaceType::Programmable)) continue;
CBrain* brain = dynamic_cast<CProgrammableObject*>(obj)->GetBrain();
if (brain->IsBusy()) return true;
}
return false;
}
//! Writes an object into the backup file
void CRobotMain::IOWriteObject(CLevelParserLine* line, CObject* obj)
{
if (obj->GetType() == OBJECT_FIX) return;
line->AddParam("type", CLevelParserParamUPtr{new CLevelParserParam(obj->GetType())});
line->AddParam("id", CLevelParserParamUPtr{new CLevelParserParam(obj->GetID())});
line->AddParam("pos", CLevelParserParamUPtr{new CLevelParserParam(obj->GetPosition()/g_unit)});
line->AddParam("angle", CLevelParserParamUPtr{new CLevelParserParam(obj->GetRotation() * Math::RAD_TO_DEG)});
line->AddParam("zoom", CLevelParserParamUPtr{new 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), CLevelParserParamUPtr{new 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), CLevelParserParamUPtr{new 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), CLevelParserParamUPtr{new CLevelParserParam(scale)});
}
}
line->AddParam("trainer", CLevelParserParamUPtr{new CLevelParserParam(obj->GetTrainer())});
line->AddParam("option", CLevelParserParamUPtr{new CLevelParserParam(obj->GetOption())});
}
if (obj == m_infoObject)
line->AddParam("select", CLevelParserParamUPtr{new CLevelParserParam(1)});
obj->Write(line);
if (obj->GetType() == OBJECT_BASE)
line->AddParam("run", CLevelParserParamUPtr{new CLevelParserParam(3)}); // stops and open (PARAM_FIXSCENE)
if (obj->Implements(ObjectInterfaceType::Programmable))
{
CBrain* brain = dynamic_cast<CProgrammableObject*>(obj)->GetBrain();
int run = brain->GetProgram();
if (run != -1)
{
line->AddParam("run", CLevelParserParamUPtr{new CLevelParserParam(run+1)});
}
auto programs = brain->GetPrograms();
for (unsigned int i = 0; i < programs.size(); i++)
{
if (programs[i]->readOnly)
{
line->AddParam("scriptReadOnly" + boost::lexical_cast<std::string>(i+1), CLevelParserParamUPtr{new CLevelParserParam(true)});
}
}
}
}
//! Saves the current game
bool CRobotMain::IOWriteScene(const char *filename, const char *filecbot, char *info)
{
CLevelParser levelParser(filename);
CLevelParserLineUPtr line;
line.reset(new CLevelParserLine("Title"));
line->AddParam("text", CLevelParserParamUPtr{new CLevelParserParam(std::string(info))});
levelParser.AddLine(std::move(line));
//TODO: Do we need that? It's not used anyway
line.reset(new CLevelParserLine("Version"));
line->AddParam("maj", CLevelParserParamUPtr{new CLevelParserParam(0)});
line->AddParam("min", CLevelParserParamUPtr{new CLevelParserParam(1)});
levelParser.AddLine(std::move(line));
line.reset(new CLevelParserLine("Created"));
line->AddParam("date", CLevelParserParamUPtr{new CLevelParserParam(GetCurrentTimestamp())});
levelParser.AddLine(std::move(line));
char* name = m_dialog->GetSceneName();
line.reset(new CLevelParserLine("Mission"));
line->AddParam("base", CLevelParserParamUPtr{new CLevelParserParam(std::string(name))});
line->AddParam("rank", CLevelParserParamUPtr{new CLevelParserParam(m_dialog->GetSceneRank())});
if (std::string(name) == "custom")
{
line->AddParam("dir", CLevelParserParamUPtr{new CLevelParserParam(std::string(m_dialog->GetSceneDir()))});
}
levelParser.AddLine(std::move(line));
line.reset(new CLevelParserLine("Map"));
line->AddParam("zoom", CLevelParserParamUPtr{new CLevelParserParam(m_map->GetZoomMap())});
levelParser.AddLine(std::move(line));
line.reset(new CLevelParserLine("DoneResearch"));
line->AddParam("bits", CLevelParserParamUPtr{new 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.reset(new CLevelParserLine("BlitzMode"));
line->AddParam("sleep", CLevelParserParamUPtr{new CLevelParserParam(sleep)});
line->AddParam("delay", CLevelParserParamUPtr{new CLevelParserParam(delay)});
line->AddParam("magnetic", CLevelParserParamUPtr{new CLevelParserParam(magnetic/g_unit)});
line->AddParam("progress", CLevelParserParamUPtr{new 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.reset(new CLevelParserLine("CreateFret"));
IOWriteObject(line.get(), cargo);
levelParser.AddLine(std::move(line));
}
}
if (obj->Implements(ObjectInterfaceType::Powered))
{
CObject* power = dynamic_cast<CPoweredObject*>(obj)->GetPower();
if (power != nullptr) // battery transported?
{
line.reset(new CLevelParserLine("CreatePower"));
IOWriteObject(line.get(), power);
levelParser.AddLine(std::move(line));
}
}
line.reset(new CLevelParserLine("CreateObject"));
IOWriteObject(line.get(), obj);
levelParser.AddLine(std::move(line));
SaveFileScript(obj, filename, objRank++);
}
try
{
levelParser.Save();
}
catch (CLevelParserException& e)
{
CLogger::GetInstancePointer()->Error("Failed to save level state - %s\n", e.what());
return false;
}
// Writes the file of stacks of execution.
FILE* file = fOpen(filecbot, "wb");
if (file == NULL) 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);
m_delayWriteMessage = 4; // displays message in 3 frames
return true;
}
//! Resumes the game
CObject* CRobotMain::IOReadObject(CLevelParserLine *line, const char* filename, int objRank)
{
Math::Vector pos = line->GetParam("pos")->AsPoint()*g_unit;
Math::Vector dir = line->GetParam("angle")->AsPoint()*(Math::PI/180.0f);
Math::Vector zoom = line->GetParam("zoom")->AsPoint();
ObjectType type = line->GetParam("type")->AsObjectType();
int id = line->GetParam("id")->AsInt();
bool trainer = line->GetParam("trainer")->AsBool(false);
bool toy = line->GetParam("toy")->AsBool(false);
int option = line->GetParam("option")->AsInt(0);
CObject* obj = m_objMan->CreateObject(pos, dir.y, type, 0.0f, 1.0f, 0.0f, trainer, toy, option, id);
if (obj->Implements(ObjectInterfaceType::Old))
{
COldObject* oldObj = dynamic_cast<COldObject*>(obj);
oldObj->SetDefRank(objRank);
oldObj->SetPosition(pos);
oldObj->SetRotation(dir);
if (zoom.x != 0.0f || zoom.y != 0.0f || zoom.z != 0.0f)
oldObj->SetScale(zoom);
for (int i = 1; i < OBJECTMAXPART; i++)
{
if (oldObj->GetObjectRank(i) == -1) continue;
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);
}
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));
}
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 (type == 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::Programmable))
{
CBrain* brain = dynamic_cast<CProgrammableObject*>(obj)->GetBrain();
if (run != -1)
{
Program* program = brain->GetOrAddProgram(run-1);
brain->SetScriptRun(program); // marks the program to be started
}
for (unsigned int i = 0; i <= 999; i++)
{
if (line->GetParam("scriptReadOnly" + boost::lexical_cast<std::string>(i+1))->AsBool(false))
{
Program* prog = brain->GetOrAddProgram(i);
prog->readOnly = true;
}
}
}
return obj;
}
//! Resumes some part of the game
CObject* CRobotMain::IOReadScene(const char *filename, const char *filecbot)
{
CLevelParser levelParser(filename);
levelParser.Load();
m_base = nullptr;
CObject* sel = nullptr;
int objRank = 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);
}
CObject* cargo = nullptr;
if (line->GetCommand() == "CreateFret")
cargo = IOReadObject(line.get(), filename, -1);
CObject* power = nullptr;
if (line->GetCommand() == "CreatePower")
power = IOReadObject(line.get(), filename, -1);
if (line->GetCommand() == "CreateObject")
{
CObject* obj = IOReadObject(line.get(), filename, 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);
CTaskManip* task = new CTaskManip(dynamic_cast<COldObject*>(obj));
task->Start(TMO_AUTO, TMA_GRAB); // holds the object!
delete task;
}
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);
}
}
}
// Compiles scripts.
int nbError = 0;
int lastError = 0;
do
{
lastError = nbError;
nbError = 0;
for (CObject* obj : m_objMan->GetAllObjects())
{
if (IsObjectBeingTransported(obj)) continue;
if (obj->Implements(ObjectInterfaceType::Programmable))
{
objRank = obj->GetDefRank();
if (objRank == -1) continue;
LoadFileScript(obj, filename, objRank, nbError);
}
}
}
while (nbError > 0 && nbError != lastError);
// Starts scripts
for (CObject* obj : m_objMan->GetAllObjects())
{
if (! obj->Implements(ObjectInterfaceType::Programmable)) continue;
if (obj->GetDefRank() == -1) continue;
CBrain* brain = dynamic_cast<CProgrammableObject*>(obj)->GetBrain();
Program* program = brain->GetScriptRun();
if (program != nullptr)
{
brain->RunProgram(program); // starts the program
}
}
// Reads the file of stacks of execution.
FILE* file = fOpen(filecbot, "rb");
if (file != NULL)
{
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);
}
return sel;
}
//! Writes the global parameters for free play
void CRobotMain::WriteFreeParam()
{
m_freeResearch |= m_researchDone[0];
m_freeBuild |= m_build;
if (m_gamerName == "") return;
COutputStream file;
file.open(std::string(GetSavegameDir()) + "/" + m_gamerName + "/research.gam");
if (!file.is_open())
{
CLogger::GetInstancePointer()->Error("Unable to write free game unlock state\n");
return;
}
file << "research=" << m_freeResearch << " build=" << m_freeBuild << "\n";
file.close();
}
//! Reads the global parameters for free play
void CRobotMain::ReadFreeParam()
{
m_freeResearch = 0;
m_freeBuild = 0;
if (m_gamerName == "") return;
if (!CResourceManager::Exists(std::string(GetSavegameDir()) + "/" + m_gamerName + "/research.gam"))
return;
CInputStream file;
file.open(std::string(GetSavegameDir()) + "/" + m_gamerName + "/research.gam");
if (!file.is_open())
{
CLogger::GetInstancePointer()->Error("Unable to read free game unlock state\n");
return;
}
std::string line;
std::getline(file, line);
sscanf(line.c_str(), "research=%d build=%d\n", &m_freeResearch, &m_freeBuild);
file.close();
}
//! 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_dialog->GetSceneSoluce(), false, true);
if (!GetNiceReset()) return;
for (CObject* obj : m_objMan->GetAllObjects())
{
if (obj->GetAnimateOnReset())
{
m_engine->GetPyroManager()->Create(Gfx::PT_RESET, obj);
}
}
}
catch (const std::runtime_error& e)
{
CLogger::GetInstancePointer()->Error("An error occured while trying to reset scene\n");
CLogger::GetInstancePointer()->Error("%s\n", e.what());
ChangePhase(PHASE_TERM);
}
}
//! Updates the audiotracks
void CRobotMain::UpdateAudio(bool frame)
{
for(std::unique_ptr<CAudioChangeCondition>& audioChange : m_audioChange)
{
if (audioChange->changed) continue;
if (audioChange->Check())
{
CLogger::GetInstancePointer()->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;
if (finalResult == INFO_LOST || finalResult == INFO_LOSTq)
{
break;
}
}
}
if (finalResult == ERR_OK && !hasWinningConditions) finalResult = ERR_MISSION_NOTERM;
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) {
CLogger::GetInstancePointer()->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)
{
CLogger::GetInstancePointer()->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)
{
CLogger::GetInstancePointer()->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)
{
CLogger::GetInstancePointer()->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 && m_base != nullptr && m_base->GetSelectable() && !isImmediat) 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)
{
CLogger::GetInstancePointer()->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::GetGlint()
{
return m_dialog->GetGlint();
}
bool CRobotMain::GetSoluce4()
{
return m_dialog->GetSoluce4();
}
bool CRobotMain::GetMovies()
{
return m_dialog->GetMovies();
}
bool CRobotMain::GetNiceReset()
{
return m_dialog->GetNiceReset();
}
bool CRobotMain::GetHimselfDamage()
{
return m_dialog->GetHimselfDamage();
}
bool CRobotMain::GetShowSoluce()
{
return m_showSoluce;
}
bool CRobotMain::GetSceneSoluce()
{
if (m_infoFilename[SATCOM_SOLUCE][0] == 0) return false;
return m_dialog->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;
}
const char* CRobotMain::GetSavegameDir()
{
return m_dialog->GetSavegameDir().c_str();
}
const char* CRobotMain::GetPublicDir()
{
return m_dialog->GetPublicDir().c_str();
}
const char* CRobotMain::GetFilesDir()
{
return m_dialog->GetFilesDir().c_str();
}
MissionType CRobotMain::GetMissionType()
{
return m_missionType;
}
//! Change the player's name
void CRobotMain::SetGamerName(const char *name)
{
m_gamerName = std::string(name);
SetGlobalGamerName(m_gamerName);
ReadFreeParam();
}
//! Gets the player's name
char* CRobotMain::GetGamerName()
{
return const_cast<char*>(m_gamerName.c_str());
}
//! Returns the representation to use for the player
int CRobotMain::GetGamerFace()
{
return m_dialog->GetGamerFace();
}
//! Returns the representation to use for the player
int CRobotMain::GetGamerGlasses()
{
return m_dialog->GetGamerGlasses();
}
//! Returns the mode with just the head
bool CRobotMain::GetGamerOnlyHead()
{
return m_dialog->GetGamerOnlyHead();
}
//! Returns the angle of presentation
float CRobotMain::GetPersoAngle()
{
return m_dialog->GetPersoAngle();
}
char* CRobotMain::GetSceneName()
{
return m_dialog->GetSceneName();
}
int CRobotMain::GetSceneRank()
{
return m_dialog->GetSceneRank();
}
//! 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_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_map->GetShowMap() && m_mapShow;
}
//! Management of the lock mode for movies
void CRobotMain::SetMovieLock(bool lock)
{
m_movieLock = lock;
m_engine->SetMovieLock(m_movieLock);
CreateShortcuts();
m_map->ShowMap(!m_movieLock && m_mapShow);
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()
{
CLogger::GetInstancePointer()->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);
}
std::string& CRobotMain::GetUserLevelName(int id)
{
return m_dialog->GetUserLevelName(id);
}
void CRobotMain::StartMissionTimer()
{
if (m_missionTimerEnabled && !m_missionTimerStarted)
{
CLogger::GetInstancePointer()->Info("Starting mission timer...\n");
m_missionTimerStarted = true;
}
}
void CRobotMain::SetAutosave(bool enable)
{
m_autosave = enable;
m_autosaveLast = m_gameTimeAbsolute;
AutosaveRotate(false);
}
bool CRobotMain::GetAutosave()
{
return m_autosave;
}
void CRobotMain::SetAutosaveInterval(int interval)
{
m_autosaveInterval = interval;
m_autosaveLast = m_gameTimeAbsolute;
}
int CRobotMain::GetAutosaveInterval()
{
return m_autosaveInterval;
}
void CRobotMain::SetAutosaveSlots(int slots)
{
m_autosaveSlots = slots;
AutosaveRotate(false);
}
int CRobotMain::GetAutosaveSlots()
{
return m_autosaveSlots;
}
int CRobotMain::AutosaveRotate(bool freeOne)
{
CLogger::GetInstancePointer()->Debug("Rotate autosaves...\n");
// Find autosave dirs
auto saveDirs = CResourceManager::ListDirectories(std::string(GetSavegameDir()) + "/" + GetGamerName());
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] = std::string(GetSavegameDir()) + "/" + GetGamerName() + "/" + dir;
}
}
catch (...)
{
CLogger::GetInstancePointer()->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)
{
CLogger::GetInstancePointer()->Trace("Remove %s\n", autosaveDirs[i].c_str());
CResourceManager::RemoveDirectory(autosaveDirs[i]);
rotate = true;
}
else
{
CLogger::GetInstancePointer()->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 = std::string(GetSavegameDir()) + "/" + GetGamerName() + "/autosave" + boost::lexical_cast<std::string>(save.first);
CLogger::GetInstancePointer()->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);
CLogger::GetInstancePointer()->Info("Autosave!\n");
std::string dir = std::string(GetSavegameDir()) + "/" + GetGamerName() + "/autosave" + boost::lexical_cast<std::string>(id);
if (!CResourceManager::DirectoryExists(dir))
{
CResourceManager::CreateDirectory(dir);
}
std::string savegameFileName = dir + "/data.sav";
std::string fileCBot = CResourceManager::GetSaveLocation() + "/" + dir + "/cbot.run";
char timestr[100];
TimeToAscii(time(NULL), timestr);
IOWriteScene(savegameFileName.c_str(), fileCBot.c_str(), const_cast<char*>((std::string("[AUTOSAVE] ")+timestr).c_str()));
m_dialog->MakeSaveScreenshot(dir + "/screen.png");
}
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];
}
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)
{
WriteFreeParam();
}
}
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;
}