1 /*
2 * Copyright (C) 2012-2013 Me and My Shadow
3 *
4 * This file is part of Me and My Shadow.
5 *
6 * Me and My Shadow is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Me and My Shadow is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "LevelEditSelect.h"
21 #include "GameState.h"
22 #include "Functions.h"
23 #include "FileManager.h"
24 #include "Globals.h"
25 #include "GUIObject.h"
26 #include "GUIListBox.h"
27 #include "GUIScrollBar.h"
28 #include "GUISpinBox.h"
29 #include "InputManager.h"
30 #include "StatisticsManager.h"
31 #include "Game.h"
32 #include "GUIOverlay.h"
33 #include <algorithm>
34 #include <string>
35 #include <iostream>
36
37 #include "libs/tinyformat/tinyformat.h"
38
39 using namespace std;
40
LevelEditSelect(ImageManager & imageManager,SDL_Renderer & renderer)41 LevelEditSelect::LevelEditSelect(ImageManager& imageManager, SDL_Renderer& renderer):LevelSelect(imageManager,renderer,_("Map Editor"),LevelPackManager::CUSTOM_PACKS){
42 //Create the gui.
43 createGUI(imageManager,renderer, true);
44
45 //Set the levelEditGUIObjectRoot.
46 levelEditGUIObjectRoot=GUIObjectRoot;
47
48 //show level list
49 changePack();
50 refresh(imageManager, renderer);
51 }
52
~LevelEditSelect()53 LevelEditSelect::~LevelEditSelect(){
54 selectedNumber=NULL;
55 }
56
createGUI(ImageManager & imageManager,SDL_Renderer & renderer,bool initial)57 void LevelEditSelect::createGUI(ImageManager& imageManager,SDL_Renderer &renderer, bool initial){
58 if(initial){
59 //The levelpack name text field.
60 levelpackName=new GUITextBox(imageManager,renderer,280,104,240,32);
61 levelpackName->eventCallback=this;
62 levelpackName->visible=false;
63 GUIObjectRoot->addChild(levelpackName);
64
65 //Create the six buttons at the bottom of the screen.
66 newPack = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("New Levelpack"));
67 newPack->name = "cmdNewLvlpack";
68 newPack->eventCallback = this;
69 GUIObjectRoot->addChild(newPack);
70
71 propertiesPack = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Pack Properties"), 0, true, true, GUIGravityCenter);
72 propertiesPack->name = "cmdLvlpackProp";
73 propertiesPack->eventCallback = this;
74 GUIObjectRoot->addChild(propertiesPack);
75
76 removePack = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Remove Pack"), 0, true, true, GUIGravityRight);
77 removePack->name = "cmdRmLvlpack";
78 removePack->eventCallback = this;
79 GUIObjectRoot->addChild(removePack);
80
81 move = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Move Map"));
82 move->name = "cmdMoveMap";
83 move->eventCallback = this;
84 //NOTE: Set enabled equal to the inverse of initial.
85 //When resizing the window initial will be false and therefor the move button can stay enabled.
86 move->enabled = false;
87 GUIObjectRoot->addChild(move);
88
89 remove = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Remove Map"), 0, false, true, GUIGravityCenter);
90 remove->name = "cmdRmMap";
91 remove->eventCallback = this;
92 GUIObjectRoot->addChild(remove);
93
94 edit = new GUIButton(imageManager, renderer, 0, 0, -1, 32, _("Edit Map"), 0, false, true, GUIGravityRight);
95 edit->name = "cmdEdit";
96 edit->eventCallback = this;
97 GUIObjectRoot->addChild(edit);
98 }
99
100 //Move buttons to default position
101 const int x1 = int(SCREEN_WIDTH*0.02), x2 = int(SCREEN_WIDTH*0.5), x3 = int(SCREEN_WIDTH*0.98);
102 const int y1 = SCREEN_HEIGHT - 120, y2 = SCREEN_HEIGHT - 60;
103 newPack->left = x1; newPack->top = y1; newPack->gravity = GUIGravityLeft;
104 propertiesPack->left = x2; propertiesPack->top = y1; propertiesPack->gravity = GUIGravityCenter;
105 removePack->left = x3; removePack->top = y1; removePack->gravity = GUIGravityRight;
106 move->left = x1; move->top = y2; move->gravity = GUIGravityLeft;
107 remove->left = x2; remove->top = y2; remove->gravity = GUIGravityCenter;
108 edit->left = x3; edit->top = y2; edit->gravity = GUIGravityRight;
109
110 isVertical = false;
111
112 //Reset the font size
113 newPack->smallFont = false; newPack->width = -1;
114 propertiesPack->smallFont = false; propertiesPack->width = -1;
115 removePack->smallFont = false; removePack->width = -1;
116 move->smallFont = false; move->width = -1;
117 remove->smallFont = false; remove->width = -1;
118 edit->smallFont = false; edit->width = -1;
119
120 //Now update widgets and then check if they overlap
121 GUIObjectRoot->render(renderer, 0, 0, false);
122 if (propertiesPack->left - propertiesPack->gravityX < newPack->left + newPack->width ||
123 propertiesPack->left - propertiesPack->gravityX + propertiesPack->width > removePack->left - removePack->gravityX)
124 {
125 newPack->smallFont = true; newPack->width = -1;
126 propertiesPack->smallFont = true; propertiesPack->width = -1;
127 removePack->smallFont = true; removePack->width = -1;
128 move->smallFont = true; move->width = -1;
129 remove->smallFont = true; remove->width = -1;
130 edit->smallFont = true; edit->width = -1;
131 }
132
133 // NOTE: the following code is necessary (e.g. for Germany)
134
135 //Check again
136 GUIObjectRoot->render(renderer, 0, 0, false);
137 if (propertiesPack->left - propertiesPack->gravityX < newPack->left + newPack->width ||
138 propertiesPack->left - propertiesPack->gravityX + propertiesPack->width > removePack->left - removePack->gravityX)
139 {
140 newPack->left = SCREEN_WIDTH*0.02;
141 newPack->top = SCREEN_HEIGHT - 140;
142 newPack->smallFont = false;
143 newPack->width = -1;
144 newPack->gravity = GUIGravityLeft;
145
146 propertiesPack->left = SCREEN_WIDTH*0.02;
147 propertiesPack->top = SCREEN_HEIGHT - 100;
148 propertiesPack->smallFont = false;
149 propertiesPack->width = -1;
150 propertiesPack->gravity = GUIGravityLeft;
151
152 removePack->left = SCREEN_WIDTH*0.02;
153 removePack->top = SCREEN_HEIGHT - 60;
154 removePack->smallFont = false;
155 removePack->width = -1;
156 removePack->gravity = GUIGravityLeft;
157
158 move->left = SCREEN_WIDTH*0.98;
159 move->top = SCREEN_HEIGHT - 140;
160 move->smallFont = false;
161 move->width = -1;
162 move->gravity = GUIGravityRight;
163
164 remove->left = SCREEN_WIDTH*0.98;
165 remove->top = SCREEN_HEIGHT - 100;
166 remove->smallFont = false;
167 remove->width = -1;
168 remove->gravity = GUIGravityRight;
169
170 edit->left = SCREEN_WIDTH*0.98;
171 edit->top = SCREEN_HEIGHT - 60;
172 edit->smallFont = false;
173 edit->width = -1;
174 edit->gravity = GUIGravityRight;
175
176 isVertical = true;
177 }
178 }
179
changePack()180 void LevelEditSelect::changePack(){
181 packPath = levelpacks->item[levelpacks->value].first;
182 packName = levelpacks->item[levelpacks->value].second;
183 if(packPath==CUSTOM_LEVELS_PATH){
184 //Disable some levelpack buttons.
185 propertiesPack->enabled=false;
186 removePack->enabled=false;
187 }else{
188 //Enable some levelpack buttons.
189 propertiesPack->enabled=true;
190 removePack->enabled=true;
191 }
192
193 //Set last levelpack.
194 getSettings()->setValue("lastlevelpack",levelpacks->getName());
195
196 //Now let levels point to the right pack.
197 levels=getLevelPackManager()->getLevelPack(levelpacks->getName());
198
199 //invalidate the tooltip
200 toolTip.number = -1;
201 }
202
packProperties(ImageManager & imageManager,SDL_Renderer & renderer,bool newPack)203 void LevelEditSelect::packProperties(ImageManager& imageManager,SDL_Renderer& renderer, bool newPack){
204 //Open a message popup.
205 GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-390)/2,600,390,_("Properties"));
206 GUIObject* obj;
207
208 obj=new GUILabel(imageManager,renderer,40,50,240,36,_("Name:"));
209 root->addChild(obj);
210
211 obj=new GUITextBox(imageManager,renderer,60,80,480,36,packName.c_str());
212 if(newPack)
213 obj->caption="";
214 obj->name="LvlpackName";
215 root->addChild(obj);
216
217 obj=new GUILabel(imageManager,renderer,40,120,240,36,_("Description:"));
218 root->addChild(obj);
219
220 obj=new GUITextBox(imageManager,renderer,60,150,480,36,levels->levelpackDescription.c_str());
221 if(newPack)
222 obj->caption="";
223 obj->name="LvlpackDescription";
224 root->addChild(obj);
225
226 obj=new GUILabel(imageManager,renderer,40,190,240,36,_("Congratulation text:"));
227 root->addChild(obj);
228
229 obj=new GUITextBox(imageManager,renderer,60,220,480,36,levels->congratulationText.c_str());
230 if(newPack)
231 obj->caption="";
232 obj->name="LvlpackCongratulation";
233 root->addChild(obj);
234
235 obj = new GUILabel(imageManager, renderer, 40, 260, 240, 36, _("Music list:"));
236 root->addChild(obj);
237
238 obj = new GUITextBox(imageManager, renderer, 60, 290, 480, 36, levels->levelpackMusicList.c_str());
239 if (newPack)
240 obj->caption = "";
241 obj->name = "LvlpackMusic";
242 root->addChild(obj);
243
244 obj=new GUIButton(imageManager,renderer,root->width*0.3,390-44,-1,36,_("OK"),0,true,true,GUIGravityCenter);
245 obj->name="cfgOK";
246 obj->eventCallback=this;
247 root->addChild(obj);
248 GUIButton *cancelButton = new GUIButton(imageManager, renderer, root->width*0.7, 390 - 44, -1, 36, _("Cancel"), 0, true, true, GUIGravityCenter);
249 cancelButton->name = "cfgCancel";
250 cancelButton->eventCallback = this;
251 root->addChild(cancelButton);
252
253 //Create the gui overlay.
254 //NOTE: We don't need to store a pointer since it will auto cleanup itself.
255 new AddonOverlay(renderer, root, cancelButton, NULL, UpDownFocus | TabFocus | ReturnControls | LeftRightControls);
256
257 if(newPack){
258 packPath.clear();
259 packName.clear();
260 }
261 }
262
addLevel(ImageManager & imageManager,SDL_Renderer & renderer)263 void LevelEditSelect::addLevel(ImageManager& imageManager,SDL_Renderer& renderer){
264 //Open a message popup.
265 GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-200)/2,600,200,_("Add level"));
266 GUIObject* obj;
267
268 obj=new GUILabel(imageManager,renderer,40,80,240,36,_("File name:"));
269 root->addChild(obj);
270
271 char s[64];
272 SDL_snprintf(s,64,"map%02d.map",levels->getLevelCount()+1);
273 obj=new GUITextBox(imageManager,renderer,300,80,240,36,s);
274 obj->name="LvlFile";
275 root->addChild(obj);
276
277 obj=new GUIButton(imageManager,renderer,root->width*0.3,200-44,-1,36,_("OK"),0,true,true,GUIGravityCenter);
278 obj->name="cfgAddOK";
279 obj->eventCallback=this;
280 root->addChild(obj);
281 GUIButton *cancelButton = new GUIButton(imageManager, renderer, root->width*0.7, 200 - 44, -1, 36, _("Cancel"), 0, true, true, GUIGravityCenter);
282 cancelButton->name = "cfgAddCancel";
283 cancelButton->eventCallback = this;
284 root->addChild(cancelButton);
285
286 //Dim the screen using the tempSurface.
287 //NOTE: We don't need to store a pointer since it will auto cleanup itself.
288 new AddonOverlay(renderer, root, cancelButton, NULL, UpDownFocus | TabFocus | ReturnControls | LeftRightControls);
289 }
290
moveLevel(ImageManager & imageManager,SDL_Renderer & renderer)291 void LevelEditSelect::moveLevel(ImageManager& imageManager,SDL_Renderer& renderer){
292 //Open a message popup.
293 GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-200)/2,600,200,_("Move level"));
294 GUIObject* obj;
295
296 obj=new GUILabel(imageManager,renderer,40,60,240,36,_("Level: "));
297 root->addChild(obj);
298
299 GUISpinBox *spinBox = new GUISpinBox(imageManager, renderer, 300, 60, 240, 36);
300 spinBox->caption = tfm::format("%d", selectedNumber->getNumber() + 1);
301 spinBox->format = "%1.0f";
302 spinBox->limitMin = 1.0f;
303 spinBox->limitMax = float(levels->getLevelCount());
304 spinBox->name = "MoveLevel";
305 root->addChild(spinBox);
306
307 obj=new GUISingleLineListBox(imageManager,renderer,root->width*0.5,110,240,36,true,true,GUIGravityCenter);
308 obj->name="lstPlacement";
309 vector<string> v;
310 v.push_back(_("Before"));
311 v.push_back(_("After"));
312 v.push_back(_("Swap"));
313 (dynamic_cast<GUISingleLineListBox*>(obj))->addItems(v);
314 obj->value=0;
315 root->addChild(obj);
316
317 obj=new GUIButton(imageManager,renderer,root->width*0.3,200-44,-1,36,_("OK"),0,true,true,GUIGravityCenter);
318 obj->name="cfgMoveOK";
319 obj->eventCallback=this;
320 root->addChild(obj);
321 GUIButton *cancelButton = new GUIButton(imageManager, renderer, root->width*0.7, 200 - 44, -1, 36, _("Cancel"), 0, true, true, GUIGravityCenter);
322 cancelButton->name = "cfgMoveCancel";
323 cancelButton->eventCallback = this;
324 root->addChild(cancelButton);
325
326 //Create the gui overlay.
327 //NOTE: We don't need to store a pointer since it will auto cleanup itself.
328 new AddonOverlay(renderer, root, cancelButton, NULL, TabFocus | ReturnControls | LeftRightControls);
329 }
330
refresh(ImageManager & imageManager,SDL_Renderer & renderer,bool change)331 void LevelEditSelect::refresh(ImageManager& imageManager, SDL_Renderer& renderer, bool change){
332 int m=levels->getLevelCount();
333
334 if(change){
335 numbers.clear();
336
337 //clear the selected level
338 if(selectedNumber!=NULL){
339 selectedNumber=NULL;
340 }
341
342 //Disable the level specific buttons.
343 move->enabled=false;
344 remove->enabled=false;
345 edit->enabled=false;
346
347 for(int n=0;n<=m;n++){
348 numbers.emplace_back(imageManager, renderer);
349 }
350 }
351
352 for(int n=0;n<m;n++){
353 SDL_Rect box={(n%LEVELS_PER_ROW)*64+80,(n/LEVELS_PER_ROW)*64+184,0,0};
354 numbers[n].init(renderer,n,box);
355 }
356 SDL_Rect box={(m%LEVELS_PER_ROW)*64+80,(m/LEVELS_PER_ROW)*64+184,0,0};
357 numbers[m].init(renderer,"+",box,m);
358
359 m++; //including the "+" button
360 if(m>LEVELS_DISPLAYED_IN_SCREEN){
361 levelScrollBar->maxValue=(m-LEVELS_DISPLAYED_IN_SCREEN+LEVELS_PER_ROW-1)/LEVELS_PER_ROW;
362 levelScrollBar->visible=true;
363 }else{
364 levelScrollBar->maxValue=0;
365 levelScrollBar->visible=false;
366 }
367 if (levels->levelpackPath == LEVELS_PATH || levels->levelpackPath == CUSTOM_LEVELS_PATH)
368 levelpackDescription->caption = _("Individual levels which are not contained in any level packs");
369 else if (!levels->levelpackDescription.empty())
370 levelpackDescription->caption = _CC(levels->getDictionaryManager(), levels->levelpackDescription);
371 else
372 levelpackDescription->caption = "";
373
374 //invalidate the tooltip
375 toolTip.number = -1;
376 }
377
selectNumber(ImageManager & imageManager,SDL_Renderer & renderer,unsigned int number,bool selected)378 void LevelEditSelect::selectNumber(ImageManager& imageManager, SDL_Renderer& renderer, unsigned int number, bool selected){
379 if (selected) {
380 if (number >= 0 && number < levels->getLevelCount()) {
381 levels->setCurrentLevel(number);
382 setNextState(STATE_LEVEL_EDITOR);
383 } else {
384 addLevel(imageManager, renderer);
385 }
386 }else{
387 move->enabled = false;
388 remove->enabled = false;
389 edit->enabled = false;
390 selectedNumber = NULL;
391 if (number == numbers.size() - 1){
392 if (isKeyboardOnly) {
393 selectedNumber = &numbers[number];
394 } else {
395 addLevel(imageManager, renderer);
396 }
397 } else if (number >= 0 && number < levels->getLevelCount()) {
398 selectedNumber=&numbers[number];
399
400 //Enable the level specific buttons.
401 //NOTE: We check if 'remove levelpack' is enabled, if not then it's the Levels levelpack.
402 if(removePack->enabled)
403 move->enabled=true;
404 remove->enabled=true;
405 edit->enabled=true;
406 }
407 }
408 }
409
handleEvents(ImageManager & imageManager,SDL_Renderer & renderer)410 void LevelEditSelect::handleEvents(ImageManager& imageManager, SDL_Renderer& renderer){
411 //Call handleEvents() of base class.
412 LevelSelect::handleEvents(imageManager, renderer);
413
414 if (section == 3) {
415 //Check focus movement
416 if (inputMgr.isKeyDownEvent(INPUTMGR_RIGHT)){
417 isKeyboardOnly = true;
418 section2 += isVertical ? 3 : 1;
419 } else if (inputMgr.isKeyDownEvent(INPUTMGR_LEFT)){
420 isKeyboardOnly = true;
421 section2 -= isVertical ? 3 : 1;
422 } else if (inputMgr.isKeyDownEvent(INPUTMGR_UP)){
423 isKeyboardOnly = true;
424 section2 -= isVertical ? 1 : 3;
425 } else if (inputMgr.isKeyDownEvent(INPUTMGR_DOWN)){
426 isKeyboardOnly = true;
427 section2 += isVertical ? 1 : 3;
428 }
429 if (section2 > 6) section2 -= 6;
430 else if (section2 < 1) section2 += 6;
431
432 //Check if enter is pressed
433 if (isKeyboardOnly && inputMgr.isKeyDownEvent(INPUTMGR_SELECT) && section2 >= 1 && section2 <= 6) {
434 GUIButton *buttons[6] = {
435 newPack, propertiesPack, removePack, move, remove, edit
436 };
437 GUIEventCallback_OnEvent(imageManager, renderer, buttons[section2 - 1]->name, buttons[section2 - 1], GUIEventClick);
438 }
439 }
440 }
441
render(ImageManager & imageManager,SDL_Renderer & renderer)442 void LevelEditSelect::render(ImageManager& imageManager,SDL_Renderer &renderer){
443 //Let the levelselect render.
444 LevelSelect::render(imageManager,renderer);
445
446 //Draw highlight in keyboard only mode.
447 if (isKeyboardOnly) {
448 GUIButton *buttons[6] = {
449 newPack, propertiesPack, removePack, move, remove, edit
450 };
451 for (int i = 0; i < 6; i++) {
452 buttons[i]->state = (section == 3 && section2 - 1 == i) ? 1 : 0;
453 }
454 }
455 }
456
resize(ImageManager & imageManager,SDL_Renderer & renderer)457 void LevelEditSelect::resize(ImageManager& imageManager, SDL_Renderer &renderer){
458 //Let the levelselect resize.
459 LevelSelect::resize(imageManager, renderer);
460
461 //Create the GUI.
462 createGUI(imageManager,renderer, false);
463
464 //NOTE: This is a workaround for buttons failing when resizing.
465 if(packPath==CUSTOM_LEVELS_PATH){
466 removePack->enabled=false;
467 propertiesPack->enabled=false;
468 }
469 if(selectedNumber)
470 selectNumber(imageManager, renderer, selectedNumber->getNumber(),false);
471
472 }
473
renderTooltip(SDL_Renderer & renderer,unsigned int number,int dy)474 void LevelEditSelect::renderTooltip(SDL_Renderer& renderer,unsigned int number,int dy){
475
476 if (!toolTip.name || toolTip.number != number) {
477 SDL_Color fg = objThemes.getTextColor(true);
478 toolTip.number = number;
479
480 if (number < (unsigned int)levels->getLevelCount()){
481 //Render the name of the level.
482 toolTip.name = textureFromText(renderer, *fontText, _CC(levels->getDictionaryManager(), levels->getLevelName(number)), fg);
483 } else {
484 //Add level button
485 toolTip.name = textureFromText(renderer, *fontText, _("Add level"), fg);
486 }
487 }
488
489 //Check if name isn't null.
490 if(!toolTip.name)
491 return;
492
493 //Now draw a square the size of the three texts combined.
494 SDL_Rect r=numbers[number].box;
495 r.y-=dy*64;
496 const SDL_Rect nameSize = rectFromTexture(*toolTip.name);
497 r.w=nameSize.w;
498 r.h=nameSize.h;
499
500 //Make sure the tooltip doesn't go outside the window.
501 if(r.y>SCREEN_HEIGHT-200){
502 r.y-=nameSize.h+4;
503 }else{
504 r.y+=numbers[number].box.h+2;
505 }
506 if(r.x+r.w>SCREEN_WIDTH-50)
507 r.x=SCREEN_WIDTH-50-r.w;
508
509 //Draw a rectange
510 Uint32 color=0xFFFFFFFF;
511 drawGUIBox(r.x-5,r.y-5,r.w+10,r.h+10,renderer,color);
512
513 //Calc the position to draw.
514 SDL_Rect r2=r;
515
516 //Now we render the name if the surface isn't null.
517 if(toolTip.name){
518 //Draw the name.
519 applyTexture(r2.x, r2.y, toolTip.name, renderer);
520 }
521 }
522
523 //Escape invalid characters in a file name (mainly for Windows).
escapeFileName(const std::string & fileName)524 static std::string escapeFileName(const std::string& fileName) {
525 std::string ret;
526
527 for (int i = 0, m = fileName.size(); i < m; i++) {
528 bool escape = false;
529 char c = fileName[i];
530
531 switch (c) {
532 case '\"': case '*': case '/': case ':': case '<':
533 case '>': case '?': case '\\': case '|': case '%':
534 escape = true;
535 break;
536 }
537 if (c <= 0x1F || c >= 0x7F) escape = true;
538 if (i == 0 || i == m - 1) {
539 switch (c) {
540 case ' ': case '.':
541 escape = true;
542 break;
543 }
544 }
545
546 if (escape) {
547 ret += "%" + tfm::format("%02X", (int)(unsigned char)c);
548 } else {
549 ret.push_back(c);
550 }
551 }
552
553 return ret;
554 }
555
GUIEventCallback_OnEvent(ImageManager & imageManager,SDL_Renderer & renderer,std::string name,GUIObject * obj,int eventType)556 void LevelEditSelect::GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType){
557 //NOTE: We check for the levelpack change to enable/disable some levelpack buttons.
558 if(name=="cmdLvlPack"){
559 //We call changepack and return to prevent the LevelSelect to undo what we did.
560 changePack();
561 refresh(imageManager, renderer);
562 return;
563 }
564
565 //Let the level select handle his GUI events.
566 LevelSelect::GUIEventCallback_OnEvent(imageManager,renderer,name,obj,eventType);
567
568 //Check for the edit button.
569 if(name=="cmdNewLvlpack"){
570 //Create a new pack.
571 packProperties(imageManager,renderer, true);
572 }else if(name=="cmdLvlpackProp"){
573 //Show the pack properties.
574 packProperties(imageManager,renderer, false);
575 }else if(name=="cmdRmLvlpack"){
576 //Show an "are you sure" message.
577 if(msgBox(imageManager,renderer,tfm::format(_("Are you sure remove the level pack '%s'?"),packName),MsgBoxYesNo,_("Remove prompt"))==MsgBoxYes){
578 //Remove the directory.
579 if(!removeDirectory(levels->levelpackPath.c_str())){
580 cerr<<"ERROR: Unable to remove levelpack directory "<<levels->levelpackPath<<endl;
581 }
582
583 //Remove it from the vector (levelpack list).
584 for (auto it = levelpacks->item.begin(); it != levelpacks->item.end(); ++it){
585 if (it->first == levels->levelpackPath) {
586 levelpacks->item.erase(it);
587 break;
588 }
589 }
590
591 //Remove it from the levelpackManager.
592 getLevelPackManager()->removeLevelPack(levels->levelpackPath, true);
593 levels = NULL;
594
595 //And call changePack.
596 levelpacks->value=levelpacks->item.size()-1;
597 changePack();
598 refresh(imageManager, renderer);
599 }
600 }else if(name=="cmdMoveMap"){
601 if(selectedNumber!=NULL){
602 moveLevel(imageManager,renderer);
603 }
604 }else if(name=="cmdRmMap"){
605 if(selectedNumber!=NULL){
606 //Show an "are you sure" message.
607 if (msgBox(imageManager, renderer, tfm::format(_("Are you sure remove the map '%s'?"), levels->getLevel(selectedNumber->getNumber())->name), MsgBoxYesNo, _("Remove prompt")) != MsgBoxYes) {
608 return;
609 }
610 if(packPath!=CUSTOM_LEVELS_PATH){
611 if(!removeFile((levels->levelpackPath+"/"+levels->getLevel(selectedNumber->getNumber())->file).c_str())){
612 cerr<<"ERROR: Unable to remove level "<<(levels->levelpackPath+"/"+levels->getLevel(selectedNumber->getNumber())->file).c_str()<<endl;
613 }
614 levels->removeLevel(selectedNumber->getNumber());
615 levels->saveLevels(levels->levelpackPath+"/levels.lst");
616 }else{
617 //This is the levels levelpack so we just remove the file.
618 if(!removeFile(levels->getLevel(selectedNumber->getNumber())->file.c_str())){
619 cerr<<"ERROR: Unable to remove level "<<levels->getLevel(selectedNumber->getNumber())->file<<endl;
620 }
621 levels->removeLevel(selectedNumber->getNumber());
622 }
623
624 //And refresh the selection screen.
625 refresh(imageManager, renderer);
626 }
627 }else if(name=="cmdEdit"){
628 if(selectedNumber!=NULL){
629 levels->setCurrentLevel(selectedNumber->getNumber());
630 setNextState(STATE_LEVEL_EDITOR);
631 }
632 }
633
634 //Check for levelpack properties events.
635 if(name=="cfgOK"){
636 GUIObject *lvlpackName = GUIObjectRoot->getChild("LvlpackName");
637 GUIObject *lvlpackDescription = GUIObjectRoot->getChild("LvlpackDescription");
638 GUIObject *lvlpackCongratulation = GUIObjectRoot->getChild("LvlpackCongratulation");
639 GUIObject *lvlpackMusic = GUIObjectRoot->getChild("LvlpackMusic");
640
641 assert(lvlpackName && lvlpackDescription && lvlpackCongratulation && lvlpackMusic);
642
643 if (lvlpackName->caption.empty()) {
644 msgBox(imageManager, renderer, _("Levelpack name cannot be empty."), MsgBoxOKOnly, _("Error"));
645 } else {
646 //Check if the name changed.
647 if (packName != lvlpackName->caption) {
648 std::string newPackPathMinusSlash = getUserPath(USER_DATA) + "custom/levelpacks/" + escapeFileName(lvlpackName->caption);
649
650 //Delete the old one.
651 if (!packName.empty()){
652 std::string oldPackPathMinusSlash = levels->levelpackPath;
653 if (!oldPackPathMinusSlash.empty()) {
654 if (oldPackPathMinusSlash[oldPackPathMinusSlash.size() - 1] == '/'
655 || oldPackPathMinusSlash[oldPackPathMinusSlash.size() - 1] == '\\')
656 {
657 oldPackPathMinusSlash.pop_back();
658 }
659 }
660
661 if (!renameDirectory(oldPackPathMinusSlash.c_str(), newPackPathMinusSlash.c_str())) {
662 cerr << "ERROR: Unable to move levelpack directory '" << oldPackPathMinusSlash << "' to '"
663 << newPackPathMinusSlash << "'! The levelpack directory will be kept unchanged." << endl;
664
665 //If we failed to rename the directory, we just keep the old directory name.
666 newPackPathMinusSlash = oldPackPathMinusSlash;
667 }
668
669 //Remove the old one from the levelpack manager.
670 getLevelPackManager()->removeLevelPack(levels->levelpackPath, false);
671
672 //And the levelpack list.
673 for (auto it = levelpacks->item.begin(); it != levelpacks->item.end(); ++it){
674 if (it->first == levels->levelpackPath) {
675 levelpacks->item.erase(it);
676 break;
677 }
678 }
679 } else {
680 //It's a new levelpack. First we try to create the dirs and the levels.lst.
681 if (dirExists(newPackPathMinusSlash.c_str())) {
682 cerr << "ERROR: The levelpack directory " << newPackPathMinusSlash << " already exists!" << endl;
683 msgBox(imageManager, renderer, tfm::format(_("The levelpack directory '%s' already exists!"), newPackPathMinusSlash), MsgBoxOKOnly, _("Error"));
684 return;
685 }
686 if (!createDirectory(newPackPathMinusSlash.c_str())) {
687 cerr << "ERROR: Unable to create levelpack directory " << newPackPathMinusSlash << endl;
688 msgBox(imageManager, renderer, tfm::format(_("Unable to create levelpack directory '%s'!"), newPackPathMinusSlash), MsgBoxOKOnly, _("Error"));
689 return;
690 }
691 if (fileExists((newPackPathMinusSlash + "/levels.lst").c_str())) {
692 cerr << "ERROR: The levelpack file " << (newPackPathMinusSlash + "/levels.lst") << " already exists!" << endl;
693 msgBox(imageManager, renderer, tfm::format(_("The levelpack file '%s' already exists!"), newPackPathMinusSlash + "/levels.lst"), MsgBoxOKOnly, _("Error"));
694 return;
695 }
696 if (!createFile((newPackPathMinusSlash + "/levels.lst").c_str())) {
697 cerr << "ERROR: Unable to create levelpack file " << (newPackPathMinusSlash + "/levels.lst") << endl;
698 msgBox(imageManager, renderer, tfm::format(_("Unable to create levelpack file '%s'!"), newPackPathMinusSlash + "/levels.lst"), MsgBoxOKOnly, _("Error"));
699 return;
700 }
701
702 //If it's successful we create a new levelpack.
703 levels = new LevelPack;
704 }
705
706 //And set the new name.
707 packName = levels->levelpackName = lvlpackName->caption;
708 packPath = levels->levelpackPath = newPackPathMinusSlash + "/";
709
710 //Also add the levelpack location
711 getLevelPackManager()->addLevelPack(levels);
712 levelpacks->addItem(packPath, packName);
713 levelpacks->value = levelpacks->item.size() - 1;
714
715 //And call changePack.
716 changePack();
717 }
718
719 levels->levelpackDescription = lvlpackDescription->caption;
720 levels->congratulationText = lvlpackCongratulation->caption;
721 levels->levelpackMusicList = lvlpackMusic->caption;
722
723 //Refresh the leveleditselect to show the correct information.
724 refresh(imageManager, renderer);
725
726 //Save the configuration.
727 levels->saveLevels(levels->levelpackPath + "levels.lst");
728 getSettings()->setValue("lastlevelpack", levels->levelpackPath);
729
730 //Clear the gui.
731 if (GUIObjectRoot) {
732 delete GUIObjectRoot;
733 GUIObjectRoot = NULL;
734 }
735 }
736 }else if(name=="cfgCancel"){
737 //Check if packName is empty, if so it was a new levelpack and we need to revert to an existing one.
738 if(packName.empty()){
739 packPath = levelpacks->item[levelpacks->value].first;
740 packName = levelpacks->item[levelpacks->value].second;
741 changePack();
742 }
743
744 //Clear the gui.
745 if(GUIObjectRoot){
746 delete GUIObjectRoot;
747 GUIObjectRoot=NULL;
748 }
749 }
750
751 //Check for add level events.
752 if(name=="cfgAddOK"){
753 //Check if the file name isn't null.
754 //Now loop throught the children of the GUIObjectRoot in search of the fields.
755 for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
756 if(GUIObjectRoot->childControls[i]->name=="LvlFile"){
757 if(GUIObjectRoot->childControls[i]->caption.empty()){
758 msgBox(imageManager,renderer,_("No file name given for the new level."),MsgBoxOKOnly,_("Missing file name"));
759 return;
760 }else{
761 string tmp_caption = GUIObjectRoot->childControls[i]->caption;
762
763 //Replace all spaces with a underline.
764 size_t j;
765 for(;(j=tmp_caption.find(" "))!=string::npos;){
766 tmp_caption.replace(j,1,"_");
767 }
768
769 //If there isn't ".map" extension add it.
770 size_t found=tmp_caption.find_first_of(".");
771 if(found!=string::npos)
772 tmp_caption.replace(tmp_caption.begin()+found+1,tmp_caption.end(),"map");
773 else if (tmp_caption.substr(found+1)!="map")
774 tmp_caption.append(".map");
775
776 /* Create path and file in it */
777 string path=(levels->levelpackPath+"/"+tmp_caption);
778 if(packPath==CUSTOM_LEVELS_PATH){
779 path=(getUserPath(USER_DATA)+"/custom/levels/"+tmp_caption);
780 }
781
782 //First check if the file doesn't exist already.
783 FILE* f;
784 f=fopen(path.c_str(),"rb");
785
786 //Check if it exists.
787 if(f){
788 //Close the file.
789 fclose(f);
790
791 //Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
792 currentState->render(imageManager,renderer);
793 levelEditGUIObjectRoot->render(renderer);
794
795 //Notify the user.
796 msgBox(imageManager, renderer, tfm::format(_("The file %s already exists."), tmp_caption), MsgBoxOKOnly, _("Error"));
797 return;
798 }
799
800 if(!createFile(path.c_str())){
801 cerr<<"ERROR: Unable to create level file "<<path<<endl;
802 }else{
803 //Update statistics.
804 statsMgr.newAchievement("create1");
805 if((++statsMgr.createdLevels)>=50) statsMgr.newAchievement("create50");
806 }
807 levels->addLevel(path);
808 //NOTE: Also add the level to the levels levelpack in case of custom levels.
809 if(packPath==CUSTOM_LEVELS_PATH){
810 LevelPack* levelsPack=getLevelPackManager()->getLevelPack(LEVELS_PATH);
811 if(levelsPack){
812 levelsPack->addLevel(path);
813 levelsPack->setLocked(levelsPack->getLevelCount()-1);
814 }else{
815 cerr<<"ERROR: Unable to add level to Levels levelpack"<<endl;
816 }
817 }
818 if(packPath!=CUSTOM_LEVELS_PATH)
819 levels->saveLevels(levels->levelpackPath+"levels.lst");
820 refresh(imageManager, renderer);
821
822 //Clear the gui.
823 if(GUIObjectRoot){
824 delete GUIObjectRoot;
825 GUIObjectRoot=NULL;
826 return;
827 }
828 }
829 }
830 }
831 }else if(name=="cfgAddCancel"){
832 //Clear the gui.
833 if(GUIObjectRoot){
834 delete GUIObjectRoot;
835 GUIObjectRoot=NULL;
836 }
837 }
838
839 //Check for move level events.
840 if(name=="cfgMoveOK"){
841 //Check if the entered level number is valid.
842 //Now loop throught the children of the GUIObjectRoot in search of the fields.
843 int level=0;
844 int placement=0;
845 for(unsigned int i=0;i<GUIObjectRoot->childControls.size();i++){
846 if(GUIObjectRoot->childControls[i]->name=="MoveLevel"){
847 level=atoi(GUIObjectRoot->childControls[i]->caption.c_str());
848 if(level<=0 || level>levels->getLevelCount()){
849 msgBox(imageManager,renderer,_("The entered level number isn't valid!"),MsgBoxOKOnly,_("Illegal number"));
850 return;
851 }
852 }
853 if(GUIObjectRoot->childControls[i]->name=="lstPlacement"){
854 placement=GUIObjectRoot->childControls[i]->value;
855 }
856 }
857
858 //Now we execute the swap/move.
859 //Check for the place before.
860 if(placement==0){
861 //We place the selected level before the entered level.
862 levels->moveLevel(selectedNumber->getNumber(),level-1);
863 }else if(placement==1){
864 //We place the selected level after the entered level.
865 if(level<selectedNumber->getNumber())
866 levels->moveLevel(selectedNumber->getNumber(),level);
867 else
868 levels->moveLevel(selectedNumber->getNumber(),level+1);
869 }else if(placement==2){
870 //We swap the selected level with the entered level.
871 levels->swapLevel(selectedNumber->getNumber(),level-1);
872 }
873
874 //And save the change.
875 if(packPath!=CUSTOM_LEVELS_PATH)
876 levels->saveLevels(levels->levelpackPath+"/levels.lst");
877
878 refresh(imageManager, renderer);
879
880 //Clear the gui.
881 if(GUIObjectRoot){
882 delete GUIObjectRoot;
883 GUIObjectRoot=NULL;
884 }
885 }else if(name=="cfgMoveCancel"){
886 //Clear the gui.
887 if(GUIObjectRoot){
888 delete GUIObjectRoot;
889 GUIObjectRoot=NULL;
890 }
891 }
892 }
893