Editor saves a correct, optimized stage.
[cantaveria.git] / edit.c
blob8bd7db32b8f7554825a8c5da2846791b99cbd168
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <limits.h>
5 #include <SDL/SDL.h>
9 #include <list.h>
10 #include <console.h>
11 #include <stage.h>
12 #include <loader.h>
13 #include <kernel.h>
14 #include <graphics.h>
15 #include <console.h>
17 struct edit {
18 int x;
19 int y;
20 char shape;
21 unsigned char fg;
22 unsigned char bg;
23 struct edit* next;
26 struct tile {
27 char shape;
28 unsigned char fg;
29 unsigned char bg;
32 struct undo_step {
33 struct tile* undo; /* writes to undo */
34 struct tile* redo; /* writes to redo */
35 struct edit* next;
36 struct edit* prev;
41 /* application state variables */
42 int toggle_background = 1;
43 int toggle_bgtiles = 1;
44 int toggle_fgtiles = 1;
45 int toggle_shapes = 0;
47 int origin_x = 0;
48 int origin_y = 0;
49 static int camera_x = 0;
50 static int camera_y = 0;
52 char my_file[256] = "";
53 char my_file_old[256] = "";
54 char bgimage_file[256] = "";
55 char fgtiles_file[256] = "";
56 char bgtiles_file[256] = "";
57 int bgimage = 0;
58 int fgtiles = 0;
59 int bgtiles = 0;
61 int select_enable = 0;
62 int select_x = 0;
63 int select_y = 0;
64 int select_w = 0;
65 int select_h = 0;
67 struct tile* raw_tiles = NULL;
68 int raw_w = 20;
69 int raw_h = 15;
71 int show_favorites = 0;
72 int bg_favorites[7] = {0,0,0,0,0,0,0};
73 int fg_favorites[7] = {0,0,0,0,0,0,0};
74 int brush_tile = 1;
75 int brush_layer = 1;
76 int brush_enable = 0;
78 int panic_flag = 0;
80 int dialog_flag = 0;
81 int brush_dialog = 0;
82 int background_dialog = 0;
83 int tileset_dialog = 0;
84 int quit_dialog = 0;
85 int save_as_dialog = 0;
86 int open_dialog = 0;
87 int confirm_save_dialog = 0;
91 char save_as_buf[256] = "";
92 int save_as_ptr = 0;
93 /* *** */
96 /* base access methods */
97 struct tile* initialize_raw(int w, int h){
98 int i;
99 int j;
100 struct tile blank = {'0', 0, 0};
101 struct tile* ptr = malloc(w*h*sizeof(struct tile));
102 for(j=0; j<h; j++){
103 for(i=0; i<w; i++){
104 *(ptr + i + j*w) = blank;
108 return ptr;
111 void unload_raw(){
112 free(raw_tiles);
115 struct tile raw_read(int x, int y){
116 struct tile blank = {'0', 0, 0};
117 if(x < 0 || y < 0 || x >= raw_w || y >= raw_h){
118 return blank;
120 else{
121 return *(raw_tiles + x + raw_w*y);
125 void expand_raw(){
126 int new_w = raw_w * 3;
127 int new_h = raw_h * 3;
128 struct tile* new_tiles = initialize_raw(new_w, new_h);
129 struct tile* ptr;
130 struct tile t;
131 int i;
132 int j;
134 for(j=raw_h; j<2*raw_h; j++){
135 for(i=raw_w; i<2*raw_w; i++){
136 ptr = new_tiles + i + j*(3*raw_w);
137 t = raw_read(i-raw_w, j-raw_h);
138 *ptr = t;
142 origin_x += raw_w;
143 origin_y += raw_h;
144 raw_w *= 3;
145 raw_h *= 3;
146 free(raw_tiles);
147 raw_tiles = new_tiles;
150 int out_of_bounds(int x, int y){
151 if(x < 0 || y < 0 || x >= raw_w || y >= raw_h)
152 return 1;
153 else
154 return 0;
157 void detect_size(int* w, int* h){
158 //see the minimum size necessary for the area
159 //used for saving
162 void raw_write(int x, int y, int layer, int value){
163 while(out_of_bounds(x, y)){
164 printf("expanding\n");
165 expand_raw();
166 x += raw_w / 3;
167 y += raw_h / 3;
170 //expand if outside
171 //shift x y by expand shift
172 //shift x and y by origin
173 //do the write
174 struct tile* ptr = raw_tiles + x + raw_w*y;
175 if(layer == 1){
176 ptr->bg = value;
178 else if(layer == 2){
179 ptr->fg = value;
181 else if(layer == 3){
182 ptr->shape = value;
186 void draw_background(){
187 int W = gfx_width(bgimage);
188 int H = gfx_height(bgimage);
189 draw_gfx_raw(bgimage, 0, 0, 0, 0, W, H);
192 void draw_raw(){
193 int x0 = camera_x + origin_x;
194 int y0 = camera_y + origin_y;
195 int i;
196 int j;
197 int x;
198 int y;
199 struct tile t;
200 int gx;
201 int gy;
203 if(toggle_background)
204 draw_background();
206 for(j=0; j<(15+5); j++){
207 y = y0 + j;
208 for(i=0; i<(20+8); i++){
209 x = x0 + i;
210 t = raw_read(x, y);
211 gy = 16*(t.bg / 16);
212 gx = 16*(t.bg % 16);
213 if(toggle_bgtiles)
214 draw_gfx_raw(bgtiles, i*16, j*16, gx, gy, 16, 16);
215 gy = 16*(t.fg / 16);
216 gx = 16*(t.fg % 16);
217 if(toggle_fgtiles)
218 draw_gfx_raw(fgtiles, i*16, j*16, gx, gy, 16, 16);
224 /* determine an optimal size for the stage */
225 void raw_optimize(int* ox, int* oy, int* ow, int* oh){
226 int i;
227 int xmax = 0;
228 int xmin = INT_MAX;
229 int ymax = 0;
230 int ymin = INT_MAX;
231 int x, y, fg, bg;
232 char shape;
234 for(i=0; i<(raw_w*raw_h); i++){
235 x = i % raw_w;
236 y = i / raw_h;
237 fg = raw_tiles[i].fg;
238 bg = raw_tiles[i].bg;
239 shape = raw_tiles[i].shape;
240 if((fg != 0 || bg != 0 || shape != '0')){
241 if(x > xmax) xmax = x;
242 if(x < xmin) xmin = x;
243 if(y > ymax) ymax = y;
244 if(y < ymin) ymin = y;
248 if(ymax - ymin < 15) *oh = 15;
249 else *oh = (ymax - ymin);
251 if(xmax - xmin < 20) *ow = 20;
252 else *ow = (xmax - xmin);
254 *ox = xmin;
255 *oy = ymin;
258 void raw_save(char* path){
259 /* save current stage to a stage file */
260 /* overwrites if already exists, no confirmation */
261 int x, y, bg, fg;
262 char shape;
263 int i;
264 struct tile* ptr = raw_tiles;
265 int opt_ox, opt_oy;
266 int opt_x, opt_y, opt_w, opt_h;
269 FILE* f = fopen(path, "w");
270 if(f == NULL){
271 console_printf("error saving file");
272 return;
275 raw_optimize(&opt_x, &opt_y, &opt_w, &opt_h);
277 fprintf(f, "%d %d %d %d\n", opt_w, opt_h, origin_x-opt_x, origin_y-opt_y);
278 fprintf(f, "%s\n", bgimage_file);
279 fprintf(f, "%s\n", fgtiles_file);
280 fprintf(f, "%s\n", bgtiles_file);
282 for(i=0; i<(raw_w*raw_h); i++){
283 x = (i % raw_w) - origin_x;
284 y = (i / raw_w) - origin_y;
285 fg = ptr[i].fg;
286 bg = ptr[i].bg;
287 shape = ptr[i].shape;
289 if(fg != 0 || bg != 0 || shape != '0'){
290 fprintf(f, "%d %d %d %d %c\n", x, y, fg, bg, shape);
295 fclose(f);
299 /* *** */
303 void update_window_name(){
304 if(my_file[0] == 0){
305 SDL_WM_SetCaption("unnamed", NULL);
307 else{
308 SDL_WM_SetCaption(my_file, NULL);
314 struct undo_step* undo_stack;
315 struct undo_step* undo_ptr;
317 /* undo operations */
318 void undo(){
319 //do the undo_ptr->undo operations
320 //move undo_ptr down one
323 void redo(){
324 //if at top of stack, do nothing
326 //do the undo_ptr->redo operations
327 //move undo_ptr up one
330 void undo_record(struct edit* edits){
331 //eliminate undo_ptr->redo and all previous edit structs
332 //change the undo_stack
334 //store the edits in undo_ptr->redo
335 //calculate the undo operation XXX
336 //push a new edit struct
337 //move undo_ptr
338 //store the undo operation in undo_ptr->undo
340 /* *** */
345 /* medium level editting commands */
346 void write_one_tile(int x, int y, int layer, int value){
347 //write x y layer value
350 void write_many_tiles(struct edit* edits){
351 //for each tiles
352 //write one tile
355 void edit_one_tile(int x, int y, int layer, int value){
356 //write_one_tile
357 //create a tile struct
358 //call edit on it
361 void edit_many_tiles(struct edit* edits){
362 //write many tiles
363 //edit(tiles)
366 void add_to_clipboard(struct edit* edits){
367 //makes a tile struct and appends to clipboard
370 void clear_clipboard(){
371 //clear the clipboard
374 struct tile* read_tile(int x, int y){
375 //make a tile struct
377 /* *** */
381 /* high level gui commands */
382 void select_brush(int layer, int value){
386 void start_box(int x, int y){
390 void move_box(int x, int y){
394 void stop_box(){
398 void clear_box(){
402 void append_to_box(int x, int y){
406 struct tile* box_select(){
411 void move_paste(int x, int y){
415 void cancel_paste(){
419 void do_paste(){
424 void redraw_all(){
425 clear_video();
427 draw_raw();
429 //gui indicators
430 if(select_enable){
431 //draw green box
434 //dialogs
435 if(save_as_dialog){
436 console_printf("save as: %s", save_as_buf);
440 console_draw();
441 console_clear();
443 update_video();
446 char* onoff(int b){
447 if(b) return "on";
448 else return "off";
452 /* *** */
455 /* utility */
456 void select_bgfile(char* path){
457 strcpy(bgimage_file, path);
460 /* *** */
466 /* dialog input handlers */
467 void confirm_save_press(SDLKey key, Uint16 c){
468 if(c == 'y' || c == 'Y'){
469 raw_save(my_file);
470 update_window_name();
471 console_printf("You're the boss. %s was overwritten", my_file);
473 else{
474 strcpy(my_file, my_file_old); /* ! */
475 console_printf("Operation cancelled");
478 confirm_save_dialog = 0;
481 void save_as_press(SDLKey key, Uint16 c){
482 FILE* f;
483 if(c == 0){
485 else if(c == '\r'){
486 if(save_as_buf[0] == 0){
487 console_printf("No name? Nevermind then.");
489 else{
490 strcpy(my_file_old, my_file); /* ! */
491 strcpy(my_file, save_as_buf); /* ! */
493 /* see if file exists */
494 f = fopen(my_file, "r");
495 if(f != NULL){
496 console_printf("ALERT: really overwrite %s? (Y/N)", my_file);
497 confirm_save_dialog = 1;
498 fclose(f);
500 else{
501 update_window_name();
502 console_printf("%s saved", my_file);
503 raw_save(my_file);
506 save_as_buf[0] = 0;
507 save_as_ptr = 0;
508 save_as_dialog = 0;
510 else if(c == 0x1b){
511 save_as_buf[0] = 0;
512 save_as_ptr = 0;
513 save_as_dialog = 0;
515 else if(c == '\b'){
516 if(save_as_ptr > 0){
517 save_as_ptr--;
518 save_as_buf[save_as_ptr] = 0;
521 else{
522 if(save_as_ptr < 255){
523 save_as_buf[save_as_ptr] = c;
524 save_as_ptr++;
525 save_as_buf[save_as_ptr] = 0;
535 void keydown(SDLKey key, SDLMod mod, Uint16 c){
537 if(save_as_dialog){
538 save_as_press(key, c);
539 redraw_all();
540 return;
543 if(confirm_save_dialog){
544 confirm_save_press(key, c);
545 redraw_all();
546 return;
549 switch(key){
550 case SDLK_u:
551 undo();
552 console_printf("undo"); break;
553 case SDLK_r:
554 redo();
555 console_printf("redo"); break;
556 case SDLK_1:
557 toggle_background = !toggle_background;
558 console_printf("background %s", onoff(toggle_background));
559 break;
560 case SDLK_2:
561 toggle_bgtiles = !toggle_bgtiles;
562 console_printf("bg tiles %s", onoff(toggle_bgtiles));
563 break;
564 case SDLK_3:
565 toggle_fgtiles = !toggle_fgtiles;
566 console_printf("fg tiles %s", onoff(toggle_fgtiles));
567 break;
568 case SDLK_4:
569 toggle_shapes = !toggle_shapes;
570 console_printf("shapes %s", onoff(toggle_shapes));
571 break;
572 case SDLK_s:
573 if(mod & (KMOD_LCTRL|KMOD_RCTRL)){
574 if(my_file[0] == 0){
575 save_as_dialog = 1;
577 else{
578 raw_save(my_file);
579 console_printf("saved %s", my_file);
582 break;
583 case SDLK_w:
584 save_as_dialog = 1;
585 break;
586 case SDLK_o:
587 console_printf("open...");
588 break;
589 case SDLK_b:
590 console_printf("change background...");
591 break;
592 case SDLK_q:
593 if(dialog_flag == 0){
594 dialog_flag = 1;
595 quit_dialog = 1;
597 break;
598 case SDLK_ESCAPE:
599 panic_flag = 1;
600 if(dialog_flag == 0){
601 dialog_flag = 1;
602 quit_dialog = 1;
604 else{
605 dialog_flag = 0;
607 break;
608 case SDLK_RETURN:
609 console_printf("OK");
610 break;
611 case SDLK_y:
612 console_printf("yes");
613 break;
614 case SDLK_n:
615 console_printf("no");
616 break;
617 case SDLK_h:
618 case SDLK_F1:
619 case SDLK_SLASH:
620 console_printf("help...");
621 break;
622 case SDLK_F2:
623 console_printf("pick fg tileset...");
624 break;
625 case SDLK_F3:
626 console_printf("pick bg tileset...");
627 break;
628 case SDLK_LEFT: camera_x--; break;
629 case SDLK_RIGHT: camera_x++; break;
630 case SDLK_UP: camera_y--; break;
631 case SDLK_DOWN: camera_y++; break;
633 /* temporary controls */
634 case SDLK_9: brush_tile--; brush_tile %= 256; break;
635 case SDLK_0: brush_tile++; brush_tile %= 256; break;
636 case SDLK_8: brush_layer = 2; break;
637 case SDLK_7: brush_layer = 1; break;
640 redraw_all();
643 U - undo
644 R - redo
645 1 - toggle layer 1
646 2 - toggle layer 2
647 3 - toggle layer 3
648 4 - toggle layer 4
649 ctrl S - save
650 W - save as
651 O - open stage
652 B - change background
653 Q - quit
654 ESC - quit / cancel
655 ENTER - yes / OK
656 Y - yes
657 N - no
658 H - help
659 ? - help
660 F1 - help
661 F2 - change fg tileset
662 F3 - change bg tileset
663 ARROW KEYS - scroll
667 void translate_pointer(int mx, int my, int *x, int *y){
668 int a, b;
669 map_pixel(mx, my, &a, &b);
670 *x = a/16 + camera_x + origin_x;
671 *y = b/16 + camera_y + origin_y;
675 void mousedown(int mx, int my, int button){
677 hold LMB - draw single tiles / deselect
678 shift LMB - start box select
679 ctrl LMB - append single tiles to selection
680 RMB - display tilesets
681 hold MMB - choose where to paste (release to execute, esc to cancel)
683 SDLMod mod = SDL_GetModState();
685 int x, y;
686 translate_pointer(mx, my, &x, &y);
688 if(brush_dialog){
689 //change brush, maybe
690 brush_dialog = 0;
691 return;
697 if(button == 1){
698 raw_write(x, y, brush_layer, brush_tile);
699 brush_enable = 1;
700 redraw_all();
702 else if(button == 3){
703 brush_dialog = 1;
704 redraw_all();
709 void mouseup(int x, int y, int button){
711 LMB - stop drawing
712 shift LMB - append box to selection
713 MMB - execute paste
716 if(button == 1){
717 brush_enable = 0;
722 void mousemove(int mx, int my, int xrel, int yrel){
724 redraw cursor
725 redraw box select
726 redraw paste box
728 int x, y;
729 translate_pointer(mx, my, &x, &y);
731 if(brush_enable){
732 raw_write(x, y, brush_layer, brush_tile);
733 redraw_all();
737 int check_events(){
738 SDL_Event e;
740 if(SDL_WaitEvent(&e) == 0){
741 printf("SDL_WaitEvent encountered an error (%s)\n", SDL_GetError());
742 return 1;
745 switch(e.type){
746 case SDL_QUIT: return 1;
747 case SDL_KEYDOWN: keydown(e.key.keysym.sym, e.key.keysym.mod, e.key.keysym.unicode); return 0;
748 case SDL_MOUSEMOTION:
749 mousemove(e.motion.x, e.motion.y, e.motion.xrel, e.motion.yrel);
750 return 0;
751 case SDL_MOUSEBUTTONDOWN: mousedown(e.button.x, e.button.y, e.button.button); return 0;
752 case SDL_MOUSEBUTTONUP: mouseup(e.button.x, e.button.y, e.button.button); return 0;
753 default: return 0;
759 void terminate(){
760 loader_quit();
761 video_quit();
764 int main(int argc, char* argv[]){
765 video_init(argc, argv);
766 loader_init();
767 graphics_init();
769 raw_tiles = initialize_raw(raw_w, raw_h);
771 update_window_name();
773 loader_data_mode(0);
774 bgimage = load_bitmap("azone/gfx/background.tga");
775 // loader_data_mode(0);
776 // fgtiles = load_bitmap("barf.tga");
777 // loader_data_mode(1);
778 fgtiles = load_bitmap("azone/gfx/barf.tga");
779 bgtiles = load_bitmap("azone/gfx/test.tga");
781 raw_write(2, 2, 1, 3);
783 redraw_all();
785 SDL_ShowCursor(1);
786 SDL_EnableUNICODE(1);
787 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
789 atexit(terminate);
791 while(check_events() == 0 && panic_flag == 0);
793 return 0;