dunger now saves player start position and cell size
[vrlugburz] / tools / dunger / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <GL/glut.h>
5 #include <utk/cubertk.h>
6 #include <drawtext.h>
7 #include "level.h"
8 #include "lview.h"
9 #include "app.h"
10
11 static int init(void);
12 static void cleanup(void);
13 static void display(void);
14 static void reshape(int x, int y);
15 static void keyb(unsigned char key, int x, int y);
16 static void keyup(unsigned char key, int x, int y);
17 static void mouse(int bn, int st, int x, int y);
18 static void motion(int x, int y);
19
20 static void cb_new(utk_event *ev, void *data);
21 static void cb_new_ok(utk_event *ev, void *data);
22 static void cb_open(utk_event *ev, void *data);
23 static void cb_open_ok(utk_event *ev, void *data);
24 static void cb_save(utk_event *ev, void *data);
25 static void cb_save_ok(utk_event *ev, void *data);
26
27 static void cb_cancel(utk_event *ev, void *data);
28
29 static void cb_toolselect(utk_event *ev, void *data);
30
31 static int parse_args(int argc, char **argv);
32
33
34 static void ucolor(int r, int g, int b, int a);
35 static void uclip(int x1, int y1, int x2, int y2);
36 static void uimage(int x, int y, const void *pix, int xsz, int ysz);
37 static void urect(int x1, int y1, int x2, int y2);
38 static void uline(int x1, int y1, int x2, int y2, int width);
39 static void utext(int x, int y, const char *txt, int sz);
40 static int utextspacing(void);
41 static int utextwidth(const char *txt, int sz);
42
43
44 int win_width, win_height;
45 int view_width, view_height;
46 float view_panx, view_pany, view_zoom = 1.0f;
47
48 static int bnstate[8];
49 int mousex, mousey, clickx, clicky;
50
51 static float uiscale = 1.0f;
52 #define UISPLIT 150
53 static int splitx;
54
55 #define FONTSZ  16
56 static struct dtx_font *uifont;
57 static utk_widget *uiroot, *uiwin_new;
58 static utk_widget *uigrab;
59 static utk_widget *cbox_newsz;
60
61 static struct level lvl;
62
63 static const char *opt_fname;
64
65
66 int main(int argc, char **argv)
67 {
68         glutInit(&argc, argv);
69
70         if(parse_args(argc, argv) == -1) {
71                 return 1;
72         }
73
74         glutInitWindowSize(1280, 800);
75         glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_MULTISAMPLE);
76         glutCreateWindow("dunger");
77
78         win_width = glutGet(GLUT_WINDOW_WIDTH);
79         win_height = glutGet(GLUT_WINDOW_HEIGHT);
80
81         glutDisplayFunc(display);
82         glutReshapeFunc(reshape);
83         glutKeyboardFunc(keyb);
84         glutKeyboardUpFunc(keyup);
85         glutMouseFunc(mouse);
86         glutMotionFunc(motion);
87         glutPassiveMotionFunc(motion);
88
89         if(init() == -1) {
90                 return 1;
91         }
92         atexit(cleanup);
93
94         glutMainLoop();
95         return 0;
96 }
97
98
99 static int init(void)
100 {
101         int pad;
102         utk_widget *win, *hbox, *vbox;
103
104         glEnable(GL_MULTISAMPLE);
105
106         glClearColor(0.15, 0.15, 0.15, 1);
107
108         if(!(uifont = dtx_open_font("uifont.ttf", 0))) {
109                 fprintf(stderr, "failed to open uifont.ttf\n");
110                 return -1;
111         }
112         dtx_prepare_range(uifont, FONTSZ, ' ', 'z');
113         dtx_use_font(uifont, FONTSZ);
114
115         if(!(uiroot = utk_init(win_width / uiscale, win_height / uiscale))) {
116                 fprintf(stderr, "failed to initialized ubertk\n");
117                 return -1;
118         }
119         utk_set_color_func(ucolor);
120         utk_set_clip_func(uclip);
121         utk_set_image_func(uimage);
122         utk_set_rect_func(urect);
123         utk_set_line_func(uline);
124         utk_set_text_func(utext);
125         utk_set_text_spacing_func(utextspacing);
126         utk_set_text_width_func(utextwidth);
127
128         win = utk_vbox(uiroot, 0, UTK_DEF_SPACING);
129         utk_set_pos(win, 15, 15);
130         utk_button(win, "New", 0, 0, cb_new, 0);
131         utk_button(win, "Open ...", 0, 0, cb_open, 0);
132         utk_button(win, "Save ...", 0, 0, cb_save, 0);
133         utk_label(win, "-- Tools --");
134         utk_radiobox(win, "Draw", 1, cb_toolselect, (void*)TOOL_DRAW);
135         utk_radiobox(win, "Player start", 0, cb_toolselect, (void*)TOOL_PSTART);
136
137         uiwin_new = utk_window(uiroot, (win_width - 220) / 2, (win_height - 150) / 2,
138                         220, 150, "New level");
139         vbox = utk_vbox(uiwin_new, UTK_DEF_PADDING, UTK_DEF_SPACING);
140         {
141                 const char *items[] = {"small (16x16)", "medium (24x24)", "large (32x32)"};
142                 cbox_newsz = utk_combobox_items(vbox, items, sizeof items / sizeof *items, 0, 0);
143                 utk_select(cbox_newsz, 2);
144         }
145         hbox = utk_hbox(vbox, UTK_DEF_PADDING, UTK_DEF_SPACING);
146         utk_button(hbox, "OK", 0, 0, cb_new_ok, 0);
147         utk_button(hbox, "Cancel", 0, 0, cb_cancel, uiwin_new);
148         pad = utk_get_padding(uiwin_new);
149         utk_set_size(uiwin_new, utk_get_width(vbox) + pad * 2.0f, utk_get_height(vbox) + pad * 2.0f);
150
151
152         if(opt_fname) {
153                 if(load_level(&lvl, opt_fname) == -1) {
154                         fprintf(stderr, "failed to load level: %s\n", opt_fname);
155                         return -1;
156                 }
157         } else {
158                 if(init_level(&lvl, 32, 32) == -1) {
159                         fprintf(stderr, "failed to create level\n");
160                         return -1;
161                 }
162         }
163         if(init_lview(&lvl) == -1) {
164                 return -1;
165         }
166
167         splitx = UISPLIT * uiscale;
168         view_width = win_width - splitx;
169         view_height = win_height;
170
171         return 0;
172 }
173
174 static void cleanup(void)
175 {
176         destroy_lview();
177         destroy_level(&lvl);
178         dtx_close_font(uifont);
179         utk_close(uiroot);
180 }
181
182 static void display(void)
183 {
184         glClear(GL_COLOR_BUFFER_BIT);
185
186         glMatrixMode(GL_MODELVIEW);
187         glLoadIdentity();
188
189         /* draw view */
190
191         glMatrixMode(GL_PROJECTION);
192         glLoadIdentity();
193         glOrtho(0, view_width, 0, view_height, -1, 1);
194         glViewport(splitx, 0, view_width, view_height);
195
196         glBegin(GL_QUADS);
197         glColor3f(0.1, 0.1, 0.1);
198         glVertex2f(0, 0);
199         glVertex2f(view_width, 0);
200         glVertex2f(view_width, view_height);
201         glVertex2f(0, view_height);
202         glEnd();
203
204         draw_lview();
205
206         /* draw UI */
207
208         glMatrixMode(GL_PROJECTION);
209         glLoadIdentity();
210         glOrtho(0, win_width, win_height, 0, -1, 1);
211         glViewport(0, 0, win_width, win_height);
212
213         glBegin(GL_QUADS);
214         glColor3f(0.25, 0.25, 0.25);
215         glVertex2f(0, 0);
216         glVertex2f(splitx, 0);
217         glVertex2f(splitx, win_height);
218         glVertex2f(0, win_height);
219         glEnd();
220
221         glEnable(GL_BLEND);
222         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
223         utk_draw(uiroot);
224         glDisable(GL_BLEND);
225
226
227         glutSwapBuffers();
228 }
229
230 static void reshape(int x, int y)
231 {
232         win_width = x;
233         win_height = y;
234
235         if(uiroot) {
236                 utk_set_size(uiroot, x / uiscale, y / uiscale);
237         }
238
239         lview_viewport(splitx, 0, x - splitx, y);
240 }
241
242 static void keyb(unsigned char key, int x, int y)
243 {
244         switch(key) {
245         case 27:
246                 if(uigrab) {
247                         if(utk_is_dialog(uigrab)) {
248                                 utk_destroy_window(uigrab);
249                         } else {
250                                 utk_hide(uigrab);
251                         }
252                         uigrab = 0;
253                 } else {
254                         exit(0);
255                 }
256                 return;
257
258         default:
259                 break;
260         }
261
262         utk_keyboard_event(key, 1);
263         glutPostRedisplay();
264 }
265
266 static void keyup(unsigned char key, int x, int y)
267 {
268         utk_keyboard_event(key, 0);
269         glutPostRedisplay();
270 }
271
272 static void mouse(int bn, int st, int x, int y)
273 {
274         int bidx = bn - GLUT_LEFT_BUTTON;
275         int press = st == GLUT_DOWN;
276
277         bnstate[bidx] = press;
278         mousex = x;
279         mousey = y;
280
281         if(bn <= 2) {
282                 if(press) {
283                         clickx = x;
284                         clicky = y;
285                 } else {
286                         clickx = clicky = -1;
287                 }
288         } else if(bn == 3) {
289                 if(press) zoom_lview(0.1);
290         } else if(bn == 4) {
291                 if(press) zoom_lview(-0.1);
292         }
293
294         if(!uigrab) {
295                 lview_mbutton(bidx, press, x, y);
296         }
297
298         utk_mbutton_event(bidx, press, x / uiscale, y / uiscale);
299         glutPostRedisplay();
300 }
301
302 static void motion(int x, int y)
303 {
304         int dx, dy;
305         dx = x - mousex;
306         dy = y - mousey;
307         mousex = x;
308         mousey = y;
309
310         if(clickx >= splitx) {
311                 if(bnstate[1]) {
312                         pan_lview(-dx, dy);
313                 }
314         }
315
316         if(!uigrab) {
317                 lview_mouse(x, y);
318         }
319
320         utk_mmotion_event(x / uiscale, y / uiscale);
321         glutPostRedisplay();
322 }
323
324 static void cb_new(utk_event *ev, void *data)
325 {
326         utk_show(uiwin_new);
327         uigrab = uiwin_new;
328 }
329
330 static void cb_new_ok(utk_event *ev, void *data)
331 {
332         static int levsz[] = {16, 24, 32};
333         int sz;
334         struct level newlvl;
335
336         sz = levsz[utk_get_selected(cbox_newsz)];
337
338         if(init_level(&newlvl, sz, sz) == -1) {
339                 utk_message_dialog("failed to create new level", UTK_MSG_TYPE_ERROR,
340                                 UTK_MSG_BN_OK, cb_cancel, 0);
341                 return;
342         }
343
344         destroy_level(&lvl);
345         destroy_lview();
346
347         lvl = newlvl;
348         init_lview(&lvl);
349
350         utk_hide(uiwin_new);
351         uigrab = 0;
352 }
353
354 static void cb_open(utk_event *ev, void *data)
355 {
356         uigrab = utk_file_dialog(UTK_FILE_DIALOG_OPEN, 0, "Level file (*.lvl) [.lvl]", 0, cb_open_ok, 0);
357 }
358
359 static void cb_open_ok(utk_event *ev, void *data)
360 {
361         char *errmsg = 0;
362         const char *path;
363
364         utk_widget *dlg = utk_event_widget(ev);
365         path = utk_file_dialog_path(dlg);
366         printf("selected: %s\n", path);
367         if(load_level(&lvl, path) == -1) {
368                 errmsg = alloca(strlen(path) + 32);
369                 sprintf(errmsg, "Failed to load level: %s", path);
370         }
371         utk_destroy_window(dlg);
372         if(uigrab == dlg) uigrab = 0;
373
374         if(errmsg) {
375                 uigrab = utk_message_dialog(errmsg, UTK_MSG_TYPE_ERROR, UTK_MSG_BN_OK, cb_cancel, 0);
376         }
377 }
378
379 static void cb_save(utk_event *ev, void *data)
380 {
381         uigrab = utk_file_dialog(UTK_FILE_DIALOG_SAVE, 0, "Level file (*.lvl) [.lvl]", 0, cb_save_ok, 0);
382 }
383
384 static void cb_save_ok(utk_event *ev, void *data)
385 {
386         char *errmsg = 0;
387         const char *path;
388
389         utk_widget *dlg = utk_event_widget(ev);
390         path = utk_file_dialog_path(dlg);
391         printf("selected: %s\n", path);
392         if(save_level(&lvl, path) == -1) {
393                 errmsg = alloca(strlen(path) + 32);
394                 sprintf(errmsg, "Failed to save level file: %s", path);
395         }
396         utk_destroy_window(dlg);
397         if(uigrab == dlg) uigrab = 0;
398
399         if(errmsg) {
400                 uigrab = utk_message_dialog(errmsg, UTK_MSG_TYPE_ERROR, UTK_MSG_BN_OK, cb_cancel, 0);
401         }
402 }
403
404 static void cb_cancel(utk_event *ev, void *data)
405 {
406         if(!data) data = utk_get_window(utk_event_widget(ev));
407
408         if(utk_is_dialog(data)) {
409                 utk_destroy_window(data);
410         } else {
411                 utk_hide(data);
412         }
413         uigrab = 0;
414 }
415
416 static void cb_toolselect(utk_event *ev, void *data)
417 {
418         utk_widget *w = utk_event_widget(ev);
419         if(utk_is_checked(w)) {
420                 tool = (intptr_t)data;
421         }
422 }
423
424 static int parse_args(int argc, char **argv)
425 {
426         int i;
427
428         for(i=1; i<argc; i++) {
429                 if(argv[i][0] == '-') {
430                         if(strcmp(argv[i], "-uiscale") == 0) {
431                                 if(!argv[++i] || !(uiscale = atoi(argv[i]))) {
432                                         fprintf(stderr, "-uiscale should be followed by a positive number\n");
433                                         return -1;
434                                 }
435                         } else if(strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "-h") == 0) {
436                                 printf("Usage: %s [options]\n", argv[0]);
437                                 printf("Options:\n");
438                                 printf(" -uiscale <scale>: UI scale factor (default: 1)\n");
439                                 printf(" -h,-help: print usage and exit\n");
440                                 exit(0);
441                         } else {
442                                 fprintf(stderr, "unknown option: %s\n", argv[i]);
443                                 return -1;
444                         }
445                 } else {
446                         if(opt_fname) {
447                                 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
448                                 return -1;
449                         }
450                         opt_fname = argv[i];
451                 }
452         }
453         return 0;
454 }
455
456 /* --- ubertk callbacks --- */
457
458 static void ucolor(int r, int g, int b, int a)
459 {
460         glColor4ub(r, g, b, a);
461 }
462
463 static void uclip(int x1, int y1, int x2, int y2)
464 {
465         if(!(x1 | y1 | x2 | y2)) {
466                 glDisable(GL_SCISSOR_TEST);
467         } else {
468                 glEnable(GL_SCISSOR_TEST);
469         }
470
471         x1 *= uiscale;
472         y1 *= uiscale;
473         x2 *= uiscale;
474         y2 *= uiscale;
475
476         glScissor(x1, win_height - y2, x2 - x1, y2 - y1);
477 }
478
479 static void uimage(int x, int y, const void *pix, int xsz, int ysz)
480 {
481         glPixelZoom(1, -1);
482         glRasterPos2f(x * uiscale, y * uiscale);
483         glDrawPixels(xsz, ysz, GL_BGRA, GL_UNSIGNED_BYTE, pix);
484 }
485
486 static void urect(int x1, int y1, int x2, int y2)
487 {
488         glRectf(x1 * uiscale, y1 * uiscale, x2 * uiscale, y2 * uiscale);
489 }
490
491 static void uline(int x1, int y1, int x2, int y2, int width)
492 {
493         glLineWidth(width);
494         glBegin(GL_LINES);
495         glVertex2f(x1 * uiscale, y1 * uiscale);
496         glVertex2f(x2 * uiscale, y2 * uiscale);
497         glEnd();
498 }
499
500 static void utext(int x, int y, const char *txt, int sz)
501 {
502         glMatrixMode(GL_PROJECTION);
503         glPushMatrix();
504         glTranslatef(x * uiscale, (y - dtx_baseline()) * uiscale, 0);
505         glScalef(uiscale, -uiscale, 1);
506
507         dtx_string(txt);
508         dtx_flush();
509
510         glPopMatrix();
511 }
512
513 static int utextspacing(void)
514 {
515         return dtx_line_height();
516 }
517
518 static int utextwidth(const char *txt, int sz)
519 {
520         return dtx_string_width(txt);
521 }