Store user data in $HOME
[kball.git] / src / mapedit.cpp
blob86abe2b7754c723ed531aad3f39256ac6a84fc8f
1 // -----------------------------------------------
2 // mapedit.cpp
3 // -----------------------------------------------
4 // Built-in map editor (totally l33t!)
5 // -----------------------------------------------
6 // By Kronoman - Copyright (c) 2003
7 // In loving memory of my father
8 // -----------------------------------------------
10 #include "mapedit.h"
11 #include "filehelp.h"
12 #include "sound.h"
13 #include "musiclvl.h"
15 CMapEditor::CMapEditor()
17 mtracer.add("CMapEditor::CMapEditor()");
19 tx = ty = 0;
20 ts = 1;
21 tl = 0;
23 dbuffer = NULL;
24 mcursor = NULL;
26 ykprint = ckprint = 0;
28 draw_grid = true;
32 CMapEditor::~CMapEditor()
34 // debug, source need here? maybe... somebody call 911
35 mtracer.add("CMapEditor::~CMapEditor()");
38 // -----------------------------------------------
39 // Helper functions
40 // -----------------------------------------------
41 void CMapEditor::kprint(char *msg)
43 textout_ex(screen, font, msg, 0, ykprint, ckprint, -1);
44 ykprint += text_height(font);
47 void CMapEditor::map_editor_help_message()
49 ykprint = 0;
50 ckprint = makecol(255,255,0);
51 scare_mouse();
52 clear_bitmap(screen);
53 kprint("Map Editor - Help");
54 kprint("-----------------");
55 kprint("");
56 kprint("");
58 ckprint = makecol(128,128,255);
59 kprint("Copyright (c) 2003, 2004, Kronoman");
60 kprint("In loving memory of my father");
62 kprint("");
63 kprint("");
65 ckprint = makecol(255,255,255);
66 kprint("Reference:");
67 kprint("----------");
68 kprint("");
69 kprint("");
70 kprint("Arrow keys: scroll the map; press SHIFT+Arrow keys for faster scroll.");
71 kprint("");
72 kprint("Left mouse button or ENTER: set pointed tile to current selected tile type.");
73 kprint("");
74 kprint("Right mouse button or DELETE: delete tile on that area.");
75 kprint("");
76 kprint("Middle mouse button or SPACE: set start position for player.");
77 kprint("");
78 kprint("Z,X or mouse wheel: select tile type to use. (Mouse cursor will reflect changes)");
79 kprint("");
80 kprint("F: find player start position.");
81 kprint("");
82 kprint("G: toggle drawing of grid.");
83 kprint("");
84 kprint("C: clear current map.");
85 kprint("");
86 kprint("P: set map properties (TIME, etc)");
87 kprint("");
88 kprint("S: save current map to a file.");
89 kprint("");
90 kprint("L: load map from a file.");
91 kprint("");
92 kprint("V: toggle 'show only current layer'");
93 kprint("");
94 kprint("D: dump tile set configuration to text file \"TILE_SET_CFG.TXT\"");
95 kprint("");
96 kprint("I: show information about current selected tile");
97 kprint("");
98 kprint("B: select background image for this level.");
99 kprint("");
100 kprint("M: select background music for this level.");
101 kprint("");
102 kprint("PLUS (+) and MINUS (-): change current layer in edition");
103 kprint("");
104 kprint("ESC: exit map editor.");
105 kprint("");
106 kprint("F1: show this help");
107 kprint("");
108 kprint("");
109 ckprint = makecol(128,128,128);
110 kprint("-- Press any key to continue --");
111 unscare_mouse();
112 clear_keybuf();
113 readkey();
116 // layer = layer to redraw, or -1 for full layers
117 // grid = true/false (draw or not the grid)
118 void CMapEditor::redraw_the_tile_map(int layer, bool grid)
120 clear_bitmap(dbuffer);
122 clear_scene(dbuffer); // clear 3D scene zbuffer
123 if (layer > -1)
125 game_map.draw_map(dbuffer, tx, ty, SCREEN_W, SCREEN_H, layer, grid); // draw it
127 else
129 for (int l=0; l < MAP_LAYERS; l++)
130 game_map.draw_map(dbuffer, tx, ty, SCREEN_W, SCREEN_H, l, grid); // draw it
132 render_scene(); // render the 3D scene to the bitmap
134 scare_mouse();
135 blit(dbuffer, screen,0,0,0,0,dbuffer->w,dbuffer->h);
136 unscare_mouse();
139 void CMapEditor::do_change_mouse_cursor()
141 // set mouse cursor on the tile to put, and also, show it
142 if (game_map.tile_set[ts].spr != NULL)
144 stretch_blit(game_map.tile_set[ts].spr, mcursor, 0,0,game_map.tile_set[ts].spr->w, game_map.tile_set[ts].spr->h,0,0, mcursor->w,mcursor->h);
146 else
148 clear_to_color(mcursor, makecol(255,0,255));
150 if (ts == 0)
151 textout_ex(mcursor, font, "CLS", 0, 0, makecol(128,128,255), -1);
152 else
153 textprintf_ex(mcursor, font, 0,0,makecol(255,0,0),-1, "%d", ts);
155 set_mouse_sprite(mcursor);
156 show_mouse(screen);
159 // -----------------------------------------------
160 // Map edit entry point
161 // -----------------------------------------------
162 void CMapEditor::start_map_editor()
164 bool dirty = false;
165 bool finish = false;
166 bool socl = false;
167 int omx=0,omy=0,old_mouse_z;
168 char str[2048];
169 str[0] = '\0';
170 usprintf(str, "levels");
172 mtracer.add("CMapEditor::start_map_editor()");
174 dbuffer = create_bitmap(SCREEN_W, SCREEN_H);
175 if (dbuffer == NULL)
176 raise_error("ERROR: unable to create doble buffer bitmap!\n");
178 mcursor = create_bitmap(32,32);
179 if (mcursor == NULL)
180 raise_error("ERROR: unable to create mouse cursor!\n");
182 clear_bitmap(screen);
183 clear_bitmap(mcursor);
184 clear_bitmap(dbuffer);
188 textout_centre_ex(screen, font, "[Map Editor]-[ Please wait... loading... ]", SCREEN_W/2, SCREEN_H/2, makecol(255,255,255), makecol(0,64,0));
190 char tmp_file_buf[2048];
191 if (game_map.load_tile_set_from_file(where_is_the_filename(tmp_file_buf,TILESET_FILE)) )
192 raise_error("ERROR! Can't load \"%s\" file!\n", TILESET_FILE); // load tileset
194 game_map.empty_the_map(); // reset the map
195 tx = 0, ty = 0; // current scroll in the map
196 ts = 1; // current tile selected from the tile set (1..255)
197 tl = 0; // current layer of map selected 0..MAP_LAYERS-1 (MAP_LAYERS in tmap.h)
199 redraw_the_tile_map(-1, draw_grid);
200 textout_centre_ex(screen, font, "[ Map editor - Press F1 for help ]", SCREEN_W/2, SCREEN_H/2, makecol(255,255,0),makecol(128,0,0));
201 do_change_mouse_cursor();
203 // go to map start point
204 tx = (game_map.sxp-(SCREEN_W/2/TMAPS_W)) * TMAPS_W;
205 ty = (game_map.syp-(SCREEN_H/2/TMAPS_H)) * TMAPS_H;
207 // main editor loop
208 while (!finish)
211 if (dirty)
213 if (socl)
214 redraw_the_tile_map(tl,draw_grid);
215 else
216 redraw_the_tile_map(-1,draw_grid);
218 dirty = false;
221 // little 'HUD'
222 textprintf_ex(screen, font, 0,0,makecol(255,255,255), makecol(0,0,0), "[%03d,%03d]-[%3d]-[L:%2d(%c)]", tx/TMAPS_W,ty/TMAPS_H,ts,tl, socl ? 'X' : ' ');
224 old_mouse_z = mouse_z;
225 while (!keypressed() && !mouse_b &&mouse_z == old_mouse_z) // wait until user action
227 if (keyboard_needs_poll())
228 poll_keyboard();
229 if (mouse_needs_poll())
230 poll_mouse();
231 //yield_timeslice(); // play nice with multitasking
233 // sub hud
234 if (omx != mouse_x/10 || omy != mouse_y/10)
236 scare_mouse();
237 textprintf_ex(screen, font, 0,text_height(font),makecol(128,255,128), makecol(0,0,0), "[%03d,%03d]", (tx+mouse_x)/TMAPS_W, (ty+mouse_y)/TMAPS_H);
238 unscare_mouse();
239 // div by 10 to get few precission
240 omx = mouse_x/10;
241 omy = mouse_y/10;
245 if (mouse_b & 1 || key[KEY_ENTER]) // click of left mouse == set the tile
247 // DEBUG : NOTICE : if the tile is a prize, it always go on tile 1, so the player can pickup it
248 game_map.set_tile_type((tx+mouse_x)/TMAPS_W, (ty+mouse_y)/TMAPS_H, (game_map.tile_set[ts].is_a_prize) ? 1 : tl, ts);
249 dirty = true;
250 while (mouse_b)
252 if (mouse_needs_poll())
253 poll_mouse();
255 ; // wait until mouse release
258 if (mouse_b & 2 || key[KEY_DEL]) // click of right mouse == clear the tile
260 game_map.set_tile_type((tx+mouse_x)/TMAPS_W, (ty+mouse_y)/TMAPS_H,tl, 0);
261 dirty = true;
262 while (mouse_b)
264 if (mouse_needs_poll())
265 poll_mouse();
267 ; // wait until mouse release
270 if (mouse_b & 4 || key[KEY_SPACE]) // change the start pos of the player
272 game_map.sxp = (tx+mouse_x)/TMAPS_W;
273 game_map.syp = (ty+mouse_y)/TMAPS_H;
274 if (game_map.sxp < 0)
275 game_map.sxp = 0;
276 if (game_map.sxp > 255)
277 game_map.sxp = 255;
278 if (game_map.syp < 0)
279 game_map.syp = 0;
280 if (game_map.syp > 255)
281 game_map.syp = 255;
282 dirty = true;
283 while (mouse_b)
285 if (mouse_needs_poll())
286 poll_mouse();
288 ; // wait until mouse release
291 // with shift, you move faster
293 if (key_shifts & KB_SHIFT_FLAG)
295 if (key[KEY_LEFT] )
297 tx -= 64;
298 dirty = true;
300 if (key[KEY_RIGHT] )
302 tx += 64;
303 dirty = true;
305 if (key[KEY_UP ] )
307 ty -= 64;
308 dirty = true;
310 if (key[KEY_DOWN] )
312 ty += 64;
313 dirty = true;
316 else
318 // without it, slower
319 if (key[KEY_LEFT] )
321 tx -= 24;
322 dirty = true;
324 if (key[KEY_RIGHT] )
326 tx += 24;
327 dirty = true;
329 if (key[KEY_UP ] )
331 ty -= 24;
332 dirty = true;
334 if (key[KEY_DOWN] )
336 ty += 24;
337 dirty = true;
341 if (key[KEY_X] || mouse_z > old_mouse_z) // change tile selected
343 ts++;
344 if (ts > 255)
345 ts = 0;
346 do_change_mouse_cursor();
349 if (key[KEY_Z] || mouse_z < old_mouse_z) // change tile selected
351 ts--;
352 if (ts < 0)
353 ts = 255;
354 do_change_mouse_cursor();
357 if (key[KEY_ESC] ) // exit editor
359 set_mouse_sprite(NULL);
360 if (alert("Exit map editor", "Are you sure?", NULL, "Yes", "No", 'y', 'n') == 1)
361 finish = true;
362 do_change_mouse_cursor();
365 if (key[KEY_I]) // show info about current tile
367 set_mouse_sprite(NULL);
368 ykprint = 0;
369 ckprint = makecol(255,255,255);
370 scare_mouse();
371 clear_bitmap(screen);
372 kprint("Info about tile");
373 kprint("");
374 kprint("");
376 if (game_map.tile_set[ts].spr != NULL)
378 draw_sprite(screen, game_map.tile_set[ts].spr, 0, ykprint );
379 ykprint += (int)(game_map.tile_set[ts].spr->w*1.1);
380 textprintf_ex(screen, font, 0, ykprint+=text_height(font), ckprint,-1, "Sprite: %s", game_map.tile_set[ts].spr_name );
382 else
383 kprint("This tile don't has sprite defined");
385 kprint("");
387 if (game_map.tile_set[ts].sound != NULL)
388 textprintf_ex(screen, font, 0, ykprint+=text_height(font), ckprint, -1, "Sound: %s", game_map.tile_set[ts].sound_name );
390 kprint("");
392 if (game_map.tile_set[ts].exit_level)
393 kprint("Is a level exit block");
395 kprint("");
397 if (game_map.tile_set[ts].solid)
399 kprint("Is a solid wall");
400 textprintf_ex(screen, font, 0, ykprint+=text_height(font), ckprint,-1, "Bounce factor: %f", game_map.tile_set[ts].bounce_factor );
403 kprint("");
405 if (game_map.tile_set[ts].is_a_prize)
407 kprint("Is a prize");
408 if (game_map.tile_set[ts].indispensable)
409 kprint("Is indispensable for grant level exit");
411 kprint("");
413 textprintf_ex(screen, font, 0, ykprint+=text_height(font), ckprint,-1, "Score: %d", game_map.tile_set[ts].score);
416 kprint("");
418 textprintf_ex(screen, font, 0, ykprint+=text_height(font), ckprint,-1, "Add factor : x:%f, y:%f, z:%f", game_map.tile_set[ts].adx, game_map.tile_set[ts].ady, game_map.tile_set[ts].adz );
420 kprint("");
422 textprintf_ex(screen, font, 0, ykprint+=text_height(font), ckprint,-1, "Multiplier factor: x:%f, y:%f, z:%f", game_map.tile_set[ts].mdx, game_map.tile_set[ts].mdy, game_map.tile_set[ts].mdz );
424 kprint("");
425 kprint("");
426 kprint("");
427 kprint("-- Press any key to continue --");
429 clear_keybuf();
430 rest(100);
431 readkey();
433 unscare_mouse();
434 do_change_mouse_cursor();
435 dirty = true;
438 if (key[KEY_B]) // select background
440 int t = 0;
442 set_mouse_sprite(NULL);
443 ykprint = 0;
444 ckprint = makecol(255,255,0);
445 scare_mouse();
447 while (t != KEY_ENTER)
449 ykprint = 0;
450 clear_bitmap(screen);
451 kprint("Select background with arrow keys, and press ENTER");
452 kprint("");
453 textprintf_ex(screen, font, 0, 32, makecol(255,255,255),-1,"Current: %d", game_map.background_index);
455 // show the current one
456 blit(background.get_background("backgr.dat", game_map.background_index), screen, 0,0,0,64,SCREEN_W, SCREEN_H);
458 t = readkey() >> 8;
459 rest(100);
460 if (t == KEY_UP || t == KEY_LEFT)
461 game_map.background_index--;
462 if (t == KEY_DOWN || t == KEY_RIGHT)
463 game_map.background_index++;
464 if (t == KEY_PGUP )
465 game_map.background_index-=10;
466 if (t == KEY_PGDN )
467 game_map.background_index+=10;
468 if (game_map.background_index < 0)
469 game_map.background_index = 255;
470 if (game_map.background_index > 255)
471 game_map.background_index = 0;
474 unscare_mouse();
475 do_change_mouse_cursor();
476 dirty = true;
479 if (key[KEY_M]) // select music
481 int t = 0;
483 set_mouse_sprite(NULL);
484 ykprint = 0;
485 ckprint = makecol(255,255,0);
486 scare_mouse();
488 while (t != KEY_ENTER)
490 ykprint = 0;
491 clear_bitmap(screen);
492 kprint("Select music with arrow keys, and press ENTER ; hit SPACE to play/stop the song");
493 kprint("");
494 textprintf_ex(screen, font, 0, 32, makecol(255,255,255),-1,"Current: %d", game_map.music_index);
496 clear_keybuf();
497 t = readkey() >> 8;
498 rest(100);
500 if (t == KEY_UP || t == KEY_LEFT)
501 game_map.music_index--;
503 if (t == KEY_DOWN || t == KEY_RIGHT)
504 game_map.music_index++;
506 if (t == KEY_PGUP )
507 game_map.music_index-=10;
509 if (t == KEY_PGDN )
510 game_map.music_index+=10;
512 if (game_map.music_index < 0)
513 game_map.music_index = 255;
515 if (game_map.music_index > 255)
516 game_map.music_index = 0;
518 if (t == KEY_SPACE)
520 // play the tune...
521 CSoundWrapper soundw; // sound system
522 CMusicLvl music_loader; // loader system for music from datafile
523 DUH *mm = music_loader.get_music("music_l.dat", game_map.music_index);
524 if (!mm)
526 clear(screen);
527 kprint("Sorry, can't play that tune ; must be non existant");
528 rest(100);
529 kprint("-- press any key --");
530 readkey();
531 rest(10);
533 else
535 clear(screen);
536 kprint("Now playing tune...");
537 kprint("-- press any key to stop--");
539 soundw.music_load(mm);
541 soundw.music_start();
543 clear_keybuf();
545 while (!keypressed())
546 soundw.music_poll();
548 clear_keybuf();
549 rest(100);
551 soundw.music_stop();
553 music_loader.free_memory();
558 unscare_mouse();
559 do_change_mouse_cursor();
560 dirty = true;
563 if (key[KEY_C] ) // clear map with C
565 set_mouse_sprite(NULL);
566 if (alert("Clear the map", "Are you sure?", NULL, "Yes", "No", 'y', 'n') == 1)
568 game_map.empty_the_map();
569 dirty = true;
571 do_change_mouse_cursor();
574 if (key[KEY_S]) // save map
576 set_mouse_sprite(NULL);
577 if (file_select_ex("Save map to file", str, NULL, 2048, (int)(SCREEN_W*0.85), (int)(SCREEN_H*0.85)))
579 if (game_map.save_map_to_file(str))
581 // failed :(
582 alert("ERROR", "File saving failed :(", NULL, "OK", NULL, 0,0);
584 else
586 // ok!
587 alert("SUCESS", "File saved! :^D", NULL, "Great!", NULL, 0,0);
590 do_change_mouse_cursor();
593 if (key[KEY_L]) // load map - THIS IS DESTRUCTIVE, IF FAILS, IT WILL CORRUPT CURRENT MAP!!!
595 set_mouse_sprite(NULL);
596 if (alert("Warning!", "If file load fails,", "your MAP will be reset to empty!", "Continue", "Cancel", 0,0) == 1)
598 if (file_select_ex("Load map from file", str, NULL, 2048, (int)(SCREEN_W*0.85), (int)(SCREEN_H*0.85)))
600 if (game_map.load_map_from_file(str))
602 // failed :(
603 alert("ERROR", "File load failed :(", "Your MAP probably has garbage, clear it with 'C' key!", "Crap!", NULL, 0,0);
605 else
607 // ok!
608 alert("SUCESS", "File loaded! :^D", NULL, "Great!", NULL, 0,0);
609 // go to map entry point
610 tx = (game_map.sxp-(SCREEN_W/2/TMAPS_W)) * TMAPS_W;
611 ty = (game_map.syp-(SCREEN_H/2/TMAPS_H)) * TMAPS_H;
613 dirty = true;
616 do_change_mouse_cursor();
619 if (key[KEY_D]) // dump tile settings to file
621 set_mouse_sprite(NULL);
622 if (alert("Dump tile settings", "Dump tile set settings to file?","File will be TILE_SET_CFG.TXT", "Do it", "Cancel", 0,0) == 1)
624 scare_mouse();
625 textout_centre_ex(screen, font, "Please wait... writing file...", SCREEN_W/2, SCREEN_H/2, makecol(255,255,255), makecol(128,0,0));
626 unscare_mouse();
627 game_map.save_tile_set_config_to_file("TILE_SET_CFG.TXT");
628 alert("SUCESS", "File saved as TILE_SET_CFG.TXT", NULL, "OK", NULL, 0,0);
629 dirty = true;
632 do_change_mouse_cursor();
635 if (key[KEY_G] ) // toggle grid
637 draw_grid = !draw_grid;
638 dirty = true;
641 if (key[KEY_F] ) // goto player's start point
643 tx = (game_map.sxp-(SCREEN_W/2/TMAPS_W)) * TMAPS_W;
644 ty = (game_map.syp-(SCREEN_H/2/TMAPS_H)) * TMAPS_H;
645 dirty = true;
648 if (key[KEY_F1])
650 set_mouse_sprite(NULL);
651 map_editor_help_message();
652 do_change_mouse_cursor();
653 dirty = true;
656 if (key[KEY_V]) // toggle 'show only current layer'
658 socl = !socl;
659 dirty = true;
663 if (key[KEY_PLUS_PAD] || key[KEY_EQUALS]) // keypad plus + = change edit layer
665 tl++;
666 dirty = true;
669 if (key[KEY_MINUS_PAD] || key[KEY_MINUS]) // keypad plus - = change edit layer
671 tl--;
672 dirty = true;
676 if (key[KEY_P]) // set map properties (time, etc)
678 char st1[255], st2[255]; // d_edit_buffers
679 int it1 = 0, it2 = 0;
680 // dialog to set time
681 DIALOG set_time_dlg[] =
683 /* (proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */
684 { d_box_proc, 0, 0, 216, 104, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL },
685 { d_text_proc, 8, 8, 144, 8, 0, 0, 0, 0, 0, 0, (void *)"Set map properties", NULL, NULL },
686 { d_text_proc, 8, 24, 40, 16, 0, 0, 0, 0, 0, 0, (void *)"Time to complete the map:", NULL, NULL },
687 { d_text_proc, 8, 40, 56, 16, 0, 0, 0, 0, 0, 0, (void *)"Minutes:", NULL, NULL },
688 { d_text_proc, 8, 56, 56, 16, 0, 0, 0, 0, 0, 0, (void *)"Seconds:", NULL, NULL },
689 { d_edit_proc, 88, 40, 56, 16, 0, 0, 0, 0, 3, 0, (void *)"edit", NULL, NULL },
690 { d_edit_proc, 88, 56, 56, 16, 0, 0, 0, 0, 2, 0, (void *)"edit", NULL, NULL },
691 { d_button_proc, 8, 72, 200, 24, 0, 0, 0, D_EXIT, 0, 0, (void *)"OK", NULL, NULL },
692 { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }
695 // set initial data
696 usprintf(st1, "%d", game_map.time_m);
697 usprintf(st2, "%d", game_map.time_s);
698 set_time_dlg[5].dp = st1;
699 set_time_dlg[6].dp = st2;
701 // do input dialog
702 set_mouse_sprite(NULL);
703 set_dialog_color(set_time_dlg, makecol(0,0,0), makecol(255,255,255));
704 centre_dialog(set_time_dlg);
705 popup_dialog(set_time_dlg, -1);
707 // validate new data
708 it1 = atoi(st1);
709 it2 = atoi(st2);
710 if (it1 >= 0 && it2 >= 0 && it1 < 256 && it2 <= 60)
712 // set new data
713 game_map.time_m = it1;
714 game_map.time_s = it2;
715 usprintf(st1, "Time of map set to %d:%d", game_map.time_m, game_map.time_s);
716 alert("Done", st1, NULL, "OK", NULL, 0,0);
718 else
720 alert("ERROR", "Range of input invalid", "Valid: 0 <= minutes <= 255, and 0 <= seconds <= 60", "OK", NULL, 0,0);
723 do_change_mouse_cursor();
724 dirty = true;
728 if (tl < 0)
729 tl = MAP_LAYERS-1;
730 if (tl > MAP_LAYERS-1)
731 tl = 0;
733 if (tx < 0)
734 tx = 0;
735 if (ty < 0)
736 ty = 0;
737 if (tx > TMAP_SIZE*TMAPS_W-SCREEN_W)
738 tx = TMAP_SIZE * TMAPS_W-SCREEN_W;
739 if (ty > TMAP_SIZE*TMAPS_H-SCREEN_H)
740 ty = TMAP_SIZE * TMAPS_H-SCREEN_H;
742 clear_keybuf();
745 // free memory
746 destroy_bitmap(dbuffer);
747 destroy_bitmap(mcursor);
748 game_map.free_memory();
749 set_mouse_sprite(NULL);