1 /*
2 * Copyright 2009-2021 Peter Kosyh <p.kosyh at gmail.com>
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25 #include "externals.h"
26 #include "internals.h"
27
28 #define MOTION_TIME (timer_counter - click_time >= 200 / HZ)
29 int game_running = 1;
30
31 char game_cwd[PATH_MAX];
32 char *curgame_dir = NULL;
33
34 int game_grab_events = 0;
35 int game_wait_use = 1;
36 int game_own_theme = 0;
37 int game_theme_changed = 0;
38 unsigned int mouse_filter_delay = 400;
39
40 static int need_restart = 0;
41 static int game_pic_w = 0;
42 static int game_pic_h = 0;
43
44 static char *last_pict = NULL;
45 static char *last_title = NULL;
46 static char *last_cmd = NULL;
47
48 void game_cursor(int on);
49
50 extern void instead_render_callback(void);
51 extern int instead_render_callback_dirty(int reset);
52
game_flip(void)53 void game_flip(void)
54 {
55 instead_render_callback();
56 gfx_flip();
57 }
58
_game_update(int x,int y,int w,int h)59 static void _game_update(int x, int y, int w, int h)
60 {
61 if (instead_render_callback_dirty(-1) == 1)
62 return;
63 gfx_update(x, y, w, h);
64 }
65
game_update(int x,int y,int w,int h)66 static void game_update(int x, int y, int w, int h)
67 {
68 if (instead_render_callback_dirty(-1) == 1)
69 return;
70 game_cursor(CURSOR_DRAW);
71 gfx_update(x, y, w, h);
72 }
73
game_res_err_msg(const char * filename,int alert)74 void game_res_err_msg(const char *filename, int alert)
75 {
76 static const char preambule[] = "Can't load: ";
77 char *msg; unsigned int s;
78 if (!filename || (!*filename && !alert))
79 return;
80 if (alert && curgame_dir) {
81 s = sizeof(preambule) + strlen(filename) + 2;
82 msg = malloc(s);
83 if (msg) {
84 snprintf(msg, s, "%s\"%s\"", preambule, filename);
85 instead_err_msg(msg);
86 free(msg);
87 }
88 }
89 fprintf(stderr, "%s%s\n", preambule, filename);
90 }
91
is_game(const char * path,const char * n)92 int is_game(const char *path, const char *n)
93 {
94 int rc = 0;
95 char *p;
96 char *pp;
97 if (!n)
98 return 0;
99 if (!strcmp("..", n) || !strcmp(".", n))
100 return 0;
101 p = getfilepath(path, n);
102 if (!p)
103 return 0;
104
105 if (idf_magic(p)) {
106 free(p);
107 return 1;
108 }
109 /* new api? */
110 pp = getfilepath(p, INSTEAD_MAIN3);
111 if (!pp)
112 goto out;
113 if (!access(pp, R_OK))
114 rc = 3;
115 free(pp);
116 if (rc) /* got one! */
117 goto out;
118 /* classic one? */
119 pp = getfilepath(p, INSTEAD_MAIN);
120 if (!pp)
121 goto out;
122 if (!access(pp, R_OK))
123 rc = 2;
124 free(pp);
125 if (rc)
126 goto out;
127 out:
128 free(p);
129 return rc;
130 }
131
132 struct game *games = NULL;
133 int games_nr = 0;
134
135 static void game_release(void);
136
game_lookup(const char * name)137 struct game *game_lookup(const char *name)
138 {
139 int i;
140 if (!name || !*name) {
141 if (games_nr == 1 && name)
142 return &games[0];
143 return NULL;
144 }
145 for (i = 0; i<games_nr; i ++) {
146 if (!strlowcmp(games[i].dir, name)) {
147 return &games[i];
148 }
149 }
150 return NULL;
151 }
152
game_reset(void)153 int game_reset(void)
154 {
155 game_release(); /* commit all user events */
156 game_release_theme(1);
157 game_theme_free();
158 if (game_select(curgame_dir))
159 goto out;
160 if (game_apply_theme())
161 goto out;
162 return 0;
163 out:
164 game_done(1);
165 if (game_init(NULL)) {
166 game_error();
167 return -1;
168 }
169 return -1;
170 }
171
game_reset_name(void)172 char *game_reset_name(void)
173 {
174 struct game *g;
175 g = game_lookup(curgame_dir);
176 if (g) {
177 gfx_set_title(g->name);
178 return g->name;
179 } else {
180 gfx_set_title(NULL);
181 return NULL;
182 }
183 }
184 static int game_cfg_load(void);
185
game_select(const char * name)186 int game_select(const char *name)
187 {
188 int rc = -1;
189 struct game *g;
190 // FREE(last_cmd);
191 // game_stop_mus(500);
192 g = game_lookup(name);
193 if ((!name || !*name) && !g) {
194 game_use_theme();
195 return game_theme_init();
196 }
197 if (g) {
198 char *oldgame = curgame_dir;
199 curgame_dir = g->dir;
200 instead_done();
201
202 instead_set_debug(debug_sw);
203 instead_set_standalone(standalone_sw);
204
205 if (instead_init(g->path)) {
206 curgame_dir = oldgame;
207 goto err;
208 }
209
210 themes_lookup_idf(instead_idf(), "themes/", THEME_GAME);
211 if (idf_only(instead_idf(), -1) != 1)
212 themes_lookup(dirpath("themes"), THEME_GAME);
213
214 game_cfg_load();
215
216 game_use_theme();
217
218 if (game_theme_init()) {
219 curgame_dir = oldgame;
220 goto err;
221 }
222 instead_set_lang(opt_lang);
223
224 if ((rc = instead_load(NULL))) {
225 curgame_dir = oldgame;
226 goto err;
227 }
228 } else {
229 game_use_theme();
230 game_theme_init();
231 rc = 0;
232 }
233 err:
234 game_reset_name();
235 return rc;
236 }
237
game_tag_valid(const char * p)238 int game_tag_valid(const char *p)
239 {
240 while (p && *p) {
241 p += strcspn(p, "<");
242 if (gfx_get_token(p, NULL, NULL, NULL))
243 return 0;
244 if (*p == '<')
245 p ++;
246 }
247 return 1;
248 }
249
game_tag(const char * path,const char * d_name,const char * tag)250 static char *game_tag(const char *path, const char *d_name, const char *tag)
251 {
252 char *l = NULL;
253 char *p;
254
255 if (idf_magic(path)) {
256 idf_t idf = idf_init(path);
257 if (idf) {
258 l = lookup_lang_tag_idf(idf, INSTEAD_MAIN3, tag, "--", opt_lang);
259 if (!l)
260 l = lookup_lang_tag_idf(idf, INSTEAD_MAIN, tag, "--", opt_lang);
261 idf_done(idf);
262 }
263 if (l)
264 goto ok;
265 goto err;
266 }
267 /* stead3 */
268 p = getfilepath(path, INSTEAD_MAIN3);
269 if (!p)
270 goto err;
271 l = lookup_lang_tag(p, tag, "--", opt_lang);
272 free(p);
273 if (l)
274 goto ok;
275 /* stead2 */
276 p = getfilepath(path, INSTEAD_MAIN);
277 if (!p)
278 goto err;
279 l = lookup_lang_tag(p, tag, "--", opt_lang);
280 free(p);
281 if (!l)
282 goto err;
283 ok:
284 if (!game_tag_valid(l)) { /* avoid dangerous tags */
285 free(l);
286 return NULL;
287 }
288 return l;
289 err:
290 return NULL;
291 }
292
293
game_name(const char * path,const char * d_name)294 static char *game_name(const char *path, const char *d_name)
295 {
296 char *p = game_tag(path, d_name, "Name");
297 if (!p)
298 return strdup(d_name);
299 trunc_lines(p, 0);
300 return p;
301 }
302
game_info(const char * path,const char * d_name)303 static char *game_info(const char *path, const char *d_name)
304 {
305 char *p = game_tag(path, d_name, "Info");
306 if (!p)
307 return p;
308 trunc_lines(p, 4);
309 return p;
310 }
311
game_author(const char * path,const char * d_name)312 static char *game_author(const char *path, const char *d_name)
313 {
314 char *p = game_tag(path, d_name, "Author");
315 trunc_lines(p, 0);
316 return p;
317 }
318
game_version(const char * path,const char * d_name)319 static char *game_version(const char *path, const char *d_name)
320 {
321 char *p = game_tag(path, d_name, "Version");
322 trunc_lines(p, 0);
323 return p;
324 }
325
game_direction(const char * path,const char * d_name)326 static char *game_direction(const char *path, const char *d_name)
327 {
328 char *p = game_tag(path, d_name, "Direction");
329 if (!p)
330 return strdup(d_name);
331 trunc_lines(p, 0);
332 return p;
333 }
334
335 #if 0
336 static char *game_api(const char *path, const char *d_name)
337 {
338 char *p = game_tag(path, d_name, "API");
339 trunc_lines(p, 0);
340 if (!p)
341 return strdup("stead2");
342 return p;
343 }
344 #endif
game_info_free(struct game * g)345 static void game_info_free(struct game *g)
346 {
347 FREE(g->name);
348 FREE(g->info);
349 FREE(g->author);
350 FREE(g->version);
351 FREE(g->direction);
352 /* FREE(g->api); */
353 }
354
game_free(struct game * g)355 static void game_free(struct game *g)
356 {
357 FREE(g->path);
358 FREE(g->dir);
359 game_info_free(g);
360 }
361
game_info_fill(struct game * g)362 static void game_info_fill(struct game *g)
363 {
364 g->name = game_name(dirpath(g->path), g->dir);
365 g->info = game_info(dirpath(g->path), g->dir);
366 g->author = game_author(dirpath(g->path), g->dir);
367 g->version = game_version(dirpath(g->path), g->dir);
368 g->direction = game_direction(dirpath(g->path), g->dir);
369 /* g->api = game_api(dirpath(g->path), g->dir); */
370 }
371
game_fill(struct game * g,const char * p,const char * dir)372 static void game_fill(struct game *g, const char *p, const char *dir)
373 {
374 g->path = strdup(p);
375 g->dir = strdup(dir);
376 game_info_fill(g);
377 }
378
games_rename(void)379 int games_rename(void)
380 {
381 int i;
382 char cwd[PATH_MAX];
383 getdir(cwd, sizeof(cwd));
384 setdir(game_cwd);
385 for (i = 0; i < games_nr; i++) {
386 game_info_free(&games[i]);
387 game_info_fill(&games[i]);
388 }
389 setdir(cwd);
390 return 0;
391 }
392
393
cmp_game(const void * p1,const void * p2)394 static int cmp_game(const void *p1, const void *p2)
395 {
396 const struct game *g1 = (const struct game*)p1;
397 const struct game *g2 = (const struct game*)p2;
398 int g1_s = !!strncmp(g1->path, GAMES_PATH, strlen(GAMES_PATH));
399 int g2_s = !!strncmp(g2->path, GAMES_PATH, strlen(GAMES_PATH));
400 if (g1_s != g2_s)
401 return g1_s - g2_s;
402 return strcmp(g1->name, g2->name);
403 }
404
games_sort()405 static void games_sort()
406 {
407 if (!games)
408 return;
409 qsort(games, games_nr, sizeof(struct game), cmp_game);
410 }
411
412
games_add(const char * path,const char * dir)413 static int games_add(const char *path, const char *dir)
414 {
415 char *p;
416 if (!is_game(path, dir))
417 return -1;
418 p = getfilepath(path, dir);
419 if (!p)
420 return -1;
421 if (!idf_magic(p)) {
422 strcat(p, "/");
423 games[games_nr].idf = 0;
424 } else
425 games[games_nr].idf = 1;
426 game_fill(&games[games_nr], p, dir);
427 free(p);
428 games_nr ++;
429 return 0;
430 }
431
games_replace(const char * path,const char * dir)432 int games_replace(const char *path, const char *dir)
433 {
434 int rc;
435 char *p;
436 struct game *g;
437 struct game *new_games;
438 if (!is_game(path, dir))
439 return -1;
440 g = game_lookup(dir);
441 if (g) {
442 if (g->idf)
443 p = getfilepath(path, dir);
444 else
445 p = getpath(path, dir);
446 if (!p)
447 return -1;
448 game_free(g);
449 game_fill(g, p, dir);
450 free(p);
451 games_sort();
452 return 0;
453 }
454 new_games = realloc(games, sizeof(struct game) * (1 + games_nr));
455 if (!new_games)
456 return -1;
457 games = new_games;
458 rc = games_add(path, dir);
459 if (!rc)
460 games_sort();
461 return rc;
462 }
463
games_lookup(const char * path)464 int games_lookup(const char *path)
465 {
466 int n = 0, i = 0;
467 DIR *d;
468 struct dirent *de;
469 struct game *new_games;
470
471 if (!path)
472 return 0;
473
474 d = opendir(path);
475 if (!d)
476 return -1;
477 while ((de = readdir(d))) {
478 /*if (de->d_type != DT_DIR)
479 continue;*/
480 if (game_lookup(de->d_name))
481 continue;
482
483 if (!is_game(path, de->d_name))
484 continue;
485 n ++;
486 }
487 if (!n)
488 goto out;
489 closedir(d); d = opendir(path);
490 if (!d)
491 return -1;
492 new_games = realloc(games, sizeof(struct game) * (n + games_nr));
493 if (!new_games) {
494 closedir(d);
495 return -1;
496 }
497 games = new_games;
498 while ((de = readdir(d)) && i < n) {
499 /*if (de->d_type != DT_DIR)
500 continue;*/
501 if (game_lookup(de->d_name))
502 continue;
503
504 if (games_add(path, de->d_name))
505 continue;
506 i ++;
507 }
508 out:
509 closedir(d);
510 games_sort();
511 return 0;
512 }
513
games_remove(int gtr)514 int games_remove(int gtr)
515 {
516 int rc;
517 struct game *new_games;
518 rc = remove_dir(games[gtr].path);
519 free(games[gtr].name); free(games[gtr].dir); free(games[gtr].path);
520 games_nr --;
521 memmove(&games[gtr], &games[gtr + 1], (games_nr - gtr) * sizeof(struct game));
522 new_games = realloc(games, games_nr * sizeof(struct game));
523 if (new_games) /* failure to shrink otherwise, and it's non-fatal */
524 games = new_games;
525 return rc;
526 }
527
528 static int motion_mode = 0;
529 static int motion_id = 0;
530 static int motion_y = 0;
531
532 static int mx, my;
533 static img_t menubg = NULL;
534 static img_t menu = NULL;
535
536 static int menu_shown = 0;
537 static int browse_dialog = 0;
538
539 int game_cmd(char *cmd, int click);
540
game_clear(int x,int y,int w,int h)541 void game_clear(int x, int y, int w, int h)
542 {
543 game_cursor(CURSOR_CLEAR);
544
545 if (game_theme.bg)
546 gfx_draw_bg(game_theme.bg, x, y, w, h);
547 else
548 gfx_clear(x, y, w, h);
549
550 if (menu_shown) {
551 int xx = x - mx;
552 int yy = y - my;
553 gfx_copy_from(menubg, xx, yy, w, h, NULL, x, y);
554 gfx_draw_from(menu, xx, yy, w, h, NULL, x, y);
555 }
556 }
557
game_clear_all(void)558 void game_clear_all(void)
559 {
560 if (DIRECT_MODE)
561 gfx_img_fill(gfx_screen(NULL), 0, 0, game_theme.w, game_theme.h,
562 gfx_col(game_theme.brdcol.r, game_theme.brdcol.g, game_theme.brdcol.b));
563 else
564 game_clear(0, 0, game_theme.w, game_theme.h);
565 }
566
567 void game_clear(int x, int y, int w, int h);
568
569 struct el {
570 int id;
571 int x;
572 int y;
573 int mx;
574 int my; /* coordinates */
575 int type;
576 int drawn;
577 /* int clone; */
578 union {
579 layout_t lay;
580 textbox_t box;
581 img_t img;
582 void *p;
583 } p;
584 };
585
586 enum {
587 elt_box,
588 elt_layout,
589 elt_image,
590 };
591
592 enum {
593 el_menu = 1,
594 el_title,
595 el_ways,
596 el_inv,
597 el_scene,
598 el_sup,
599 el_sdown,
600 /* el_sslide, */
601 el_iup,
602 el_idown,
603 /* el_islide, */
604 el_spic,
605 el_menu_button,
606 el_max,
607 };
608
609 static struct el objs[el_max];
610
el_set(int i,int t,int x,int y,void * p)611 static void el_set(int i, int t, int x, int y, void *p)
612 {
613 objs[i].id = i;
614 objs[i].x = x;
615 objs[i].y = y;
616 objs[i].p.p = p;
617 objs[i].type = t;
618 objs[i].drawn = 0;
619 /* objs[i].clone = 0; */
620 }
621
el_zero(int num)622 static void el_zero(int num)
623 {
624 memset(&objs[num], 0, sizeof(struct el));
625 }
626
el(int num)627 static struct el *el(int num)
628 {
629 return &objs[num];
630 }
el_box(int num)631 static textbox_t el_box(int num)
632 {
633 return objs[num].p.box;
634 }
635
el_layout(int num)636 static layout_t el_layout(int num)
637 {
638 return objs[num].p.lay;
639 }
640
el_img(int num)641 static img_t el_img(int num)
642 {
643 return objs[num].p.img;
644 }
645
646 char *game_menu_gen(void);
647
game_menu(int nr)648 void game_menu(int nr)
649 {
650 mouse_reset(1);
651 cur_menu = nr;
652 game_menu_box(1, game_menu_gen());
653 }
654
game_error(void)655 int game_error(void)
656 {
657 game_done(1);
658 if (game_init(NULL)) {
659 fprintf(stderr,"Fatal error! Can't init anything!!!\n");
660 exit(1);
661 }
662 game_menu(menu_error);
663 return 0;
664 }
665
666 static void el_draw(int n);
667
668 int window_sw = 0;
669 int fullscreen_sw = 0;
670 int hires_sw = -1;
671
game_load(int nr)672 int game_load(int nr)
673 {
674 char *s;
675 s = game_save_path(0, nr);
676 if (s && !access(s, R_OK)) {
677 char cmd[PATH_MAX];
678 char sav[PATH_MAX];
679 strcpy(sav, s);
680 snprintf(cmd, sizeof(cmd) - 1, "load %s", s);
681 game_cmd(cmd, GAME_CMD_FILE);
682 if (nr == -1)
683 unlink(sav);
684 return 0;
685 }
686 return -1;
687 }
688
game_saves_enabled(void)689 int game_saves_enabled(void)
690 {
691 int rc;
692 instead_lock();
693 instead_function("instead.isEnableSave", NULL);
694 rc = instead_bretval(0);
695 instead_clear();
696 instead_unlock();
697 return rc;
698 }
699
game_autosave_enabled(void)700 int game_autosave_enabled(void)
701 {
702 int rc;
703 instead_lock();
704 instead_function("instead.isEnableAutosave", NULL);
705 rc = instead_bretval(0);
706 instead_clear();
707 instead_unlock();
708 return rc;
709 }
710
game_save(int nr)711 int game_save(int nr)
712 {
713 char *s = game_save_path(1, nr);
714 char cmd[PATH_MAX];
715 char *p;
716 int rc;
717 if (!s)
718 return -1;
719
720 if (nr == -1 || nr == 0) {
721 struct instead_args args_1[] = {
722 { .val = "-1", .type = INSTEAD_NUM },
723 { .val = NULL, }
724 };
725 struct instead_args args_0[] = {
726 { .val = "0", .type = INSTEAD_NUM },
727 { .val = NULL, }
728 };
729 if (nr == -1) {
730 instead_lock();
731 instead_function("instead.autosave", args_1); /* enable saving for -1 */
732 } else if (!game_autosave_enabled())
733 return 0; /* nothing todo */
734 else {
735 instead_lock();
736 instead_function("instead.autosave", args_0); /* enable saving for 0 */
737 }
738 instead_clear();
739 instead_unlock();
740 }
741 snprintf(cmd, sizeof(cmd) - 1, "save %s", s);
742 instead_lock();
743 p = instead_file_cmd(cmd, &rc);
744 instead_unlock();
745 if (p)
746 free(p);
747 if (rc || (!p && instead_err())) {
748 game_menu(menu_warning);
749 return -1;
750 }
751 data_sync();
752 return 0;
753 }
754
inv_enabled(void)755 static int inv_enabled(void)
756 {
757 return (game_theme.inv_mode != INV_MODE_DISABLED);
758 }
759
760
game_apply_theme(void)761 int game_apply_theme(void)
762 {
763 int align = game_theme.win_align;
764 layout_t lay = NULL;
765 textbox_t box = NULL;
766 struct game *g = game_lookup(curgame_dir);
767 int rtl = 0;
768 if (g && g->direction) {
769 if (!strcmp(g->direction, "rtl"))
770 rtl = 1;
771 else if (!strcmp(g->direction, "auto"))
772 rtl = -1;
773 }
774 gfx_bg(game_theme.xoff, game_theme.yoff,
775 game_theme.w - 2*game_theme.xoff, game_theme.h - 2*game_theme.yoff,
776 game_theme.bgcol, game_theme.brdcol);
777 if (!DIRECT_MODE)
778 game_clear_all();
779 gfx_flip();
780 if (opt_justify == JUST_NO && align == ALIGN_JUSTIFY)
781 align = ALIGN_LEFT;
782 else if (opt_justify == JUST_YES && align == ALIGN_LEFT)
783 align = ALIGN_JUSTIFY;
784
785 if (!el_box(el_scene)) {
786 lay = txt_layout(game_theme.font, align, game_theme.win_w, game_theme.win_h);
787 if (!lay)
788 return -1;
789 box = txt_box(game_theme.win_w, game_theme.win_h);
790 if (!box)
791 return -1;
792 txt_layout_color(lay, game_theme.fgcol);
793 txt_layout_link_color(lay, game_theme.lcol);
794 /* txt_layout_link_style(lay, 4); */
795 txt_layout_active_color(lay, game_theme.acol);
796 txt_layout_font_height(lay, game_theme.font_height);
797 txt_layout_rtl(lay, rtl);
798
799 txt_box_set(box, lay);
800 el_set(el_scene, elt_box, game_theme.win_x, 0, box);
801 }
802
803 if (inv_enabled()) {
804 if (!el_box(el_inv)) {
805 lay = txt_layout(game_theme.inv_font, INV_ALIGN(game_theme.inv_mode),
806 game_theme.inv_w, game_theme.inv_h);
807 if (!lay)
808 return -1;
809 txt_layout_color(lay, game_theme.icol);
810 txt_layout_link_color(lay, game_theme.ilcol);
811 txt_layout_active_color(lay, game_theme.iacol);
812 txt_layout_font_height(lay, game_theme.inv_font_height);
813 txt_layout_rtl(lay, rtl);
814
815 box = txt_box(game_theme.inv_w, game_theme.inv_h);
816 if (!box)
817 return -1;
818 txt_box_set(box, lay);
819 el_set(el_inv, elt_box, game_theme.inv_x, game_theme.inv_y, box);
820 }
821 } else
822 el_set(el_inv, elt_box, game_theme.inv_x, game_theme.inv_y, NULL);
823
824 if (!el_layout(el_title)) {
825 lay = txt_layout(game_theme.font, ALIGN_CENTER, game_theme.win_w, 0);
826 if (!lay)
827 return -1;
828
829 txt_layout_color(lay, game_theme.fgcol);
830 txt_layout_link_color(lay, game_theme.lcol);
831 txt_layout_active_color(lay, game_theme.acol);
832 txt_layout_font_height(lay, game_theme.font_height);
833 txt_layout_rtl(lay, rtl);
834
835 el_set(el_title, elt_layout, game_theme.win_x, game_theme.win_y, lay);
836 }
837
838 if (!el_layout(el_ways)) {
839 lay = txt_layout(game_theme.font, ALIGN_CENTER, game_theme.win_w, 0);
840 if (!lay)
841 return -1;
842
843 txt_layout_color(lay, game_theme.fgcol);
844 txt_layout_link_color(lay, game_theme.lcol);
845 txt_layout_active_color(lay, game_theme.acol);
846 txt_layout_font_height(lay, game_theme.font_height);
847 txt_layout_rtl(lay, rtl);
848
849 el_set(el_ways, elt_layout, game_theme.win_x, 0, lay);
850 }
851
852 el_set(el_sdown, elt_image, 0, 0, game_theme.a_down);
853 el_set(el_sup, elt_image, 0, 0, game_theme.a_up);
854 el_set(el_idown, elt_image, 0, 0, game_theme.inv_a_down);
855 el_set(el_iup, elt_image, 0, 0, game_theme.inv_a_up);
856
857 el_set(el_spic, elt_image, game_theme.win_x, game_theme.win_y, NULL);
858 el_set(el_menu, elt_layout, 0, 0, NULL);
859 el_set(el_menu_button, elt_image, game_theme.menu_button_x, game_theme.menu_button_y, game_theme.menu_button);
860
861 gfx_set_icon(game_theme.icon);
862
863 if (!DIRECT_MODE) {
864 el_draw(el_menu_button);
865 }
866 return 0;
867 }
868
game_restart(void)869 int game_restart(void)
870 {
871 char *og = curgame_dir;
872 game_save(-1);
873 game_done(0);
874 if (game_init(og)) {
875 game_error();
876 return 0;
877 }
878 return 0;
879 }
880
881 unsigned long timer_counter = 0;
882
883 gtimer_t timer_han = NULL_TIMER;
884
885 static int gfx_commit_event = 0;
886
_game_gfx_commit(void * data)887 static void _game_gfx_commit(void *data)
888 {
889 gfx_commit_event = 0;
890 gfx_commit();
891 }
892
game_gfx_commit(int sync)893 void game_gfx_commit(int sync)
894 {
895 if (gfx_fading()) /* to avoid flickering */
896 return;
897 if (gfx_pending()) {
898 if (sync) {
899 _game_gfx_commit(NULL);
900 } else {
901 if (!gfx_commit_event) {
902 gfx_commit_event ++;
903 push_user_event(_game_gfx_commit, NULL);
904 }
905 }
906 }
907 }
908
909 static int game_render_callback_redraw(void);
910
anim_do(void * data)911 static void anim_do(void *data)
912 {
913 void *v;
914 img_t img;
915
916 if (browse_dialog || menu_shown || gfx_fading() || minimized())
917 return;
918
919 game_cursor(CURSOR_CLEAR);
920
921 if (gfx_frame_anim(el_img(el_spic))) { /* scene */
922 game_render_callback_redraw();
923 gfx_update_anim(el_img(el_spic), game_update);
924 }
925
926 game_cursor(CURSOR_CLEAR);
927
928 for (v = NULL; (img = txt_layout_images(txt_box_layout(el_box(el_scene)), &v)); ) { /* scene */
929 game_cursor(CURSOR_CLEAR);
930 if ((img != el_img(el_spic)) && gfx_frame_anim(img)) {
931 game_render_callback_redraw();
932 gfx_update_anim(img, game_update);
933 }
934 }
935 game_cursor(CURSOR_CLEAR);
936
937 for (v = NULL; (img = txt_layout_images(txt_box_layout(el_box(el_inv)), &v)); ) { /* inv */
938 game_cursor(CURSOR_CLEAR);
939 if (gfx_frame_anim(img)) {
940 game_render_callback_redraw();
941 gfx_update_anim(img, game_update);
942 }
943 }
944
945 game_cursor(CURSOR_CLEAR);
946
947 for (v = NULL; (img = txt_layout_images(el_layout(el_title), &v)); ) { /* title */
948 game_cursor(CURSOR_CLEAR);
949 if (gfx_frame_anim(img)) {
950 game_render_callback_redraw();
951 gfx_update_anim(img, game_update);
952 }
953 }
954
955 game_cursor(CURSOR_CLEAR);
956
957 for (v = NULL; (img = txt_layout_images(el_layout(el_ways), &v)); ) { /* ways */
958 game_cursor(CURSOR_CLEAR);
959 if (gfx_frame_anim(img)) {
960 game_render_callback_redraw();
961 gfx_update_anim(img, game_update);
962 }
963 }
964 game_cursor(CURSOR_ON);
965 game_flip();
966 game_gfx_commit(0);
967 }
968
counter_fn(int interval,void * p)969 int counter_fn(int interval, void *p)
970 {
971 timer_counter ++;
972 #ifdef SAILFISHOS /* idle response sometimes */
973 if ((timer_counter % 25) == 0) {
974 push_user_event(NULL, NULL);
975 }
976 #endif
977 if (gfx_is_drawn_anims() && !DIRECT_MODE)
978 push_user_event(anim_do, NULL);
979 return interval;
980 }
981
parse_curtheme(const char * v,void * data)982 static int parse_curtheme(const char *v, void *data)
983 {
984 char **p = ((char **)data);
985 struct theme *theme;
986 theme = theme_lookup(v, THEME_GAME);
987 if (theme)
988 *p = theme->dir;
989 else
990 *p = NULL;
991 return 0;
992 }
993
994 static struct parser cmd_game_parser[] = {
995 { "theme", parse_curtheme, &curtheme_dir[THEME_GAME], 0 },
996 };
997
game_cfg_load(void)998 static int game_cfg_load(void)
999 {
1000 char *p = getfilepath(dirname(game_save_path(1, 0)), "config.ini");
1001 curtheme_dir[THEME_GAME] = NULL;
1002 if (!p)
1003 return -1;
1004 parse_ini(p, cmd_game_parser);
1005 free(p);
1006 return 0;
1007 }
1008
game_cfg_save(void)1009 int game_cfg_save(void)
1010 {
1011 FILE *fp;
1012 char *p;
1013 if (!curgame_dir)
1014 return 0;
1015 if (!curtheme_dir[THEME_GAME]) /* nothing todo */
1016 return 0;
1017 p = getfilepath(dirname(game_save_path(1, 0)), "config.ini");
1018 if (!p)
1019 return -1;
1020 fp = fopen(p, "wb");
1021 if (fp) {
1022 if (curtheme_dir[THEME_GAME]) {
1023 fprintf(fp, "theme = %s\n", curtheme_dir[THEME_GAME]);
1024 }
1025 fclose(fp);
1026 }
1027 free(p);
1028 return 0;
1029 }
1030
game_use_theme(void)1031 int game_use_theme(void)
1032 {
1033 int rc = 0;
1034 game_theme_changed = 0;
1035 game_own_theme = 0;
1036
1037 memset(objs, 0, sizeof(struct el) * el_max);
1038
1039 if (game_default_theme()) {
1040 fprintf(stderr, "Can't load default theme.\n");
1041 return -1;
1042 }
1043
1044 if (themes_count(THEME_GAME) > 0) { /* new scheme with own themes? */
1045 game_own_theme = 2;
1046 if (opt_owntheme) {
1047 fprintf(stderr, "Using own themes directory...\n");
1048 if (curtheme_dir[THEME_GAME] && strlowcmp(DEFAULT_THEME, curtheme_dir[THEME_GAME])) {
1049 rc = game_theme_load(curtheme_dir[THEME_GAME], THEME_GAME);
1050 } else
1051 curtheme_dir[THEME_GAME] = DEFAULT_THEME;
1052 return rc;
1053 }
1054 } else if (curgame_dir && (!idf_access(instead_idf(), THEME_FILE) || !access(dirpath(THEME_FILE), R_OK))) {
1055 game_own_theme = 1;
1056 if (opt_owntheme) {
1057 fprintf(stderr, "Using own theme file...\n");
1058 theme_relative = 1;
1059 rc = theme_load(THEME_FILE);
1060 theme_relative = 0;
1061 return rc;
1062 }
1063 }
1064 if (curtheme_dir[THEME_GLOBAL] && strlowcmp(DEFAULT_THEME, curtheme_dir[THEME_GLOBAL])) {
1065 rc = game_theme_load(curtheme_dir[THEME_GLOBAL], THEME_GLOBAL);
1066 }
1067 return rc;
1068 }
1069
game_init(const char * name)1070 int game_init(const char *name)
1071 {
1072 getdir(game_cwd, sizeof(game_cwd));
1073 unix_path(game_cwd);
1074
1075 if (game_select(name))
1076 return -1;
1077 if (game_theme_optimize())
1078 return -1;
1079
1080 if (game_apply_theme()) {
1081 game_theme_select(DEFAULT_THEME);
1082 return -1;
1083 }
1084 timer_han = gfx_add_timer(HZ, counter_fn, NULL);
1085 if (!curgame_dir) {
1086 game_menu(menu_games);
1087 } else {
1088 if (!game_load(-1)) /* tmp save */
1089 goto out;
1090 if ((opt_autosave & 1) && !game_load(0)) /* autosave */
1091 goto out;
1092 game_cmd("look", 0);
1093 custom_theme_warn();
1094 if (opt_autosave)
1095 game_save(0);
1096 }
1097 out:
1098 return 0;
1099 }
1100
game_release(void)1101 static void game_release(void)
1102 {
1103 input_uevents(); /* all callbacks */
1104 if (last_pict)
1105 free(last_pict);
1106 if (last_title)
1107 free(last_title);
1108 if (last_cmd)
1109 free(last_cmd);
1110 last_pict = last_title = last_cmd = NULL;
1111 // game_stop_mus(500);
1112 // sounds_free();
1113 }
1114
game_release_theme(int force)1115 void game_release_theme(int force)
1116 {
1117 int i;
1118 mouse_reset(1);
1119 game_cursor(CURSOR_OFF);
1120 if (el_img(el_spic)) {
1121 gfx_free_image(el_img(el_spic));
1122 el_zero(el_spic);
1123 if (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_EMBEDDED)
1124 txt_layout_add_img(txt_box_layout(el_box(el_scene)),
1125 "scene", NULL);
1126 }
1127
1128 for (i = 0; i < el_max; i++) {
1129 struct el *o;
1130 if (!force) {
1131 switch (i) {
1132 case el_title:
1133 case el_ways:
1134 case el_scene:
1135 if (!(game_theme.changed & CHANGED_WIN))
1136 continue;
1137 break;
1138 case el_inv:
1139 if (!(game_theme.changed & CHANGED_INV))
1140 continue;
1141 break;
1142 default:
1143 break;
1144 }
1145 }
1146 o = el(i);
1147 if (o->type == elt_layout && o->p.p) {
1148 txt_layout_free(o->p.lay);
1149 } else if (o->type == elt_box && o->p.p) {
1150 txt_layout_free(txt_box_layout(o->p.box));
1151 txt_box_free(o->p.box);
1152 }
1153 el_zero(i);
1154 }
1155 if (menu)
1156 gfx_free_image(menu);
1157 if (menubg)
1158 gfx_free_image(menubg);
1159 menu = menubg = NULL;
1160 _game_update(0, 0, game_theme.w, game_theme.h);
1161 }
1162
1163 static int game_event(const char *ev);
1164
game_done(int err)1165 void game_done(int err)
1166 {
1167 if (!err && curgame_dir) {
1168 game_event("quit");
1169 if (opt_autosave)
1170 game_save(0);
1171 game_cfg_save();
1172 }
1173 gfx_del_timer(timer_han);
1174 timer_han = NULL_TIMER;
1175 if (menu_shown)
1176 menu_toggle(-1);
1177 game_release(); /* here all lost user callback are */
1178 game_release_theme(1);
1179 game_theme_free();
1180 themes_drop(THEME_GAME);
1181 input_clear();
1182
1183 instead_done();
1184 curgame_dir = NULL;
1185
1186 game_own_theme = 0;
1187 need_restart = 0;
1188 }
1189
el_size(int i,int * w,int * h)1190 static void el_size(int i, int *w, int *h)
1191 {
1192 int type;
1193 type = el(i)->type;
1194 if (type == elt_layout)
1195 txt_layout_size(el_layout(i), w, h);
1196 else if (type == elt_box)
1197 txt_box_size(el_box(i), w, h);
1198 else if (type == elt_image) {
1199 if (w)
1200 *w = gfx_img_w(el_img(i));
1201 if (h)
1202 *h = gfx_img_h(el_img(i));
1203 } else {
1204 /* impossible type */
1205 if (w)
1206 *w = 0;
1207 if (h)
1208 *h = 0;
1209 }
1210 }
1211
1212 #define el_clear(n) _el_clear(n, game_clear)
1213 #define el_clear_nobg(n) _el_clear(n, NULL)
1214
_el_clear(int n,clear_fn clear)1215 static int _el_clear(int n, clear_fn clear)
1216 {
1217 void *v;
1218 img_t img;
1219 int x, y, w, h;
1220 struct el *o;
1221 o = el(n);
1222 if (!o || !o->drawn)
1223 return 0;
1224 x = o->x;
1225 y = o->y;
1226 el_size(n, &w, &h);
1227 o->drawn = 0;
1228 if (clear)
1229 clear(x, y, w, h);
1230 if (o->type == elt_box) {
1231 for (v = NULL; (img = txt_layout_images(txt_box_layout(el_box(n)), &v)); )
1232 gfx_dispose_anim(img);
1233 } else if (o->type == elt_layout) {
1234 for (v = NULL; (img = txt_layout_images(el_layout(n), &v)); )
1235 gfx_dispose_anim(img);
1236 } else if (o->type == elt_image)
1237 gfx_dispose_anim(el_img(n));
1238
1239 return 1;
1240 }
1241
el_update(int n)1242 static void el_update(int n)
1243 {
1244 int x, y, w, h;
1245 struct el *o;
1246 o = el(n);
1247 /* if (!o->drawn)
1248 return; */
1249 x = o->x;
1250 y = o->y;
1251 el_size(n, &w, &h);
1252 game_update(x, y, w, h);
1253 return;
1254 }
1255
el_clip(int n)1256 static void el_clip(int n)
1257 {
1258 int x, y, w, h;
1259 struct el *o;
1260 o = el(n);
1261 x = o->x;
1262 y = o->y;
1263 el_size(n, &w, &h);
1264 gfx_clip(x, y, w, h);
1265 return;
1266 }
1267
box_isscroll_up(int n)1268 int box_isscroll_up(int n)
1269 {
1270 if (el(n)->type != elt_box)
1271 return -1;
1272 if (txt_box_off(el_box(n)))
1273 return 0;
1274 return -1;
1275 }
1276
box_isscroll_down(int n)1277 int box_isscroll_down(int n)
1278 {
1279 int off;
1280 int h, hh;
1281 if (el(n)->type != elt_box)
1282 return -1;
1283 el_size(n, NULL, &h);
1284 txt_box_real_size(el_box(n), NULL, &hh);
1285 off = txt_box_off(el_box(n));
1286
1287 if (hh - off > h)
1288 return 0;
1289 return -1;
1290 }
1291
box_update_scrollbar(int n)1292 void box_update_scrollbar(int n)
1293 {
1294 struct el *elup = NULL;
1295 struct el *eldown = NULL;
1296 /* struct el *elslide; */
1297
1298 int x1, y1;
1299 int x2, y2;
1300
1301 int off;
1302 int w, h, hh;
1303
1304 if (n == el_scene) {
1305 elup = el(el_sup);
1306 eldown = el(el_sdown);
1307 x1 = game_theme.a_up_x;
1308 y1 = game_theme.a_up_y;
1309 x2 = game_theme.a_down_x;
1310 y2 = game_theme.a_down_y;
1311 /* elslide = el(el_sslide); */
1312 } else if (n == el_inv) {
1313 elup = el(el_iup);
1314 eldown = el(el_idown);
1315 x1 = game_theme.inv_a_up_x;
1316 y1 = game_theme.inv_a_up_y;
1317 x2 = game_theme.inv_a_down_x;
1318 y2 = game_theme.inv_a_down_y;
1319 /* elslide = el(el_islide); */
1320 } else /* impossible case */
1321 return;
1322 if (!elup || !eldown)
1323 return;
1324
1325 /* if (x1 == -1 || y1 == -1 || x2 == -1 || y2 == -1) */
1326 el_size(n, &w, &h);
1327
1328 if (x1 == -1)
1329 x1 = el(n)->x + w + game_theme.pad;
1330
1331 if (y1 == -1)
1332 y1 = el(n)->y;
1333
1334 if (x2 == -1)
1335 x2 = x1;
1336
1337 if (y2 == -1)
1338 y2 = y1 + h - gfx_img_h(game_theme.a_down);
1339
1340 txt_box_real_size(el_box(n), NULL, &hh);
1341 off = txt_box_off(el_box(n));
1342
1343 if (el_clear(elup->id)) {
1344 if (elup->x != x1 || elup->y != y1)
1345 el_update(elup->id);
1346 }
1347
1348 if (el_clear(eldown->id)) {
1349 if (eldown->x != x2 || eldown->y != y2)
1350 el_update(eldown->id);
1351 }
1352
1353 elup->x = x1;
1354 elup->y = y1;
1355 eldown->x = x2;
1356 eldown->y = y2;
1357
1358 el_clear(elup->id);
1359 el_clear(eldown->id);
1360
1361 if (hh - off > h)
1362 el_draw(eldown->id);
1363 if (off)
1364 el_draw(elup->id);
1365 el_update(elup->id);
1366 el_update(eldown->id);
1367 }
1368
1369 /* Decides what to draw */
el_draw(int n)1370 void el_draw(int n)
1371 {
1372 int x, y;
1373 struct el *o;
1374 o = el(n);
1375 x = o->x;
1376 y = o->y;
1377 if (!o->p.p)
1378 return;
1379 game_gfx_clip();
1380 game_cursor(CURSOR_CLEAR);
1381 if (o->type == elt_image)
1382 gfx_draw(o->p.img, x, y);
1383 else if (o->type == elt_layout)
1384 txt_layout_draw(o->p.lay, x, y);
1385 else if (o->type == elt_box) {
1386 txt_box_draw(o->p.box, x, y);
1387 box_update_scrollbar(o->id);
1388 }
1389 o->drawn = 1;
1390 game_gfx_noclip();
1391 return;
1392 }
1393
game_pict_scale(img_t img,int ww,int hh)1394 img_t game_pict_scale(img_t img, int ww, int hh)
1395 {
1396 img_t img2 = img;
1397 int w, h, www, hhh;
1398 float scale1, scale2, scale = 1.0f;
1399
1400 game_pic_w = gfx_img_w(img);
1401 game_pic_h = gfx_img_h(img);
1402
1403 if (!cache_have(gfx_image_cache(), img)) {
1404 game_pic_w = (int)((float)game_pic_w / (float)game_theme.scale);
1405 game_pic_h = (int)((float)game_pic_h / (float)game_theme.scale);
1406 return img; /* do not scale sprites! */
1407 }
1408
1409 if (game_theme.img_scale * game_theme.scale > 1.0f)
1410 scale = game_theme.img_scale * game_theme.scale;
1411
1412 w = gfx_img_w(img) * scale;
1413 h = gfx_img_h(img) * scale;
1414
1415 if (ww == -1)
1416 ww = w;
1417 if (hh == -1)
1418 hh = h;
1419
1420 if (w <= ww && h <= hh) {
1421 theme_img_scale(&img);
1422 return img;
1423 }
1424
1425 www = ww;
1426 hhh = hh;
1427
1428 while (scale * (float)w > ww || scale * (float)h > hh) {
1429 scale1 = (float)(www - 2) / (float)(w);
1430 scale2 = (float)(hhh - 2) / (float)(h);
1431 scale = (scale1<scale2) ? scale1:scale2;
1432 www -= 1;
1433 hhh -= 1;
1434 if (www <= 0 || hhh <=0)
1435 break;
1436 }
1437 if (scale < 0)
1438 scale = 0;
1439 if (game_theme.img_scale * game_theme.scale > 1.0f)
1440 scale *= (game_theme.img_scale * game_theme.scale);
1441 img2 = gfx_scale(img, scale, scale, SCALABLE_THEME_SMOOTH);
1442 gfx_free_image(img);
1443 return img2;
1444 }
1445
game_menu_box_wh(const char * txt,int * w,int * h)1446 int game_menu_box_wh(const char *txt, int *w, int *h)
1447 {
1448 layout_t lay;
1449 int b = game_theme.border_w;
1450 int pad = game_theme.pad;
1451
1452 lay = txt_layout(game_theme.menu_font, ALIGN_CENTER, game_theme.w - 2 * (b + pad), 0);
1453 txt_layout_set(lay, (char*)txt);
1454 txt_layout_real_size(lay, w, h);
1455 txt_layout_free(lay);
1456 return 0;
1457 }
1458
game_menu_box_width(int show,const char * txt,int width)1459 int game_menu_box_width(int show, const char *txt, int width)
1460 {
1461 /* img_t menu; */
1462 int w, h, mw, mh;
1463 int x, y;
1464 int b = game_theme.border_w;
1465 int pad = game_theme.pad;
1466 layout_t lay = NULL;
1467
1468 el(el_menu)->drawn = 0;
1469
1470 if (el_layout(el_menu)) {
1471 _txt_layout_free(el_layout(el_menu));
1472 lay = el_layout(el_menu);
1473 }
1474
1475 if (menubg) {
1476 game_cursor(CURSOR_CLEAR);
1477 gfx_copy(menubg, mx, my);
1478 gfx_free_image(menubg);
1479 menubg = NULL;
1480 }
1481
1482 if (menu) {
1483 gfx_free_image(menu);
1484 menu = NULL;
1485 }
1486
1487 if (!DIRECT_MODE) {
1488 el_clear(el_menu_button);
1489
1490 if (!show)
1491 el_draw(el_menu_button);
1492 }
1493
1494 if (!show) {
1495 menu_shown = 0;
1496 game_cursor(CURSOR_DRAW);
1497 gfx_flip();
1498 return 0;
1499 }
1500 instead_render_callback();
1501 menu_shown = 1;
1502
1503 if (!lay) {
1504 lay = txt_layout(game_theme.menu_font, ALIGN_CENTER, game_theme.w - 2 * (b + pad), 0);
1505 txt_layout_color(lay, game_theme.menu_fg);
1506 txt_layout_link_color(lay, game_theme.menu_link);
1507 txt_layout_active_color(lay, game_theme.menu_alink);
1508 txt_layout_font_height(lay, game_theme.menu_font_height);
1509 }
1510 else
1511 txt_layout_set_size(lay, game_theme.w - 2 * (b + pad), 0);
1512
1513 txt_layout_set(lay, (char*)txt);
1514 txt_layout_real_size(lay, &w, &h);
1515 if (width)
1516 w = width;
1517
1518 txt_layout_set_size(lay, w, h);
1519 txt_layout_set(lay, (char*)txt);
1520 menu = gfx_new(w + (b + pad)*2, h + (b + pad)*2);
1521 gfx_img_fill(menu, 0, 0, w + (b + pad)*2, h + (b + pad)*2, game_theme.border_col);
1522 gfx_img_fill(menu, b, b, w + pad*2, h + pad*2, game_theme.menu_bg);
1523 gfx_set_alpha(menu, game_theme.menu_alpha);
1524 x = (game_theme.w - w)/2;
1525 y = (game_theme.h - h)/2;
1526 mx = x - b - pad;
1527 my = y - b - pad;
1528 mw = w + (b + pad) * 2;
1529 mh = h + (b + pad) * 2;
1530
1531 game_cursor(CURSOR_CLEAR);
1532 menubg = gfx_grab_screen(mx, my, mw, mh);
1533 gfx_draw(menu, mx, my);
1534 el_set(el_menu, elt_layout, /*game_theme.win_x*/ x, y, lay);
1535 el_draw(el_menu);
1536 game_cursor(CURSOR_DRAW);
1537 gfx_flip();
1538 return 0;
1539 }
1540
game_menu_box(int show,const char * txt)1541 int game_menu_box(int show, const char *txt)
1542 {
1543 int w = 0, rc;
1544 if (show && cur_menu != menu_wait)
1545 gfx_cancel_change_screen();
1546 if (show)
1547 game_event("pause");
1548 if (cur_menu == menu_games) { /* hack a bit :( */
1549 w = games_menu_maxw();
1550 game_menu_gen();
1551 } else if (cur_menu == menu_themes) {
1552 w = themes_menu_maxw();
1553 game_menu_gen();
1554 }
1555 rc = game_menu_box_width(show, txt, w);
1556 if (!show) {
1557 game_event("resume");
1558 }
1559 #ifdef __EMSCRIPTEN__
1560 if (!show)
1561 cfg_save();
1562 #endif
1563 return rc;
1564 }
1565
check_new_place(char * title)1566 int check_new_place(char *title)
1567 {
1568 int rc = 0;
1569 if (!title && !last_title)
1570 return 0;
1571
1572 if (!title && last_title) {
1573 rc = 1;
1574 } else if (!last_title || strcmp(title, last_title)) {
1575 rc = 1;
1576 }
1577 if (last_title) {
1578 free(last_title);
1579 }
1580 last_title = title;
1581 return rc;
1582 }
1583
check_new_pict(char * pict)1584 int check_new_pict(char *pict)
1585 {
1586 int rc = 0;
1587
1588 if (!pict && !last_pict)
1589 return 0;
1590
1591 if (!pict && last_pict) {
1592 rc = 1;
1593 } else if (!last_pict || strcmp(pict, last_pict)) {
1594 rc = 1;
1595 }
1596 if (last_pict) {
1597 free(last_pict);
1598 }
1599 last_pict = pict;
1600 return rc;
1601 }
1602
check_fading(int * new_scene)1603 static int check_fading(int *new_scene)
1604 {
1605 int rc;
1606 int st;
1607 instead_lock();
1608 instead_function("instead.get_fading", NULL);
1609 rc = instead_bretval(0);
1610 st = instead_iretval(1);
1611
1612 if (st < 0)
1613 st = 0;
1614 else if (st > 255)
1615 st = 255;
1616
1617 instead_clear();
1618 instead_unlock();
1619 if (new_scene)
1620 *new_scene = rc;
1621 return st;
1622 }
1623
game_autosave(void)1624 static void game_autosave(void)
1625 {
1626 int b,r;
1627 if (!curgame_dir)
1628 return;
1629 instead_lock();
1630 instead_function("instead.get_autosave", NULL);
1631 b = instead_bretval(0);
1632 r = instead_iretval(1);
1633 instead_clear();
1634 instead_unlock();
1635 if (b) {
1636 r = r % MAX_SAVE_SLOTS;
1637 game_save(r);
1638 /* instead_eval("game.autosave = false;"); instead_clear();*/
1639 }
1640 }
1641
game_instead_restart(void)1642 static void game_instead_restart(void)
1643 {
1644 int b;
1645 if (!curgame_dir)
1646 return;
1647 instead_lock();
1648 instead_function("instead.get_restart", NULL);
1649 b = instead_bretval(0);
1650 instead_clear();
1651 instead_unlock();
1652 need_restart = b;
1653 }
1654
game_instead_menu(void)1655 static void game_instead_menu(void)
1656 {
1657 char *menu;
1658 if (!curgame_dir)
1659 return;
1660 instead_lock();
1661 instead_function("instead.get_menu", NULL);
1662 menu = instead_retval(0);
1663 instead_clear();
1664 instead_unlock();
1665 if (!menu)
1666 return;
1667 if (!strcmp(menu, "save"))
1668 menu_toggle(menu_save);
1669 else if (!strcmp(menu, "load"))
1670 menu_toggle(menu_load);
1671 else if (!strcmp(menu, "quit") || !strcmp(menu, "exit"))
1672 menu_toggle(menu_askquit);
1673 else if (!strcmp(menu, "themes"))
1674 menu_toggle(menu_themes);
1675 else if (!strcmp(menu, "settings"))
1676 menu_toggle(menu_settings);
1677 else if (!strcmp(menu, "main"))
1678 menu_toggle(menu_main);
1679 else
1680 menu_toggle(-1);
1681 free(menu);
1682 }
1683
get_inv(void)1684 static char *get_inv(void)
1685 {
1686 char *ni;
1687 struct instead_args args[] = {
1688 { .val = NULL, .type = INSTEAD_BOOL },
1689 { .val = NULL, },
1690 };
1691 args[0].val = (INV_MODE(game_theme.inv_mode) == INV_MODE_HORIZ)?"true":"false";
1692 instead_lock();
1693 instead_function("instead.get_inv", args);
1694 ni = instead_retval(0);
1695 instead_clear();
1696 instead_unlock();
1697 return ni;
1698 }
1699
find_diff_pos(const char * p1,const char * p2)1700 static int find_diff_pos(const char *p1, const char *p2)
1701 {
1702 int pos = 0;
1703 if (!p1 || !p2)
1704 return -1;
1705
1706 while ((*p1 == *p2) && *p1) {
1707 p1 ++;
1708 p2 ++;
1709 pos ++;
1710 }
1711 if (!*p1)
1712 return -1;
1713 return pos;
1714 }
1715
scroll_to_diff(const char * cmdstr,int cur_off,int new_scene)1716 static void scroll_to_diff(const char *cmdstr, int cur_off, int new_scene)
1717 {
1718 int off = 0;
1719 int pos = 0;
1720 int h = 0;
1721 int hh = 0;
1722
1723 off = txt_layout_anchor(txt_box_layout(el_box(el_scene)), &hh); /* <a:#> tag? */
1724 if (off == -1) {
1725 if (new_scene)
1726 return; /* nothing to do */
1727 pos = find_diff_pos(cmdstr, last_cmd);
1728 if (pos != -1)
1729 off = txt_layout_pos2off(txt_box_layout(el_box(el_scene)), pos, &hh);
1730 if (off == -1)
1731 off = cur_off;
1732 }
1733
1734 el_size(el_scene, NULL, &h);
1735
1736 if (game_theme.win_scroll_mode == 2 && (cur_off <= off && cur_off + h >= off + hh)) { /* do not scroll */
1737 off = cur_off;
1738 }
1739
1740 txt_box_scroll(el_box(el_scene), off);
1741 }
1742
scroll_to_last(void)1743 static void scroll_to_last(void)
1744 {
1745 int w, h;
1746
1747 txt_layout_size(txt_box_layout(el_box(el_scene)), &w, &h);
1748 txt_box_scroll(el_box(el_scene), h);
1749 }
1750
1751 int game_highlight(int x, int y, int on);
1752
game_pict_modify(img_t p)1753 int game_pict_modify(img_t p)
1754 {
1755 static int modify = 0;
1756 int last = modify;
1757 game_bg_modify(p);
1758 if (p && ((el_img(el_spic) == p) || p == gfx_screen(NULL)))
1759 modify = 1;
1760 else if (!p) /* use NULL to reset modify flag */
1761 modify = 0;
1762 return last;
1763 }
1764
game_bg_modify(img_t p)1765 int game_bg_modify(img_t p)
1766 {
1767 static int modify = 0;
1768 int last = modify;
1769 if (p && p == game_theme.bg)
1770 modify = 1;
1771 else if (!p) /* use NULL to reset modify flag */
1772 modify = 0;
1773 return last;
1774 }
1775
game_pict_clip(void)1776 static void game_pict_clip(void)
1777 {
1778 int x, y, w, h;
1779
1780 if (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_EMBEDDED) {
1781 el_clip(el_scene);
1782 return;
1783 }
1784
1785 if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_FLOAT) {
1786 x = game_theme.win_x;
1787 y = game_theme.win_y;
1788 w = game_theme.win_w;
1789 h = game_theme.win_h;
1790 } else {
1791 x = game_theme.gfx_x;
1792 y = game_theme.gfx_y;
1793 w = game_theme.max_scene_w;
1794 h = game_theme.max_scene_h;
1795 }
1796 gfx_clip(x, y, w, h);
1797 }
1798
game_gfx_clip(void)1799 void game_gfx_clip(void)
1800 {
1801 if (game_theme.bg) {
1802 gfx_img_clip(game_theme.bg, game_theme.xoff, game_theme.yoff,
1803 game_theme.w - 2 * game_theme.xoff,
1804 game_theme.h - 2 * game_theme.yoff);
1805 }
1806 gfx_clip(game_theme.xoff, game_theme.yoff,
1807 game_theme.w - 2 * game_theme.xoff,
1808 game_theme.h - 2 * game_theme.yoff);
1809 }
1810
game_gfx_noclip(void)1811 void game_gfx_noclip(void)
1812 {
1813 if (game_theme.bg) {
1814 gfx_img_noclip(game_theme.bg);
1815 }
1816 gfx_noclip();
1817 }
1818
game_redraw_pic(void)1819 static void game_redraw_pic(void)
1820 {
1821 int x, y, ox, oy;
1822 if (game_pict_coord(&x, &y, NULL, NULL))
1823 return;
1824 game_pict_clip();
1825
1826 ox = el(el_spic)->x;
1827 oy = el(el_spic)->y;
1828
1829 el(el_spic)->x = x;
1830 el(el_spic)->y = y;
1831
1832 el_clear(el_spic);
1833 el_draw(el_spic);
1834
1835 gfx_noclip();
1836 el_update(el_spic);
1837
1838 el(el_spic)->x = ox;
1839 el(el_spic)->y = oy;
1840 }
1841
1842 static xref_t hl_xref = NULL;
1843 static struct el *hl_el = NULL;
1844
1845 static struct {
1846 img_t offscreen;
1847 int flags;
1848 int m_restore;
1849 } fade_ctx;
1850
after_click(int flags,int m_restore)1851 static void after_click(int flags, int m_restore)
1852 {
1853 if (DIRECT_MODE)
1854 return;
1855 #if 1
1856 {
1857 int x, y;
1858 if (!(flags & GAME_CMD_NOHL) &&
1859 (!m_restore || mouse_restore())) {
1860 gfx_cursor(&x, &y);
1861 game_highlight(x, y, 1); /* highlight new scene, to avoid flickering */
1862 }
1863 }
1864 #endif
1865 }
1866
1867 extern void instead_ready(void);
after_cmd(void)1868 static void after_cmd(void)
1869 {
1870 game_autosave();
1871 game_instead_restart();
1872 game_instead_menu();
1873 game_gfx_commit(0);
1874 }
1875
after_fading(void * aux)1876 static void after_fading(void *aux)
1877 {
1878 gfx_start_anim(el_img(el_spic));
1879 gfx_free_image(fade_ctx.offscreen);
1880 game_render_callback_redraw();
1881 after_click(fade_ctx.flags, fade_ctx.m_restore);
1882 after_cmd();
1883 game_cursor(CURSOR_DRAW);
1884 game_flip();
1885 }
1886
game_redraw_all(void)1887 void game_redraw_all(void)
1888 {
1889 if (menu_shown || DIRECT_MODE)
1890 return;
1891 game_clear_all();
1892 el_draw(el_title);
1893 el_draw(el_ways);
1894 if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_EMBEDDED)
1895 el_draw(el_spic);
1896 el_draw(el_scene);
1897 if (inv_enabled())
1898 el_draw(el_inv);
1899 el_draw(el_menu_button);
1900 game_update(0, 0, game_theme.w, game_theme.h);
1901 }
1902
game_render_callback_redraw(void)1903 static int game_render_callback_redraw(void)
1904 {
1905 if (instead_render_callback_dirty(0) == -1) {
1906 instead_render_callback_dirty(1); /* disable updates */
1907 game_redraw_all();
1908 return 1;
1909 }
1910 return 0;
1911 }
1912
game_cmd(char * cmd,int flags)1913 int game_cmd(char *cmd, int flags)
1914 {
1915 int old_off;
1916 int fading = 0;
1917 int new_pict = 0;
1918 int new_place = 0;
1919 int redraw_pict = 0;
1920 int title_h = 0, ways_h = 0, pict_h = 0;
1921 char buf[1024];
1922 char *cmdstr = NULL;
1923 char *invstr = NULL;
1924 char *waystr = NULL;
1925 char *title = NULL;
1926 char *pict = NULL;
1927 img_t oldscreen = NULL;
1928 int dd = (DIRECT_MODE);
1929 int rc = 0;
1930 int new_scene = 0;
1931 int m_restore = 0;
1932 int win_spacing;
1933
1934 if (menu_shown)
1935 return -1;
1936 /* if (dd) */
1937 game_cursor(CURSOR_CLEAR);
1938
1939 instead_lock();
1940 if (flags & GAME_CMD_FILE) /* file command */
1941 cmdstr = instead_file_cmd(cmd, &rc);
1942 else
1943 cmdstr = instead_cmd(cmd, &rc);
1944 instead_unlock();
1945 instead_ready();
1946 if (opt_click && (flags & GAME_CMD_CLICK) && !rc)
1947 sound_play_click();
1948
1949 if (DIRECT_MODE) {
1950 if (cmdstr)
1951 free(cmdstr);
1952
1953 if (game_theme_changed) { /* cursor change only? */
1954 img_t offscreen = gfx_new(game_theme.w, game_theme.h);
1955 if (!offscreen)
1956 goto fatal;
1957 oldscreen = gfx_screen(offscreen);
1958 gfx_copy(oldscreen, 0, 0);
1959 if ((rc = game_theme_update()))
1960 goto out;
1961 offscreen = gfx_screen(oldscreen);
1962 gfx_change_screen(offscreen, 1, NULL, NULL);
1963 gfx_free_image(offscreen);
1964 }
1965
1966 if (game_pict_modify(NULL))
1967 goto out;
1968 return rc;
1969 } else if (dd) { /* disable direct mode on the fly */
1970 game_theme_changed = 1; /* force redraw */
1971 game_cursor(CURSOR_DRAW);
1972 }
1973
1974 if (!cmdstr) {
1975 game_render_callback_redraw();
1976 if (game_bg_modify(NULL)) {
1977 game_redraw_all();
1978 } else if (game_pict_modify(NULL)) /* redraw pic only */
1979 game_redraw_pic();
1980 if (!rc) {
1981 if (hl_el == el(el_inv)) {
1982 m_restore = !(flags & GAME_CMD_CLICK);
1983 mouse_reset(0);
1984 }
1985 goto inv; /* hackish? ok, yes it is... */
1986 }
1987 if (instead_render_callback_dirty(-1) == 1) {
1988 game_cursor(CURSOR_DRAW);
1989 game_flip();
1990 }
1991 goto err; /* really nothing to do */
1992 }
1993
1994 if (game_bg_modify(NULL))
1995 game_theme_changed = 1; /* force redraw */
1996
1997 m_restore = !(flags & GAME_CMD_CLICK);
1998 mouse_reset(0); /* redraw all, so, reset mouse */
1999
2000 fading = check_fading(&new_scene);
2001
2002 instead_lock();
2003 instead_function("instead.get_title", NULL);
2004 title = instead_retval(0);
2005 instead_clear();
2006 instead_unlock();
2007
2008 new_place = check_new_place(title);
2009
2010 instead_lock();
2011 instead_function("instead.get_picture", NULL);
2012 pict = instead_retval(0);
2013 instead_clear();
2014 instead_unlock();
2015
2016 unix_path(pict);
2017
2018 new_pict = check_new_pict(pict);
2019
2020 if (game_theme_changed && !fading)
2021 fading = 1; /* one frame at least */
2022
2023 if (fading) { /* take old screen */
2024 img_t offscreen;
2025 game_cursor(CURSOR_CLEAR);
2026 offscreen = gfx_new(game_theme.w, game_theme.h);
2027 if (!offscreen)
2028 goto fatal;
2029 oldscreen = gfx_screen(offscreen);
2030 gfx_copy(oldscreen, 0, 0);
2031 }
2032
2033 game_render_callback_redraw();
2034
2035 if (game_theme_changed) {
2036 if ((rc = game_theme_update()))
2037 goto out;
2038 new_place = 1;
2039 if (pict)
2040 new_pict = 1;
2041 }
2042
2043 if (new_place)
2044 el_clear(el_title);
2045
2046 win_spacing = game_theme.font_size * game_theme.font_height / 2;
2047
2048 if (title && *title) {
2049 snprintf(buf, sizeof(buf), "<a:look>%s</a>", title);
2050 txt_layout_set(el_layout(el_title), buf);
2051 txt_layout_size(el_layout(el_title), NULL, &title_h);
2052 title_h += win_spacing;
2053 } else
2054 txt_layout_set(el_layout(el_title), NULL);
2055
2056 if (new_pict || fading || game_pict_modify(NULL) ||
2057 (new_place && (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_FIXED))) {
2058 redraw_pict = 1;
2059 }
2060 game_pict_clip();
2061
2062 if (redraw_pict) {
2063 if (el_img(el_spic)) {
2064 if (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_EMBEDDED)
2065 el_clear_nobg(el_spic);
2066 else
2067 el_clear(el_spic);
2068 if (new_pict) {
2069 gfx_free_image(el_img(el_spic));
2070 el(el_spic)->p.p = NULL;
2071 if (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_EMBEDDED) /* clear embedded gfx */
2072 txt_layout_add_img(txt_box_layout(el_box(el_scene)),
2073 "scene", NULL);
2074 }
2075 }
2076 }
2077
2078 if (pict) {
2079 int w, h, x;
2080 img_t img;
2081
2082 if (new_pict) {
2083 img = gfx_load_image(pict);
2084 if (!img)
2085 game_res_err_msg(pict, debug_sw);
2086 if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_FLOAT)
2087 img = game_pict_scale(img, game_theme.win_w, game_theme.max_scene_h);
2088 else
2089 img = game_pict_scale(img, game_theme.max_scene_w, game_theme.max_scene_h);
2090 } else
2091 img = el_img(el_spic);
2092
2093 if (img) {
2094 w = gfx_img_w(img);
2095 h = gfx_img_h(img);
2096 if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_FLOAT) {
2097 x = (game_theme.win_w - w)/2 + game_theme.win_x;
2098 if (redraw_pict)
2099 el_set(el_spic, elt_image, x, game_theme.win_y + title_h, img);
2100 } else {
2101 int xx, yy;
2102
2103 if (GFX_ALIGN(game_theme.gfx_mode) & ALIGN_TOP)
2104 yy = 0;
2105 else if (GFX_ALIGN(game_theme.gfx_mode) & ALIGN_BOTTOM)
2106 yy = game_theme.max_scene_h - h;
2107 else
2108 yy = (game_theme.max_scene_h - h)/2;
2109
2110 if (GFX_ALIGN(game_theme.gfx_mode) & ALIGN_LEFT)
2111 xx = 0;
2112 else if (GFX_ALIGN(game_theme.gfx_mode) & ALIGN_RIGHT)
2113 xx = game_theme.max_scene_w - w;
2114 else
2115 xx = (game_theme.max_scene_w - w)/2;
2116
2117 x = xx + game_theme.gfx_x;
2118 if (redraw_pict)
2119 el_set(el_spic, elt_image, x, game_theme.gfx_y + yy, img);
2120 }
2121 pict_h = h;
2122 }
2123 }
2124 gfx_noclip();
2125
2126 /* clear area */
2127 el_clear(el_ways);
2128
2129 el_clear(el_scene);
2130
2131 instead_lock();
2132 instead_function("instead.get_ways", NULL);
2133 waystr = instead_retval(0);
2134 instead_clear();
2135 instead_unlock();
2136
2137 if (waystr) {
2138 int l = strlen(waystr);
2139 if (l && waystr[l - 1] == '\n')
2140 waystr[l - 1] = 0;
2141 }
2142
2143 if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_EMBEDDED) {
2144 txt_layout_set(el_layout(el_ways), waystr);
2145 txt_layout_size(el_layout(el_ways), NULL, &ways_h);
2146 if ((ways_h == 0 || WAYS_BOTTOM) && pict_h != 0)
2147 pict_h += win_spacing;
2148 }
2149 old_off = txt_box_off(el_box(el_scene));
2150
2151 if (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_EMBEDDED) {
2152 char *p;
2153 pict_h = 0; /* to fake code bellow */
2154 txt_box_resize(el_box(el_scene), game_theme.win_w, game_theme.win_h - title_h - ways_h - pict_h);
2155 txt_layout_set(txt_box_layout(el_box(el_scene)), ""); /* hack, to null layout, but not images */
2156 if (el_img(el_spic)) {
2157 txt_layout_add_img(txt_box_layout(el_box(el_scene)),"scene", el_img(el_spic));
2158 txt_layout_add(txt_box_layout(el_box(el_scene)), "<c><g:scene></c>\n");
2159 }
2160 p = malloc(strlen(cmdstr) + ((waystr)?strlen(waystr):0) + 16);
2161 if (p) {
2162 *p = 0;
2163 if (!WAYS_BOTTOM && waystr && *waystr) {
2164 strcpy(p, waystr);
2165 strcat(p, "\n");
2166 } else if (el_img(el_spic))
2167 strcat(p, "\n");
2168 strcat(p, cmdstr);
2169 if (WAYS_BOTTOM && waystr) {
2170 strcat(p, "\n");
2171 strcat(p, waystr);
2172 }
2173 free(cmdstr);
2174 cmdstr = p;
2175 } else { /* paranoia? Yes... */
2176 txt_layout_add(txt_box_layout(el_box(el_scene)), waystr);
2177 txt_layout_add(txt_box_layout(el_box(el_scene)), "<l></l>\n"); /* small hack */
2178 }
2179 txt_layout_add(txt_box_layout(el_box(el_scene)), cmdstr);
2180 txt_box_set(el_box(el_scene), txt_box_layout(el_box(el_scene)));
2181 } else {
2182 if (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_FLOAT)
2183 pict_h = 0;
2184 txt_box_resize(el_box(el_scene), game_theme.win_w, game_theme.win_h - title_h - ways_h - pict_h);
2185 txt_layout_set(txt_box_layout(el_box(el_scene)), cmdstr);
2186 txt_box_set(el_box(el_scene), txt_box_layout(el_box(el_scene)));
2187 }
2188
2189 if (WAYS_BOTTOM)
2190 el(el_ways)->y = game_theme.win_h - ways_h + game_theme.win_y;
2191 else
2192 el(el_ways)->y = el(el_title)->y + title_h + pict_h;
2193
2194 if (waystr)
2195 free(waystr);
2196
2197 if (WAYS_BOTTOM)
2198 el(el_scene)->y = el(el_title)->y + title_h + pict_h;
2199 else
2200 el(el_scene)->y = el(el_ways)->y + ways_h;
2201
2202 /*
2203 game_clear(game_theme.win_x, game_theme.win_y + pict_h + title_h,
2204 game_theme.win_w, game_theme.win_h - pict_h - title_h); */
2205
2206 /* draw title and ways */
2207 if (new_place)
2208 el_draw(el_title);
2209
2210 if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_EMBEDDED) {
2211 el_draw(el_ways);
2212 if (redraw_pict) {
2213 game_pict_clip();
2214 el_draw(el_spic);
2215 gfx_noclip();
2216 }
2217 }
2218
2219 if (game_theme.win_scroll_mode == 1 || game_theme.win_scroll_mode == 2) {
2220 scroll_to_diff(cmdstr, old_off, new_scene);
2221 } else if (game_theme.win_scroll_mode == 3) {
2222 scroll_to_last();
2223 }
2224 FREE(last_cmd);
2225 last_cmd = cmdstr;
2226
2227 el_draw(el_scene);
2228
2229 inv:
2230 if (inv_enabled()) {
2231 int off;
2232
2233 invstr = get_inv();
2234
2235 off = txt_box_off(el_box(el_inv));
2236 txt_layout_set(txt_box_layout(el_box(el_inv)), invstr);
2237 txt_box_set(el_box(el_inv), txt_box_layout(el_box(el_inv)));
2238 txt_box_scroll(el_box(el_inv), off);
2239
2240 if (invstr)
2241 free(invstr);
2242
2243 el_clear(el_inv);
2244 el_draw(el_inv);
2245 }
2246
2247 if (fading) {
2248 img_t offscreen;
2249 game_cursor(CURSOR_OFF);
2250 gfx_stop_anim(el_img(el_spic));
2251 instead_render_callback();
2252 offscreen = gfx_screen(oldscreen);
2253 fade_ctx.offscreen = offscreen;
2254 fade_ctx.flags = flags;
2255 fade_ctx.m_restore = m_restore;
2256 gfx_change_screen(offscreen, fading, after_fading, offscreen);
2257 return 0;
2258 }
2259 after_click(flags, m_restore);
2260 out:
2261 game_cursor(CURSOR_DRAW);
2262 game_flip();
2263 /* input_clear(); */
2264 err:
2265 after_cmd();
2266 return rc;
2267 fatal:
2268 fprintf(stderr, "Fatal error! (can't alloc offscreen)\n");
2269 exit(1);
2270 }
2271
2272
2273 /*
2274 void game_update(int x, int y, int w, int h)
2275 {
2276 game_cursor(CURSOR_DRAW);
2277 gfx_update(x, y, w, h);
2278 }*/
2279
game_xref_update(xref_t xref,int x,int y)2280 void game_xref_update(xref_t xref, int x, int y)
2281 {
2282 if (instead_render_callback_dirty(-1) == -1)
2283 return;
2284 game_cursor(CURSOR_CLEAR);
2285 xref_update(xref, x, y, game_clear, game_update);
2286 }
2287
2288 xref_t use_xref = NULL;
2289
2290
disable_use(void)2291 int disable_use(void)
2292 {
2293 if (use_xref) {
2294 xref_set_active(use_xref, 0);
2295 if (xref_layout(use_xref) == txt_box_layout(el_box(el_inv)))
2296 game_xref_update(use_xref, el(el_inv)->x, el(el_inv)->y);
2297 else
2298 game_xref_update(use_xref, el(el_scene)->x, el(el_scene)->y);
2299 use_xref = NULL;
2300 return 1;
2301 }
2302 return 0;
2303 }
2304
enable_use(xref_t xref)2305 void enable_use(xref_t xref)
2306 {
2307 use_xref = xref;
2308 xref_set_active(xref, 1);
2309 if (xref_layout(use_xref) == txt_box_layout(el_box(el_inv)))
2310 game_xref_update(use_xref, el(el_inv)->x, el(el_inv)->y);
2311 else
2312 game_xref_update(use_xref, el(el_scene)->x, el(el_scene)->y);
2313 }
2314
2315
look_obj(int x,int y)2316 struct el *look_obj(int x, int y)
2317 {
2318 int i;
2319 for (i = 0; i < el_max; i++) {
2320 int w, h;
2321
2322 if (el(i)->drawn && el(i)->id == el_menu) {
2323 return el(i);
2324 }
2325 if (x < el(i)->x || y < el(i)->y || !el(i)->drawn)
2326 continue;
2327 el_size(i, &w, &h);
2328 if (x >= el(i)->x && y >= el(i)->y && x < el(i)->x + w && y < el(i)->y + h)
2329 return el(i);
2330 }
2331 return NULL;
2332 }
2333
2334 static xref_t get_nearest_xref(int i, int mx, int my);
2335
look_xref(int x,int y,struct el ** elem)2336 xref_t look_xref(int x, int y, struct el **elem)
2337 {
2338 struct el *o;
2339 int type;
2340 xref_t xref = NULL;
2341 o = look_obj(x, y);
2342 if (elem)
2343 *elem = o;
2344 if (!o)
2345 return NULL;
2346 type = o->type;
2347 if (type == elt_layout)
2348 xref = txt_layout_xref(o->p.lay, x - o->x, y - o->y);
2349 else if (type == elt_box)
2350 xref = txt_box_xref(o->p.box, x - o->x, y - o->y);
2351 #if defined(ANDROID) || defined(IOS) || defined(SAILFISHOS) || defined(WINRT)
2352 if (!xref) {
2353 int xc, yc, r;
2354 xref = get_nearest_xref(o->id, x, y);
2355 if (!xref)
2356 return NULL;
2357 r = fnt_height(txt_layout_font(xref_layout(xref))) * 2; /* radius is here */
2358 if (!xref_position(xref, &xc, &yc)) {
2359 if (o->type == elt_box && yc)
2360 yc -= txt_box_off(el_box(o->id));
2361 xc += o->x;
2362 yc += o->y;
2363 if (((x - xc)*(x - xc) + (y - yc)*(y - yc)) < (r * r))
2364 return xref;
2365 }
2366 return NULL;
2367 }
2368 #endif
2369 return xref;
2370 }
2371
2372 static char click_xref[1024];
2373 static struct el *click_el = NULL;
2374 static unsigned long click_time = 0;
2375 static int click_x = -1;
2376 static int click_y = -1;
menu_visible(void)2377 int menu_visible(void)
2378 {
2379 if (menu_shown)
2380 return cur_menu;
2381 return 0;
2382 }
2383
game_freezed(void)2384 int game_freezed(void)
2385 {
2386 return browse_dialog || menu_shown || gfx_fading() || minimized();
2387 }
2388
2389
game_paused(void)2390 int game_paused(void)
2391 {
2392 return game_freezed() || (use_xref && game_wait_use) || instead_busy();
2393 }
2394
menu_update(struct el * elem)2395 void menu_update(struct el *elem)
2396 {
2397 gfx_copy(menubg, mx, my);
2398 gfx_draw(menu, mx, my);
2399 txt_layout_draw(elem->p.lay, elem->x, elem->y);
2400 gfx_update(mx, my, gfx_img_w(menu), gfx_img_h(menu));
2401 /* gfx_fill(x, y, w, h, game_theme.menu_bg); */
2402 }
2403
game_highlight(int x,int y,int on)2404 int game_highlight(int x, int y, int on)
2405 {
2406 struct el *elem = NULL;
2407 xref_t xref = NULL;
2408
2409 if (on == 1) {
2410 xref = look_xref(x, y, &elem);
2411 if (xref && opt_hl && !xref_get_active(xref)) {
2412 xref_set_active(xref, 1);
2413 game_xref_update(xref, elem->x, elem->y);
2414 }
2415 }
2416
2417 if (hl_xref != xref && hl_el) {
2418 if (hl_xref != use_xref && xref_get_active(hl_xref)) {
2419 xref_set_active(hl_xref, 0);
2420 game_xref_update(hl_xref, hl_el->x, hl_el->y);
2421 }
2422 }
2423
2424 hl_xref = xref;
2425 hl_el = elem;
2426
2427 return 0;
2428 }
2429
2430
mouse_reset(int hl)2431 void mouse_reset(int hl)
2432 {
2433 if (hl && (menu_shown || !DIRECT_MODE)) {
2434 game_highlight(-1, -1, 0);
2435 } else
2436 hl_xref = hl_el = NULL;
2437
2438 disable_use();
2439
2440 motion_mode = 0;
2441 click_el = NULL;
2442 click_xref[0] = 0;
2443 }
2444
2445
menu_toggle(int menu)2446 void menu_toggle(int menu)
2447 {
2448 int on = menu_shown;
2449 mouse_reset(1);
2450 on ^= 1;
2451 if (!on)
2452 cur_menu = menu_main;
2453 else if (menu != -1)
2454 cur_menu = menu;
2455 top_menu = cur_menu;
2456 game_menu_box(on, game_menu_gen());
2457 menu_shown = on;
2458 }
2459
scroll_pup(int id)2460 static int scroll_pup(int id)
2461 {
2462 int hh;
2463 if (box_isscroll_up(id))
2464 return -1;
2465 el_size(el_scene, NULL, &hh);
2466 txt_box_scroll(el_box(id), -hh);
2467 el_clear(id);
2468 el_draw(id);
2469 el_update(id);
2470 return 0;
2471 }
2472
scroll_pdown(int id)2473 static int scroll_pdown(int id)
2474 {
2475 int hh;
2476 if (box_isscroll_down(id))
2477 return -1;
2478 el_size(el_scene, NULL, &hh);
2479 txt_box_scroll(el_box(id), hh);
2480 el_clear(id);
2481 el_draw(id);
2482 el_update(id);
2483 return 0;
2484 }
2485
mouse_filter(int filter)2486 int mouse_filter(int filter)
2487 {
2488 static unsigned long old_counter = 0;
2489 if (!opt_filter || !mouse_filter_delay)
2490 return 0;
2491 if (filter && (old_counter - timer_counter <= (mouse_filter_delay / HZ))) /* 400 ms */
2492 return -1;
2493 old_counter = timer_counter;
2494 return 0;
2495 }
2496 /* action: 0 - first click,1 - second, -1 - restore */
game_click(int x,int y,int action,int filter)2497 int game_click(int x, int y, int action, int filter)
2498 {
2499 int menu_mode = 0;
2500 int use_mode = 0;
2501 int go_mode = 0;
2502 struct el *elem = NULL;
2503 char buf[1024];
2504 xref_t xref = NULL;
2505 char *xref_txt = NULL;
2506 xref_t new_xref = NULL;
2507 struct el *new_elem = NULL;
2508
2509 int was_motion = (motion_mode == 2);
2510
2511 if (!action) {
2512 click_x = x;
2513 click_y = y;
2514 motion_y = y;
2515 click_time = timer_counter;
2516 } else if (action == 1) {
2517 click_x = -1;
2518 click_y = -1;
2519 }
2520
2521 if (action)
2522 motion_mode = 0;
2523
2524 if (action == 1) {
2525 char *link;
2526
2527 new_xref = look_xref(x, y, &new_elem);
2528 link = (new_xref)?xref_get_text(new_xref):"";
2529
2530 if (new_elem != click_el || strcmp(link, click_xref)) {
2531 click_el = NULL;
2532 new_xref = NULL;
2533 new_elem = NULL;
2534 if (click_xref[0]) {
2535 click_xref[0] = 0;
2536 return 0; /* just filtered */
2537 }
2538 }
2539 }
2540
2541 if (action == 1) {
2542 xref = new_xref;
2543 elem = new_elem;
2544 click_xref[0] = 0;
2545 click_el = NULL;
2546 } else { /* just press */
2547 xref = look_xref(x, y, &elem);
2548 click_xref[0] = 0;
2549 if (xref) {
2550 xref_set_active(xref, 1);
2551 game_xref_update(xref, elem->x, elem->y);
2552 } else if (elem && elem->type == elt_box && opt_motion &&
2553 (!box_isscroll_up(elem->id) || !box_isscroll_down(elem->id))) {
2554 motion_mode = 1;
2555 motion_id = elem->id;
2556 return 0;
2557 }
2558 if (xref) {
2559 snprintf(click_xref, sizeof(click_xref), "%s", xref_get_text(xref));
2560 click_xref[sizeof(click_xref) - 1] = 0;
2561 }
2562 click_el = elem;
2563 return 0;
2564 }
2565 /* now look only second press */
2566
2567
2568 if (!xref) {
2569 if (elem) {
2570 if (elem->id == el_menu_button) {
2571 menu_toggle(-1);
2572 } else if (elem->id == el_sdown) {
2573 scroll_pdown(el_scene);
2574 } else if (elem->id == el_sup) {
2575 scroll_pup(el_scene);
2576 } else if (elem->id == el_idown) {
2577 scroll_pdown(el_inv);
2578 } else if (elem->id == el_iup) {
2579 scroll_pup(el_inv);
2580 } else disable_use();
2581 /* el_update(el_inv); */
2582 motion_mode = 0;
2583 } else if (!(was_motion && MOTION_TIME)) {
2584 disable_use();
2585 /* el_update(el_inv);
2586 gfx_flip(); */
2587 }
2588 return 0;
2589 }
2590 /* second xref */
2591
2592 if (elem->id == el_menu) {
2593 /* xref_set_active(xref, 0);
2594 txt_layout_update_links(elem->p.lay, elem->x, elem->y, game_clear); */
2595 if (game_menu_act(xref_get_text(xref))) {
2596 return -1;
2597 }
2598 /* game_menu_box(menu_shown, game_menu_gen());
2599 gfx_flip(); */
2600 return 1;
2601 }
2602
2603 xref_txt = xref_get_text(xref);
2604
2605 if (!strncmp("act ", xref_get_text(xref), 4)) {
2606 menu_mode = 1;
2607 xref_txt += 4;
2608 } else if (!strncmp("use ", xref_get_text(xref), 4)) {
2609 use_mode = 1;
2610 xref_txt += 4;
2611 } else if (!strncmp("go ", xref_get_text(xref), 3)) {
2612 go_mode = 1;
2613 /* xref_txt += 3; */
2614 } else if (!strncmp("obj/act ", xref_get_text(xref), 8)) {
2615 if (!use_xref)
2616 xref_txt += 4; /* act */
2617 else
2618 xref_txt += 8; /* obj */
2619 } else if (elem->id == el_inv) {
2620 use_mode = 1;
2621 }
2622
2623 if (!use_xref) {
2624 if (use_mode) {
2625 enable_use(xref);
2626 /* el_update(el_inv); */
2627 return 0;
2628 }
2629 if (menu_mode) {
2630 if (elem->id == el_inv)
2631 snprintf(buf, sizeof(buf), "use %s", xref_txt);
2632 else
2633 snprintf(buf, sizeof(buf), "act %s", xref_txt);
2634 } else
2635 snprintf(buf, sizeof(buf), "%s", xref_txt);
2636 if (mouse_filter(filter))
2637 return 0;
2638 buf[sizeof(buf) - 1] = 0;
2639 game_cmd(buf, GAME_CMD_CLICK);
2640 return 1;
2641 }
2642
2643 if (menu_mode || go_mode || elem->id == el_title)
2644 return 0;
2645
2646 if (use_xref == xref)
2647 snprintf(buf,sizeof(buf), "use %s", xref_txt);
2648 else {
2649 if (!strncmp("use ", xref_get_text(use_xref), 4)) /* already use */
2650 snprintf(buf,sizeof(buf), "%s,%s", xref_get_text(use_xref), xref_txt);
2651 else
2652 snprintf(buf,sizeof(buf), "use %s,%s", xref_get_text(use_xref), xref_txt);
2653 }
2654 if (mouse_filter(filter))
2655 return 0;
2656
2657 disable_use();
2658 buf[sizeof(buf) - 1] = 0;
2659 game_cmd(buf, GAME_CMD_CLICK);
2660 return 1;
2661 }
2662
mouse_restore(void)2663 int mouse_restore(void)
2664 {
2665 if (click_x == -1 || click_y == -1)
2666 return -1;
2667 game_click(click_x, click_y, -1, 0);
2668 return 0;
2669 }
2670
2671 int game_cursor_show = 1;
2672
game_cursor(int on)2673 void game_cursor(int on)
2674 {
2675 static img_t grab = NULL;
2676 static img_t cur = NULL;
2677 static int xc = 0, yc = 0, w = 0, h = 0; /*, w, h; */
2678 int xx, yy, ww, hh;
2679 gfx_getclip(&xx, &yy, &ww, &hh);
2680 gfx_noclip();
2681 if (on == CURSOR_OFF)
2682 cur = NULL;
2683
2684 if (grab) {
2685 gfx_copy(grab, xc, yc);
2686 gfx_free_image(grab);
2687 grab = NULL;
2688 }
2689
2690 if (on == CURSOR_OFF) {
2691 _game_update(xc, yc, w, h);
2692 goto out;
2693 }
2694
2695 if (on == CURSOR_CLEAR)
2696 goto out;
2697
2698 if (on != CURSOR_DRAW)
2699 cur = (use_xref) ? game_theme.use:game_theme.cursor;
2700 if (!cur)
2701 goto out;
2702 do {
2703 int ox = xc;
2704 int oy = yc;
2705 int ow = w;
2706 int oh = h;
2707
2708 if (on != CURSOR_DRAW) {
2709 gfx_cursor(&xc, &yc);
2710 xc -= game_theme.cur_x;
2711 yc -= game_theme.cur_y;
2712 }
2713
2714 w = gfx_img_w(cur);
2715 h = gfx_img_h(cur);
2716
2717 grab = gfx_grab_screen(xc, yc, w, h);
2718 if (!nocursor_sw && mouse_focus() && (game_cursor_show || menu_shown))
2719 gfx_draw(cur, xc, yc);
2720
2721 if (on != CURSOR_DRAW) {
2722 _game_update(xc, yc, w, h);
2723 _game_update(ox, oy, ow, oh);
2724 }
2725 } while (0);
2726 out:
2727 gfx_clip(xx, yy, ww, hh);
2728 return;
2729 }
2730
2731
scroll_up(int id,int count)2732 static void scroll_up(int id, int count)
2733 {
2734 /* int i; */
2735 if (box_isscroll_up(id))
2736 return;
2737 txt_box_scroll(el_box(id), -(FONT_SZ(game_theme.font_size)) * count);
2738 el_clear(id);
2739 el_draw(id);
2740 el_update(id);
2741 }
2742
scroll_down(int id,int count)2743 static void scroll_down(int id, int count)
2744 {
2745 /* int i; */
2746 if (box_isscroll_down(id))
2747 return;
2748 txt_box_scroll(el_box(id), (FONT_SZ(game_theme.font_size)) * count);
2749 el_clear(id);
2750 el_draw(id);
2751 el_update(id);
2752 }
2753
scroll_possible(int id,int off)2754 static int scroll_possible(int id, int off)
2755 {
2756 if (!off || (off > 0 && box_isscroll_down(id)) ||
2757 (off < 0 && box_isscroll_up(id)))
2758 return -1;
2759 return 0;
2760 }
2761
scroll_motion(int id,int off)2762 static void scroll_motion(int id, int off)
2763 {
2764 if (scroll_possible(id, off))
2765 return;
2766 game_highlight(-1, -1, 0);
2767 txt_box_scroll(el_box(id), off);
2768 el_clear(id);
2769 el_draw(id);
2770 el_update(id);
2771 }
2772
2773
2774 static int sel_el = 0;
2775
frame_next(void)2776 static void frame_next(void)
2777 {
2778 if (sel_el == el_scene && !inv_enabled())
2779 sel_el = el_inv;
2780 switch(sel_el) {
2781 default:
2782 case 0:
2783 sel_el = el_scene;
2784 break;
2785 case el_ways:
2786 sel_el = el_scene;
2787 break;
2788 case el_scene:
2789 sel_el = el_inv;
2790 break;
2791 case el_inv:
2792 if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_EMBEDDED &&
2793 txt_layout_xrefs(el_layout(el_ways)))
2794 sel_el = el_ways;
2795 else
2796 sel_el = el_scene;
2797 break;
2798 }
2799 }
2800
frame_prev(void)2801 static void frame_prev(void)
2802 {
2803 switch(sel_el) {
2804 default:
2805 case 0:
2806 sel_el = el_inv;
2807 break;
2808 case el_title:
2809 sel_el = el_inv;
2810 break;
2811 case el_ways:
2812 sel_el = el_inv;
2813 break;
2814 case el_scene:
2815 if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_EMBEDDED &&
2816 txt_layout_xrefs(el_layout(el_ways)))
2817 sel_el = el_ways;
2818 else
2819 sel_el = el_inv;
2820 break;
2821 case el_inv:
2822 sel_el = el_scene;
2823 break;
2824 }
2825 if (sel_el == el_inv && !inv_enabled())
2826 sel_el = el_scene;
2827 }
2828
2829 static int select_ref(int prev, int last);
2830 static xref_t get_xref(int i, int last);
2831 static void xref_jump(xref_t xref, struct el* elem);
2832
2833
select_frame(int prev)2834 static void select_frame(int prev)
2835 {
2836 struct el *elem = NULL;
2837 int x, y, w, h;
2838
2839 gfx_cursor(&x, &y);
2840
2841 elem = look_obj(x, y);
2842
2843 if (elem)
2844 sel_el = elem->id;
2845
2846 el(sel_el)->mx = x;
2847 el(sel_el)->my = y;
2848
2849 if (menu_shown) {
2850 sel_el = el_menu;
2851 } else {
2852 /* int old_sel;
2853 if (!sel_el)
2854 frame_next();
2855 old_sel = sel_el;
2856 do { */
2857 if (prev) {
2858 frame_prev();
2859 } else {
2860 frame_next();
2861 }
2862 /* } while (!get_xref(sel_el, 0) && sel_el != old_sel); */
2863 }
2864 el_size(sel_el, &w, &h);
2865 x = el(sel_el)->mx;
2866 y = el(sel_el)->my;
2867 if (x < el(sel_el)->x || y < el(sel_el)->y ||
2868 x > el(sel_el)->x + w || y > el(sel_el)->y + h) {
2869 x = el(sel_el)->x + w / 2;
2870 y = el(sel_el)->y + h / 2;
2871 }
2872
2873 gfx_warp_cursor(x, y);
2874
2875 if (!look_xref(x, y, &elem) && elem) {
2876 xref_t xref = get_nearest_xref(elem->id, x, y);
2877 xref_jump(xref, elem);
2878 }
2879 }
2880
xref_rel_position(xref_t xref,struct el * elem,int * x,int * y)2881 static int xref_rel_position(xref_t xref, struct el *elem, int *x, int *y)
2882 {
2883 int rc = xref_position(xref, x, y);
2884 if (!rc && elem->type == elt_box && y) {
2885 *y -= txt_box_off(el_box(elem->id));
2886 }
2887 return rc;
2888 }
2889
xref_visible(xref_t xref,struct el * elem)2890 static int xref_visible(xref_t xref, struct el *elem)
2891 {
2892 int x, y, w, h;
2893 if (!elem || !xref)
2894 return -1;
2895
2896 if (xref_rel_position(xref, elem, &x, &y))
2897 return -1;
2898
2899 el_size(elem->id, &w, &h);
2900 if (y < 0 || y >= h)
2901 return -1;
2902 return 0;
2903 }
2904
get_nearest_xref(int i,int mx,int my)2905 static xref_t get_nearest_xref(int i, int mx, int my)
2906 {
2907 xref_t xref = NULL;
2908 xref_t min_xref = NULL;
2909 int min_disp = game_theme.h * game_theme.h + game_theme.w * game_theme.w;
2910 if (!i)
2911 return NULL;
2912 for (xref = get_xref(i, 0); !xref_visible(xref, el(i)); xref = xref_next(xref)) {
2913 int x, y, disp;
2914 if (xref_rel_position(xref, el(i), &x, &y))
2915 continue;
2916
2917 disp = (x + el(i)->x - mx) * (x + el(i)->x - mx) + (y + el(i)->y - my) * (y + el(i)->y - my);
2918 if (disp < min_disp) {
2919 min_disp = disp;
2920 min_xref = xref;
2921 }
2922 }
2923 return min_xref;
2924 }
2925
get_xref(int i,int last)2926 static xref_t get_xref(int i, int last)
2927 {
2928 xref_t xref = NULL;
2929 int type;
2930 type = el(i)->type;
2931 if (type == elt_layout) {
2932 xref = txt_layout_xrefs(el_layout(i));
2933 while (last && xref && xref_next(xref))
2934 xref = xref_next(xref);
2935 } else if (type == elt_box) {
2936 xref = txt_box_xrefs(el_box(i));
2937 while (!last && xref && !xref_visible(xref_prev(xref), el(i))) /* try find visible one */
2938 xref = xref_prev(xref);
2939 while (last && xref && !xref_visible(xref_next(xref), el(i)))
2940 xref = xref_next(xref);
2941 }
2942 return xref;
2943 }
2944
xref_jump(xref_t xref,struct el * elem)2945 static void xref_jump(xref_t xref, struct el* elem)
2946 {
2947 int x, y;
2948 if (!elem || !xref || xref_visible(xref, elem) ||
2949 xref_rel_position(xref, elem, &x, &y))
2950 return;
2951 gfx_warp_cursor(elem->x + x, elem->y + y);
2952 }
2953
select_ref(int prev,int last)2954 static int select_ref(int prev, int last)
2955 {
2956 int x, y;
2957 struct el *elem = NULL;
2958 xref_t xref = NULL;
2959 gfx_cursor(&x, &y);
2960
2961 xref = look_xref(x, y, &elem);
2962
2963 if (!elem) {
2964 if (!sel_el)
2965 select_frame(0);
2966 elem = el(sel_el);
2967 }
2968 if (last) {
2969 if (!(xref = get_xref(elem->id, !prev)))
2970 return -1;
2971 } else if (xref) {
2972 if (prev) {
2973 do {
2974 xref = xref_prev(xref);
2975 } while (xref && xref_valid(xref));
2976 if (!xref || xref_visible(xref, elem)) {
2977 if (!box_isscroll_up(elem->id) || !box_isscroll_down(elem->id))
2978 return -1;
2979 else
2980 xref = get_xref(elem->id, 1);
2981 }
2982 } else {
2983 do {
2984 xref = xref_next(xref);
2985 } while (xref && xref_valid(xref));
2986 if (!xref || xref_visible(xref, elem)) {
2987 if (!box_isscroll_down(elem->id) || !box_isscroll_up(elem->id))
2988 return -1;
2989 else
2990 xref = get_xref(elem->id, 0);
2991 }
2992 }
2993 }
2994 if (!xref)
2995 xref = get_nearest_xref(elem->id, x, y);
2996 if (!xref)
2997 return -1;
2998 xref_jump(xref, elem);
2999 return 0;
3000 }
3001
game_scroll_up(int count)3002 static void game_scroll_up(int count)
3003 {
3004 int xm, ym;
3005 struct el *o;
3006 gfx_cursor(&xm, &ym);
3007 o = look_obj(xm, ym);
3008 if (o && (o->id == el_scene || o->id == el_inv)) {
3009 scroll_up(o->id, count);
3010 }
3011 }
3012
game_scroll_down(int count)3013 static void game_scroll_down(int count)
3014 {
3015 int xm, ym;
3016 struct el *o;
3017 gfx_cursor(&xm, &ym);
3018 o = look_obj(xm, ym);
3019 if (o && (o->id == el_scene || o->id == el_inv)) {
3020 scroll_down(o->id, count);
3021 }
3022 }
3023
game_scroll_pup(void)3024 static int game_scroll_pup(void)
3025 {
3026 int xm, ym;
3027 struct el *o;
3028 gfx_cursor(&xm, &ym);
3029 o = look_obj(xm, ym);
3030 if (o && (o->id == el_scene || o->id == el_inv)) {
3031 return scroll_pup(o->id);
3032 }
3033 return -1;
3034 }
3035
game_scroll_pdown(void)3036 static int game_scroll_pdown(void)
3037 {
3038 int xm, ym;
3039 struct el *o;
3040 gfx_cursor(&xm, &ym);
3041 o = look_obj(xm, ym);
3042 if (o && (o->id == el_scene || o->id == el_inv)) {
3043 return scroll_pdown(o->id);
3044 }
3045 return -1;
3046 }
3047
is_key(struct inp_event * ev,const char * name)3048 static int is_key(struct inp_event *ev, const char *name)
3049 {
3050 return strcmp(ev->sym, name);
3051 }
game_pict_coord(int * x,int * y,int * w,int * h)3052 int game_pict_coord(int *x, int *y, int *w, int *h)
3053 {
3054 img_t img;
3055 int ww, hh;
3056 int xx, yy;
3057 word_t word;
3058
3059 img = el_img(el_spic);
3060 if (!img)
3061 return -1;
3062 if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_EMBEDDED) {
3063 xx = el(el_spic)->x;
3064 yy = el(el_spic)->y;
3065 ww = gfx_img_w(img);
3066 hh = gfx_img_h(img);
3067 goto out;
3068 }
3069 el_size(el_scene, &ww, &hh);
3070 for (word = NULL; (word = txt_layout_words(txt_box_layout(el_box(el_scene)), word)); ) { /* scene */
3071 if (word_image(word) != img) {
3072 word = NULL;
3073 /* first word is always pic */
3074 break;
3075 /* continue; */
3076 }
3077 word_geom(word, &xx, &yy, &ww, &hh);
3078 yy -= txt_box_off(el_box(el_scene));
3079 xx += el(el_scene)->x;
3080 yy += el(el_scene)->y;
3081 break;
3082 }
3083 if (!word)
3084 return -1;
3085 out:
3086 if (x)
3087 *x = xx;
3088 if (y)
3089 *y = yy;
3090 if (w)
3091 *w = ww;
3092 if (h)
3093 *h = hh;
3094 return 0;
3095 }
3096
game_pic_click(int x,int y,int * ox,int * oy)3097 static int game_pic_click(int x, int y, int *ox, int *oy)
3098 {
3099 int xx, yy, ww, hh;
3100
3101 if (game_pict_coord(&xx, &yy, &ww, &hh))
3102 return -1;
3103
3104 if (x >= xx && y >= yy && x < (xx + ww) && y < (yy + hh)) {
3105 *ox = x - xx;
3106 *oy = y - yy;
3107 if (ww)
3108 *ox = (int)((float)(*ox) * (float)game_pic_w / (float)ww);
3109 else
3110 *ox = 0;
3111 if (hh)
3112 *oy = (int)((float)(*oy) * (float)game_pic_h / (float)hh);
3113 else
3114 *oy = 0;
3115 return 0;
3116 }
3117 return -1;
3118 }
3119
game_bg_click(int mb,int x,int y,int * ox,int * oy)3120 static int game_bg_click(int mb, int x, int y, int *ox, int *oy)
3121 {
3122 struct el *o = NULL;
3123 struct game_theme *t = &game_theme;
3124 int bg = 1;
3125 if (x < t->xoff || y < t->yoff || x >= (t->w - t->xoff) || y >= (t->h - t->yoff))
3126 bg = 0;
3127 else
3128 o = look_obj(x, y);
3129 *ox = (int)((float)(x - t->xoff) / (float)t->scale);
3130 *oy = (int)((float)(y - t->yoff) / (float)t->scale);
3131
3132 if (!game_grab_events && ((o && (o->id == el_sup || o->id == el_sdown ||
3133 o->id == el_iup || o->id == el_idown ||
3134 o->id == el_menu_button)) ||
3135 look_xref(x, y, NULL)))
3136 return -1; /* ask Odyssey for that ;) */
3137
3138 if (bg || mb == EV_CODE_FINGER) /* fingers area may be larger */
3139 return 0;
3140 return -1;
3141 }
3142
game_event(const char * ev)3143 static int game_event(const char *ev)
3144 {
3145 char *p; int rc;
3146 struct instead_args args[8];
3147 if (!curgame_dir)
3148 return -1;
3149 if (game_paused())
3150 return -1;
3151 args[0].val = "event"; args[0].type = INSTEAD_STR;
3152 args[1].val = ev; args[1].type = INSTEAD_STR;
3153 args[2].val = NULL;
3154 instead_lock();
3155 if (instead_function("iface:input", args)) {
3156 instead_clear();
3157 instead_unlock();
3158 return -1;
3159 }
3160 p = instead_retval(0); instead_clear();
3161 instead_unlock();
3162 if (!p)
3163 return -1;
3164 rc = game_cmd(p, GAME_CMD_NOHL); free(p);
3165 return (rc)?-1:0;
3166 }
3167
game_input(int down,const char * key,int x,int y,int mb)3168 static int game_input(int down, const char *key, int x, int y, int mb)
3169 {
3170 char *p;
3171 struct instead_args args[8];
3172 int rc = 0;
3173
3174 char tx[16];
3175 char ty[16];
3176 char tpx[16];
3177 char tpy[16];
3178 char tmb[16];
3179
3180 if (game_paused())
3181 return -1;
3182
3183 if (mb == EV_CODE_KBD) {
3184 const char *k;
3185 args[0].val = "kbd"; args[0].type = INSTEAD_STR;
3186 args[1].val = (down)?"true":"false"; args[1].type = INSTEAD_BOOL;
3187 k = (key) ? key:"unknown";
3188 args[2].val = (char*)k; args[2].type = INSTEAD_STR;
3189 args[3].val = NULL;
3190 } else if (mb == EV_CODE_TEXT) {
3191 const char *k;
3192 args[0].val = "text"; args[0].type = INSTEAD_STR;
3193 k = (key) ? key:"";
3194 args[1].val = (char*)k; args[1].type = INSTEAD_STR;
3195 args[2].val = NULL;
3196 } else {
3197 const char *k;
3198 int px = -1;
3199 int py = -1;
3200 game_pic_click(x, y, &px, &py); /* got picture coord */
3201 if (game_bg_click(mb, x, y, &x, &y)) { /* no click on bg */
3202 return -1;
3203 }
3204 snprintf(tx, sizeof(tx), "%d", x);
3205 snprintf(ty, sizeof(ty), "%d", y);
3206 snprintf(tmb, sizeof(tmb), "%d", mb);
3207 if (mb == EV_CODE_FINGER) {
3208 args[0].val = "finger"; args[0].type = INSTEAD_STR;
3209 } else {
3210 args[0].val = "mouse"; args[0].type = INSTEAD_STR;
3211 }
3212 args[1].val = (down)?"true":"false"; args[1].type = INSTEAD_BOOL;
3213 if (mb == EV_CODE_FINGER) {
3214 k = (key)?key:"unknown";
3215 args[2].val = k; args[2].type = INSTEAD_STR;
3216 } else {
3217 args[2].val = tmb; args[2].type = INSTEAD_NUM;
3218 }
3219 args[3].val = tx; args[3].type = INSTEAD_NUM;
3220 args[4].val = ty; args[4].type = INSTEAD_NUM;
3221 args[5].val = NULL;
3222 if (px != -1) {
3223 snprintf(tpx, sizeof(tpx), "%d", px);
3224 snprintf(tpy, sizeof(tpy), "%d", py);
3225 args[5].val = tpx; args[5].type = INSTEAD_NUM;
3226 args[6].val = tpy; args[6].type = INSTEAD_NUM;
3227 args[7].val = NULL;
3228 }
3229 }
3230 instead_lock();
3231 if (instead_function("iface:input", args)) {
3232 instead_clear();
3233 instead_unlock();
3234 return -1;
3235 }
3236
3237 p = instead_retval(0); instead_clear();
3238 instead_unlock();
3239 if (!p) {
3240 return -1;
3241 }
3242
3243 rc = game_cmd(p, (mb >= 0 || mb == EV_CODE_FINGER)?GAME_CMD_CLICK:0);
3244 free(p);
3245
3246 return (rc)?-1:0;
3247 }
3248
3249 extern char zip_game_dirname[];
3250 extern int unpack(const char *zipfilename, const char *dirname);
3251 #ifdef _USE_BROWSE
game_from_disk(void)3252 int game_from_disk(void)
3253 {
3254 int i = 0;
3255 char *g, *p, *b, *d;
3256 char dir[PATH_MAX];
3257 char base[PATH_MAX];
3258 #ifndef MAEMO
3259 #ifndef S60
3260 if (opt_fs) {
3261 int old_menu = (menu_shown) ? cur_menu: -1;
3262 opt_fs ^= 1;
3263 game_restart();
3264 if (old_menu != -1)
3265 game_menu(old_menu);
3266 }
3267 #endif
3268 #endif
3269 mouse_cursor(1);
3270 game_cursor(CURSOR_OFF);
3271 browse_dialog = 1;
3272 getdir(dir, sizeof(dir));
3273 #ifdef LC_MESSAGES
3274 setlocale(LC_MESSAGES, "");
3275 #endif
3276 g = p = open_file_dialog();
3277 #ifdef LC_MESSAGES
3278 setlocale(LC_MESSAGES, "C");
3279 #endif
3280 setdir(dir); /* dir can be changed */
3281 browse_dialog = 0;
3282 game_cursor(CURSOR_ON);
3283 mouse_cursor(0);
3284 gfx_flip();
3285 if (!p)
3286 return -1;
3287 game_done(0);
3288 strcpy(dir, p);
3289 strcpy(base, p);
3290 d = dir; b = base;
3291 i = strlen(d);
3292 if (i && d[i - 1] != '/') { /* file */
3293 if (!idf_magic(d)) {
3294 d = dirname(d);
3295 strcpy(b, d);
3296 }
3297 }
3298 d = dirname(d);
3299 b = basename(b);
3300 #ifdef _USE_UNPACK
3301 p = games_sw ? games_sw:game_local_games_path(1);
3302 fprintf(stderr,"Trying to install: %s\n", g);
3303 if (!unpack(g, p)) {
3304 if (!zip_game_dirname[0])
3305 goto err;
3306 if (games_replace(p, zip_game_dirname))
3307 goto clean;
3308 p = zip_game_dirname;
3309 } else if (zip_game_dirname[0]) { /* error, needs to clean */
3310 goto clean;
3311 #else
3312 if (0) {
3313 #endif
3314 } else if (games_replace(d, b)) {
3315 goto err;
3316 } else
3317 p = b;
3318
3319 if (game_init(p)) {
3320 game_error();
3321 }
3322 return 0;
3323 #ifdef _USE_UNPACK
3324 clean:
3325 p = getpath(p, zip_game_dirname);
3326 fprintf(stderr, "Cleaning: '%s'...\n", p);
3327 remove_dir(p);
3328 free(p);
3329 err:
3330 game_error();
3331 return -1;
3332 #endif
3333 }
3334 #endif
3335
3336 static int game_input_events(struct inp_event *ev)
3337 {
3338 if (!curgame_dir)
3339 return 0;
3340 if (ev->type == KEY_DOWN || ev->type == KEY_UP) {
3341 #if defined(ANDROID) || defined(IOS)
3342 if (!is_key(ev, "f12") && !menu_shown) /* f12 is keyboard toggle */
3343 return 0;
3344 #endif
3345 if (!game_input((ev->type == KEY_DOWN), ev->sym, -1, -1, EV_CODE_KBD))
3346 return 1;
3347 } else if (ev->type == FINGER_DOWN || ev->type == FINGER_UP) {
3348 if (!game_input((ev->type == FINGER_DOWN), ev->sym, ev->x, ev->y, EV_CODE_FINGER))
3349 return 1;
3350 } else if (ev->type == MOUSE_DOWN || ev->type == MOUSE_UP) {
3351 if (!game_input((ev->type == MOUSE_DOWN), "mouse", ev->x, ev->y, ev->code))
3352 return 1;
3353 } else if (ev->type == KEY_TEXT) {
3354 if (!game_input(1, ev->sym, -1, -1, EV_CODE_TEXT))
3355 return 1;
3356 }
3357 return 0;
3358 }
3359
3360 static int alt_pressed = 0;
3361 static int shift_pressed = 0;
3362 static int control_pressed = 0;
3363
3364 static int kbd_modifiers(struct inp_event *ev)
3365 {
3366 if (ev->type != KEY_DOWN && ev->type != KEY_UP)
3367 return 0;
3368 if (!is_key(ev, "left alt") || !is_key(ev, "right alt")) {
3369 alt_pressed = (ev->type == KEY_DOWN);
3370 return 1;
3371 } else if (!is_key(ev, "left shift") || !is_key(ev, "right shift")) {
3372 shift_pressed = (ev->type == KEY_DOWN);
3373 return 1;
3374 } else if (!is_key(ev, "left ctrl") || !is_key(ev, "right ctrl")) {
3375 control_pressed = (ev->type == KEY_DOWN);
3376 return 1;
3377 }
3378 return 0;
3379 }
3380
3381 static int is_key_back(struct inp_event *ev)
3382 {
3383 if (!is_key(ev, "escape")
3384 #if defined(S60) || defined(_WIN32_WCE) || defined(WINRT)
3385 || !is_key(ev, "space")
3386 #endif
3387 #if defined(_WIN32_WCE) || defined(WINRT)
3388 || (ev->code >= 0xc0 && ev->code <= 0xcf) ||
3389 !is_key(ev, "f1") ||
3390 !is_key(ev, "f2") ||
3391 !is_key(ev, "f3") ||
3392 !is_key(ev, "f4") ||
3393 !is_key(ev, "f5")
3394 #endif
3395 #ifdef ANDROID
3396 || ev->code == 118
3397 #endif
3398 )
3399 return 0;
3400 return -1;
3401 }
3402
3403 static int kbd_instead(struct inp_event *ev, int *x, int *y)
3404 {
3405 if (ev->type != KEY_DOWN)
3406 return 0;
3407
3408 if (!is_key_back(ev)) {
3409 if (use_xref)
3410 disable_use();
3411 else
3412 menu_toggle(-1);
3413 } else if (!is_key(ev, "f1")) {
3414 if (!menu_shown)
3415 menu_toggle(-1);
3416 } else if (!is_key(ev, "f2") && curgame_dir) {
3417 game_menu(menu_save);
3418 } else if (!is_key(ev, "f3") && curgame_dir) {
3419 game_menu(menu_load);
3420 } else if (!is_key(ev, "f8") && curgame_dir && !menu_shown) {
3421 if (game_saves_enabled())
3422 game_save(9);
3423 } else if (!is_key(ev, "f9") && curgame_dir && !menu_shown) {
3424 if (game_saves_enabled()) {
3425 if (!access(game_save_path(0, 9), R_OK)) {
3426 if (!game_reset())
3427 game_load(9);
3428 }
3429 }
3430 #if defined(ANDROID) || defined(IOS)
3431 } else if (!is_key(ev, "f12") && curgame_dir && !menu_shown) {
3432 input_text(!(input_text(-1) > 0));
3433 #endif
3434 } else if (!is_key(ev, "f5") && curgame_dir && !menu_shown) {
3435 mouse_reset(1);
3436 game_cmd("look", 0);
3437 } else if ((alt_pressed || control_pressed) && !is_key(ev, "r") && curgame_dir && !menu_shown && debug_sw) {
3438 mouse_reset(1);
3439 game_menu_act("/new");
3440 shift_pressed = alt_pressed = control_pressed = 0;
3441 } else if (!is_key(ev, "f10")
3442 #ifdef ANDROID
3443 || ev->code == 270
3444 #endif
3445 ) {
3446 #ifdef ANDROID
3447 return -1;
3448 #else
3449 game_menu(menu_askquit);
3450 #endif
3451 } else if ((alt_pressed | control_pressed) && (!is_key(ev, "q") || !is_key(ev, "f4"))) {
3452 game_running = 0;
3453 return -1;
3454 } else if (alt_pressed &&
3455 (!is_key(ev, "enter") || !is_key(ev, "return"))) {
3456 int old_menu = (menu_shown) ? cur_menu: -1;
3457 shift_pressed = alt_pressed = control_pressed = 0;
3458 opt_fs ^= 1;
3459 game_restart();
3460 if (old_menu != -1)
3461 game_menu(old_menu);
3462 } else if (!is_key(ev, "f4") && !alt_pressed && !standalone_sw) {
3463 #ifdef _USE_UNPACK
3464 #ifdef _USE_BROWSE
3465 mouse_reset(1);
3466 if (!game_from_disk()) {
3467 shift_pressed = alt_pressed = control_pressed = 0;
3468 }
3469 #endif
3470 #endif
3471 } else if (DIRECT_MODE && !menu_shown) {
3472 ; /* nothing todo */
3473 } else if (!alt_pressed && (!is_key(ev, "return") || !is_key(ev, "enter")
3474 #ifdef S60
3475 || !is_key(ev, ".")
3476 #endif
3477 )) {
3478 gfx_cursor(x, y);
3479 game_highlight(-1, -1, 0); /* reset */
3480
3481 game_click(*x, *y, 0, 0);
3482 game_highlight(*x, *y, 1); /* hl on/off */
3483 game_highlight(*x, *y, 0);
3484
3485 if (game_click(*x, *y, 1, 0) == -1) {
3486 game_running = 0;
3487 return -1;
3488 }
3489 } else if (!is_key(ev, "tab")) {
3490 select_frame(shift_pressed);
3491 } else if (!is_key(ev, "up") || !is_key(ev, "down") ||
3492 !is_key(ev, "[8]") || !is_key(ev, "[2]")) {
3493
3494 int lm;
3495 int prev = !is_key(ev, "up") || !is_key(ev, "[8]");
3496
3497 if (opt_kbd == KBD_INVERSE)
3498 lm = (alt_pressed || shift_pressed);
3499 else
3500 lm = (!alt_pressed && !shift_pressed);
3501
3502 if (menu_shown || lm) {
3503 if (select_ref(prev, 0)) {
3504 if (opt_kbd == KBD_SMART) {
3505 (prev)?game_scroll_up(1):game_scroll_down(1);
3506 select_ref(prev, 1);
3507 } else
3508 select_ref(prev, 1);
3509 }
3510 } else
3511 (prev)?game_scroll_up(1):game_scroll_down(1);
3512 } else if (!is_key(ev, "page up") || !is_key(ev, "[9]") ||
3513 !is_key(ev, "page down") || !is_key(ev, "[3]")) {
3514 int lm;
3515 int prev = !is_key(ev, "page up") || !is_key(ev, "[9]");
3516
3517 if (opt_kbd == KBD_INVERSE)
3518 lm = (alt_pressed || shift_pressed);
3519 else
3520 lm = (!alt_pressed && !shift_pressed);
3521 if (menu_shown || lm) {
3522 if (select_ref(prev, 0) || select_ref(prev, 1)) {
3523 if (opt_kbd == KBD_SMART) {
3524 int s = (prev)?game_scroll_pup():game_scroll_pdown();
3525 if (!s)
3526 select_ref(!prev, 1);
3527 } else
3528 select_ref(prev, 0);
3529 }
3530 } else {
3531 if (prev)
3532 game_scroll_pup();
3533 else
3534 game_scroll_pdown();
3535 }
3536 #if !defined(S60) && !defined(_WIN32_WCE) && !defined(WINRT)
3537 } else if (!is_key(ev, "left") || !is_key(ev, "[4]")) {
3538 select_ref(1, 0);
3539 } else if (!is_key(ev, "right") || !is_key(ev, "[6]")) {
3540 select_ref(0, 0);
3541 } else if (!is_key(ev, "backspace") && !menu_shown) {
3542 scroll_pup(el_scene);
3543 } else if (!is_key(ev, "space") && !menu_shown) {
3544 scroll_pdown(el_scene);
3545 #else
3546 } else if (!is_key(ev, "left") || !is_key(ev, "[4]")) {
3547 if (menu_shown)
3548 select_ref(1, 0);
3549 else
3550 select_frame(1);
3551 } else if (!is_key(ev, "right") || !is_key(ev, "[6]")) {
3552 if (menu_shown)
3553 select_ref(0, 0);
3554 else
3555 select_frame(0);
3556 #endif
3557 } else
3558 return 0;
3559 return 1;
3560 }
3561
3562 static int mouse_instead(struct inp_event *ev, int *x, int *y)
3563 {
3564 if (ev->type == MOUSE_DOWN) {
3565 if (ev->code != 1)
3566 disable_use();
3567 else {
3568 game_highlight(-1, -1, 0);
3569 game_click(ev->x, ev->y, 0, 1);
3570 *x = ev->x;
3571 *y = ev->y;
3572 }
3573 } else if (ev->type == MOUSE_UP && ev->code == 1) {
3574 game_highlight(-1, -1, 0);
3575 if (game_click(ev->x, ev->y, 1, 1) == -1)
3576 return -1;
3577 } else if (ev->type == MOUSE_WHEEL_UP && !menu_shown) {
3578 game_scroll_up(ev->count);
3579 } else if (ev->type == MOUSE_WHEEL_DOWN && !menu_shown) {
3580 game_scroll_down(ev->count);
3581 } else if (ev->type == MOUSE_MOTION) {
3582 if (opt_motion && !motion_mode && click_el &&
3583 MOTION_TIME &&
3584 !scroll_possible(click_el->id, click_y - ev->y)) {
3585 motion_id = click_el->id;
3586 motion_y = click_y;
3587 motion_mode = 1;
3588 click_el = NULL;
3589 click_xref[0] = 0;
3590 }
3591 if (motion_mode) {
3592 motion_mode = 2;
3593 scroll_motion(motion_id, motion_y - ev->y);
3594 motion_y = ev->y;
3595 }
3596 /* game_highlight(ev.x, ev.y, 1); */
3597 }
3598 return 0;
3599 }
3600
3601 static __inline int game_cycle(void)
3602 {
3603 static int x = 0, y = 0, rc;
3604 struct inp_event ev;
3605 ev.x = -1;
3606
3607 /* game_cursor(CURSOR_CLEAR); */ /* release bg */
3608 if (((rc = input(&ev, 1)) == AGAIN) && !need_restart) {
3609 game_gfx_commit(1);
3610 return rc;
3611 }
3612
3613 if (!rc || gfx_fading()) /* just skip */
3614 return 0;
3615
3616 if (rc == -1) {/* close */
3617 goto out;
3618 } else if (game_input_events(&ev)) { /* kbd, mouse and touch -> pass in game */
3619 ; /* all is done in game_input */
3620 } else if (kbd_modifiers(&ev)) { /* ctrl, alt, shift */
3621 ; /* got modifiers */
3622 } else if ((rc = kbd_instead(&ev, &x, &y))) { /* ui keys */
3623 if (rc < 0)
3624 goto out;
3625 } else if (DIRECT_MODE && !menu_shown) {
3626 ; /* nothing todo */
3627 } else if ((rc = mouse_instead(&ev, &x, &y)) < 0) { /* ui mouse */
3628 goto out;
3629 }
3630
3631 if (gfx_fading()) /* just fading */
3632 return 0;
3633
3634 game_render_callback_redraw();
3635 if (need_restart) {
3636 need_restart = 0;
3637 game_menu_act("/new");
3638 }
3639 if ((!DIRECT_MODE || menu_shown)) {
3640 if (click_xref[0]) {
3641 game_highlight(x, y, 1);
3642 } else if (!motion_mode) {
3643 int x, y;
3644 gfx_cursor(&x, &y);
3645 game_highlight(x, y, 1);
3646 }
3647 }
3648 game_cursor(CURSOR_ON);
3649 if (instead_err()) {
3650 game_menu(menu_warning);
3651 }
3652 rc = 0;
3653 out:
3654 game_flip();
3655 game_gfx_commit(rc < 0);
3656 if (rc < 0)
3657 game_render_callback_redraw();
3658 return rc;
3659 }
3660 #ifdef __EMSCRIPTEN__
3661 const char *em_beforeunload(int eventType, const void *reserved, void *userData)
3662 {
3663 if (curgame_dir) {
3664 game_event("quit");
3665 if (opt_autosave)
3666 game_save(0);
3667 game_cfg_save();
3668 }
3669 return NULL;
3670 }
3671
3672 static void game_void_cycle(void)
3673 {
3674 int rc;
3675 while ((rc = game_cycle()) == AGAIN);
3676 if (rc < 0) {
3677 cfg_save();
3678 if (curgame_dir)
3679 game_done(0);
3680 gfx_clear(0, 0, game_theme.w, game_theme.h);
3681 gfx_flip();
3682 gfx_commit();
3683 emscripten_cancel_main_loop();
3684 emscripten_force_exit(1);
3685 }
3686 }
3687 #endif
3688 int game_loop(void)
3689 {
3690 #ifdef __EMSCRIPTEN__
3691 emscripten_set_beforeunload_callback(NULL, em_beforeunload);
3692 emscripten_set_main_loop(game_void_cycle, 0, 0);
3693 return -1;
3694 #else
3695 while (game_running) {
3696 if (game_cycle() < 0) {
3697 break;
3698 }
3699 }
3700 #endif
3701 return 0;
3702 }
3703
3704 extern int instead_bits_init(void);
3705 extern int instead_timer_init(void);
3706 extern int instead_sprites_init(void);
3707 extern int instead_sound_init(void);
3708 extern int instead_paths_init(void);
3709
3710 int game_instead_extensions(void)
3711 {
3712 instead_bits_init();
3713 instead_timer_init();
3714 instead_sprites_init();
3715 instead_sound_init();
3716 instead_paths_init();
3717 return 0;
3718 }
3719