Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F118517
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
64 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/LevelEditSelect.cpp b/src/LevelEditSelect.cpp
index 7ef7c1f..1cd2366 100644
--- a/src/LevelEditSelect.cpp
+++ b/src/LevelEditSelect.cpp
@@ -1,758 +1,770 @@
/*
* Copyright (C) 2012-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow 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.
*
* Me and My Shadow 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 Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LevelEditSelect.h"
#include "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include "GUIScrollBar.h"
#include "InputManager.h"
#include "StatisticsManager.h"
#include "Game.h"
#include "GUIOverlay.h"
#include <algorithm>
#include <string>
#include <iostream>
#include "libs/tinyformat/tinyformat.h"
using namespace std;
LevelEditSelect::LevelEditSelect(ImageManager& imageManager, SDL_Renderer& renderer):LevelSelect(imageManager,renderer,_("Map Editor"),LevelPackManager::CUSTOM_PACKS){
//Create the gui.
createGUI(imageManager,renderer, true);
//Set the levelEditGUIObjectRoot.
levelEditGUIObjectRoot=GUIObjectRoot;
//show level list
changePack();
refresh(imageManager, renderer);
}
LevelEditSelect::~LevelEditSelect(){
selectedNumber=NULL;
}
void LevelEditSelect::createGUI(ImageManager& imageManager,SDL_Renderer &renderer, bool initial){
if(initial){
//The levelpack name text field.
levelpackName=new GUITextBox(imageManager,renderer,280,104,240,32);
levelpackName->eventCallback=this;
levelpackName->visible=false;
GUIObjectRoot->addChild(levelpackName);
}
if(!initial){
//Remove the previous buttons.
//TODO: better way to do this?
for(int i=0;i<(int)GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->caption!=_("Back")){
delete GUIObjectRoot->childControls[i];
GUIObjectRoot->childControls.erase(GUIObjectRoot->childControls.begin()+i);
i--;
}
}
}
//Create the six buttons at the bottom of the screen.
GUIButton* obj=new GUIButton(imageManager,renderer,SCREEN_WIDTH*0.02,SCREEN_HEIGHT-120,-1,32,_("New Levelpack"));
obj->name="cmdNewLvlpack";
obj->eventCallback=this;
GUIObjectRoot->addChild(obj);
propertiesPack=new GUIButton(imageManager,renderer,SCREEN_WIDTH*0.5,SCREEN_HEIGHT-120,-1,32,_("Pack Properties"),0,true,true,GUIGravityCenter);
propertiesPack->name="cmdLvlpackProp";
propertiesPack->eventCallback=this;
GUIObjectRoot->addChild(propertiesPack);
removePack=new GUIButton(imageManager,renderer,SCREEN_WIDTH*0.98,SCREEN_HEIGHT-120,-1,32,_("Remove Pack"),0,true,true,GUIGravityRight);
removePack->name="cmdRmLvlpack";
removePack->eventCallback=this;
GUIObjectRoot->addChild(removePack);
move=new GUIButton(imageManager,renderer,SCREEN_WIDTH*0.02,SCREEN_HEIGHT-60,-1,32,_("Move Map"));
move->name="cmdMoveMap";
move->eventCallback=this;
//NOTE: Set enabled equal to the inverse of initial.
//When resizing the window initial will be false and therefor the move button can stay enabled.
move->enabled=false;
GUIObjectRoot->addChild(move);
remove=new GUIButton(imageManager,renderer,SCREEN_WIDTH*0.5,SCREEN_HEIGHT-60,-1,32,_("Remove Map"),0,false,true,GUIGravityCenter);
remove->name="cmdRmMap";
remove->eventCallback=this;
GUIObjectRoot->addChild(remove);
edit=new GUIButton(imageManager,renderer,SCREEN_WIDTH*0.98,SCREEN_HEIGHT-60,-1,32,_("Edit Map"),0,false,true,GUIGravityRight);
edit->name="cmdEdit";
edit->eventCallback=this;
GUIObjectRoot->addChild(edit);
//Now update widgets and then check if they overlap
GUIObjectRoot->render(renderer,0,0,false);
if(propertiesPack->left-propertiesPack->gravityX < obj->left+obj->width ||
propertiesPack->left-propertiesPack->gravityX+propertiesPack->width > removePack->left-removePack->gravityX){
obj->smallFont=true;
obj->width=-1;
propertiesPack->smallFont=true;
propertiesPack->width=-1;
removePack->smallFont=true;
removePack->width=-1;
move->smallFont=true;
move->width=-1;
remove->smallFont=true;
remove->width=-1;
edit->smallFont=true;
edit->width=-1;
}
//Check again
GUIObjectRoot->render(renderer,0,0,false);
if(propertiesPack->left-propertiesPack->gravityX < obj->left+obj->width ||
propertiesPack->left-propertiesPack->gravityX+propertiesPack->width > removePack->left-removePack->gravityX){
obj->left = SCREEN_WIDTH*0.02;
obj->top = SCREEN_HEIGHT-140;
obj->smallFont=false;
obj->width=-1;
obj->gravity = GUIGravityLeft;
propertiesPack->left = SCREEN_WIDTH*0.02;
propertiesPack->top = SCREEN_HEIGHT-100;
propertiesPack->smallFont=false;
propertiesPack->width=-1;
propertiesPack->gravity = GUIGravityLeft;
removePack->left = SCREEN_WIDTH*0.02;
removePack->top = SCREEN_HEIGHT-60;
removePack->smallFont=false;
removePack->width=-1;
removePack->gravity = GUIGravityLeft;
move->left = SCREEN_WIDTH*0.98;
move->top = SCREEN_HEIGHT-140;
move->smallFont=false;
move->width=-1;
move->gravity = GUIGravityRight;
remove->left = SCREEN_WIDTH*0.98;
remove->top = SCREEN_HEIGHT-100;
remove->smallFont=false;
remove->width=-1;
remove->gravity = GUIGravityRight;
edit->left = SCREEN_WIDTH*0.98;
edit->top = SCREEN_HEIGHT-60;
edit->smallFont=false;
edit->width=-1;
edit->gravity = GUIGravityRight;
}
}
void LevelEditSelect::changePack(){
packName=levelpacks->item[levelpacks->value].second;
if(packName=="Custom Levels"){
//Disable some levelpack buttons.
propertiesPack->enabled=false;
removePack->enabled=false;
}else{
//Enable some levelpack buttons.
propertiesPack->enabled=true;
removePack->enabled=true;
}
//Set last levelpack.
getSettings()->setValue("lastlevelpack",levelpacks->getName());
//Now let levels point to the right pack.
levels=getLevelPackManager()->getLevelPack(levelpacks->getName());
//invalidate the tooltip
toolTip.number = -1;
}
void LevelEditSelect::packProperties(ImageManager& imageManager,SDL_Renderer& renderer, bool newPack){
//Open a message popup.
GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-390)/2,600,390,_("Properties"));
GUIObject* obj;
obj=new GUILabel(imageManager,renderer,40,50,240,36,_("Name:"));
root->addChild(obj);
obj=new GUITextBox(imageManager,renderer,60,80,480,36,packName.c_str());
if(newPack)
obj->caption="";
obj->name="LvlpackName";
root->addChild(obj);
obj=new GUILabel(imageManager,renderer,40,120,240,36,_("Description:"));
root->addChild(obj);
obj=new GUITextBox(imageManager,renderer,60,150,480,36,levels->levelpackDescription.c_str());
if(newPack)
obj->caption="";
obj->name="LvlpackDescription";
root->addChild(obj);
obj=new GUILabel(imageManager,renderer,40,190,240,36,_("Congratulation text:"));
root->addChild(obj);
obj=new GUITextBox(imageManager,renderer,60,220,480,36,levels->congratulationText.c_str());
if(newPack)
obj->caption="";
obj->name="LvlpackCongratulation";
root->addChild(obj);
obj = new GUILabel(imageManager, renderer, 40, 260, 240, 36, _("Music list:"));
root->addChild(obj);
obj = new GUITextBox(imageManager, renderer, 60, 290, 480, 36, levels->levelpackMusicList.c_str());
if (newPack)
obj->caption = "";
obj->name = "LvlpackMusic";
root->addChild(obj);
obj=new GUIButton(imageManager,renderer,root->width*0.3,390-44,-1,36,_("OK"),0,true,true,GUIGravityCenter);
obj->name="cfgOK";
obj->eventCallback=this;
root->addChild(obj);
obj=new GUIButton(imageManager,renderer,root->width*0.7,390-44,-1,36,_("Cancel"),0,true,true,GUIGravityCenter);
obj->name="cfgCancel";
obj->eventCallback=this;
root->addChild(obj);
//Create the gui overlay.
//NOTE: We don't need to store a pointer since it will auto cleanup itself.
new GUIOverlay(renderer,root);
if(newPack){
packName.clear();
}
}
void LevelEditSelect::addLevel(ImageManager& imageManager,SDL_Renderer& renderer){
//Open a message popup.
GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-200)/2,600,200,_("Add level"));
GUIObject* obj;
obj=new GUILabel(imageManager,renderer,40,80,240,36,_("File name:"));
root->addChild(obj);
char s[64];
SDL_snprintf(s,64,"map%02d.map",levels->getLevelCount()+1);
obj=new GUITextBox(imageManager,renderer,300,80,240,36,s);
obj->name="LvlFile";
root->addChild(obj);
obj=new GUIButton(imageManager,renderer,root->width*0.3,200-44,-1,36,_("OK"),0,true,true,GUIGravityCenter);
obj->name="cfgAddOK";
obj->eventCallback=this;
root->addChild(obj);
obj=new GUIButton(imageManager,renderer,root->width*0.7,200-44,-1,36,_("Cancel"),0,true,true,GUIGravityCenter);
obj->name="cfgAddCancel";
obj->eventCallback=this;
root->addChild(obj);
//Dim the screen using the tempSurface.
//NOTE: We don't need to store a pointer since it will auto cleanup itself.
new GUIOverlay(renderer,root);
}
void LevelEditSelect::moveLevel(ImageManager& imageManager,SDL_Renderer& renderer){
//Open a message popup.
GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-200)/2,600,200,_("Move level"));
GUIObject* obj;
obj=new GUILabel(imageManager,renderer,40,60,240,36,_("Level: "));
root->addChild(obj);
obj=new GUITextBox(imageManager,renderer,300,60,240,36,"1");
obj->name="MoveLevel";
root->addChild(obj);
obj=new GUISingleLineListBox(imageManager,renderer,root->width*0.5,110,240,36,true,true,GUIGravityCenter);
obj->name="lstPlacement";
vector<string> v;
v.push_back(_("Before"));
v.push_back(_("After"));
v.push_back(_("Swap"));
(dynamic_cast<GUISingleLineListBox*>(obj))->addItems(v);
obj->value=0;
root->addChild(obj);
obj=new GUIButton(imageManager,renderer,root->width*0.3,200-44,-1,36,_("OK"),0,true,true,GUIGravityCenter);
obj->name="cfgMoveOK";
obj->eventCallback=this;
root->addChild(obj);
obj=new GUIButton(imageManager,renderer,root->width*0.7,200-44,-1,36,_("Cancel"),0,true,true,GUIGravityCenter);
obj->name="cfgMoveCancel";
obj->eventCallback=this;
root->addChild(obj);
//Create the gui overlay.
//NOTE: We don't need to store a pointer since it will auto cleanup itself.
new GUIOverlay(renderer,root);
}
void LevelEditSelect::refresh(ImageManager& imageManager, SDL_Renderer& renderer, bool change){
int m=levels->getLevelCount();
if(change){
numbers.clear();
//clear the selected level
if(selectedNumber!=NULL){
selectedNumber=NULL;
}
//Disable the level specific buttons.
move->enabled=false;
remove->enabled=false;
edit->enabled=false;
for(int n=0;n<=m;n++){
numbers.emplace_back(imageManager, renderer);
}
}
for(int n=0;n<m;n++){
SDL_Rect box={(n%LEVELS_PER_ROW)*64+80,(n/LEVELS_PER_ROW)*64+184,0,0};
numbers[n].init(renderer,n,box);
}
SDL_Rect box={(m%LEVELS_PER_ROW)*64+80,(m/LEVELS_PER_ROW)*64+184,0,0};
- numbers[m].init(renderer,"+",box);
-
+ numbers[m].init(renderer,"+",box,m);
+
m++; //including the "+" button
if(m>LEVELS_DISPLAYED_IN_SCREEN){
levelScrollBar->maxValue=(m-LEVELS_DISPLAYED_IN_SCREEN+LEVELS_PER_ROW-1)/LEVELS_PER_ROW;
levelScrollBar->visible=true;
}else{
levelScrollBar->maxValue=0;
levelScrollBar->visible=false;
}
if(!levels->levelpackDescription.empty())
levelpackDescription->caption=_CC(levels->getDictionaryManager(),levels->levelpackDescription);
else
levelpackDescription->caption="";
}
void LevelEditSelect::selectNumber(ImageManager& imageManager, SDL_Renderer& renderer, unsigned int number, bool selected){
- if(selected){
- levels->setCurrentLevel(number);
- setNextState(STATE_LEVEL_EDITOR);
+ if (selected) {
+ if (number >= 0 && number < levels->getLevelCount()) {
+ levels->setCurrentLevel(number);
+ setNextState(STATE_LEVEL_EDITOR);
+ } else {
+ addLevel(imageManager, renderer);
+ }
}else{
- if(number==numbers.size()-1){
- addLevel(imageManager,renderer);
- }else if(number<numbers.size()){
+ move->enabled = false;
+ remove->enabled = false;
+ edit->enabled = false;
+ selectedNumber = NULL;
+ if (number == numbers.size() - 1){
+ if (isKeyboardOnly) {
+ selectedNumber = &numbers[number];
+ } else {
+ addLevel(imageManager, renderer);
+ }
+ } else if (number >= 0 && number < levels->getLevelCount()) {
selectedNumber=&numbers[number];
//Enable the level specific buttons.
//NOTE: We check if 'remove levelpack' is enabled, if not then it's the Levels levelpack.
if(removePack->enabled)
move->enabled=true;
remove->enabled=true;
edit->enabled=true;
}
}
}
void LevelEditSelect::render(ImageManager& imageManager,SDL_Renderer &renderer){
//Let the levelselect render.
LevelSelect::render(imageManager,renderer);
}
void LevelEditSelect::resize(ImageManager& imageManager, SDL_Renderer &renderer){
//Let the levelselect resize.
LevelSelect::resize(imageManager, renderer);
//Create the GUI.
createGUI(imageManager,renderer, false);
//NOTE: This is a workaround for buttons failing when resizing.
if(packName=="Custom Levels"){
removePack->enabled=false;
propertiesPack->enabled=false;
}
if(selectedNumber)
selectNumber(imageManager, renderer, selectedNumber->getNumber(),false);
}
void LevelEditSelect::renderTooltip(SDL_Renderer& renderer,unsigned int number,int dy){
if (!toolTip.name || toolTip.number != number) {
SDL_Color fg = objThemes.getTextColor(true);
toolTip.number = number;
- if(number==static_cast<size_t>(levels->getLevelCount())){
- //Add level button
- toolTip.name=textureFromText(renderer,*fontText,_("Add level"),fg);
- }else{
- //Render the name of the level.
- toolTip.name=textureFromText(renderer,*fontText,_CC(levels->getDictionaryManager(),levels->getLevelName(number)),fg);
- }
+ if (number < (unsigned int)levels->getLevelCount()){
+ //Render the name of the level.
+ toolTip.name = textureFromText(renderer, *fontText, _CC(levels->getDictionaryManager(), levels->getLevelName(number)), fg);
+ } else {
+ //Add level button
+ toolTip.name = textureFromText(renderer, *fontText, _("Add level"), fg);
+ }
}
//Check if name isn't null.
if(!toolTip.name)
return;
//Now draw a square the size of the three texts combined.
SDL_Rect r=numbers[number].box;
r.y-=dy*64;
const SDL_Rect nameSize = rectFromTexture(*toolTip.name);
r.w=nameSize.w;
r.h=nameSize.h;
//Make sure the tooltip doesn't go outside the window.
if(r.y>SCREEN_HEIGHT-200){
r.y-=nameSize.h+4;
}else{
r.y+=numbers[number].box.h+2;
}
if(r.x+r.w>SCREEN_WIDTH-50)
r.x=SCREEN_WIDTH-50-r.w;
//Draw a rectange
Uint32 color=0xFFFFFFFF;
drawGUIBox(r.x-5,r.y-5,r.w+10,r.h+10,renderer,color);
//Calc the position to draw.
SDL_Rect r2=r;
//Now we render the name if the surface isn't null.
if(toolTip.name){
//Draw the name.
applyTexture(r2.x, r2.y, toolTip.name, renderer);
}
}
void LevelEditSelect::GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType){
//NOTE: We check for the levelpack change to enable/disable some levelpack buttons.
if(name=="cmdLvlPack"){
//We call changepack and return to prevent the LevelSelect to undo what we did.
changePack();
refresh(imageManager, renderer);
return;
}
//Let the level select handle his GUI events.
LevelSelect::GUIEventCallback_OnEvent(imageManager,renderer,name,obj,eventType);
//Check for the edit button.
if(name=="cmdNewLvlpack"){
//Create a new pack.
packProperties(imageManager,renderer, true);
}else if(name=="cmdLvlpackProp"){
//Show the pack properties.
packProperties(imageManager,renderer, false);
}else if(name=="cmdRmLvlpack"){
//Show an "are you sure" message.
if(msgBox(imageManager,renderer,tfm::format(_("Are you sure remove the level pack '%s'?"),packName),MsgBoxYesNo,_("Remove prompt"))==MsgBoxYes){
//Remove the directory.
if(!removeDirectory(levels->levelpackPath.c_str())){
cerr<<"ERROR: Unable to remove levelpack directory "<<levels->levelpackPath<<endl;
}
//Remove it from the vector (levelpack list).
vector<pair<string,string> >::iterator it;
for(it=levelpacks->item.begin();it!=levelpacks->item.end();++it){
if(it->second==packName)
levelpacks->item.erase(it);
}
//Remove it from the levelpackManager.
getLevelPackManager()->removeLevelPack(levels->levelpackPath);
//And call changePack.
levelpacks->value=levelpacks->item.size()-1;
changePack();
refresh(imageManager, renderer);
}
}else if(name=="cmdMoveMap"){
if(selectedNumber!=NULL){
moveLevel(imageManager,renderer);
}
}else if(name=="cmdRmMap"){
if(selectedNumber!=NULL){
//Show an "are you sure" message.
if (msgBox(imageManager, renderer, tfm::format(_("Are you sure remove the map '%s'?"), levels->getLevel(selectedNumber->getNumber())->name), MsgBoxYesNo, _("Remove prompt")) != MsgBoxYes) {
return;
}
if(packName!="Custom Levels"){
if(!removeFile((levels->levelpackPath+"/"+levels->getLevel(selectedNumber->getNumber())->file).c_str())){
cerr<<"ERROR: Unable to remove level "<<(levels->levelpackPath+"/"+levels->getLevel(selectedNumber->getNumber())->file).c_str()<<endl;
}
levels->removeLevel(selectedNumber->getNumber());
levels->saveLevels(levels->levelpackPath+"/levels.lst");
}else{
//This is the levels levelpack so we just remove the file.
if(!removeFile(levels->getLevel(selectedNumber->getNumber())->file.c_str())){
cerr<<"ERROR: Unable to remove level "<<levels->getLevel(selectedNumber->getNumber())->file<<endl;
}
levels->removeLevel(selectedNumber->getNumber());
}
//And refresh the selection screen.
refresh(imageManager, renderer);
}
}else if(name=="cmdEdit"){
if(selectedNumber!=NULL){
levels->setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_LEVEL_EDITOR);
}
}
//Check for levelpack properties events.
if(name=="cfgOK"){
//Now loop throught the children of the GUIObjectRoot in search of the fields.
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="LvlpackName"){
//Check if the name changed.
if(packName!=GUIObjectRoot->childControls[i]->caption){
//Delete the old one.
if(!packName.empty()){
if(!renameDirectory((getUserPath(USER_DATA)+"custom/levelpacks/"+packName).c_str(),(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption).c_str())){
cerr<<"ERROR: Unable to move levelpack directory "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+packName)<<" to "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption)<<endl;
}
//Remove the old one from the levelpack manager.
getLevelPackManager()->removeLevelPack(levelpacks->getName());
//And the levelpack list.
vector<pair<string,string> >::iterator it1;
for(it1=levelpacks->item.begin();it1!=levelpacks->item.end();++it1){
if(it1!=levelpacks->item.end()){
levelpacks->item.erase(it1);
break;
}
}
}else{
//It's a new levelpack so we need to change the levels array.
LevelPack* pack=new LevelPack;
levels=pack;
//Now create the dirs.
if(!createDirectory((getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption).c_str())){
cerr<<"ERROR: Unable to create levelpack directory "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption)<<endl;
}
if(!createFile((getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption+"/levels.lst").c_str())){
cerr<<"ERROR: Unable to create levelpack file "<<(getUserPath(USER_DATA)+"custom/levelpacks/"+GUIObjectRoot->childControls[i]->caption+"/levels.lst")<<endl;
}
}
//And set the new name.
packName=GUIObjectRoot->childControls[i]->caption;
levels->levelpackName=packName;
levels->levelpackPath=(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/");
//Also add the levelpack location
getLevelPackManager()->addLevelPack(levels);
levelpacks->addItem(levels->levelpackPath,GUIObjectRoot->childControls[i]->caption);
levelpacks->value=levelpacks->item.size()-1;
//And call changePack.
changePack();
}
}
if(GUIObjectRoot->childControls[i]->name=="LvlpackDescription"){
levels->levelpackDescription=GUIObjectRoot->childControls[i]->caption;
}
if(GUIObjectRoot->childControls[i]->name=="LvlpackCongratulation"){
levels->congratulationText=GUIObjectRoot->childControls[i]->caption;
}
if (GUIObjectRoot->childControls[i]->name == "LvlpackMusic"){
levels->levelpackMusicList = GUIObjectRoot->childControls[i]->caption;
}
}
//Refresh the leveleditselect to show the correct information.
refresh(imageManager, renderer);
//Save the configuration.
levels->saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
getSettings()->setValue("lastlevelpack",levels->levelpackPath);
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="cfgCancel"){
//Check if packName is empty, if so it was a new levelpack and we need to revert to an existing one.
if(packName.empty()){
packName=levelpacks->item[levelpacks->value].second;
changePack();
}
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Check for add level events.
if(name=="cfgAddOK"){
//Check if the file name isn't null.
//Now loop throught the children of the GUIObjectRoot in search of the fields.
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="LvlFile"){
if(GUIObjectRoot->childControls[i]->caption.empty()){
msgBox(imageManager,renderer,_("No file name given for the new level."),MsgBoxOKOnly,_("Missing file name"));
return;
}else{
string tmp_caption = GUIObjectRoot->childControls[i]->caption;
//Replace all spaces with a underline.
size_t j;
for(;(j=tmp_caption.find(" "))!=string::npos;){
tmp_caption.replace(j,1,"_");
}
//If there isn't ".map" extension add it.
size_t found=tmp_caption.find_first_of(".");
if(found!=string::npos)
tmp_caption.replace(tmp_caption.begin()+found+1,tmp_caption.end(),"map");
else if (tmp_caption.substr(found+1)!="map")
tmp_caption.append(".map");
/* Create path and file in it */
string path=(levels->levelpackPath+"/"+tmp_caption);
if(packName=="Custom Levels"){
path=(getUserPath(USER_DATA)+"/custom/levels/"+tmp_caption);
}
//First check if the file doesn't exist already.
FILE* f;
f=fopen(path.c_str(),"rb");
//Check if it exists.
if(f){
//Close the file.
fclose(f);
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render(imageManager,renderer);
levelEditGUIObjectRoot->render(renderer);
//Notify the user.
msgBox(imageManager,renderer,string("The file "+tmp_caption+" already exists."),MsgBoxOKOnly,"Error");
return;
}
if(!createFile(path.c_str())){
cerr<<"ERROR: Unable to create level file "<<path<<endl;
}else{
//Update statistics.
statsMgr.newAchievement("create1");
if((++statsMgr.createdLevels)>=50) statsMgr.newAchievement("create50");
}
levels->addLevel(path);
//NOTE: Also add the level to the levels levelpack in case of custom levels.
if(packName=="Custom Levels"){
LevelPack* levelsPack=getLevelPackManager()->getLevelPack("Levels/");
if(levelsPack){
levelsPack->addLevel(path);
levelsPack->setLocked(levelsPack->getLevelCount()-1);
}else{
cerr<<"ERROR: Unable to add level to Levels levelpack"<<endl;
}
}
if(packName!="Custom Levels")
levels->saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
refresh(imageManager, renderer);
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
return;
}
}
}
}
}else if(name=="cfgAddCancel"){
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Check for move level events.
if(name=="cfgMoveOK"){
//Check if the entered level number is valid.
//Now loop throught the children of the GUIObjectRoot in search of the fields.
int level=0;
int placement=0;
for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
if(GUIObjectRoot->childControls[i]->name=="MoveLevel"){
level=atoi(GUIObjectRoot->childControls[i]->caption.c_str());
if(level<=0 || level>levels->getLevelCount()){
msgBox(imageManager,renderer,_("The entered level number isn't valid!"),MsgBoxOKOnly,_("Illegal number"));
return;
}
}
if(GUIObjectRoot->childControls[i]->name=="lstPlacement"){
placement=GUIObjectRoot->childControls[i]->value;
}
}
//Now we execute the swap/move.
//Check for the place before.
if(placement==0){
//We place the selected level before the entered level.
levels->moveLevel(selectedNumber->getNumber(),level-1);
}else if(placement==1){
//We place the selected level after the entered level.
if(level<selectedNumber->getNumber())
levels->moveLevel(selectedNumber->getNumber(),level);
else
levels->moveLevel(selectedNumber->getNumber(),level+1);
}else if(placement==2){
//We swap the selected level with the entered level.
levels->swapLevel(selectedNumber->getNumber(),level-1);
}
//And save the change.
if(packName!="Custom Levels")
levels->saveLevels(getUserPath(USER_DATA)+"custom/levelpacks/"+packName+"/levels.lst");
refresh(imageManager, renderer);
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="cfgMoveCancel"){
//Clear the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
}
diff --git a/src/LevelPlaySelect.cpp b/src/LevelPlaySelect.cpp
index 9808506..67df690 100644
--- a/src/LevelPlaySelect.cpp
+++ b/src/LevelPlaySelect.cpp
@@ -1,410 +1,425 @@
/*
* Copyright (C) 2011-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow 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.
*
* Me and My Shadow 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 Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LevelPlaySelect.h"
#include "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "LevelSelect.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include "GUIScrollBar.h"
#include "InputManager.h"
#include "ThemeManager.h"
#include "Game.h"
#include <stdio.h>
#include <string>
#include <sstream>
#include <iostream>
/////////////////////LEVEL SELECT/////////////////////
LevelPlaySelect::LevelPlaySelect(ImageManager& imageManager, SDL_Renderer& renderer)
:LevelSelect(imageManager,renderer,_("Select Level")),
levelInfoRender(imageManager,renderer,getDataPath(),*fontText,objThemes.getTextColor(false)){
//Load the play button if needed.
playButtonImage=imageManager.loadTexture(getDataPath()+"gfx/playbutton.png", renderer);
//Create the gui.
createGUI(imageManager,renderer, true);
//Show level list
refresh(imageManager,renderer);
}
LevelPlaySelect::~LevelPlaySelect(){
play=NULL;
//Clear the selected level.
if(selectedNumber!=NULL){
delete selectedNumber;
selectedNumber=NULL;
}
}
void LevelPlaySelect::createGUI(ImageManager& imageManager,SDL_Renderer &renderer, bool initial){
//Create the play button.
if(initial){
play=new GUIButton(imageManager,renderer,SCREEN_WIDTH-240,SCREEN_HEIGHT-60,240,32,_("Play"));
}else{
play->left=SCREEN_WIDTH-240;
play->top=SCREEN_HEIGHT-60;
}
play->name="cmdPlay";
play->eventCallback=this;
play->enabled=false;
if(initial)
GUIObjectRoot->addChild(play);
}
void LevelPlaySelect::refresh(ImageManager& imageManager, SDL_Renderer& renderer, bool /*change*/){
- int m=levels->getLevelCount();
+ const int m=levels->getLevelCount();
numbers.clear();
levelInfoRender.resetText(renderer, *fontText, objThemes.getTextColor(false));
- //Clear the selected level.
- if(selectedNumber!=NULL){
- delete selectedNumber;
- selectedNumber=NULL;
+ //Create the non selected number.
+ if (selectedNumber == NULL){
+ selectedNumber = new Number(imageManager, renderer);
}
- //Recreate the non selected number.
- selectedNumber=new Number(imageManager, renderer);
SDL_Rect box={40,SCREEN_HEIGHT-130,50,50};
selectedNumber->init(renderer," ",box);
selectedNumber->setLocked(true);
+ selectedNumber->setMedal(0);
bestTimeFilePath.clear();
bestRecordingFilePath.clear();
//Disable the play button.
play->enabled=false;
for(int n=0; n<m; n++){
numbers.emplace_back(imageManager, renderer);
}
for(int n=0; n<m; n++){
SDL_Rect box={(n%LEVELS_PER_ROW)*64+static_cast<int>(SCREEN_WIDTH*0.2)/2,(n/LEVELS_PER_ROW)*64+184,0,0};
numbers[n].init(renderer,n,box);
numbers[n].setLocked(levels->getLocked(n));
int medal=levels->getLevel(n)->won;
if(medal){
if(levels->getLevel(n)->targetTime<0 || levels->getLevel(n)->time<=levels->getLevel(n)->targetTime)
medal++;
if(levels->getLevel(n)->targetRecordings<0 || levels->getLevel(n)->recordings<=levels->getLevel(n)->targetRecordings)
medal++;
}
numbers[n].setMedal(medal);
}
if(m>LEVELS_DISPLAYED_IN_SCREEN){
levelScrollBar->maxValue=(m-LEVELS_DISPLAYED_IN_SCREEN+(LEVELS_PER_ROW-1))/LEVELS_PER_ROW;
levelScrollBar->visible=true;
}else{
levelScrollBar->maxValue=0;
levelScrollBar->visible=false;
}
if(!levels->levelpackDescription.empty())
levelpackDescription->caption=_CC(levels->getDictionaryManager(),levels->levelpackDescription);
else
levelpackDescription->caption="";
}
void LevelPlaySelect::selectNumber(ImageManager& imageManager, SDL_Renderer& renderer, unsigned int number,bool selected){
- if(selected){
- levels->setCurrentLevel(number);
- setNextState(STATE_GAME);
+ if (selected) {
+ if (number >= 0 && number < levels->getLevelCount()) {
+ levels->setCurrentLevel(number);
+ setNextState(STATE_GAME);
+ }
}else{
displayLevelInfo(imageManager, renderer,number);
}
}
void LevelPlaySelect::checkMouse(ImageManager &imageManager, SDL_Renderer &renderer){
int x,y;
//Get the current mouse location.
SDL_GetMouseState(&x,&y);
//Check if we should replay the record.
if(selectedNumber!=NULL){
SDL_Rect mouse={x,y,0,0};
if(!bestTimeFilePath.empty()){
SDL_Rect box={SCREEN_WIDTH-420,SCREEN_HEIGHT-130,372,32};
if(checkCollision(box,mouse)){
Game::recordFile=bestTimeFilePath;
levels->setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_GAME);
return;
}
}
if(!bestRecordingFilePath.empty()){
SDL_Rect box={SCREEN_WIDTH-420,SCREEN_HEIGHT-98,372,32};
if(checkCollision(box,mouse)){
Game::recordFile=bestRecordingFilePath;
levels->setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_GAME);
return;
}
}
}
//Call the base method from the super class.
LevelSelect::checkMouse(imageManager, renderer);
}
void LevelPlaySelect::displayLevelInfo(ImageManager& imageManager, SDL_Renderer& renderer, int number){
//Update currently selected level
if(selectedNumber==NULL){
selectedNumber=new Number(imageManager, renderer);
}
SDL_Rect box={40,SCREEN_HEIGHT-130,50,50};
- selectedNumber->init(renderer,number,box);
- selectedNumber->setLocked(false);
-
- //Show level medal
- int medal=levels->getLevel(number)->won;
- int time=levels->getLevel(number)->time;
- int targetTime=levels->getLevel(number)->targetTime;
- int recordings=levels->getLevel(number)->recordings;
- int targetRecordings=levels->getLevel(number)->targetRecordings;
-
- if(medal){
- if(targetTime<0){
- medal=-1;
- }else{
- if(targetTime<0 || time<=targetTime)
- medal++;
- if(targetRecordings<0 || recordings<=targetRecordings)
- medal++;
+
+ if (number >= 0 && number < levels->getLevelCount()) {
+ selectedNumber->init(renderer, number, box);
+ selectedNumber->setLocked(false);
+
+ //Show level medal
+ int medal = levels->getLevel(number)->won;
+ int time = levels->getLevel(number)->time;
+ int targetTime = levels->getLevel(number)->targetTime;
+ int recordings = levels->getLevel(number)->recordings;
+ int targetRecordings = levels->getLevel(number)->targetRecordings;
+
+ if (medal){
+ if (targetTime < 0){
+ medal = -1;
+ } else{
+ if (targetTime < 0 || time <= targetTime)
+ medal++;
+ if (targetRecordings < 0 || recordings <= targetRecordings)
+ medal++;
+ }
}
- }
- selectedNumber->setMedal(medal);
- std::string levelTime;
- std::string levelRecs;
-
- //Show best time and recordings
- if(medal){
- char s[64];
-
- if(time>0)
- if(targetTime>0)
- sprintf(s,"%-.2fs / %-.2fs",time/40.0f,targetTime/40.0f);
+ selectedNumber->setMedal(medal);
+ std::string levelTime;
+ std::string levelRecs;
+
+ //Show best time and recordings
+ if (medal){
+ char s[64];
+
+ if (time > 0)
+ if (targetTime>0)
+ sprintf(s, "%-.2fs / %-.2fs", time / 40.0f, targetTime / 40.0f);
+ else
+ sprintf(s, "%-.2fs / -", time / 40.0f);
else
- sprintf(s,"%-.2fs / -",time/40.0f);
- else
- s[0]='\0';
- levelTime=s;
-
- if(recordings>=0)
- if(targetRecordings>=0)
- sprintf(s,"%5d / %d",recordings,targetRecordings);
+ s[0] = '\0';
+ levelTime = s;
+
+ if (recordings >= 0)
+ if (targetRecordings >= 0)
+ sprintf(s, "%5d / %d", recordings, targetRecordings);
+ else
+ sprintf(s, "%5d / -", recordings);
else
- sprintf(s,"%5d / -",recordings);
- else
- s[0]='\0';
- levelRecs=s;
- }else{
- levelTime="- / -";
- levelRecs="- / -";
- }
-
- //Show the play button.
- play->enabled=true;
-
- //Check if there is auto record file
- levels->getLevelAutoSaveRecordPath(number,bestTimeFilePath,bestRecordingFilePath,false);
- if(!bestTimeFilePath.empty()){
- FILE *f;
- f=fopen(bestTimeFilePath.c_str(),"rb");
- if(f==NULL){
- bestTimeFilePath.clear();
- }else{
- fclose(f);
+ s[0] = '\0';
+ levelRecs = s;
+ } else{
+ levelTime = "- / -";
+ levelRecs = "- / -";
}
- }
- if(!bestRecordingFilePath.empty()){
- FILE *f;
- f=fopen(bestRecordingFilePath.c_str(),"rb");
- if(f==NULL){
- bestRecordingFilePath.clear();
- }else{
- fclose(f);
+
+ //Show the play button.
+ play->enabled = true;
+
+ //Check if there is auto record file
+ levels->getLevelAutoSaveRecordPath(number, bestTimeFilePath, bestRecordingFilePath, false);
+ if (!bestTimeFilePath.empty()){
+ FILE *f;
+ f = fopen(bestTimeFilePath.c_str(), "rb");
+ if (f == NULL){
+ bestTimeFilePath.clear();
+ } else{
+ fclose(f);
+ }
+ }
+ if (!bestRecordingFilePath.empty()){
+ FILE *f;
+ f = fopen(bestRecordingFilePath.c_str(), "rb");
+ if (f == NULL){
+ bestRecordingFilePath.clear();
+ } else{
+ fclose(f);
+ }
}
- }
- //Show level description
- const std::string& levelDescription=levels->getLevelName(number);
+ //Show level description
+ const std::string& levelDescription = levels->getLevelName(number);
- levelInfoRender.update(renderer, *fontText, objThemes.getTextColor(false),
- levelDescription, levelTime, levelRecs);
+ levelInfoRender.update(renderer, *fontText, objThemes.getTextColor(false),
+ levelDescription, levelTime, levelRecs);
+ } else {
+ levelInfoRender.resetText(renderer, *fontText, objThemes.getTextColor(false));
+
+ selectedNumber->init(renderer, " ", box);
+ selectedNumber->setLocked(true);
+ selectedNumber->setMedal(0);
+
+ bestTimeFilePath.clear();
+ bestRecordingFilePath.clear();
+
+ //Disable the play button.
+ play->enabled = false;
+ }
}
void LevelPlaySelect::render(ImageManager& imageManager, SDL_Renderer &renderer){
//First let the levelselect render.
LevelSelect::render(imageManager,renderer);
int x,y,dy=0;
//Get the current mouse location.
SDL_GetMouseState(&x,&y);
if(levelScrollBar)
dy=levelScrollBar->value;
//Upper bound of levels we'd like to display.
y+=dy*64;
SDL_Rect mouse={x,y,0,0};
//Show currently selected level (if any)
if(selectedNumber!=NULL){
selectedNumber->show(renderer, 0);
levelInfoRender.render(renderer);
//Only show the replay button if the level is completed (won).
if(selectedNumber->getNumber()>=0 && selectedNumber->getNumber()<levels->getLevelCount()) {
if(levels->getLevel(selectedNumber->getNumber())->won){
if(!bestTimeFilePath.empty()){
SDL_Rect r={0,0,32,32};
const SDL_Rect box={SCREEN_WIDTH-420,SCREEN_HEIGHT-130,372,32};
if(checkCollision(box,mouse)){
r.x=32;
SDL_SetRenderDrawColor(&renderer, 0xFF,0xCC,0xCC,0xCC);
SDL_RenderDrawRect(&renderer, &box);
}
const SDL_Rect dstRect = {SCREEN_WIDTH-80,SCREEN_HEIGHT-130,r.w,r.h};
SDL_RenderCopy(&renderer,playButtonImage.get(),&r, &dstRect);
}
if(!bestRecordingFilePath.empty()){
SDL_Rect r={0,0,32,32};
const SDL_Rect box={SCREEN_WIDTH-420,SCREEN_HEIGHT-98,372,32};
if(checkCollision(box,mouse)){
r.x=32;
SDL_SetRenderDrawColor(&renderer, 0xFF,0xCC,0xCC,0xCC);
SDL_RenderDrawRect(&renderer, &box);
}
const SDL_Rect dstRect = {SCREEN_WIDTH-80,SCREEN_HEIGHT-98,r.w,r.h};
SDL_RenderCopy(&renderer,playButtonImage.get(),&r, &dstRect);
}
}
}
}
}
void LevelPlaySelect::renderTooltip(SDL_Renderer &renderer, unsigned int number, int dy){
if (!toolTip.name || toolTip.number != number) {
const int SLEN = 64;
char s[SLEN];
//Render the name of the level.
toolTip.name = textureFromText(renderer, *fontText, _CC(levels->getDictionaryManager(), levels->getLevelName(number)), objThemes.getTextColor(true));
toolTip.time=nullptr;
toolTip.recordings=nullptr;
toolTip.number=number;
//The time it took.
if(levels->getLevel(number)->time>0){
SDL_snprintf(s,SLEN,"%-.2fs",levels->getLevel(number)->time/40.0f);
toolTip.time = textureFromText(renderer, *fontText, s, objThemes.getTextColor(true));
}
//The number of recordings it took.
if(levels->getLevel(number)->recordings>=0){
SDL_snprintf(s,SLEN,"%d",levels->getLevel(number)->recordings);
toolTip.recordings = textureFromText(renderer, *fontText, s, objThemes.getTextColor(true));
}
}
const SDL_Rect nameSize = rectFromTexture(*toolTip.name);
//Now draw a square the size of the three texts combined.
SDL_Rect r=numbers[number].box;
r.y-=dy*64;
if(toolTip.time && toolTip.recordings){
const int recW = textureWidth(*toolTip.recordings);
const int timeW = textureWidth(*toolTip.time);
r.w=(nameSize.w)>(25+timeW+40+recW)?(nameSize.w):(25+timeW+40+recW);
r.h=nameSize.h+5+20;
}else{
r.w=nameSize.w;
r.h=nameSize.h;
}
//Make sure the tooltip doesn't go outside the window.
if(r.y>SCREEN_HEIGHT-200){
r.y-=nameSize.h+4;
}else{
r.y+=numbers[number].box.h+2;
}
if(r.x+r.w>SCREEN_WIDTH-50)
r.x=SCREEN_WIDTH-50-r.w;
//Draw a rectange
Uint32 color=0xFFFFFFFF;
drawGUIBox(r.x-5,r.y-5,r.w+10,r.h+10,renderer,color);
//Calc the position to draw.
SDL_Rect r2=r;
//Now we render the name if the surface isn't null.
if(toolTip.name){
//Draw the name.
applyTexture(r2.x, r2.y, toolTip.name, renderer);
}
//Increase the height to leave a gap between name and stats.
r2.y+=30;
if(toolTip.time){
//Now draw the time.
applyTexture(r2.x,r2.y,levelInfoRender.timeIcon,renderer);
r2.x+=25;
applyTexture(r2.x, r2.y, toolTip.time, renderer);
r2.x+=textureWidth(*toolTip.time)+15;
}
if(toolTip.recordings){
//Now draw the recordings.
applyTexture(r2.x,r2.y,levelInfoRender.recordingsIcon,renderer);
r2.x+=25;
applyTexture(r2.x, r2.y, toolTip.recordings, renderer);
}
}
void LevelPlaySelect::resize(ImageManager &imageManager, SDL_Renderer &renderer){
//Let the LevelSelect do his stuff.
LevelSelect::resize(imageManager, renderer);
//Now create our gui again.
createGUI(imageManager,renderer, false);
}
void LevelPlaySelect::GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType){
//Let the level select handle his GUI events.
LevelSelect::GUIEventCallback_OnEvent(imageManager,renderer,name,obj,eventType);
//Check for the play button.
if(name=="cmdPlay"){
if(selectedNumber!=NULL){
levels->setCurrentLevel(selectedNumber->getNumber());
setNextState(STATE_GAME);
}
}
}
diff --git a/src/LevelSelect.cpp b/src/LevelSelect.cpp
index e128816..22b5c33 100644
--- a/src/LevelSelect.cpp
+++ b/src/LevelSelect.cpp
@@ -1,410 +1,436 @@
/*
* Copyright (C) 2011-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow 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.
*
* Me and My Shadow 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 Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "LevelSelect.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include "GUIScrollBar.h"
#include "InputManager.h"
#include <stdio.h>
#include <string>
#include <sstream>
#include <iostream>
#include "libs/tinyformat/tinyformat.h"
using namespace std;
////////////////////NUMBER////////////////////////
Number::Number(ImageManager& imageManager, SDL_Renderer& renderer){
image=NULL;
number=0;
medal=0;
selected=false;
locked=false;
//Set the default dimensions.
box.x=0;
box.y=0;
box.h=50;
box.w=50;
//Load the medals image.
medals=imageManager.loadTexture(getDataPath()+"gfx/medals.png", renderer);
//To make sure it can be added to a vector, stop here rather than generate a massive error.
static_assert(std::is_move_constructible<Number>::value, "Not move constructable!");
}
void Number::init(SDL_Renderer& renderer,int number,SDL_Rect box){
//First set the number and update our status.
this->number=number;
//Write our text, number+1 since the counting doens't start with 0, but with 1.
std::stringstream text;
number++;
text<<number;
//Create the text image.
//Also check which font to use, if the number is higher than 100 use the small font.
image = textureFromText(renderer,*fontGUI,text.str().c_str(),objThemes.getTextColor(true));
//Set the new location of the number.
this->box.x=box.x;
this->box.y=box.y;
//Load background blocks.
objThemes.getBlock(TYPE_BLOCK,true)->createInstance(&block);
block.changeState("unlocked");
objThemes.getBlock(TYPE_SHADOW_BLOCK,true)->createInstance(&blockLocked);
blockLocked.changeState("locked");
}
-void Number::init(SDL_Renderer& renderer,std::string text,SDL_Rect box){
+void Number::init(SDL_Renderer& renderer,std::string text,SDL_Rect box,int number_){
//First set the number and update our status.
- this->number=-1;
+ this->number=number_;
//Create the text image.
image = textureFromText(renderer,*fontGUI,text.c_str(),objThemes.getTextColor(true));
//Set the new location of the number.
this->box.x=box.x;
this->box.y=box.y;
//Load background blocks.
objThemes.getBlock(TYPE_BLOCK,true)->createInstance(&block);
block.changeState("unlocked");
objThemes.getBlock(TYPE_SHADOW_BLOCK,true)->createInstance(&blockLocked);
blockLocked.changeState("locked");
}
void Number::show(SDL_Renderer& renderer, int dy){
//First draw the background, also apply the yOffset(dy).
if(!locked)
block.draw(renderer,box.x,box.y-dy);
else
blockLocked.draw(renderer,box.x,box.y-dy);
//Now draw the text image over the background.
//We draw it centered inside the box.
applyTexture(box.x+25-(textureWidth(*image)/2),box.y-dy,image,renderer);
//Draw the selection mark.
if(selected){
drawGUIBox(box.x,box.y-dy,50,50,renderer,0xFFFFFF23);
}
//Draw the medal.
if(medal>0&&medals){
const SDL_Rect srcRect={(medal-1)*30,0,30,30};
const SDL_Rect dstRect={box.x+30,(box.y+30)-dy,30,30};
SDL_RenderCopy(&renderer,medals.get(),&srcRect,&dstRect);
}
}
void Number::setLocked(bool locked){
this->locked=locked;
}
void Number::setMedal(int medal){
this->medal=medal;
}
/////////////////////LEVEL SELECT/////////////////////
LevelSelect::LevelSelect(ImageManager& imageManager,SDL_Renderer& renderer, const char* titleText, LevelPackManager::LevelPackLists packType){
//clear the selected level
selectedNumber=NULL;
//Calculate the LEVELS_PER_ROW and LEVEL_ROWS if they aren't calculated already.
calcRows();
//Render the title.
title=textureFromText(renderer,*fontTitle,titleText,objThemes.getTextColor(false));
//create GUI (test only)
GUIObject* obj;
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
GUIObjectRoot=new GUIObject(imageManager,renderer,0,0,SCREEN_WIDTH,SCREEN_HEIGHT);
//the level select scroll bar
levelScrollBar=new GUIScrollBar(imageManager,renderer,SCREEN_WIDTH*0.9,184,16,SCREEN_HEIGHT-344,ScrollBarVertical,0,0,0,1,4,true,false);
GUIObjectRoot->addChild(levelScrollBar);
//level pack description
levelpackDescription=new GUILabel(imageManager,renderer,0,140,SCREEN_WIDTH,32,"",0,true,true,GUIGravityCenter);
GUIObjectRoot->addChild(levelpackDescription);
levelpacks=new GUISingleLineListBox(imageManager,renderer,(SCREEN_WIDTH-500)/2,104,500,32);
levelpacks->name="cmdLvlPack";
levelpacks->eventCallback=this;
vector<pair<string,string> > v=getLevelPackManager()->enumLevelPacks(packType);
levelpacks->addItems(v);
levelpacks->value=0;
//Check if we can find the lastlevelpack.
for(vector<pair<string,string> >::iterator i=v.begin(); i!=v.end(); ++i){
if(i->first==getSettings()->getValue("lastlevelpack")){
levelpacks->value=i-v.begin();
}
}
//Load the progress.
levels=getLevelPackManager()->getLevelPack(v[levelpacks->value].first);
levels->loadProgress();
//And add the levelpack single line listbox to the GUIObjectRoot.
GUIObjectRoot->addChild(levelpacks);
obj=new GUIButton(imageManager,renderer,20,20,-1,32,_("Back"));
obj->name="cmdBack";
obj->eventCallback=this;
GUIObjectRoot->addChild(obj);
section=1;
}
LevelSelect::~LevelSelect(){
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
levelScrollBar=NULL;
levelpackDescription=NULL;
selectedNumber=NULL;
}
void LevelSelect::calcRows(){
//Calculate the number of rows and the number of levels per row.
LEVELS_PER_ROW=(SCREEN_WIDTH*0.8)/64;
int LEVEL_ROWS=(SCREEN_HEIGHT-344)/64;
LEVELS_DISPLAYED_IN_SCREEN=LEVELS_PER_ROW*LEVEL_ROWS;
}
void LevelSelect::selectNumberKeyboard(ImageManager& imageManager, SDL_Renderer& renderer, int x,int y){
+ isKeyboardOnly = true;
+
if(section==2){
//Move selection
- int realNumber=0;
+ int realNumber=-1;
if(selectedNumber)
realNumber=selectedNumber->getNumber()+x+(y*LEVELS_PER_ROW);
+
+ int delta = (x + y < 0) ? -1 : 1;
- //If selection is outside of the map grid, change section
- if(realNumber<0 || realNumber>(int)numbers.size()-1){
- section=1;
- for(int i=0;i<levels->getLevelCount();i++){
- numbers[i].selected=false;
- refresh(imageManager, renderer);
- }
- }else{
- //If not, move selection
- if(!numbers[realNumber].getLocked()){
- for(int i=0;i<levels->getLevelCount();i++){
- numbers[i].selected=(i==realNumber);
+ for (;;) {
+ //If selection is outside of the map grid, change section
+ if (realNumber<0 || realNumber>(int)numbers.size() - 1){
+ section = 1;
+ for (int i = 0; i < (int)numbers.size(); i++){
+ numbers[i].selected = false;
+ }
+ selectNumber(imageManager, renderer, -1, false);
+ break;
+ } else {
+ //If not, move selection
+ if (!numbers[realNumber].getLocked()){
+ for (int i = 0; i < (int)numbers.size(); i++){
+ numbers[i].selected = (i == realNumber);
+ }
+ selectNumber(imageManager, renderer, realNumber, false);
+ break;
}
- selectNumber(imageManager,renderer,realNumber,false);
}
+ realNumber += delta;
}
}else if(section==1){
- //Loop through levelpacks and update GUI
- levelpacks->value+=x;
-
- if(levelpacks->value<0){
- levelpacks->value=levelpacks->item.size()-1;
- }else if(levelpacks->value>(int)levelpacks->item.size()-1){
- levelpacks->value=0;
+ if (x != 0) {
+ //Loop through levelpacks and update GUI
+ levelpacks->value += x;
+
+ if (levelpacks->value<0){
+ levelpacks->value = levelpacks->item.size() - 1;
+ } else if (levelpacks->value>(int)levelpacks->item.size() - 1){
+ levelpacks->value = 0;
+ }
+
+ GUIEventCallback_OnEvent(imageManager, renderer, "cmdLvlPack", static_cast<GUIObject*>(levelpacks), 0);
}
-
- GUIEventCallback_OnEvent(imageManager,renderer,"cmdLvlPack",static_cast<GUIObject*>(levelpacks),0);
-
- //If up is pressed, change section
+
+ //If down is pressed, change section
if(y==1){
section=2;
selectNumber(imageManager,renderer,0,false);
numbers[0].selected=true;
}
}else{
section=clamp(section+y,0,2);
}
}
void LevelSelect::handleEvents(ImageManager& imageManager, SDL_Renderer& renderer){
//Check for an SDL_QUIT event.
if(event.type==SDL_QUIT){
setNextState(STATE_EXIT);
}
//Check for a mouse click.
if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT){
checkMouse(imageManager, renderer);
}
//Check focus movement
if(inputMgr.isKeyDownEvent(INPUTMGR_RIGHT)){
selectNumberKeyboard(imageManager, renderer, 1,0);
}else if(inputMgr.isKeyDownEvent(INPUTMGR_LEFT)){
selectNumberKeyboard(imageManager, renderer, -1,0);
}else if(inputMgr.isKeyDownEvent(INPUTMGR_UP)){
selectNumberKeyboard(imageManager, renderer, 0,-1);
}else if(inputMgr.isKeyDownEvent(INPUTMGR_DOWN)){
selectNumberKeyboard(imageManager, renderer, 0,1);
}
//Check if enter is pressed
if(section==2 && inputMgr.isKeyUpEvent(INPUTMGR_SELECT)){
selectNumber(imageManager,renderer,selectedNumber->getNumber(),true);
}
//Check if escape is pressed.
if(inputMgr.isKeyUpEvent(INPUTMGR_ESCAPE)){
setNextState(STATE_MENU);
}
//Check for scrolling down and up.
// if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELDOWN && levelScrollBar){
if(event.type==SDL_MOUSEWHEEL && levelScrollBar){
if(levelScrollBar->value<levelScrollBar->maxValue) {
//TODO - tweak the scroll amount
levelScrollBar->value += event.wheel.y;
if(levelScrollBar->value < 0) {
levelScrollBar->value = 0;
}
}
}
// return;
// }else if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELUP && levelScrollBar){
// if(levelScrollBar->value>0) levelScrollBar->value--;
// return;
// }
}
void LevelSelect::checkMouse(ImageManager &imageManager, SDL_Renderer &renderer){
int x,y,dy=0,m=numbers.size();
//Get the current mouse location.
SDL_GetMouseState(&x,&y);
//Check if there's a scrollbar, if so get the value.
if(levelScrollBar)
dy=levelScrollBar->value;
//Upper bound of levels we'd like to display.
if(m>dy*LEVELS_PER_ROW+LEVELS_DISPLAYED_IN_SCREEN)
m=dy*LEVELS_PER_ROW+LEVELS_DISPLAYED_IN_SCREEN;
y+=dy*64;
SDL_Rect mouse={x,y,0,0};
for(int n=dy*LEVELS_PER_ROW; n<m; n++){
if(!numbers[n].getLocked()){
if(checkCollision(mouse,numbers[n].box)==true){
if(numbers[n].selected){
selectNumber(imageManager, renderer, n,true);
}else{
//Select current level
for(int i=0;i<levels->getLevelCount();i++){
numbers[i].selected=(i==n);
}
selectNumber(imageManager, renderer,n,false);
}
section=2;
break;
}
}
}
}
void LevelSelect::logic(ImageManager&, SDL_Renderer&){}
void LevelSelect::render(ImageManager&, SDL_Renderer& renderer){
int x,y,dy=0,m=numbers.size();
int idx=-1;
//Get the current mouse location.
SDL_GetMouseState(&x,&y);
if(levelScrollBar)
dy=levelScrollBar->value;
//Upper bound of levels we'd like to display.
if(m>dy*LEVELS_PER_ROW+LEVELS_DISPLAYED_IN_SCREEN)
m=dy*LEVELS_PER_ROW+LEVELS_DISPLAYED_IN_SCREEN;
y+=dy*64;
SDL_Rect mouse={x,y,0,0};
//Draw background.
objThemes.getBackground(true)->draw(renderer);
objThemes.getBackground(true)->updateAnimation();
//Draw the title.
drawTitleTexture(SCREEN_WIDTH, *title, renderer);
+ //Draw highlight and do some calculations in keyboard-only mode.
+ int realNumber = -1;
+ if (isKeyboardOnly) {
+ if (section == 1) {
+ drawGUIBox((SCREEN_WIDTH - 508) / 2, 100, 508, 36, renderer, 0xFFFFFF40);
+ }
+ if (selectedNumber)
+ realNumber = selectedNumber->getNumber();
+ }
+
//Loop through the level blocks and draw them.
for(int n=dy*LEVELS_PER_ROW;n<m;n++){
numbers[n].show(renderer,dy*64);
- if(numbers[n].getLocked()==false && checkCollision(mouse,numbers[n].box)==true)
- idx=n;
+ if (!numbers[n].getLocked()) {
+ if (isKeyboardOnly) {
+ if (realNumber == n) idx = n;
+ } else {
+ if (checkCollision(mouse, numbers[n].box)) idx = n;
+ }
+ }
}
//Show the tool tip text.
if(idx>=0){
renderTooltip(renderer,idx,dy);
}
}
void LevelSelect::resize(ImageManager& imageManager,SDL_Renderer& renderer){
calcRows();
refresh(imageManager,renderer,false);
//NOTE: We don't need to recreate the listbox and the back button, only resize the list.
levelpacks->left=(SCREEN_WIDTH-500)/2;
levelpackDescription->width = SCREEN_WIDTH;
}
void LevelSelect::GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType){
if(name=="cmdLvlPack"){
getSettings()->setValue("lastlevelpack",static_cast<GUISingleLineListBox*>(obj)->item[obj->value].first);
}else if(name=="cmdBack"){
setNextState(STATE_MENU);
return;
}else{
return;
}
//new: reset the level list scroll bar
if(levelScrollBar)
levelScrollBar->value=0;
levels=getLevelPackManager()->getLevelPack(static_cast<GUISingleLineListBox*>(obj)->item[obj->value].first);
//Load the progress file.
levels->loadProgress();
//And refresh the numbers.
refresh(imageManager, renderer);
//invalidate the tooltip
toolTip.number = -1;
}
diff --git a/src/LevelSelect.h b/src/LevelSelect.h
index dd230b2..1545cc6 100644
--- a/src/LevelSelect.h
+++ b/src/LevelSelect.h
@@ -1,172 +1,172 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow 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.
*
* Me and My Shadow 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 Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LEVELSELECT_H
#define LEVELSELECT_H
#include "GameState.h"
#include "GameObjects.h"
#include "GUIObject.h"
#include "LevelPackManager.h"
#include <vector>
#include <string>
class GUIScrollBar;
class GUISingleLineListBox;
//Class that represents a level in the levelselect menu.
class Number{
private:
//The background image of the number.
ThemeBlockInstance block;
//The background image of the number when it's locked.
ThemeBlockInstance blockLocked;
//The (text) image of the number.
SharedTexture image;
//Image containing the three stars a player can earn.
SharedTexture medals;
//The number (or text).
int number;
//Integer containing the medal the player got.
//0 = none, 1 = bronze, 2 = silver, 3 = gold
int medal;
//Boolean if the number is locked or not.
bool locked;
public:
//The location and size of the number.
SDL_Rect box;
//If the Number is selected then we draw something indicates it.
bool selected;
//Constructor.
Number(ImageManager &imageManager, SDL_Renderer &renderer);
//Method used for initialising the number.
//number: The number.
//box: The location and size of the number.
void init(SDL_Renderer &renderer, int number, SDL_Rect box);
//Method used for initialising the number.
//text: The caption of the number.
//box: The location and size of the number.
- void init(SDL_Renderer& renderer,std::string text,SDL_Rect box);
+ void init(SDL_Renderer& renderer,std::string text,SDL_Rect box,int number_=-1);
//get current number.
inline int getNumber(){return number;}
//Method used to set the locked status of the number.
//locked: Boolean if it should be locked or not.
void setLocked(bool locked=true);
//Method used to retrieve the locked status of the number.
//Returns: True if the number is locked.
inline bool getLocked(){return locked;}
//Method used to set the medal for this number.
//medal: The new medal for this number.
void setMedal(int medal);
//Method that is used to draw the number.
//dy: The y offset.
void show(SDL_Renderer &renderer, int dy);
};
struct ToolTip {
TexturePtr name;
TexturePtr time;
TexturePtr recordings;
size_t number;
};
//This is the LevelSelect state, here you can select levelpacks and levels.
class LevelSelect : public GameState,public GUIEventCallback{
protected:
//Surface containing the title.
TexturePtr title;
ToolTip toolTip;
//Vector containing the numbers.
std::vector<Number> numbers;
//Contains selected level number (displayed at bottom left corner).
//If it's NULL then nothing selected.
Number* selectedNumber;
//Pointer to the scrollbar.
GUIScrollBar* levelScrollBar;
//Pointer to the description.
GUIObject* levelpackDescription;
//Pointer to the levelpack list.
GUISingleLineListBox* levelpacks;
//Check where and if the mouse clicked on a number.
//If so select that number.
virtual void checkMouse(ImageManager& imageManager, SDL_Renderer& renderer);
//Selected section for keyboard/gamepad control
int section;
//The number of blocks in a row.
int LEVELS_PER_ROW;
//The number of levels displayed on screen at once.
int LEVELS_DISPLAYED_IN_SCREEN;
public:
//Constructor.
//titleText: The title that is shown at the top of the screen.
//packType: The type of levelpacks that should be listed (See LevelPackManager.h).
LevelSelect(ImageManager& imageManager, SDL_Renderer &renderer, const char* titleText, LevelPackManager::LevelPackLists packType=LevelPackManager::ALL_PACKS);
//Destructor.
virtual ~LevelSelect();
//Method that will calculate the number of rows and the number of levels per row.
void calcRows();
//Method used to update the numbers and the scrollbar.
//change: Boolean if the levelpack changed, if not only the numbers need to be replaced.
virtual void refresh(ImageManager& imageManager, SDL_Renderer& renderer, bool change=true)=0;
//Method that is called when a number is selected.
//number: The selected number.
//selected: Boolean if the number was already selected.
virtual void selectNumber(ImageManager &imageManager, SDL_Renderer &renderer, unsigned int number,bool selected)=0;
//Used for keyboard/gamepad navigation
void selectNumberKeyboard(ImageManager &imageManager, SDL_Renderer &renderer, int x, int y);
//Inherited from GameState.
void handleEvents(ImageManager& imageManager, SDL_Renderer& renderer) override;
void logic(ImageManager&, SDL_Renderer&) override;
void render(ImageManager&, SDL_Renderer& renderer) override;
void resize(ImageManager &imageManager, SDL_Renderer& renderer) override;
//Method that is called to render the tooltip.
//number: The number that the tooltip should be drawn for.
//dy: The y offset of the number, used to draw the tooltip in the right place.
virtual void renderTooltip(SDL_Renderer& renderer,unsigned int number,int dy)=0;
//GUI events will be handled here.
virtual void GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType)=0;
};
#endif
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, May 16, 12:37 PM (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
64154
Default Alt Text
(64 KB)
Attached To
Mode
R79 meandmyshadow
Attached
Detach File
Event Timeline