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