1 /************************************************************************
2  * This file is part of Wizznic.                                        *
3  * Copyright 2009-2015 Jimmy Christensen <dusted@dusted.dk>             *
4  * Wizznic is free software: you can redistribute it and/or modify      *
5  * it under the terms of the GNU General Public License as published by *
6  * the Free Software Foundation, either version 3 of the License, or    *
7  * (at your option) any later version.                                  *
8  *                                                                      *
9  * Wizznic is distributed in the hope that it will be useful,           *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
12  * GNU General Public License for more details.                         *
13  *                                                                      *
14  * You should have received a copy of the GNU General Public License    *
15  * along with Wizznic.  If not, see <http://www.gnu.org/licenses/>.     *
16  ************************************************************************/
17 
18 #include "settings.h"
19 #include "leveleditor.h"
20 #include "cursor.h"
21 #include "board.h"
22 #include "draw.h"
23 #include "states.h"
24 #include "input.h"
25 #include "text.h"
26 #include "levels.h"
27 #include "list/list.h"
28 #include <stdio.h>
29 
30 #include "strings.h"
31 #include "teleport.h"
32 
33 #include "transition.h"
34 #include "defs.h"
35 
36 #define EDITOR_MAIN 0
37 #define EDITOR_BRICKS_SELECTION 1
38 #define EDITOR_SAVEBTN_CLICKED 3 /*Has to be 3 */
39 
40 static playField pf;
41 static cursorType cur;
42 static int selBrick=1;
43 static int changed=0;
44 static char fileName[64];
45 static int teleState=0; //Teleport placement iteration
46 static int teleSrcPos[2];
47 static int editorState;
48 SDL_Surface* selBrickBG;
49 static int allowBrickToBePlaced;
50 
51 SDL_Surface* saveBtnBG;
52 spriteType* saveBtnSprite;
53 
54 static SDL_Rect fieldRect; //Used to detect when the cursor is inside the brick-field.
55 
editorLoad(const char * fn,SDL_Surface * screen)56 void editorLoad(const char* fn, SDL_Surface* screen)
57 {
58   //Set filename
59   editorFileName(fn);
60 
61   //Init cursor
62   initCursor(&cur);
63   //Read info's for level.
64   pf.levelInfo = mkLevelInfo( fn );
65   //Load field
66   loadField(&pf, fn);
67 
68   initDraw(pf.levelInfo, screen);
69   SDL_FreeSurface(stealGfxPtr()->boardImg);
70   stealGfxPtr()->boardImg = loadImg( DATADIR"data/editbg.png" );
71 
72   selBrickBG = loadImg( DATADIR"data/editselbrick.png" );
73 
74   saveBtnBG = loadImg( DATADIR"data/edit-save.png" );
75   saveBtnSprite = cutSprite( saveBtnBG, 0,0, saveBtnBG->w, saveBtnBG->h );
76 
77   changed=0;
78   selBrick=BRICKSBEGIN;
79 
80   teleState=0;
81 
82   fieldRect.x = HSCREENW -  74;
83   fieldRect.y = HSCREENH - 114;
84   fieldRect.w = HSCREENW + 154;
85   fieldRect.h = HSCREENH + 114;
86 }
87 
editorCleanUp()88 void editorCleanUp()
89 {
90 
91   resetBtn(C_UP);
92   resetBtn(C_DOWN);
93   resetBtn(C_LEFT);
94   resetBtn(C_RIGHT);
95   resetBtn(C_BTNX);
96   resetBtn(C_BTNB);
97 
98   //Free memory used for levelInfo
99   freeLevelInfo( &pf.levelInfo );
100   //Free board and graphics here
101   cleanUpDraw();
102   changed=0;
103   freeField(&pf);
104 
105   //Free graphics
106   SDL_FreeSurface(selBrickBG);
107 
108   SDL_FreeSurface(saveBtnBG);
109   free(saveBtnSprite);
110 }
111 
editorFileName(const char * fn)112 void editorFileName(const char* fn)
113 {
114   changed=1;
115   strcpy(fileName,fn);
116 }
117 
editorPickBrickUnderCursor()118 void editorPickBrickUnderCursor()
119 {
120   if(pf.board[cur.x][cur.y])
121   {
122     selBrick=pf.board[cur.x][cur.y]->type;
123   }
124 }
125 
editIsSwitch(int s)126 int editIsSwitch(int s)
127 {
128   return(s==SWON||s==SWOFF);
129 }
130 
editorRemoveBrickUnderCursor()131 void editorRemoveBrickUnderCursor()
132 {
133   if(pf.board[cur.x][cur.y])
134   {
135     //Switch?
136     if( editIsSwitch(pf.board[cur.x][cur.y]->type) )
137     {
138       teleRemoveFromList(pf.levelInfo->switchList,cur.x,cur.y);
139     }
140     free(pf.board[cur.x][cur.y]);
141     pf.board[cur.x][cur.y]=0;
142   }
143 
144   //teleport?
145   teleRemoveFromList(pf.levelInfo->teleList,cur.x,cur.y);
146 
147 
148 
149   boardSetWalls(&pf);
150   changed=1;
151 }
152 
editAddToBoard(int s)153 void editAddToBoard(int s)
154 {
155   pf.board[cur.x][cur.y]=malloc(sizeof(brickType));
156   pf.board[cur.x][cur.y]->type=s;
157   pf.board[cur.x][cur.y]->isActive=1;
158   pf.board[cur.x][cur.y]->pxx=cur.x*20+boardOffsetX;
159   pf.board[cur.x][cur.y]->pxy=cur.y*20+boardOffsetY;
160   boardSetWalls(&pf);
161 }
162 
163 
runEditor(SDL_Surface * screen)164 int runEditor(SDL_Surface* screen)
165 {
166   SDL_Rect selBrickRect;
167   getInpPointerState()->escEnable=1;
168 
169   if( editorState == EDITOR_MAIN )
170   {
171 
172     if(getButton(C_BTNMENU) || isPointerEscapeClicked() )
173     {
174       resetBtn( C_BTNMENU );
175       resetMouseBtn();
176 
177       changed++; //If it was 0 then it will become 1 (saved) exit. If it was 1 it becomes 2 (not saved).
178       if( changed != 2 )
179       {
180         editorCleanUp();
181         startTransition(screen, TRANSITION_TYPE_ROLL_IN, 500 );
182         return(STATEMENU);
183       }
184     }
185 
186     //We detect if the "preview" brick on the left is clicked, we do this now so we can reset the click so that it does not hit the board
187     selBrickRect.x = HSCREENW-125;
188     selBrickRect.y = HSCREENH-85;
189     selBrickRect.w = selBrickRect.x+20;
190     selBrickRect.h = selBrickRect.y+20;
191     //Also, we can only click it if a teleport destination is not being placed.
192     if( isBoxClicked(&selBrickRect) && teleState==0 )
193     {
194       editorState=EDITOR_BRICKS_SELECTION;
195       resetMouseBtn();
196     }
197 
198     //We detect mouse-save input here so it won't hit the board.
199     selBrickRect.x = HSCREENW-145;
200     selBrickRect.y = HSCREENH+42;
201     selBrickRect.w = selBrickRect.x+59;
202     selBrickRect.h = selBrickRect.y+24;
203     if( isBoxClicked(&selBrickRect) && changed>0)
204     {
205       changed=EDITOR_SAVEBTN_CLICKED;
206       resetMouseBtn();
207     }
208 
209     //We check if the cursor is in the field (if it is not, brick-placement is blocked so we don't place bricks when clicking outside of the field).
210     if( isPointerInBox(&fieldRect) || getInpPointerState()->timeSinceMoved > POINTER_SHOW_TIMEOUT )
211     {
212       allowBrickToBePlaced=1;
213     } else {
214       allowBrickToBePlaced=0;
215     }
216 
217     //Handle movement
218     if(getButton(C_UP))
219     {
220       resetBtn(C_UP);
221       moveCursor(&cur, 0,DIRUP, 0);
222     }
223 
224     if(getButton(C_DOWN))
225     {
226       resetBtn(C_DOWN);
227       moveCursor(&cur, 0,DIRDOWN,0);
228     }
229 
230     if(getButton(C_LEFT))
231     {
232       resetBtn(C_LEFT);
233       moveCursor(&cur, DIRLEFT,0, 0);
234     }
235 
236     if(getButton(C_RIGHT))
237     {
238       resetBtn(C_RIGHT);
239       moveCursor(&cur, DIRRIGHT,0, 0);
240     }
241 
242     //Handle mouse input
243     if( getInpPointerState()->timeSinceMoved==0 && !cur.lock )
244     {
245       setCursor(&cur, getInpPointerState()->curX,getInpPointerState()->curY );
246     }
247 
248     if(getButton(C_BTNB))
249     {
250       resetBtn(C_BTNB);
251       selBrick++;
252 
253       if(selBrick==RESERVED)
254         selBrick++;
255 
256       if(selBrick>NUMTILES)
257         selBrick=1;
258     }
259 
260     if(getButton(C_BTNA))
261     {
262       resetBtn(C_BTNA);
263 
264       selBrick--;
265       if(selBrick==RESERVED)
266         selBrick--;
267 
268       if(selBrick<1)
269         selBrick=NUMTILES;
270     }
271 
272 
273 
274     //Is place brick button being pressed, and if it is, are we allowed to place the brick?
275     if( (getButton(C_BTNX) || getInpPointerState()->isDown ) && selBrick != RESERVED && allowBrickToBePlaced )
276     {
277 
278       //We remove the brick before placing a new one if it's not a teleport or if it's a switch (not switch-target).
279       if( selBrick!=TELESRC && !((editIsSwitch(selBrick)&&teleState==1)  ) )
280       {
281         editorRemoveBrickUnderCursor();
282       }
283 
284       if(selBrick==TELESRC || editIsSwitch(selBrick) )
285       {
286         resetMouseBtn();
287         resetBtn(C_BTNX);
288 
289         if(teleState==0)
290         {
291           //Save source pos
292           teleSrcPos[0] = cur.x;
293           teleSrcPos[1] = cur.y;
294           teleState++;
295         } else {
296           //Add to list
297           if(editIsSwitch(selBrick))
298           {
299             teleAddToList( pf.levelInfo->switchList, teleSrcPos[0], teleSrcPos[1], cur.x, cur.y );
300             //printf("Number of members in switchList: %i\n", listSize(pf.levelInfo->switchList) );
301             cur.x = teleSrcPos[0];
302             cur.y = teleSrcPos[1];
303             editAddToBoard(selBrick);
304           } else {
305             teleAddToList( pf.levelInfo->teleList, teleSrcPos[0], teleSrcPos[1], cur.x, cur.y );
306           }
307           //Reset state
308           teleState=0;
309         }
310       } else {
311         editAddToBoard(selBrick);
312       } //Not a teleport
313 
314       changed=1;
315     }
316 
317     if( getButton(C_BTNY) )
318     {
319       //If we are trying to remove an empty teleport
320       if(telePresent(pf.levelInfo->teleList, cur.x, cur.y) && selBrick!=TELESRC)
321       {
322         resetBtn(C_BTNY);
323         selBrick=TELESRC;
324       } else {
325         if(selBrick!=RESERVED)
326         {
327           editorPickBrickUnderCursor();
328         }
329         editorRemoveBrickUnderCursor();
330       }
331     }
332 
333     if( getInpPointerState()->isDown && selBrick==RESERVED )
334     {
335       editorRemoveBrickUnderCursor();
336     }
337 
338     if(getButton(C_BTNSELECT) || changed==EDITOR_SAVEBTN_CLICKED)
339     {
340       resetBtn(C_BTNSELECT);
341 
342       pf.levelInfo->completable=0;
343       if( saveLevel(fileName, &pf) )
344       {
345         //Refresh the list of userLevels.
346         addUserLevel(fileName);
347         changed=0;
348       }
349 
350 
351     }
352 
353   } //Editor in main state, don't ignore input
354 
355 
356   draw(&cur, &pf, screen);
357 
358 
359   if(changed==2)
360   {
361     txtWriteCenter(screen, FONTMEDIUM, STR_EDIT_NOT_SAVED_WARNING, HSCREENW,HSCREENH-20);
362     txtWriteCenter(screen, FONTSMALL, STR_EDIT_PRESS_EXIT_TO_EXIT, HSCREENW,HSCREENH);
363     txtWriteCenter(screen, FONTSMALL, STR_EDIT_PRESS_SAVE_TO_SAVE, HSCREENW,HSCREENH+10);
364   }
365 
366 
367   txtWriteCenter(screen, FONTSMALL,STR_EDIT_STATUS, HSCREENW-115,HSCREENH+80);
368   txtWriteCenter(screen, FONTSMALL, (changed)?STR_EDIT_UNSAVED:STR_EDIT_SAVED, HSCREENW-115,HSCREENH+89);
369 
370   txtWriteCenter(screen, FONTSMALL,fileName, HSCREENW,HSCREENH+110);
371 
372   txtWriteCenter(screen, FONTSMALL,STR_EDIT_CONTROLS, HSCREENW,HSCREENH-120);
373 
374 
375   //Write which keys are used to cycle selected brick.
376   txtWriteCenter(screen, FONTSMALL,STR_EDIT_PREVBRICK_KEY,HSCREENW-142,HSCREENH-80);
377   txtWriteCenter(screen, FONTSMALL,STR_EDIT_NEXTBRICK_KEY,HSCREENW-88,HSCREENH-80);
378 
379 
380   //Draw the currently selected brick.
381   drawBrick(screen, selBrick,HSCREENW-125,HSCREENH-85);
382 
383   //Write brick name.
384   txtWriteCenter(screen, FONTSMALL, str_brick_names[selBrick], HSCREENW-116,HSCREENH-56 );
385 
386   //Tell if we're placing teleport source or destination
387   if(selBrick==TELESRC && teleState==0)
388   {
389     txtWriteCenter(screen, FONTSMALL, "(From)", HSCREENW-115,HSCREENH-41);
390   } else if(teleState)
391   {
392     if(selBrick==TELESRC)
393     {
394       txtWriteCenter(screen, FONTSMALL, "(To)", HSCREENW-115,HSCREENH-41);
395     } else {
396       txtWriteCenter(screen, FONTSMALL, "(Target)", HSCREENW-115,HSCREENH-41);
397     }
398     drawPath(screen, teleSrcPos[0], teleSrcPos[1], cur.x, cur.y, 1);
399   }
400 
401   //Draw all the telepaths.
402   drawAllTelePaths(screen, pf.levelInfo->teleList);
403 
404 
405   //Draw switchpath we hover above
406   listItem* t = &pf.levelInfo->switchList->begin;
407   while( LISTFWD(pf.levelInfo->switchList, t) )
408   {
409     telePort_t* tp=(telePort_t*)t->data;
410     if(cur.x == tp->sx && cur.y == tp->sy)
411     {
412       drawTelePath( screen, tp, 1 );
413     }
414   }
415   //Draw all switchpaths
416   drawAllTelePaths(screen, pf.levelInfo->switchList);
417 
418   if( (getInpPointerState()->timeSinceMoved < POINTER_SHOW_TIMEOUT) && (changed > 0) )
419   {
420     drawSprite(screen, saveBtnSprite, HSCREENW-145, HSCREENH+42 );
421   }
422 
423   //Draw brick-selection
424   if( editorState == EDITOR_BRICKS_SELECTION )
425   {
426     SDL_BlitSurface(selBrickBG , NULL, screen, &(setting()->bgPos) );
427 
428     //Draw bricks in a 6*4 grid
429     int px,py,bnum=BRICKSBEGIN;
430     static int brickSelOfX = HSCREENW - 78 + 8;
431     static int brickSelOfY = HSCREENH - 54 + 8;
432     for(py=0;py < 4; py++)
433     {
434       for(px=0; px < 6; px++)
435       {
436         if( bnum > NUMTILES )
437           break;
438         selBrickRect.x = brickSelOfX+(24*px);
439         selBrickRect.y = brickSelOfY+(24*py);
440         selBrickRect.w = selBrickRect.x+20;
441         selBrickRect.h = selBrickRect.y+20;
442 
443 
444         //We set bricks on mouseover, this way we get the description too (maybe punch a hole in the dots where the text is?)
445         if( isPointerInBox(&selBrickRect) )
446         {
447           selBrick=bnum;
448         }
449 
450         //We continue back to the main editor
451         if( isPointerClicked() )
452         {
453           resetMouseBtn();
454           editorState=EDITOR_MAIN;
455         }
456 
457         drawBrick(screen, bnum, selBrickRect.x, selBrickRect.y );
458         bnum++;
459       }
460     }
461   }
462 
463   return(STATEEDIT);
464 }
465 
466 
467