colobot/src/ui/screen/screen_level_list.cpp

622 lines
18 KiB
C++

/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2015, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "ui/screen/screen_level_list.h"
#include "app/app.h"
#include "common/settings.h"
#include "common/resources/resourcemanager.h"
#include "level/player_profile.h"
#include "level/parser/parser.h"
#include "ui/maindialog.h"
#include "ui/controls/button.h"
#include "ui/controls/check.h"
#include "ui/controls/edit.h"
#include "ui/controls/interface.h"
#include "ui/controls/label.h"
#include "ui/controls/list.h"
#include "ui/controls/window.h"
#include <algorithm>
namespace Ui
{
CScreenLevelList::CScreenLevelList(CMainDialog* mainDialog)
: m_dialog(mainDialog),
m_category{},
m_listCategory{},
m_sceneSoluce{false},
m_maxList{0},
m_accessChap{0}
{
}
void CScreenLevelList::SetLevelCategory(LevelCategory category)
{
m_category = category;
if ( static_cast<int>(m_category) >= static_cast<int>(LevelCategory::Max) )
{
m_category = m_listCategory;
}
else
{
m_listCategory = m_category;
}
}
void CScreenLevelList::CreateInterface()
{
CWindow* pw;
CEdit* pe;
CLabel* pl;
CButton* pb;
CCheck* pc;
CList* pli;
Math::Point pos, ddim;
int res;
std::string name;
if ( m_category == LevelCategory::FreeGame )
{
m_accessChap = m_main->GetPlayerProfile()->GetChapPassed(LevelCategory::Missions);
}
pos.x = 0.10f;
pos.y = 0.10f;
ddim.x = 0.80f;
ddim.y = 0.80f;
pw = m_interface->CreateWindows(pos, ddim, 12, EVENT_WINDOW5);
pw->SetClosable(true);
if ( m_category == LevelCategory::Exercises ) res = RT_TITLE_TRAINER;
if ( m_category == LevelCategory::Challenges ) res = RT_TITLE_DEFI;
if ( m_category == LevelCategory::Missions ) res = RT_TITLE_MISSION;
if ( m_category == LevelCategory::FreeGame ) res = RT_TITLE_FREE;
if ( m_category == LevelCategory::CodeBattles ) res = RT_TITLE_CODE_BATTLES;
if ( m_category == LevelCategory::CustomLevels ) res = RT_TITLE_USER;
GetResource(RES_TEXT, res, name);
pw->SetName(name);
pos.x = 0.10f;
pos.y = 0.40f;
ddim.x = 0.50f;
ddim.y = 0.50f;
pw->CreateGroup(pos, ddim, 5, EVENT_INTERFACE_GLINTl); // orange corner
pos.x = 0.40f;
pos.y = 0.10f;
ddim.x = 0.50f;
ddim.y = 0.50f;
pw->CreateGroup(pos, ddim, 4, EVENT_INTERFACE_GLINTr); // blue corner
// Displays a list of chapters:
pos.x = ox+sx*3;
pos.y = oy+sy*10.5f;
ddim.x = dim.x*7.5f;
ddim.y = dim.y*0.6f;
res = RT_PLAY_CHAP_CHAPTERS;
if ( m_category == LevelCategory::Missions ) res = RT_PLAY_CHAP_PLANETS;
if ( m_category == LevelCategory::FreeGame ) res = RT_PLAY_CHAP_PLANETS;
if ( m_category == LevelCategory::CustomLevels ) res = RT_PLAY_CHAP_USERLVL;
GetResource(RES_TEXT, res, name);
pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL11, name);
pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);
pos.y = oy+sy*6.7f;
ddim.y = dim.y*4.5f;
ddim.x = dim.x*6.5f;
pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_CHAP);
pli->SetState(STATE_SHADOW);
m_chap[m_category] = m_main->GetPlayerProfile()->GetSelectedChap(m_category)-1;
UpdateSceneChap(m_chap[m_category]);
if ( m_category != LevelCategory::FreeGame && m_category != LevelCategory::CustomLevels ) // Don't show completion marks in free game and userlevels
{
pli->SetState(STATE_EXTEND);
}
// Displays a list of missions:
pos.x = ox+sx*9.5f;
pos.y = oy+sy*10.5f;
ddim.x = dim.x*7.5f;
ddim.y = dim.y*0.6f;
res = RT_PLAY_LIST_LEVELS;
if ( m_category == LevelCategory::Exercises ) res = RT_PLAY_LIST_EXERCISES;
if ( m_category == LevelCategory::Challenges ) res = RT_PLAY_LIST_CHALLENGES;
if ( m_category == LevelCategory::Missions ) res = RT_PLAY_LIST_MISSIONS;
if ( m_category == LevelCategory::FreeGame ) res = RT_PLAY_LIST_FREEGAME;
GetResource(RES_TEXT, res, name);
pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL12, name);
pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);
pos.y = oy+sy*6.7f;
ddim.y = dim.y*4.5f;
ddim.x = dim.x*6.5f;
pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_LIST);
pli->SetState(STATE_SHADOW);
m_sel[m_category] = m_main->GetPlayerProfile()->GetSelectedRank(m_category)-1;
UpdateSceneList(m_chap[m_category], m_sel[m_category]);
if ( m_category != LevelCategory::FreeGame && m_category != LevelCategory::CustomLevels ) // Don't show completion marks in free game and userlevels
{
pli->SetState(STATE_EXTEND);
}
pos = pli->GetPos();
ddim = pli->GetDim();
// Displays the summary:
pos.x = ox+sx*3;
pos.y = oy+sy*5.4f;
ddim.x = dim.x*6.5f;
ddim.y = dim.y*0.6f;
GetResource(RES_TEXT, RT_PLAY_RESUME, name);
pl = pw->CreateLabel(pos, ddim, 0, EVENT_LABEL13, name);
pl->SetTextAlign(Gfx::TEXT_ALIGN_LEFT);
pos.x = ox+sx*3;
pos.y = oy+sy*3.6f;
ddim.x = dim.x*13.4f;
ddim.y = dim.y*1.9f;
pe = pw->CreateEdit(pos, ddim, 0, EVENT_INTERFACE_RESUME);
pe->SetState(STATE_SHADOW);
pe->SetMaxChar(500);
pe->SetEditCap(false); // just to see
pe->SetHighlightCap(false);
// Button displays the "soluce":
if ( m_category != LevelCategory::Exercises &&
m_category != LevelCategory::FreeGame )
{
pos.x = ox+sx*9.5f;
pos.y = oy+sy*5.8f;
ddim.x = dim.x*6.5f;
ddim.y = dim.y*0.5f;
pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_SOLUCE);
pc->SetState(STATE_SHADOW);
pc->ClearState(STATE_CHECK);
}
m_sceneSoluce = false;
UpdateSceneResume(m_chap[m_category]+1, m_sel[m_category]+1);
if ( m_category == LevelCategory::Missions ||
m_category == LevelCategory::FreeGame ||
m_category == LevelCategory::CustomLevels )
{
pos.x = ox+sx*9.5f;
pos.y = oy+sy*2;
ddim.x = dim.x*3.7f;
ddim.y = dim.y*1;
pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PLAY);
pb->SetState(STATE_SHADOW);
if ( m_maxList == 0 )
{
pb->ClearState(STATE_ENABLE);
}
pos.x += dim.x*4.0f;
ddim.x = dim.x*2.5f;
pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_READ);
pb->SetState(STATE_SHADOW);
if ( !m_main->GetPlayerProfile()->HasAnySavedScene() ) // no file to read?
{
pb->ClearState(STATE_ENABLE);
}
}
else
{
pos.x = ox+sx*9.5f;
pos.y = oy+sy*2;
ddim.x = dim.x*6.5f;
ddim.y = dim.y*1;
pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_PLAY);
pb->SetState(STATE_SHADOW);
if ( m_maxList == 0 )
{
pb->ClearState(STATE_ENABLE);
}
}
pos.x = ox+sx*3;
ddim.x = dim.x*4;
pb = pw->CreateButton(pos, ddim, -1, EVENT_INTERFACE_BACK);
pb->SetState(STATE_SHADOW);
SetBackground("textures/interface/interface.png");
CreateVersionDisplay();
if (m_category == LevelCategory::CustomLevels)
{
if(m_customLevelList.size() == 0)
{
m_main->ChangePhase(PHASE_MAIN_MENU);
std::string title, text;
GetResource(RES_TEXT, RT_DIALOG_NOUSRLVL_TITLE, title);
GetResource(RES_TEXT, RT_DIALOG_NOUSRLVL_TEXT, text);
m_dialog->StartInformation(title, title, text);
}
}
}
bool CScreenLevelList::EventProcess(const Event &event)
{
CWindow* pw;
CList* pl;
CButton* pb;
pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if ( pw == nullptr ) return false;
if ( event.type == pw->GetEventTypeClose() ||
event.type == EVENT_INTERFACE_BACK ||
(event.type == EVENT_KEY_DOWN && event.GetData<KeyEventData>()->key == KEY(ESCAPE)) )
{
m_main->ChangePhase(PHASE_MAIN_MENU);
return false;
}
switch( event.type )
{
case EVENT_INTERFACE_CHAP:
pl = static_cast<CList*>(pw->SearchControl(EVENT_INTERFACE_CHAP));
if ( pl == nullptr ) break;
m_chap[m_category] = pl->GetSelect();
m_main->GetPlayerProfile()->SetSelectedChap(m_category, m_chap[m_category]+1);
UpdateSceneList(m_chap[m_category], m_sel[m_category]);
UpdateSceneResume(m_chap[m_category]+1, m_sel[m_category]+1);
break;
case EVENT_INTERFACE_LIST:
pl = static_cast<CList*>(pw->SearchControl(EVENT_INTERFACE_LIST));
if ( pl == nullptr ) break;
m_sel[m_category] = pl->GetSelect();
m_main->GetPlayerProfile()->SetSelectedRank(m_category, m_sel[m_category]+1);
UpdateSceneResume(m_chap[m_category]+1, m_sel[m_category]+1);
break;
case EVENT_INTERFACE_SOLUCE:
pb = static_cast<CButton*>(pw->SearchControl(EVENT_INTERFACE_SOLUCE));
if ( pb == nullptr ) break;
m_sceneSoluce = !m_sceneSoluce;
pb->SetState(STATE_CHECK, m_sceneSoluce);
break;
case EVENT_INTERFACE_PLAY:
m_main->SetLevel(m_category, m_chap[m_category]+1, m_sel[m_category]+1);
m_main->ChangePhase(PHASE_SIMUL);
break;
case EVENT_INTERFACE_READ:
m_main->ChangePhase(PHASE_READ);
break;
default:
return true;
}
return false;
}
void CScreenLevelList::SetSelection(LevelCategory category, int chap, int rank)
{
m_chap[category] = chap;
m_sel[category] = rank;
}
// Updates the lists according to the cheat code.
void CScreenLevelList::AllMissionUpdate()
{
UpdateSceneChap(m_chap[m_category]);
UpdateSceneList(m_chap[m_category], m_sel[m_category]);
}
// Whether to show the solution.
bool CScreenLevelList::GetSceneSoluce()
{
return m_sceneSoluce;
}
// Updates the chapters of exercises or missions.
void CScreenLevelList::UpdateSceneChap(int &chap)
{
CWindow* pw;
CList* pl;
std::string fileName;
char line[500] = {0};
bool bPassed;
pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if ( pw == nullptr ) return;
pl = static_cast<CList*>(pw->SearchControl(EVENT_INTERFACE_CHAP));
if ( pl == nullptr ) return;
pl->Flush();
int j;
if ( m_category == LevelCategory::CustomLevels )
{
UpdateCustomLevelList();
for ( j=0 ; j < static_cast<int>(m_customLevelList.size()) ; j++ )
{
try
{
CLevelParser levelParser("custom", j+1, 0);
levelParser.Load();
pl->SetItemName(j, levelParser.Get("Title")->GetParam("text")->AsString().c_str());
pl->SetEnable(j, true);
}
catch (CLevelParserException& e)
{
pl->SetItemName(j, (std::string("[ERROR]: ")+e.what()).c_str());
pl->SetEnable(j, false);
}
}
}
else
{
for ( j=0 ; j<MAXSCENE ; j++ )
{
CLevelParser levelParser(m_category, j+1, 0);
if (!levelParser.Exists())
break;
try
{
levelParser.Load();
sprintf(line, "%d: %s", j+1, levelParser.Get("Title")->GetParam("text")->AsString().c_str());
}
catch (CLevelParserException& e)
{
sprintf(line, "%s", (std::string("[ERROR]: ")+e.what()).c_str());
}
bPassed = m_main->GetPlayerProfile()->GetLevelPassed(m_category, j+1, 0);
pl->SetItemName(j, line);
pl->SetCheck(j, bPassed);
pl->SetEnable(j, true);
if ( m_category == LevelCategory::Missions && !m_main->GetShowAll() && !bPassed )
{
j ++;
break;
}
if ( m_category == LevelCategory::FreeGame && j == m_accessChap )
{
j ++;
break;
}
}
}
if ( chap > j-1 ) chap = j-1;
pl->SetSelect(chap);
pl->ShowSelect(false); // shows the selected columns
}
// Updates the list of exercises or missions.
void CScreenLevelList::UpdateSceneList(int chap, int &sel)
{
CWindow* pw;
CList* pl;
std::string fileName;
char line[500] = {0};
int j;
bool bPassed;
pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if ( pw == nullptr ) return;
pl = static_cast<CList*>(pw->SearchControl(EVENT_INTERFACE_LIST));
if ( pl == nullptr ) return;
pl->Flush();
bool readAll = true;
for ( j=0 ; j<MAXSCENE ; j++ )
{
CLevelParser levelParser(m_category, chap+1, j+1);
if (!levelParser.Exists())
{
readAll = true;
break;
}
else
{
if (!readAll)
break;
}
try
{
levelParser.Load();
sprintf(line, "%d: %s", j+1, levelParser.Get("Title")->GetParam("text")->AsString().c_str());
}
catch (CLevelParserException& e)
{
sprintf(line, "%s", (std::string("[ERROR]: ")+e.what()).c_str());
}
bPassed = m_main->GetPlayerProfile()->GetLevelPassed(m_category, chap+1, j+1);
pl->SetItemName(j, line);
pl->SetCheck(j, bPassed);
pl->SetEnable(j, true);
if ( m_category == LevelCategory::Missions && !m_main->GetShowAll() && !bPassed )
{
readAll = false;
}
}
if (readAll)
{
m_maxList = j;
}
else
{
m_maxList = j+1; // this is not the last!
}
if ( sel > j-1 ) sel = j-1;
pl->SetSelect(sel);
pl->ShowSelect(false); // shows the selected columns
}
// Updates the button "solution" according to cheat code.
void CScreenLevelList::ShowSoluceUpdate()
{
CWindow* pw;
CEdit* pe;
CCheck* pc;
m_sceneSoluce = false;
pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if ( pw == nullptr ) return;
pe = static_cast<CEdit*>(pw->SearchControl(EVENT_INTERFACE_RESUME));
if ( pe == nullptr ) return;
pc = static_cast<CCheck*>(pw->SearchControl(EVENT_INTERFACE_SOLUCE));
if ( pc == nullptr ) return;
if ( m_main->GetShowSoluce() )
{
pc->SetState(STATE_VISIBLE);
pc->SetState(STATE_CHECK);
m_sceneSoluce = true;
}
else
{
pc->ClearState(STATE_VISIBLE);
pc->ClearState(STATE_CHECK);
m_sceneSoluce = false;
}
}
// Updates a summary of exercise or mission.
void CScreenLevelList::UpdateSceneResume(int chap, int rank)
{
CWindow* pw;
CEdit* pe;
CCheck* pc;
std::string fileName;
int numTry;
bool bPassed, bVisible;
pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if ( pw == nullptr ) return;
pe = static_cast<CEdit*>(pw->SearchControl(EVENT_INTERFACE_RESUME));
if ( pe == nullptr ) return;
pc = static_cast<CCheck*>(pw->SearchControl(EVENT_INTERFACE_SOLUCE));
if ( pc == nullptr )
{
m_sceneSoluce = false;
}
else
{
numTry = m_main->GetPlayerProfile()->GetLevelTryCount(m_category, chap, rank);
bPassed = m_main->GetPlayerProfile()->GetLevelPassed(m_category, chap, rank);
bVisible = ( numTry > 2 || bPassed || m_main->GetShowSoluce() );
if ( !CSettings::GetInstancePointer()->GetSoluce4() ) bVisible = false;
pc->SetState(STATE_VISIBLE, bVisible);
if ( !bVisible )
{
pc->ClearState(STATE_CHECK);
m_sceneSoluce = false;
}
}
if(chap == 0 || rank == 0) return;
try
{
CLevelParser levelParser(m_category, chap, rank);
levelParser.Load();
pe->SetText(levelParser.Get("Resume")->GetParam("text")->AsString().c_str());
}
catch (CLevelParserException& e)
{
pe->SetText((std::string("[ERROR]: ")+e.what()).c_str());
}
}
void CScreenLevelList::UpdateChapterPassed()
{
// TODO: CScreenLevelList is a bad place for this function
bool bAll = true;
for ( int i=0 ; i<m_maxList ; i++ )
{
if (!m_main->GetPlayerProfile()->GetLevelPassed(m_category, m_chap[m_category]+1, i+1))
{
bAll = false;
break;
}
}
m_main->GetPlayerProfile()->IncrementLevelTryCount(m_category, m_chap[m_category]+1, 0);
m_main->GetPlayerProfile()->SetLevelPassed(m_category, m_chap[m_category]+1, 0, bAll);
}
// Passes to the next mission, and possibly in the next chapter.
void CScreenLevelList::NextMission()
{
m_sel[m_category] ++; // next mission
if ( m_sel[m_category] >= m_maxList ) // last mission of the chapter?
{
m_chap[m_category] ++; // next chapter
m_sel[m_category] = 0; // first mission
}
m_main->GetPlayerProfile()->SetSelectedChap(m_category, m_chap[m_category]+1);
m_main->GetPlayerProfile()->SetSelectedRank(m_category, m_sel[m_category]+1);
}
// TODO: Separate class for userlevels?
void CScreenLevelList::UpdateCustomLevelList()
{
auto userLevelDirs = CResourceManager::ListDirectories("levels/custom/");
std::sort(userLevelDirs.begin(), userLevelDirs.end());
m_customLevelList = userLevelDirs;
}
std::string CScreenLevelList::GetCustomLevelName(int id)
{
if(id < 1 || id > static_cast<int>(m_customLevelList.size())) return "";
return m_customLevelList[id-1];
}
const std::vector<std::string>& CScreenLevelList::GetCustomLevelList()
{
return m_customLevelList;
}
} // namespace Ui