2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include <gdk/gdkkeysyms.h>
19 #include <glib/gi18n.h>
22 #include "caveengine.h"
23 #include "caveobject.h"
24 #include "cavesound.h"
26 #include "c64import.h"
35 #include "editorexport.h"
42 static gboolean paused
=FALSE
;
43 static gboolean fast_forward
=FALSE
;
44 static gboolean fullscreen
=FALSE
;
46 typedef struct _gd_main_window
{
48 GtkActionGroup
*actions_normal
, *actions_title
, *actions_title_replay
, *actions_game
, *actions_snapshot
;
49 GtkWidget
*scroll_window
;
50 GtkWidget
*drawing_area
, *title_image
, *story_label
; /* three things that could be drawn in the main window */
51 GdkPixmap
**title_pixmaps
;
52 GtkWidget
*labels
; /* parts of main window which have to be shown or hidden */
53 GtkWidget
*label_topleft
, *label_topright
;
54 GtkWidget
*label_bottomleft
, *label_bottomright
;
55 GtkWidget
*label_variables
;
56 GtkWidget
*error_hbox
, *error_label
;
57 GtkWidget
*menubar
, *toolbar
;
58 GtkWidget
*replay_image_align
;
63 static GdMainWindow main_window
;
65 static gboolean key_fire_1
=FALSE
, key_fire_2
=FALSE
, key_right
=FALSE
, key_up
=FALSE
, key_down
=FALSE
, key_left
=FALSE
, key_suicide
=FALSE
;
66 static gboolean key_alt_status_bar
=FALSE
;
67 static gboolean restart
; /* keys which control the game, but are handled differently than the above */
68 static int mouse_cell_x
=-1, mouse_cell_y
=-1, mouse_cell_click
=0;
71 static GdCave
*snapshot
=NULL
;
77 static void main_stop_game_but_maybe_highscore();
78 static void main_free_game();
83 /************************************************
89 /* closing the window by the window manager.
90 * ask the user if he want to save the cave first, and
91 * only then do quit the main loop. */
93 main_window_delete_event (GtkWidget
* widget
, GdkEvent
* event
, gpointer data
)
95 if (gd_discard_changes(main_window
.window
))
100 /* keypress. key_* can be (event_type==gdk_key_press), as we
101 * connected this function to key press and key release.
104 main_window_keypress_event (GtkWidget
* widget
, GdkEventKey
* event
, gpointer data
)
106 gboolean press
=event
->type
==GDK_KEY_PRESS
; /* true for press, false for release */
108 if (event
->keyval
==gd_gtk_key_left
) {
112 if (event
->keyval
==gd_gtk_key_right
) {
116 if (event
->keyval
==gd_gtk_key_up
) {
120 if (event
->keyval
==gd_gtk_key_down
) {
124 if (event
->keyval
==gd_gtk_key_fire_1
) {
128 if (event
->keyval
==GDK_Shift_L
) {
129 key_alt_status_bar
=press
;
132 if (event
->keyval
==gd_gtk_key_fire_2
) {
136 if (event
->keyval
==gd_gtk_key_suicide
) {
141 return FALSE
; /* if any other key, we did not process it. go on, let gtk handle it. */
144 /* focus leaves game play window. remember that keys are not pressed!
145 as we don't receive key released events along with focus out. */
147 main_window_focus_out_event(GtkWidget
*widget
, GdkEvent
*event
, gpointer data
)
156 key_alt_status_bar
=FALSE
;
161 /* for mouse play. */
162 /* mouse leaves drawing area event */
163 /* if pointer not inside window, no control of player. */
165 drawing_area_leave_event(GtkWidget
* widget
, GdkEventCrossing
* event
, gpointer data
)
173 /* mouse button press event */
175 drawing_area_button_event(GtkWidget
* widget
, GdkEventButton
* event
, gpointer data
)
177 /* see if it is a click or a release. */
178 mouse_cell_click
=event
->type
== GDK_BUTTON_PRESS
;
182 /* mouse motion event */
184 drawing_area_motion_event(GtkWidget
* widget
, GdkEventMotion
* event
, gpointer data
)
187 GdkModifierType state
;
190 gdk_window_get_pointer (event
->window
, &x
, &y
, &state
);
197 mouse_cell_x
=x
/ gd_cell_size_game
;
198 mouse_cell_y
=y
/ gd_cell_size_game
;
205 * draws the cave during game play. also called by the timers,
206 * and the drawing area expose event.
209 drawing_area_draw_cave()
213 if (!main_window
.drawing_area
->window
)
215 if (!main_window
.game
)
217 if (!main_window
.game
->cave
) /* if already no cave, just return */
219 if (!main_window
.game
->gfx_buffer
) /* if already no cave, just return */
222 /* x and y are cave coordinates, which might not start from 0, as the top or the left hand side of the cave might not be visible. */
223 /* on the other hand, xd and yd are screen coordinates (of course, multiply by cell size) */
224 /* inside the loop, calculate cave coordinates, which might not be equal to screen coordinates. */
225 /* top left part of cave:
228 *y=0 wwww|wwwwwwwwwwwwww
229 *yd=0 ----+--------------
230 * w...|o p........ visible part
231 * w...|..d...........
233 /* before drawing, process all pending events for the drawing area, so no confusion occurs. */
234 for (y
=main_window
.game
->cave
->y1
, yd
=0; y
<=main_window
.game
->cave
->y2
; y
++, yd
++) {
235 for (x
=main_window
.game
->cave
->x1
, xd
=0; x
<=main_window
.game
->cave
->x2
; x
++, xd
++) {
236 if (main_window
.game
->gfx_buffer
[y
][x
] & GD_REDRAW
) {
237 main_window
.game
->gfx_buffer
[y
][x
] &= ~GD_REDRAW
;
238 gdk_draw_drawable(main_window
.drawing_area
->window
, main_window
.drawing_area
->style
->black_gc
, gd_game_pixmap(main_window
.game
->gfx_buffer
[y
][x
]),
239 0, 0, xd
*gd_cell_size_game
, yd
*gd_cell_size_game
, gd_cell_size_game
, gd_cell_size_game
);
248 drawing_area_expose_event(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer data
)
250 int x1
, y1
, x2
, y2
, xd
, yd
;
254 if (!main_window
.game
)
256 if (!main_window
.game
->cave
) /* if already no cave, just return */
258 if (!main_window
.game
->gfx_buffer
) /* if already no cave, just return */
261 /* redraw entire area, not specific rectangles.
262 * this function gets called only when the main window gets exposed
263 * by the user removing another window. */
264 /* these are screen coordinates. */
265 x1
=event
->area
.x
/gd_cell_size_game
;
266 y1
=event
->area
.y
/gd_cell_size_game
;
267 x2
=(event
->area
.x
+event
->area
.width
-1)/gd_cell_size_game
;
268 y2
=(event
->area
.y
+event
->area
.height
-1)/gd_cell_size_game
;
269 /* when resizing the cave, we may get events which store the old size, if the drawing area is not yet resized. */
272 if (x2
>=main_window
.game
->cave
->w
) x2
=main_window
.game
->cave
->w
-1;
273 if (y2
>=main_window
.game
->cave
->h
) y2
=main_window
.game
->cave
->h
-1;
275 /* run through screen coordinates to refresh. */
276 /* inside the loop, calculate cave coordinates, which might not be equal to screen coordinates. */
277 /* top left part of cave:
280 *y=0 wwww|wwwwwwwwwwwwww
281 *yd=0 ----+--------------
282 * w...|o p........ visible part
283 * w...|..d...........
285 /* mark all cells to be redrawn with GD_REDRAW, and then call the normal drawcave routine. */
286 for (yd
=y1
; yd
<=y2
; yd
++) {
287 int y
=yd
+main_window
.game
->cave
->y1
;
288 for (xd
=x1
; xd
<=x2
; xd
++) {
289 int x
=xd
+main_window
.game
->cave
->x1
;
290 if (main_window
.game
->gfx_buffer
[y
][x
]!=-1)
291 main_window
.game
->gfx_buffer
[y
][x
] |= GD_REDRAW
;
294 drawing_area_draw_cave();
305 * functions for the main window
313 main_window_set_fullscreen_idle_func(gpointer data
)
315 gtk_window_fullscreen(GTK_WINDOW(data
));
319 /* set or unset fullscreen if necessary */
320 /* hack: gtk-win32 does not correctly handle fullscreen & removing widgets.
321 so we put fullscreening call into a low priority idle function, which will be called
322 after all window resizing & the like did take place. */
324 main_window_set_fullscreen(gboolean ingame
)
326 if (ingame
&& fullscreen
) {
327 gtk_widget_hide(main_window
.menubar
);
328 gtk_widget_hide(main_window
.toolbar
);
329 g_idle_add_full(G_PRIORITY_LOW
, (GSourceFunc
) main_window_set_fullscreen_idle_func
, main_window
.window
, NULL
);
332 g_idle_remove_by_data(main_window
.window
);
333 gtk_window_unfullscreen(GTK_WINDOW(main_window
.window
));
334 gtk_widget_show(main_window
.menubar
);
335 gtk_widget_show(main_window
.toolbar
);
342 * creates title screen or drawing area
346 main_window_title_animation_func(gpointer data
)
348 static int animcycle
=0;
351 /* if title image widget does not exist for some reason, we quit now */
352 if (main_window
.title_image
==NULL
)
355 /* count the number of frames. */
357 while (main_window
.title_pixmaps
[count
]!=NULL
)
360 if (gtk_window_has_toplevel_focus(GTK_WINDOW(main_window
.window
))) {
361 animcycle
=(animcycle
+1)%count
;
362 gtk_image_set_from_pixmap(GTK_IMAGE(main_window
.title_image
), main_window
.title_pixmaps
[animcycle
], NULL
);
368 main_window_title_animation_remove()
372 g_source_remove_by_user_data(main_window_title_animation_func
);
374 /* free animation frames, if any */
375 if (main_window
.title_pixmaps
!=NULL
) {
376 while (main_window
.title_pixmaps
[i
]!=NULL
) {
377 g_object_unref(main_window
.title_pixmaps
[i
]);
380 g_free(main_window
.title_pixmaps
);
381 main_window
.title_pixmaps
=NULL
;
386 gd_main_window_set_title_animation()
390 /* if main window does not exist, ignore. */
391 if (main_window
.window
==NULL
)
393 /* if exists but not showing a title image, that is an error. */
394 g_return_if_fail(main_window
.title_image
!=NULL
);
396 main_window_title_animation_remove();
397 main_window
.title_pixmaps
=gd_create_title_animation();
398 /* set first frame immediately */
399 gtk_image_set_from_pixmap(GTK_IMAGE(main_window
.title_image
), main_window
.title_pixmaps
[0], NULL
);
400 /* and if more frames, add animation timeout */
401 if (main_window
.title_pixmaps
[1]!=NULL
)
402 g_timeout_add(40, main_window_title_animation_func
, main_window_title_animation_func
); /* 25fps animation */
404 /* resize the scrolling window so the image fits - a bit larger than the image so scroll bars do not appear*/
405 gdk_drawable_get_size(GDK_DRAWABLE(main_window
.title_pixmaps
[0]), &w
, &h
);
406 /* also some minimum size... 320x200 is some adhoc value only. */
407 gtk_widget_set_size_request(main_window
.scroll_window
, MAX(w
, 320)+24, MAX(h
, 200)+24);
409 /* with the title screen, it is usually smaller than in the game. shrink it. */
410 gtk_window_resize(GTK_WINDOW(main_window
.window
), 1, 1);
414 main_window_init_title()
416 if (!main_window
.title_image
) {
417 /* destroy current widget in main window */
418 if (gtk_bin_get_child(GTK_BIN(main_window
.scroll_window
)))
419 gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(main_window
.scroll_window
)));
422 main_window
.title_image
=gtk_image_new();
423 g_signal_connect (G_OBJECT(main_window
.title_image
), "destroy", G_CALLBACK(gtk_widget_destroyed
), &main_window
.title_image
);
424 g_signal_connect (G_OBJECT(main_window
.title_image
), "destroy", G_CALLBACK(main_window_title_animation_remove
), NULL
);
425 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(main_window
.scroll_window
), main_window
.title_image
);
427 gd_main_window_set_title_animation();
429 /* show newly created widgets */
430 gtk_widget_show_all(main_window
.scroll_window
);
434 gtk_widget_hide(main_window
.labels
);
435 gtk_widget_hide(main_window
.label_variables
);
436 if (gd_has_new_error()) {
437 gtk_widget_show(main_window
.error_hbox
);
438 gtk_label_set(GTK_LABEL(main_window
.error_label
), ((GdErrorMessage
*)(g_list_last(gd_errors
)->data
))->message
);
440 gtk_widget_hide(main_window
.error_hbox
);
443 /* enable menus and buttons of game */
444 gtk_action_group_set_sensitive(main_window
.actions_title
, !gd_editor_window
);
445 gtk_action_group_set_sensitive(main_window
.actions_title_replay
, !gd_editor_window
&& gd_caveset_has_replays());
446 gtk_action_group_set_sensitive(main_window
.actions_game
, FALSE
);
447 gtk_action_group_set_sensitive(main_window
.actions_snapshot
, snapshot
!=NULL
);
448 /* if editor window exists, no music. */
449 if (gd_editor_window
)
452 gd_music_play_random();
453 if (gd_editor_window
)
454 gtk_widget_set_sensitive(gd_editor_window
, TRUE
);
455 gtk_widget_hide(main_window
.replay_image_align
);
457 /* set or unset fullscreen if necessary */
458 main_window_set_fullscreen(FALSE
);
464 main_window_init_cave(GdCave
*cave
)
468 g_assert(cave
!=NULL
);
471 if (!main_window
.drawing_area
) {
474 /* destroy current widget in main window */
475 if (gtk_bin_get_child(GTK_BIN(main_window
.scroll_window
)))
476 gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(main_window
.scroll_window
)));
478 /* put drawing area in an alignment, so window can be any large w/o problems */
479 align
=gtk_alignment_new(0.5, 0.5, 0, 0);
480 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(main_window
.scroll_window
), align
);
482 main_window
.drawing_area
=gtk_drawing_area_new();
483 gtk_widget_set_events (main_window
.drawing_area
, GDK_EXPOSURE_MASK
| GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
| GDK_POINTER_MOTION_MASK
| GDK_POINTER_MOTION_HINT_MASK
| GDK_LEAVE_NOTIFY_MASK
);
484 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "button_press_event", G_CALLBACK(drawing_area_button_event
), NULL
);
485 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "button_release_event", G_CALLBACK(drawing_area_button_event
), NULL
);
486 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "motion_notify_event", G_CALLBACK(drawing_area_motion_event
), NULL
);
487 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "leave_notify_event", G_CALLBACK(drawing_area_leave_event
), NULL
);
488 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "expose_event", G_CALLBACK(drawing_area_expose_event
), NULL
);
489 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "destroy", G_CALLBACK(gtk_widget_destroyed
), &main_window
.drawing_area
);
490 gtk_container_add (GTK_CONTAINER (align
), main_window
.drawing_area
);
492 gdk_window_set_cursor (main_window
.drawing_area
->window
, gdk_cursor_new(GDK_CROSSHAIR
));
494 /* show newly created widgets */
495 gtk_widget_show_all(main_window
.scroll_window
);
497 /* set the minimum size of the scroll window: 20*12 cells */
498 /* XXX adding some pixels for the scrollbars-here we add 24 */
499 gtk_widget_set_size_request(main_window
.scroll_window
, 20*gd_cell_size_game
+24, 12*gd_cell_size_game
+24);
500 gtk_widget_set_size_request(main_window
.drawing_area
, (cave
->x2
-cave
->x1
+1)*gd_cell_size_game
, (cave
->y2
-cave
->y1
+1)*gd_cell_size_game
);
503 gtk_widget_show(main_window
.labels
);
504 if (main_window
.game
->type
==GD_GAMETYPE_TEST
&& gd_show_test_label
)
505 gtk_widget_show(main_window
.label_variables
);
507 gtk_widget_hide(main_window
.label_variables
);
508 gtk_widget_hide(main_window
.error_hbox
);
510 name_escaped
=g_markup_escape_text(cave
->name
, -1);
511 /* TRANSLATORS: cave name, level x */
512 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topleft
), _("<b>%s</b>, level %d"), name_escaped
, cave
->rendered
);
513 g_free(name_escaped
);
515 /* enable menus and buttons of game */
516 gtk_action_group_set_sensitive(main_window
.actions_title
, FALSE
);
517 gtk_action_group_set_sensitive(main_window
.actions_title_replay
, FALSE
);
518 gtk_action_group_set_sensitive(main_window
.actions_game
, TRUE
);
519 gtk_action_group_set_sensitive(main_window
.actions_snapshot
, snapshot
!=NULL
);
522 if (gd_editor_window
)
523 gtk_widget_set_sensitive(gd_editor_window
, FALSE
);
524 gtk_widget_hide(main_window
.replay_image_align
); /* it will be shown if needed. */
526 /* set or unset fullscreen if necessary */
527 main_window_set_fullscreen(TRUE
);
531 main_window_init_story(GdCave
*cave
)
533 char *escaped_name
, *escaped_story
;
535 if (!main_window
.story_label
) {
536 /* destroy current widget in main window */
537 if (gtk_bin_get_child(GTK_BIN(main_window
.scroll_window
)))
538 gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(main_window
.scroll_window
)));
541 main_window
.story_label
=gtk_label_new(NULL
);
542 gtk_label_set_line_wrap(GTK_LABEL(main_window
.story_label
), TRUE
);
543 g_signal_connect(G_OBJECT(main_window
.story_label
), "destroy", G_CALLBACK(gtk_widget_destroyed
), &main_window
.story_label
);
544 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(main_window
.scroll_window
), main_window
.story_label
);
546 /* show newly created widgets */
547 gtk_widget_show_all(main_window
.scroll_window
);
549 escaped_name
=g_markup_escape_text(cave
->name
, -1);
550 escaped_story
=g_markup_escape_text(cave
->story
->str
, -1);
551 gd_label_set_markup_printf(GTK_LABEL(main_window
.story_label
), _("<big><b>%s</b></big>\n\n%s\n\n<i>Press fire to continue</i>"), escaped_name
, escaped_story
);
552 g_free(escaped_name
);
553 g_free(escaped_story
);
554 /* the width of the label is 320, as the windows is usually not smaller than 320x200 */
555 gtk_widget_set_size_request(main_window
.story_label
, 320, -1);
558 gtk_widget_hide(main_window
.labels
);
559 gtk_widget_hide(main_window
.label_variables
);
560 if (gd_has_new_error()) {
561 gtk_widget_show(main_window
.error_hbox
);
562 gtk_label_set(GTK_LABEL(main_window
.error_label
), ((GdErrorMessage
*)(g_list_last(gd_errors
)->data
))->message
);
564 gtk_widget_hide(main_window
.error_hbox
);
567 /* enable menus and buttons of game */
568 gtk_action_group_set_sensitive(main_window
.actions_title
, FALSE
);
569 gtk_action_group_set_sensitive(main_window
.actions_title_replay
, FALSE
);
570 gtk_action_group_set_sensitive(main_window
.actions_game
, TRUE
);
571 gtk_action_group_set_sensitive(main_window
.actions_snapshot
, snapshot
!=NULL
);
572 /* if editor window exists, no music. */
574 if (gd_editor_window
)
575 gtk_widget_set_sensitive(gd_editor_window
, FALSE
);
576 gtk_widget_hide(main_window
.replay_image_align
);
578 /* set or unset fullscreen if necessary */
579 main_window_set_fullscreen(TRUE
);
584 /* game over, and highscore is achieved.
585 * ask for name, and if given, record it! */
587 game_over_highscore()
592 text
=g_strdup_printf(_("You have %d points, and achieved a highscore."), main_window
.game
->player_score
);
593 gd_infomessage(_("Game over!"), text
);
596 /* enter to highscore table */
597 rank
=gd_add_highscore(gd_caveset_data
->highscore
, main_window
.game
->player_name
, main_window
.game
->player_score
);
598 gd_show_highscore(main_window
.window
, NULL
, FALSE
, NULL
, rank
);
601 /* game over, but no highscore.
602 * only pops up a simple dialog box. */
604 game_over_without_highscore()
608 text
=g_strdup_printf(_("You have %d points."), main_window
.game
->player_score
);
609 gd_infomessage(_("Game over!"), text
);
618 main_int_set_labels()
620 const GdCave
*cave
=main_window
.game
->cave
;
623 /* lives reamining in game. */
624 /* not trivial, but this sometimes DOES change during the running of a cave. */
625 /* as when playing a replay, it might change from playing replay to continuing replay. */
626 switch (main_window
.game
->type
) {
627 /* for a normal cave, show number of lives or "bonus life" if it is an intermission */
628 case GD_GAMETYPE_NORMAL
:
629 if (!cave
->intermission
)
630 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topright
), _("Lives: <b>%d</b>"), main_window
.game
->player_lives
);
632 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topright
), _("<b>Bonus life</b>"));
635 /* for other game types, the number of lives is zero. so show the game type */
636 case GD_GAMETYPE_SNAPSHOT
:
637 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topright
), _("Continuing from <b>snapshot</b>"));
639 case GD_GAMETYPE_TEST
:
640 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topright
), _("<b>Testing</b> cave"));
642 case GD_GAMETYPE_REPLAY
:
643 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topright
), _("Playing <b>replay</b>"));
645 case GD_GAMETYPE_CONTINUE_REPLAY
:
646 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topright
), _("Continuing <b>replay</b>"));
651 /* SECOND ROW OF STATUS BAR. */
652 /* if the user is not pressing the left shift, show the normal status bar. otherwise, show the alternative. */
653 if (!key_alt_status_bar
) {
654 /* NORMAL STATUS BAR */
656 if (cave
->diamonds_needed
>0)
657 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_bottomleft
), _("Diamonds: <b>%03d</b> Value: <b>%02d</b>"), cave
->diamonds_collected
>=cave
->diamonds_needed
?0:cave
->diamonds_needed
-cave
->diamonds_collected
, cave
->diamond_value
);
659 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_bottomleft
), _("Diamonds: <b>??""?</b> Value: <b>%02d</b>"), cave
->diamond_value
);/* "" to avoid C trigraph ??< */
661 /* show time & score */
662 time
=gd_cave_time_show(cave
, cave
->time
);
664 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_bottomright
), "Time: <b>%02d:%02d</b> Score: <b>%06d</b>", time
/60, time
%60, main_window
.game
->player_score
);
666 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_bottomright
), "Time: <b>%03d</b> Score: <b>%06d</b>", time
, main_window
.game
->player_score
);
668 /* ALTERNATIVE STATUS BAR */
669 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_bottomleft
), _("Keys: <b>%d</b>, <b>%d</b>, <b>%d</b>"), cave
->key1
, cave
->key2
, cave
->key3
);
670 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_bottomright
), _("Skeletons: <b>%d</b> Gravity change: <b>%d</b>"), cave
->skeletons_collected
, gd_cave_time_show(cave
, cave
->gravity_will_change
));
673 if (gd_editor_window
&& gd_show_test_label
) {
674 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_variables
),
675 _("Speed: %dms, Amoeba timer: %ds %d, %ds %d, Magic wall timer: %ds\n"
676 "Expanding wall: %s, Creatures: %ds, %s, Gravity: %s\n"
677 "Kill player: %s, Sweet eaten: %s, Diamond key: %s, Diamonds: %d"),
679 gd_cave_time_show(cave
, cave
->amoeba_time
),
681 gd_cave_time_show(cave
, cave
->amoeba_2_time
),
682 cave
->amoeba_2_state
,
683 gd_cave_time_show(cave
, cave
->magic_wall_time
),
684 // XXX cave->magic_wall_state,
685 cave
->expanding_wall_changed
?_("vertical"):_("horizontal"),
686 gd_cave_time_show(cave
, cave
->creatures_direction_will_change
),
687 cave
->creatures_backwards
?_("backwards"):_("forwards"),
688 gettext(gd_direction_get_visible_name(cave
->gravity_disabled
?MV_STILL
:cave
->gravity
)),
689 cave
->kill_player
?_("yes"):_("no"),
690 cave
->sweet_eaten
?_("yes"):_("no"),
691 cave
->diamond_key_collected
?_("yes"):_("no"),
692 cave
->diamonds_collected
698 /* THE MAIN GAME TIMER */
700 /* for the gtk version, it seems nicer if we first draw, then scroll. */
701 /* this is because there is an expose event; scrolling the "old" drawing would draw the old, and then the new. */
702 /* (this is not true for the sdl version) */
704 static GTimer
*timer
=NULL
;
710 * scrolls to the player during game play.
715 static int scroll_desired_x
=0, scroll_desired_y
=0;
716 GtkAdjustment
*adjustment
;
717 int scroll_center_x
, scroll_center_y
;
718 gboolean out_of_window
=FALSE
;
721 int player_x
, player_y
;
722 const GdCave
*cave
=main_window
.game
->cave
;
723 gboolean exact_scroll
=FALSE
; /* to avoid compiler warning */
724 /* hystheresis size is this, multiplied by two.
725 * so player can move half the window without scrolling. */
726 int scroll_start_x
=main_window
.scroll_window
->allocation
.width
/4;
727 int scroll_to_x
=main_window
.scroll_window
->allocation
.width
/8;
728 int scroll_start_y
=main_window
.scroll_window
->allocation
.height
/5; /* window usually smaller vertically, so let the region be also smaller than the horizontal one */
729 int scroll_to_y
=main_window
.scroll_window
->allocation
.height
/10;
732 /* if cave not yet rendered, return. (might be the case for 50hz scrolling */
733 if (main_window
.game
==NULL
|| main_window
.game
->cave
==NULL
)
735 if (paused
&& main_window
.game
->cave
->player_state
!=GD_PL_NOT_YET
)
736 return; /* no scrolling when pause button is pressed, BUT ALLOW SCROLLING when the player is not yet born */
738 /* max scrolling speed depends on the speed of the cave. */
739 /* game moves cell_size_game* 1s/cave time pixels in a second. */
740 /* scrolling moves scroll speed * 1s/scroll_time in a second. */
741 /* these should be almost equal; scrolling speed a little slower. */
742 /* that way, the player might reach the border with a small probability, */
743 /* but the scrolling will not "oscillate", ie. turn on for little intervals as it has */
744 /* caught up with the desired position. smaller is better. */
745 scroll_speed
=gd_cell_size_game
*(gd_fine_scroll
?20:40)/cave
->speed
;
747 /* check player state. */
748 switch (main_window
.game
->cave
->player_state
) {
760 /* do not scroll when the player is dead or cave time is over. */
761 return; /* return from function */
764 player_x
=cave
->player_x
-cave
->x1
;
765 player_y
=cave
->player_y
-cave
->y1
;
767 /* get the size of the window so we know where to place player.
768 * first guess is the middle of the screen.
769 * main_window.drawing_area->parent->parent is the viewport.
770 * +cellsize/2 gets the stomach of player :) so the very center */
771 scroll_center_x
=player_x
*gd_cell_size_game
+ gd_cell_size_game
/2-main_window
.drawing_area
->parent
->parent
->allocation
.width
/2;
772 scroll_center_y
=player_y
*gd_cell_size_game
+ gd_cell_size_game
/2-main_window
.drawing_area
->parent
->parent
->allocation
.height
/2;
775 /* hystheresis function.
776 * when scrolling left, always go a bit less left than player being at the middle.
777 * when scrolling right, always go a bit less to the right. */
778 adjustment
=gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW(main_window
.scroll_window
));
780 scroll_desired_x
=scroll_center_x
;
782 if (adjustment
->value
+scroll_start_x
<scroll_center_x
)
783 scroll_desired_x
=scroll_center_x
-scroll_to_x
;
784 if (adjustment
->value
-scroll_start_x
>scroll_center_x
)
785 scroll_desired_x
=scroll_center_x
+scroll_to_x
;
787 scroll_desired_x
=CLAMP(scroll_desired_x
, 0, adjustment
->upper
-adjustment
->step_increment
-adjustment
->page_increment
);
790 if (adjustment
->value
<scroll_desired_x
) {
791 for (i
=0; i
<scroll_speed
; i
++)
792 if ((int)adjustment
->value
<scroll_desired_x
)
796 if (adjustment
->value
>scroll_desired_x
) {
797 for (i
=0; i
<scroll_speed
; i
++)
798 if ((int)adjustment
->value
>scroll_desired_x
)
803 gtk_adjustment_value_changed (adjustment
);
805 /* check if active player is outside drawing area. if yes, we should wait for scrolling */
806 if ((player_x
*gd_cell_size_game
)<adjustment
->value
|| (player_x
*gd_cell_size_game
+gd_cell_size_game
-1)>adjustment
->value
+main_window
.drawing_area
->parent
->parent
->allocation
.width
)
807 /* but only do the wait, if the player SHOULD BE visible, ie. he is inside the defined visible area of the cave */
808 if (cave
->player_x
>=cave
->x1
&& cave
->player_x
<=cave
->x2
)
815 adjustment
=gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW(main_window
.scroll_window
));
817 scroll_desired_y
=scroll_center_y
;
819 if (adjustment
->value
+scroll_start_y
<scroll_center_y
)
820 scroll_desired_y
=scroll_center_y
-scroll_to_y
;
821 if (adjustment
->value
-scroll_start_y
>scroll_center_y
)
822 scroll_desired_y
=scroll_center_y
+scroll_to_y
;
824 scroll_desired_y
=CLAMP(scroll_desired_y
, 0, adjustment
->upper
-adjustment
->step_increment
-adjustment
->page_increment
);
827 if (adjustment
->value
<scroll_desired_y
) {
828 for (i
=0; i
<scroll_speed
; i
++)
829 if ((int)adjustment
->value
<scroll_desired_y
)
833 if (adjustment
->value
> scroll_desired_y
) {
834 for (i
=0; i
<scroll_speed
; i
++)
835 if ((int)adjustment
->value
>scroll_desired_y
)
840 gtk_adjustment_value_changed(adjustment
);
842 /* check if active player is outside drawing area. if yes, we should wait for scrolling */
843 if ((player_y
*gd_cell_size_game
)<adjustment
->value
|| (player_y
*gd_cell_size_game
+gd_cell_size_game
-1)>adjustment
->value
+main_window
.drawing_area
->parent
->parent
->allocation
.height
)
844 /* but only do the wait, if the player SHOULD BE visible, ie. he is inside the defined visible area of the cave */
845 if (cave
->player_y
>=cave
->y1
&& cave
->player_y
<=cave
->y2
)
848 /* remember if player is visible inside window */
849 main_window
.game
->out_of_window
=out_of_window
;
851 /* if not yet born, we treat as visible. so cave will run. the user is unable to control an unborn player, so this is the right behaviour. */
852 if (main_window
.game
->cave
->player_state
==GD_PL_NOT_YET
)
853 main_window
.game
->out_of_window
=FALSE
;
855 gdk_window_process_updates(main_window
.drawing_area
->window
, TRUE
);
858 /* the timing thread runs in a separate thread. this variable is set to true,
859 * then the function exits (and also the thread.) */
860 static gboolean main_int_quit_thread
;
863 main_int(gpointer data
)
865 static gboolean toggle
=FALSE
; /* value irrelevant */
866 int up
, down
, left
, right
;
867 GdDirection player_move
;
874 g_message("%8d. call, avg %gms", called
, g_timer_elapsed(timer
, NULL
)*1000/called
);
877 if (main_window
.game
->type
==GD_GAMETYPE_REPLAY
)
878 gtk_widget_show(main_window
.replay_image_align
);
880 gtk_widget_hide(main_window
.replay_image_align
);
886 fire
=key_fire_1
|| key_fire_2
;
888 /* compare mouse coordinates to player coordinates, and make up movements */
889 if (gd_mouse_play
&& mouse_cell_x
>=0) {
890 down
=down
|| (main_window
.game
->cave
->player_y
<mouse_cell_y
);
891 up
=up
|| (main_window
.game
->cave
->player_y
>mouse_cell_y
);
892 left
=left
|| (main_window
.game
->cave
->player_x
>mouse_cell_x
);
893 right
=right
|| (main_window
.game
->cave
->player_x
<mouse_cell_x
);
894 fire
=fire
|| mouse_cell_click
;
897 /* call the game "interrupt" to do all things. */
898 player_move
=gd_direction_from_keypress(up
, down
, left
, right
);
899 /* tell the interrupt that 20ms has passed. */
900 state
=gd_game_main_int(main_window
.game
, 20, player_move
, fire
, key_suicide
, restart
, !paused
&& !main_window
.game
->out_of_window
, paused
, fast_forward
);
902 /* the game "interrupt" gives signals to us, which we act upon: update status bar, resize the drawing area... */
904 case GD_GAME_INVALID_STATE
:
905 g_assert_not_reached();
908 case GD_GAME_SHOW_STORY
:
909 main_window_init_story(main_window
.game
->cave
);
910 main_int_set_labels();
913 case GD_GAME_CAVE_LOADED
:
914 gd_select_pixbuf_colors(main_window
.game
->cave
->color0
, main_window
.game
->cave
->color1
, main_window
.game
->cave
->color2
, main_window
.game
->cave
->color3
, main_window
.game
->cave
->color4
, main_window
.game
->cave
->color5
);
915 main_window_init_cave(main_window
.game
->cave
);
916 main_int_set_labels();
917 restart
=FALSE
; /* so we do not remember the restart key from a previous cave run */
920 case GD_GAME_NO_MORE_LIVES
: /* <- only used by sdl version */
921 case GD_GAME_NOTHING
:
922 /* normally continue. */
925 case GD_GAME_LABELS_CHANGED
:
926 case GD_GAME_TIMEOUT_NOW
: /* <- maybe we should do something else for this */
927 /* normal, but we are told that the labels (score, ...) might have changed. */
928 main_int_set_labels();
933 return FALSE
; /* do not call again - it will be created later */
935 case GD_GAME_GAME_OVER
:
936 main_stop_game_but_maybe_highscore();
937 if (gd_is_highscore(gd_caveset_data
->highscore
, main_window
.game
->player_score
))
938 game_over_highscore(); /* achieved a high score! */
940 game_over_without_highscore(); /* no high score */
942 return FALSE
; /* do not call again - it will be created later */
945 /* if drawing area already exists, draw cave. */
946 /* remember that the drawings are cached, so if we did no change, this will barely do anything - so will not slow down. */
947 if (main_window
.drawing_area
)
948 drawing_area_draw_cave();
949 /* do the scrolling at the given interval. */
950 /* but only if the drawing area already exists. */
951 /* if fine scrolling, drawing is called at a 50hz rate. */
952 /* if not, only at a 25hz rate */
954 if (main_window
.drawing_area
&& (gd_fine_scroll
|| toggle
)) {
958 return TRUE
; /* call again */
961 /* this is a simple wrapper which makes the main_int to be callable as an idle func. */
962 /* when used as an idle func by the thread routine, it should run only once for every g_idle_add */
964 main_int_return_false_wrapper(gpointer data
)
970 /* this function will run in its own thread, and add the main int as an idle func in every 20ms */
972 main_int_timer_thread(gpointer data
)
974 int interval_msec
=20;
976 /* wait before we first call it */
977 g_usleep(interval_msec
*1000);
978 while (!main_int_quit_thread
) {
979 /* add processing as an idle func */
980 /* no need to lock the main loop context, as glib does that automatically */
981 g_idle_add(main_int_return_false_wrapper
, data
);
983 g_usleep(interval_msec
*1000);
989 uninstall game timers, if any installed.
992 main_int_uninstall_timer()
994 main_int_quit_thread
=TRUE
;
995 /* remove timeout associated to game play */
996 while (g_source_remove_by_user_data (main_window
.window
)) {
1003 main_int_install_timer()
1008 /* remove timer, if it is installed for some reason */
1009 main_int_uninstall_timer();
1012 gtk_window_present(GTK_WINDOW(main_window
.window
));
1016 timer
=g_timer_new();
1019 /* this makes the main int load the first cave, and then we do the drawing. */
1020 main_int(main_window
.window
);
1021 gdk_window_process_all_updates();
1022 /* after that, install timer. create a thread with higher priority than normal: */
1023 /* so its priority will be higher than the main thread, which does the drawing etc. */
1024 /* if the scheduling thread wants to do something, it gets processed first. this makes */
1025 /* the intervals more even. */
1026 main_int_quit_thread
=FALSE
;
1027 #ifdef G_THREADS_ENABLED
1028 thread
=g_thread_create_full(main_int_timer_thread
, main_window
.window
, 0, FALSE
, FALSE
, G_THREAD_PRIORITY_HIGH
, &error
);
1030 thread
=NULL
; /* if glib without thread support, we will use timeout source. */
1033 /* if unable to create thread */
1035 g_critical("%s", error
->message
);
1036 g_error_free(error
);
1038 /* use the main int as a timeout routine. */
1039 g_timeout_add(20, main_int
, main_window
.window
);
1043 g_timer_start(timer
);
1049 main_stop_game_but_maybe_highscore()
1051 main_int_uninstall_timer();
1052 gd_sound_off(); /* hack for game over dialog */
1054 main_window_init_title();
1055 /* if editor is active, go back to its window. */
1056 if (gd_editor_window
)
1057 gtk_window_present(GTK_WINDOW(gd_editor_window
));
1063 if (main_window
.game
) {
1064 gd_game_free(main_window
.game
);
1065 main_window
.game
=NULL
;
1072 main_stop_game_but_maybe_highscore();
1078 /* this starts a new game */
1080 main_new_game(const char *player_name
, const int cave
, const int level
)
1082 if (main_window
.game
)
1083 gd_main_stop_game();
1085 main_window
.game
=gd_game_new(player_name
, cave
, level
);
1086 main_int_install_timer();
1091 main_new_game_snapshot(GdCave
*snapshot
)
1093 if (main_window
.game
)
1094 gd_main_stop_game();
1096 main_window
.game
=gd_game_new_snapshot(snapshot
);
1097 main_int_install_timer();
1100 /* the new game for testing a level is global, not static.
1101 * it is used by the editor. */
1103 gd_main_new_game_test(GdCave
*test
, int level
)
1105 if (main_window
.game
)
1106 gd_main_stop_game();
1108 main_window
.game
=gd_game_new_test(test
, level
);
1109 main_int_install_timer();
1114 main_new_game_replay(GdCave
*cave
, GdReplay
*replay
)
1116 if (main_window
.game
)
1117 gd_main_stop_game();
1119 main_window
.game
=gd_game_new_replay(cave
, replay
);
1120 main_int_install_timer();
1131 * set the main window title from the caveset name.
1132 * made global, as the editor might ocassionally call it.
1136 gd_main_window_set_title()
1138 if (!g_str_equal(gd_caveset_data
->name
, "")) {
1141 text
=g_strdup_printf("GDash - %s", gd_caveset_data
->name
);
1142 gtk_window_set_title (GTK_WINDOW(main_window
.window
), text
);
1146 gtk_window_set_title (GTK_WINDOW(main_window
.window
), "GDash");
1153 /**********************************-
1160 help_cb(GtkWidget
* widget
, gpointer data
)
1162 gd_show_game_help (((GdMainWindow
*)data
)->window
);
1166 preferences_cb(GtkWidget
*widget
, gpointer data
)
1168 gd_preferences(((GdMainWindow
*)data
)->window
);
1172 control_settings_cb(GtkWidget
*widget
, gpointer data
)
1174 gd_control_settings(((GdMainWindow
*)data
)->window
);
1178 quit_cb(GtkWidget
* widget
, const gpointer data
)
1180 if (gd_discard_changes(main_window
.window
))
1185 stop_game_cb(GtkWidget
*widget
, gpointer data
)
1187 gd_main_stop_game();
1191 save_snapshot_cb(GtkWidget
* widget
, gpointer data
)
1194 gd_cave_free(snapshot
);
1196 snapshot
=gd_create_snapshot(main_window
.game
);
1197 gtk_action_group_set_sensitive (main_window
.actions_snapshot
, snapshot
!=NULL
);
1201 load_snapshot_cb(GtkWidget
* widget
, gpointer data
)
1203 g_return_if_fail(snapshot
!=NULL
);
1204 main_new_game_snapshot(snapshot
);
1207 /* restart level button clicked */
1209 restart_level_cb(GtkWidget
* widget
, gpointer data
)
1211 g_return_if_fail(main_window
.game
!=NULL
);
1212 g_return_if_fail(main_window
.game
->cave
!=NULL
);
1213 /* sets the restart variable, which will be interpreted by the iterate routine */
1218 about_cb(GtkWidget
*widget
, gpointer data
)
1220 gtk_show_about_dialog(GTK_WINDOW(main_window
.window
), "program-name", "GDash", "license", gd_about_license
, "wrap-license", TRUE
, "authors", gd_about_authors
, "version", PACKAGE_VERSION
, "comments", _(gd_about_comments
), "translator-credits", _(gd_about_translator_credits
), "website", gd_about_website
, "artists", gd_about_artists
, "documenters", gd_about_documenters
, NULL
);
1224 open_caveset_cb(GtkWidget
* widget
, gpointer data
)
1226 gd_open_caveset(main_window
.window
, NULL
);
1227 gd_main_window_set_title();
1231 open_caveset_dir_cb(GtkWidget
* widget
, gpointer data
)
1233 gd_open_caveset(main_window
.window
, gd_system_caves_dir
);
1234 gd_main_window_set_title();
1238 save_caveset_as_cb(GtkWidget
* widget
, gpointer data
)
1240 gd_save_caveset_as(main_window
.window
);
1244 save_caveset_cb(GtkWidget
* widget
, gpointer data
)
1246 gd_save_caveset(main_window
.window
);
1249 /* load internal game from the executable. those are inlined in caveset.c. */
1251 load_internal_cb(GtkWidget
* widget
, gpointer data
)
1253 gd_load_internal(main_window
.window
, GPOINTER_TO_INT(data
));
1254 gd_main_window_set_title();
1258 highscore_cb(GtkWidget
*widget
, gpointer data
)
1260 gd_show_highscore(main_window
.window
, NULL
, FALSE
, NULL
, -1);
1266 show_errors_cb(GtkWidget
*widget
, gpointer data
)
1268 gtk_widget_hide(main_window
.error_hbox
); /* if the user is presented the error list, the label is to be hidden */
1269 gd_show_errors(main_window
.window
);
1275 gd_open_cave_editor();
1276 /* to be sure no cave is playing. */
1277 /* this will also stop music. to be called after opening editor window, so the music stops. */
1278 main_window_init_title();
1281 /* called from the menu when a recent file is activated. */
1283 recent_chooser_activated_cb(GtkRecentChooser
*chooser
, gpointer data
)
1285 GtkRecentInfo
*current
;
1286 char *filename_utf8
, *filename
;
1288 current
=gtk_recent_chooser_get_current_item(chooser
);
1289 /* we do not support non-local files */
1290 if (!gtk_recent_info_is_local(current
)) {
1293 display_name
=gtk_recent_info_get_uri_display(current
);
1294 gd_errormessage(_("GDash cannot load file from a network link."), display_name
);
1295 g_free(display_name
);
1299 /* if the edited caveset is to be saved, but user cancels */
1300 if (!gd_discard_changes(main_window
.window
))
1303 filename_utf8
=gtk_recent_info_get_uri_display(current
);
1304 filename
=g_filename_from_utf8(filename_utf8
, -1, NULL
, NULL
, NULL
);
1305 /* ask for save first? */
1306 gd_open_caveset_in_ui(filename
, gd_use_bdcff_highscore
);
1308 /* things to do after loading. */
1309 gd_main_window_set_title();
1310 if (gd_has_new_error())
1311 gd_show_last_error(main_window
.window
);
1313 gd_infomessage(_("Loaded caveset from file:"), filename_utf8
);
1316 g_free(filename_utf8
);
1322 toggle_pause_cb(GtkWidget
* widget
, gpointer data
)
1324 paused
=gtk_toggle_action_get_active(GTK_TOGGLE_ACTION (widget
));
1331 toggle_fullscreen_cb (GtkWidget
* widget
, gpointer data
)
1333 fullscreen
=gtk_toggle_action_get_active(GTK_TOGGLE_ACTION (widget
));
1334 main_window_set_fullscreen(main_window
.game
!=NULL
); /* we do not exactly know if in game, but try to guess */
1338 toggle_fast_cb (GtkWidget
* widget
, gpointer data
)
1340 fast_forward
=gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (widget
));
1352 * START NEW GAME DIALOG
1354 * show a dialog to the user so he can select the cave to start game at.
1358 typedef struct _new_game_dialog
{
1360 GtkWidget
*combo_cave
;
1361 GtkWidget
*spin_level
;
1362 GtkWidget
*entry_name
;
1367 /* keypress. key_* can be event_type==gdk_key_press, as we
1368 connected this function to key press and key release.
1371 new_game_keypress_event(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
1373 NewGameDialog
*jump_dialog
=(NewGameDialog
*)data
;
1374 int level
=gtk_range_get_value (GTK_RANGE(jump_dialog
->spin_level
));
1376 switch (event
->keyval
) {
1381 gtk_range_set_value (GTK_RANGE(jump_dialog
->spin_level
), level
);
1387 gtk_range_set_value (GTK_RANGE(jump_dialog
->spin_level
), level
);
1390 gtk_dialog_response(GTK_DIALOG(jump_dialog
->dialog
), GTK_RESPONSE_ACCEPT
);
1393 return FALSE
; /* if any other key, we did not process it. go on, let gtk handle it. */
1399 new_game_update_preview(GtkWidget
*widget
, gpointer data
)
1401 NewGameDialog
*jump_dialog
=(NewGameDialog
*)data
;
1402 GdkPixbuf
*cave_image
;
1405 /* loading cave, draw cave and scale to specified size. seed=0 */
1406 cave
=gd_cave_new_from_caveset(gtk_combo_box_get_active(GTK_COMBO_BOX (jump_dialog
->combo_cave
)), gtk_range_get_value (GTK_RANGE(jump_dialog
->spin_level
))-1, 0);
1407 cave_image
=gd_drawcave_to_pixbuf(cave
, 320, 240, TRUE
, TRUE
);
1408 gtk_image_set_from_pixbuf(GTK_IMAGE (jump_dialog
->image
), cave_image
);
1409 g_object_unref(cave_image
);
1411 /* freeing temporary cave data */
1416 new_game_cb (const GtkWidget
* widget
, const gpointer data
)
1418 static GdString player_name
="";
1419 GtkWidget
*table
, *expander
, *eventbox
;
1420 NewGameDialog jump_dialog
;
1421 GtkCellRenderer
*renderer
;
1422 GtkListStore
*store
;
1425 /* check if caveset is empty! */
1426 if (gd_caveset_count()==0) {
1427 gd_warningmessage(_("There are no caves in this cave set!"), NULL
);
1431 jump_dialog
.dialog
=gtk_dialog_new_with_buttons(_("Select cave to play"), GTK_WINDOW(main_window
.window
), GTK_DIALOG_NO_SEPARATOR
| GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_STOCK_CANCEL
, GTK_RESPONSE_REJECT
, GTK_STOCK_JUMP_TO
, GTK_RESPONSE_ACCEPT
, NULL
);
1432 gtk_dialog_set_default_response (GTK_DIALOG(jump_dialog
.dialog
), GTK_RESPONSE_ACCEPT
);
1433 gtk_window_set_resizable (GTK_WINDOW(jump_dialog
.dialog
), FALSE
);
1435 table
=gtk_table_new(0, 0, FALSE
);
1436 gtk_box_pack_start(GTK_BOX (GTK_DIALOG(jump_dialog
.dialog
)->vbox
), table
, FALSE
, FALSE
, 0);
1437 gtk_container_set_border_width(GTK_CONTAINER (table
), 6);
1438 gtk_table_set_row_spacings(GTK_TABLE(table
), 6);
1439 gtk_table_set_col_spacings(GTK_TABLE(table
), 6);
1441 /* name, which will be used for highscore & the like */
1442 gtk_table_attach_defaults(GTK_TABLE(table
), gd_label_new_printf(_("Name:")), 0, 1, 0, 1);
1443 if (g_str_equal(player_name
, ""))
1444 gd_strcpy(player_name
, g_get_real_name());
1445 jump_dialog
.entry_name
=gtk_entry_new();
1446 /* little inconsistency below: max length has unicode characters, while gdstring will have utf-8.
1447 however this does not make too much difference */
1448 gtk_entry_set_max_length(GTK_ENTRY(jump_dialog
.entry_name
), sizeof(GdString
));
1449 gtk_entry_set_activates_default(GTK_ENTRY(jump_dialog
.entry_name
), TRUE
);
1450 gtk_entry_set_text(GTK_ENTRY(jump_dialog
.entry_name
), player_name
);
1451 gtk_table_attach_defaults(GTK_TABLE(table
), jump_dialog
.entry_name
, 1, 2, 0, 1);
1453 gtk_table_attach_defaults(GTK_TABLE(table
), gd_label_new_printf(_("Cave:")), 0, 1, 1, 2);
1455 /* store of caves: cave pointer, cave name, selectable */
1456 store
=gtk_list_store_new(3, G_TYPE_POINTER
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
1457 for (iter
=gd_caveset
; iter
; iter
=g_list_next (iter
)) {
1458 GdCave
*cave
=iter
->data
;
1459 GtkTreeIter treeiter
;
1461 gtk_list_store_insert_with_values (store
, &treeiter
, -1, 0, iter
->data
, 1, cave
->name
, 2, cave
->selectable
|| gd_all_caves_selectable
, -1);
1463 jump_dialog
.combo_cave
=gtk_combo_box_new_with_model (GTK_TREE_MODEL (store
));
1464 g_object_unref(store
);
1466 renderer
=gtk_cell_renderer_text_new();
1467 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(jump_dialog
.combo_cave
), renderer
, TRUE
);
1468 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT(jump_dialog
.combo_cave
), renderer
, "text", 1, "sensitive", 2, NULL
);
1469 /* we put the combo in an event box, so we can receive keypresses on our own */
1470 eventbox
=gtk_event_box_new();
1471 gtk_container_add(GTK_CONTAINER(eventbox
), jump_dialog
.combo_cave
);
1472 gtk_table_attach_defaults(GTK_TABLE(table
), eventbox
, 1, 2, 1, 2);
1474 gtk_table_attach_defaults(GTK_TABLE(table
), gd_label_new_printf(_("Level:")), 0, 1, 2, 3);
1475 jump_dialog
.spin_level
=gtk_hscale_new_with_range(1.0, 5.0, 1.0);
1476 gtk_range_set_increments(GTK_RANGE(jump_dialog
.spin_level
), 1.0, 1.0);
1477 gtk_scale_set_value_pos(GTK_SCALE(jump_dialog
.spin_level
), GTK_POS_LEFT
);
1478 gtk_table_attach_defaults(GTK_TABLE(table
), jump_dialog
.spin_level
, 1, 2, 2, 3);
1480 g_signal_connect(G_OBJECT(jump_dialog
.combo_cave
), "changed", G_CALLBACK(new_game_update_preview
), &jump_dialog
);
1481 gtk_widget_add_events(eventbox
, GDK_KEY_PRESS_MASK
);
1482 g_signal_connect(G_OBJECT(eventbox
), "key_press_event", G_CALLBACK(new_game_keypress_event
), &jump_dialog
);
1483 g_signal_connect(G_OBJECT(jump_dialog
.spin_level
), "value-changed", G_CALLBACK(new_game_update_preview
), &jump_dialog
);
1485 /* this allows the user to select if he wants to see a preview of the cave */
1486 expander
=gtk_expander_new(_("Preview"));
1487 gtk_expander_set_expanded(GTK_EXPANDER (expander
), gd_show_preview
);
1488 gtk_table_attach_defaults(GTK_TABLE(table
), expander
, 0, 2, 3, 4);
1489 jump_dialog
.image
=gtk_image_new();
1490 gtk_container_add(GTK_CONTAINER (expander
), jump_dialog
.image
);
1492 gtk_widget_show_all(jump_dialog
.dialog
);
1493 gtk_widget_grab_focus(jump_dialog
.combo_cave
);
1494 gtk_editable_select_region(GTK_EDITABLE(jump_dialog
.entry_name
), 0, 0);
1495 /* set default and also trigger redrawing */
1496 gtk_combo_box_set_active(GTK_COMBO_BOX(jump_dialog
.combo_cave
), gd_caveset_last_selected
);
1497 gtk_range_set_value(GTK_RANGE(jump_dialog
.spin_level
), 1);
1499 if (gtk_dialog_run (GTK_DIALOG(jump_dialog
.dialog
))==GTK_RESPONSE_ACCEPT
) {
1500 gd_strcpy(player_name
, gtk_entry_get_text(GTK_ENTRY(jump_dialog
.entry_name
)));
1501 gd_caveset_last_selected
=gtk_combo_box_get_active(GTK_COMBO_BOX(jump_dialog
.combo_cave
));
1502 gd_caveset_last_selected_level
=gtk_range_get_value(GTK_RANGE(jump_dialog
.spin_level
))-1;
1503 main_new_game (player_name
, gd_caveset_last_selected
, gd_caveset_last_selected_level
);
1505 gd_show_preview
=gtk_expander_get_expanded(GTK_EXPANDER(expander
)); /* remember expander state-even if cancel pressed */
1506 gtk_widget_destroy(jump_dialog
.dialog
);
1511 enum _replay_fields
{
1512 COL_REPLAY_CAVE_POINTER
,
1513 COL_REPLAY_REPLAY_POINTER
,
1514 COL_REPLAY_NAME
, /* cave or player name */
1515 COL_REPLAY_LEVEL
, /* level the replay is played on */
1521 COL_REPLAY_VISIBLE
, /* set to true for replay lines, false for cave lines. so "saved" toggle and comment are not visible. */
1526 show_replays_tree_view_row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
, GtkTreeViewColumn
*column
, gpointer user_data
)
1528 GtkTreeModel
*model
=gtk_tree_view_get_model(view
);
1533 gtk_tree_model_get_iter(model
, &iter
, path
);
1534 gtk_tree_model_get(model
, &iter
, COL_REPLAY_CAVE_POINTER
, &cave
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1535 if (cave
!=NULL
&& replay
!=NULL
)
1536 main_new_game_replay(cave
, replay
);
1540 show_replays_dialog_response(GtkDialog
*dialog
, int response_id
, gpointer data
)
1542 gtk_widget_destroy(GTK_WIDGET(dialog
));
1546 show_replays_comment_edited(GtkCellRendererText
*cell
, const gchar
*path_string
, const gchar
*new_text
, gpointer data
)
1548 GtkTreeModel
*model
=(GtkTreeModel
*)data
;
1549 GtkTreePath
*path
=gtk_tree_path_new_from_string(path_string
);
1554 gtk_tree_model_get_iter (model
, &iter
, path
);
1555 gtk_tree_model_get(model
, &iter
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1556 /* if not available, maybe the user edited a cave line? do nothing. */
1560 gtk_tree_store_set(GTK_TREE_STORE(model
), &iter
, COL_REPLAY_COMMENT
, new_text
, -1);
1561 gd_strcpy(replay
->comment
, new_text
);
1562 /* if this is a saved replay, now the caveset is edited. */
1564 gd_caveset_edited
=TRUE
;
1568 show_replays_saved_toggled(GtkCellRendererText
*cell
, const gchar
*path_string
, gpointer data
)
1570 GtkTreeModel
*model
=(GtkTreeModel
*)data
;
1571 GtkTreePath
*path
=gtk_tree_path_new_from_string(path_string
);
1576 gtk_tree_model_get_iter (model
, &iter
, path
);
1577 gtk_tree_model_get(model
, &iter
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1578 /* if not available, maybe the user edited a cave line? do nothing. */
1582 replay
->saved
=!replay
->saved
;
1583 gtk_tree_store_set(GTK_TREE_STORE(model
), &iter
, COL_REPLAY_SAVED
, replay
->saved
, -1);
1584 /* we selected or unselected a replay for saving - the caveset is now edited. */
1585 gd_caveset_edited
=TRUE
;
1589 show_replays_play_button_clicked(GtkWidget
*widget
, gpointer data
)
1591 GtkTreeView
*view
=GTK_TREE_VIEW(data
);
1592 GtkTreeSelection
*selection
=gtk_tree_view_get_selection(view
);
1593 GtkTreeModel
*model
;
1594 gboolean got_selected
;
1599 got_selected
=gtk_tree_selection_get_selected(selection
, &model
, &iter
);
1600 if (!got_selected
) /* if nothing selected, return */
1603 gtk_tree_model_get(model
, &iter
, COL_REPLAY_CAVE_POINTER
, &cave
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1604 if (cave
!=NULL
&& replay
!=NULL
)
1605 main_new_game_replay(cave
, replay
);
1608 /* enables or disables play button on selection change */
1610 show_replays_tree_view_selection_changed(GtkTreeSelection
*selection
, gpointer data
)
1612 GtkWidget
*button
=GTK_WIDGET(data
);
1613 GtkTreeModel
*model
;
1617 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
)) {
1620 gtk_tree_model_get(model
, &iter
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1621 enable
=replay
!=NULL
;
1625 gtk_widget_set_sensitive(button
, enable
);
1631 show_replays_cb(GtkWidget
*widget
, gpointer data
)
1633 static GtkWidget
*dialog
=NULL
;
1634 GtkWidget
*scroll
, *view
, *button
;
1636 GtkTreeStore
*model
;
1637 GtkCellRenderer
*renderer
;
1639 /* if window already open, just show it and return */
1641 gtk_window_present(GTK_WINDOW(dialog
));
1645 dialog
=gtk_dialog_new_with_buttons(_("Replays"), GTK_WINDOW(main_window
.window
), 0, NULL
);
1646 g_signal_connect(G_OBJECT(dialog
), "destroy", G_CALLBACK(gtk_widget_destroyed
), &dialog
);
1647 g_signal_connect(G_OBJECT(dialog
), "response", G_CALLBACK(show_replays_dialog_response
), NULL
);
1648 gtk_window_set_default_size(GTK_WINDOW(dialog
), 480, 360);
1650 gd_dialog_add_hint(GTK_DIALOG(dialog
), _("Hint: When watching a replay, you can use the usual movement keys (left, right...) to "
1651 "stop the replay and immediately continue the playing of the cave yourself."));
1653 /* scrolled window to show replays tree view */
1654 scroll
=gtk_scrolled_window_new(NULL
, NULL
);
1655 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(scroll
), GTK_SHADOW_ETCHED_IN
);
1656 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), scroll
, TRUE
, TRUE
, 6);
1658 /* create store containing replays */
1659 model
=gtk_tree_store_new(COL_REPLAY_MAX
, G_TYPE_POINTER
, G_TYPE_POINTER
, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_BOOLEAN
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
1660 for (iter
=gd_caveset
; iter
!=NULL
; iter
=iter
->next
) {
1662 GdCave
*cave
=(GdCave
*)iter
->data
;
1664 /* if the cave has replays */
1665 if (cave
->replays
) {
1666 GtkTreeIter caveiter
;
1668 gtk_tree_store_append(model
, &caveiter
, NULL
);
1669 gtk_tree_store_set(model
, &caveiter
, COL_REPLAY_NAME
, cave
->name
, -1);
1671 /* add each replay */
1672 for (replayiter
=cave
->replays
; replayiter
!=NULL
; replayiter
=replayiter
->next
) {
1674 GdReplay
*replay
=(GdReplay
*)replayiter
->data
;
1676 const char *imagestock
;
1678 /* we have to store the score as string, as for the cave lines the unset score field would also show zero */
1679 g_snprintf(score
, sizeof(score
), "%d", replay
->score
);
1680 gtk_tree_store_append(model
, &riter
, &caveiter
);
1681 if (replay
->wrong_checksum
)
1682 imagestock
=GTK_STOCK_DIALOG_ERROR
;
1684 imagestock
=replay
->success
?GTK_STOCK_YES
:GTK_STOCK_NO
;
1685 gtk_tree_store_set(model
, &riter
, COL_REPLAY_CAVE_POINTER
, cave
, COL_REPLAY_REPLAY_POINTER
, replay
, COL_REPLAY_NAME
, replay
->player_name
,
1686 COL_REPLAY_LEVEL
, replay
->level
+1,
1687 COL_REPLAY_DATE
, replay
->date
, COL_REPLAY_SCORE
, score
, COL_REPLAY_SUCCESS
, imagestock
,
1688 COL_REPLAY_COMMENT
, replay
->comment
, COL_REPLAY_SAVED
, replay
->saved
, COL_REPLAY_VISIBLE
, TRUE
, -1);
1693 view
=gtk_tree_view_new_with_model(GTK_TREE_MODEL(model
)); /* create tree view which will show data */
1694 gtk_tree_view_expand_all(GTK_TREE_VIEW(view
));
1695 gtk_container_add(GTK_CONTAINER(scroll
), view
);
1697 renderer
=gtk_cell_renderer_text_new();
1698 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 0, _("Name"), renderer
, "text", COL_REPLAY_NAME
, NULL
); /* 0 = column number */
1699 gtk_tree_view_column_set_expand(gtk_tree_view_get_column(GTK_TREE_VIEW(view
), 0), TRUE
); /* name column expands */
1701 renderer
=gtk_cell_renderer_text_new();
1702 /* TRANSLATORS: "Lvl" here stands for Level. Some shorthand should be used.*/
1703 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 1, _("Lvl"), renderer
, "text", COL_REPLAY_LEVEL
, "visible", COL_REPLAY_VISIBLE
, NULL
); /* 0 = column number */
1704 gtk_tree_view_column_set_expand(gtk_tree_view_get_column(GTK_TREE_VIEW(view
), 0), TRUE
); /* name column expands */
1706 renderer
=gtk_cell_renderer_text_new();
1707 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 2, _("Date"), renderer
, "text", COL_REPLAY_DATE
, NULL
); /* 1 = column number */
1709 renderer
=gtk_cell_renderer_pixbuf_new();
1710 g_object_set(G_OBJECT(renderer
), "stock-size", GTK_ICON_SIZE_MENU
, NULL
);
1711 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 3, NULL
, renderer
, "stock-id", COL_REPLAY_SUCCESS
, NULL
);
1713 renderer
=gtk_cell_renderer_text_new();
1714 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 4, _("Score"), renderer
, "text", COL_REPLAY_SCORE
, NULL
);
1716 renderer
=gtk_cell_renderer_text_new();
1717 g_object_set(G_OBJECT(renderer
), "editable", TRUE
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
1718 g_signal_connect(renderer
, "edited", G_CALLBACK(show_replays_comment_edited
), model
);
1719 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 5, _("Comment"), renderer
, "text", COL_REPLAY_COMMENT
, "visible", COL_REPLAY_VISIBLE
, NULL
);
1720 /* doubleclick will play the replay */
1721 g_signal_connect(G_OBJECT(view
), "row-activated", G_CALLBACK(show_replays_tree_view_row_activated_cb
), NULL
);
1722 gtk_tree_view_column_set_expand(gtk_tree_view_get_column(GTK_TREE_VIEW(view
), 4), TRUE
); /* name column expands */
1724 renderer
=gtk_cell_renderer_toggle_new();
1725 g_signal_connect(renderer
, "toggled", G_CALLBACK(show_replays_saved_toggled
), model
);
1726 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 6, _("Saved"), renderer
, "active", COL_REPLAY_SAVED
, "visible", COL_REPLAY_VISIBLE
, NULL
);
1729 button
=gtk_button_new_from_stock(GTK_STOCK_MEDIA_PLAY
);
1730 gtk_widget_set_sensitive(button
, FALSE
); /* not sensitive by default. when the user selects a line, it will be enabled */
1731 g_signal_connect(G_OBJECT(button
), "clicked", G_CALLBACK(show_replays_play_button_clicked
), view
);
1732 g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(view
))), "changed", G_CALLBACK(show_replays_tree_view_selection_changed
), button
);
1733 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog
)->action_area
), button
);
1735 /* this must be added after the play button, so it is the rightmost one */
1736 gtk_dialog_add_button(GTK_DIALOG(dialog
), GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
);
1737 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_CLOSE
);
1739 gtk_widget_show_all(dialog
);
1747 /* a cave name is selected, update the text boxt with current cave's data */
1749 cave_info_combo_changed(GtkComboBox
*widget
, gpointer data
)
1751 GtkTextBuffer
*buffer
=GTK_TEXT_BUFFER(data
);
1755 /* clear text buffer */
1756 gtk_text_buffer_set_text(buffer
, "", -1);
1757 gtk_text_buffer_get_iter_at_offset (buffer
, &iter
, 0);
1759 i
=gtk_combo_box_get_active(widget
);
1762 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, gd_caveset_data
->name
, -1, "heading", NULL
);
1763 gtk_text_buffer_insert(buffer
, &iter
, "\n\n", -1);
1765 /* show properties with title only if they are not empty string */
1766 if (!g_str_equal(gd_caveset_data
->description
, "")) {
1767 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Description: "), -1, "name", NULL
);
1768 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->description
, -1);
1769 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1771 if (!g_str_equal(gd_caveset_data
->author
, "")) {
1772 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Author: "), -1, "name", NULL
);
1773 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->author
, -1);
1774 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1776 if (!g_str_equal(gd_caveset_data
->date
, "")) {
1777 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Date: "), -1, "name", NULL
);
1778 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->date
, -1);
1779 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1781 if (!g_str_equal(gd_caveset_data
->difficulty
, "")) {
1782 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Diffuculty: "), -1, "name", NULL
);
1783 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->difficulty
, -1);
1784 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1786 if (!g_str_equal(gd_caveset_data
->story
->str
, "")) {
1787 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Story:\n"), -1, "name", NULL
);
1788 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->story
->str
, -1);
1789 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1791 if (!g_str_equal(gd_caveset_data
->remark
->str
, "")) {
1792 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Remark:\n"), -1, "name", NULL
);
1793 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->remark
->str
, -1);
1794 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1799 GdCave
*cave
=gd_return_nth_cave(i
-1);
1801 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, cave
->name
, -1, "heading", NULL
);
1802 gtk_text_buffer_insert(buffer
, &iter
, "\n\n", -1);
1804 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Type: "), -1, "name", NULL
);
1805 gtk_text_buffer_insert(buffer
, &iter
, cave
->intermission
?_("Intermission"):_("Normal cave"), -1);
1806 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1808 /* show properties with title only if they are not empty string */
1809 if (!g_str_equal(cave
->description
, "")) {
1810 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Description: "), -1, "name", NULL
);
1811 gtk_text_buffer_insert(buffer
, &iter
, cave
->description
, -1);
1812 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1814 if (!g_str_equal(cave
->author
, "")) {
1815 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Author: "), -1, "name", NULL
);
1816 gtk_text_buffer_insert(buffer
, &iter
, cave
->author
, -1);
1817 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1819 if (!g_str_equal(cave
->date
, "")) {
1820 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Date: "), -1, "name", NULL
);
1821 gtk_text_buffer_insert(buffer
, &iter
, cave
->date
, -1);
1822 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1824 if (!g_str_equal(cave
->difficulty
, "")) {
1825 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Difficulty: "), -1, "name", NULL
);
1826 gtk_text_buffer_insert(buffer
, &iter
, cave
->difficulty
, -1);
1827 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1830 if (!g_str_equal(cave
->story
->str
, "")) {
1831 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Story:\n"), -1, "name", NULL
);
1832 gtk_text_buffer_insert(buffer
, &iter
, cave
->story
->str
, -1);
1833 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1835 if (!g_str_equal(cave
->remark
->str
, "")) {
1836 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Remark:\n"), -1, "name", NULL
);
1837 gtk_text_buffer_insert(buffer
, &iter
, cave
->remark
->str
, -1);
1838 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1844 /* show info about cave or caveset */
1846 cave_info_cb(GtkWidget
*widget
, gpointer data
)
1848 GtkWidget
*dialog
, *view
, *sw
, *combo
;
1851 GtkTextBuffer
*buffer
;
1853 gboolean paused_save
;
1856 dialog
=gtk_dialog_new_with_buttons(_("Caveset information"), GTK_WINDOW(main_window
.window
),
1857 GTK_DIALOG_DESTROY_WITH_PARENT
| GTK_DIALOG_MODAL
| GTK_DIALOG_NO_SEPARATOR
,
1858 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
,
1860 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_CLOSE
);
1861 gtk_window_set_default_size(GTK_WINDOW(dialog
), 360, 480);
1863 /* create a combo box. 0=caveset, 1, 2, 3... = caves */
1864 combo
=gtk_combo_box_new_text();
1865 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), combo
, FALSE
, FALSE
, 6);
1866 text
=g_strdup_printf("[%s]", gd_caveset_data
->name
); /* caveset name = line 0 */
1867 gtk_combo_box_append_text(GTK_COMBO_BOX(combo
), text
);
1869 for (citer
=gd_caveset
; citer
!=NULL
; citer
=citer
->next
) {
1870 GdCave
*c
=citer
->data
;
1872 gtk_combo_box_append_text(GTK_COMBO_BOX(combo
), c
->name
);
1875 /* create text buffer */
1876 buffer
=gtk_text_buffer_new(NULL
);
1877 gtk_text_buffer_get_iter_at_offset (buffer
, &iter
, 0);
1878 gtk_text_buffer_create_tag (buffer
, "heading", "weight", PANGO_WEIGHT_BOLD
, "scale", PANGO_SCALE_X_LARGE
, NULL
);
1879 gtk_text_buffer_create_tag (buffer
, "name", "weight", PANGO_WEIGHT_BOLD
, NULL
);
1880 gtk_text_buffer_insert_with_tags_by_name (buffer
, &iter
, "GDash " PACKAGE_VERSION
"\n\n", -1, "heading", NULL
);
1882 sw
=gtk_scrolled_window_new(NULL
, NULL
);
1883 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
), GTK_SHADOW_ETCHED_IN
);
1884 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
1885 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), sw
);
1886 view
=gtk_text_view_new_with_buffer(buffer
);
1887 gtk_container_add(GTK_CONTAINER (sw
), view
);
1888 g_object_unref(buffer
);
1889 gtk_text_view_set_editable (GTK_TEXT_VIEW (view
), FALSE
);
1890 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view
), FALSE
);
1891 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view
), GTK_WRAP_WORD
);
1892 gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (view
), 3);
1893 gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view
), 6);
1894 gtk_text_view_set_right_margin (GTK_TEXT_VIEW (view
), 6);
1896 g_signal_connect(G_OBJECT(combo
), "changed", G_CALLBACK(cave_info_combo_changed
), buffer
);
1897 if (main_window
.game
&& main_window
.game
->original_cave
)
1898 /* currently playing a cave - show info for that */
1899 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), g_list_index(gd_caveset
, main_window
.game
->original_cave
)+1);
1901 /* show info for caveset */
1902 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
1905 gtk_widget_show_all(dialog
);
1907 paused
=TRUE
; /* set paused game, so it stops while the users sees the message box */
1908 gtk_dialog_run(GTK_DIALOG(dialog
));
1909 gtk_widget_destroy(dialog
);
1916 * Creates main window
1921 create_main_window()
1924 static GtkActionEntry action_entries_normal
[]={
1925 {"PlayMenu", NULL
, N_("_Play")},
1926 {"FileMenu", NULL
, N_("_File")},
1927 {"SettingsMenu", NULL
, N_("_Settings")},
1928 {"HelpMenu", NULL
, N_("_Help")},
1929 {"Quit", GTK_STOCK_QUIT
, NULL
, NULL
, NULL
, G_CALLBACK(quit_cb
)},
1930 {"About", GTK_STOCK_ABOUT
, NULL
, NULL
, NULL
, G_CALLBACK(about_cb
)},
1931 {"Errors", GTK_STOCK_DIALOG_ERROR
, N_("_Error console"), NULL
, NULL
, G_CALLBACK(show_errors_cb
)},
1932 {"Help", GTK_STOCK_HELP
, NULL
, NULL
, NULL
, G_CALLBACK(help_cb
)},
1933 {"CaveInfo", GTK_STOCK_DIALOG_INFO
, N_("Caveset _information"), NULL
, N_("Show information about the game and its caves"), G_CALLBACK(cave_info_cb
)},
1936 static GtkActionEntry action_entries_title
[]={
1937 {"GamePreferences", GTK_STOCK_PREFERENCES
, NULL
, NULL
, NULL
, G_CALLBACK(preferences_cb
)},
1938 {"GameControlSettings", GD_ICON_KEYBOARD
, N_("_Control keys"), NULL
, NULL
, G_CALLBACK(control_settings_cb
)},
1939 {"NewGame", GTK_STOCK_MEDIA_PLAY
, N_("_New game"), "<control>N", N_("Start new game"), G_CALLBACK(new_game_cb
)},
1940 {"CaveEditor", GD_ICON_CAVE_EDITOR
, N_("Cave _editor"), NULL
, NULL
, G_CALLBACK(cave_editor_cb
)},
1941 {"OpenFile", GTK_STOCK_OPEN
, NULL
, NULL
, NULL
, G_CALLBACK(open_caveset_cb
)},
1942 {"LoadInternal", GTK_STOCK_INDEX
, N_("Load _internal game")},
1943 {"LoadRecent", GTK_STOCK_DIALOG_INFO
, N_("Open _recent")},
1944 {"OpenCavesDir", GTK_STOCK_CDROM
, N_("O_pen shipped"), NULL
, NULL
, G_CALLBACK(open_caveset_dir_cb
)},
1945 {"SaveFile", GTK_STOCK_SAVE
, NULL
, NULL
, NULL
, G_CALLBACK(save_caveset_cb
)},
1946 {"SaveAsFile", GTK_STOCK_SAVE_AS
, NULL
, NULL
, NULL
, G_CALLBACK(save_caveset_as_cb
)},
1947 {"HighScore", GD_ICON_AWARD
, N_("Hi_ghscores"), NULL
, NULL
, G_CALLBACK(highscore_cb
)},
1950 static GtkActionEntry action_entries_title_replay
[]={
1951 {"ShowReplays", GD_ICON_REPLAY
, N_("Show _replays"), NULL
, N_("List replays which are recorded for caves in this caveset"), G_CALLBACK(show_replays_cb
)},
1954 static GtkActionEntry action_entries_game
[]={
1955 {"TakeSnapshot", GD_ICON_SNAPSHOT
, N_("_Take snapshot"), "F5", NULL
, G_CALLBACK(save_snapshot_cb
)},
1956 {"Restart", GD_ICON_RESTART_LEVEL
, N_("Re_start level"), "Escape", N_("Restart current level"), G_CALLBACK(restart_level_cb
)},
1957 {"EndGame", GTK_STOCK_STOP
, N_("_End game"), "F1", N_("End current game"), G_CALLBACK(stop_game_cb
)},
1960 static GtkActionEntry action_entries_snapshot
[]={
1961 {"RevertToSnapshot", GTK_STOCK_UNDO
, N_("_Revert to snapshot"), "F6", NULL
, G_CALLBACK(load_snapshot_cb
)},
1964 static GtkToggleActionEntry action_entries_toggle
[]={
1965 {"PauseGame", GTK_STOCK_MEDIA_PAUSE
, NULL
, "space", N_("Pause game"), G_CALLBACK(toggle_pause_cb
), FALSE
},
1966 {"FullScreen", GTK_STOCK_FULLSCREEN
, NULL
, "F11", N_("Fullscreen mode during play"), G_CALLBACK(toggle_fullscreen_cb
), FALSE
},
1967 {"FastForward", GTK_STOCK_MEDIA_FORWARD
, N_("Fast for_ward"), "<control>F", N_("Fast forward"), G_CALLBACK(toggle_fast_cb
), FALSE
},
1970 static const char *ui_info
=
1972 "<menubar name='MenuBar'>"
1973 "<menu action='FileMenu'>"
1975 "<menuitem action='OpenFile'/>"
1976 "<menuitem action='LoadRecent'/>"
1977 "<menuitem action='OpenCavesDir'/>"
1978 "<menuitem action='LoadInternal'/>"
1980 "<menuitem action='CaveEditor'/>"
1982 "<menuitem action='SaveFile'/>"
1983 "<menuitem action='SaveAsFile'/>"
1985 "<menuitem action='Quit'/>"
1987 "<menu action='PlayMenu'>"
1988 "<menuitem action='NewGame'/>"
1989 "<menuitem action='PauseGame'/>"
1990 "<menuitem action='FastForward'/>"
1991 "<menuitem action='TakeSnapshot'/>"
1992 "<menuitem action='RevertToSnapshot'/>"
1993 "<menuitem action='Restart'/>"
1994 "<menuitem action='EndGame'/>"
1996 "<menuitem action='CaveInfo'/>"
1997 "<menuitem action='HighScore'/>"
1998 "<menuitem action='ShowReplays'/>"
2000 "<menuitem action='FullScreen'/>"
2001 "<menuitem action='GameControlSettings'/>"
2002 "<menuitem action='GamePreferences'/>"
2004 "<menu action='HelpMenu'>"
2005 "<menuitem action='Help'/>"
2007 "<menuitem action='Errors'/>"
2008 "<menuitem action='About'/>"
2012 "<toolbar name='ToolBar'>"
2013 "<toolitem action='NewGame'/>"
2014 "<toolitem action='EndGame'/>"
2015 "<toolitem action='FullScreen'/>"
2017 "<toolitem action='PauseGame'/>"
2018 "<toolitem action='FastForward'/>"
2019 "<toolitem action='Restart'/>"
2021 "<toolitem action='CaveInfo'/>"
2022 "<toolitem action='ShowReplays'/>"
2026 GtkWidget
*vbox
, *hbox
, *recent_chooser
;
2027 GtkRecentFilter
*recent_filter
;
2031 const gchar
**names
;
2035 gtk_window_set_default_icon (logo
);
2036 g_object_unref(logo
);
2038 main_window
.window
=gtk_window_new(GTK_WINDOW_TOPLEVEL
);
2039 gtk_window_set_default_size(GTK_WINDOW(main_window
.window
), 360, 300);
2040 g_signal_connect(G_OBJECT(main_window
.window
), "focus_out_event", G_CALLBACK(main_window_focus_out_event
), NULL
);
2041 g_signal_connect(G_OBJECT(main_window
.window
), "delete_event", G_CALLBACK(main_window_delete_event
), NULL
);
2042 g_signal_connect(G_OBJECT(main_window
.window
), "key_press_event", G_CALLBACK(main_window_keypress_event
), NULL
);
2043 g_signal_connect(G_OBJECT(main_window
.window
), "key_release_event", G_CALLBACK(main_window_keypress_event
), NULL
);
2046 vbox
=gtk_vbox_new(FALSE
, 0);
2047 gtk_container_add (GTK_CONTAINER (main_window
.window
), vbox
);
2050 main_window
.actions_normal
=gtk_action_group_new("main_window.actions_normal");
2051 gtk_action_group_set_translation_domain(main_window
.actions_normal
, PACKAGE
);
2052 gtk_action_group_add_actions(main_window
.actions_normal
, action_entries_normal
, G_N_ELEMENTS(action_entries_normal
), &main_window
);
2053 gtk_action_group_add_toggle_actions(main_window
.actions_normal
, action_entries_toggle
, G_N_ELEMENTS(action_entries_toggle
), NULL
);
2054 main_window
.actions_title
=gtk_action_group_new("main_window.actions_title");
2055 gtk_action_group_set_translation_domain(main_window
.actions_title
, PACKAGE
);
2056 gtk_action_group_add_actions(main_window
.actions_title
, action_entries_title
, G_N_ELEMENTS(action_entries_title
), &main_window
);
2057 main_window
.actions_title_replay
=gtk_action_group_new("main_window.actions_title_replay");
2058 gtk_action_group_set_translation_domain(main_window
.actions_title_replay
, PACKAGE
);
2059 gtk_action_group_add_actions(main_window
.actions_title_replay
, action_entries_title_replay
, G_N_ELEMENTS(action_entries_title_replay
), &main_window
);
2060 /* make this toolbar button always have a title */
2061 g_object_set (gtk_action_group_get_action (main_window
.actions_title
, "NewGame"), "is_important", TRUE
, NULL
);
2062 main_window
.actions_game
=gtk_action_group_new("main_window.actions_game");
2063 gtk_action_group_set_translation_domain(main_window
.actions_game
, PACKAGE
);
2064 gtk_action_group_add_actions(main_window
.actions_game
, action_entries_game
, G_N_ELEMENTS(action_entries_game
), &main_window
);
2065 main_window
.actions_snapshot
=gtk_action_group_new("main_window.actions_snapshot");
2066 gtk_action_group_set_translation_domain(main_window
.actions_snapshot
, PACKAGE
);
2067 gtk_action_group_add_actions(main_window
.actions_snapshot
, action_entries_snapshot
, G_N_ELEMENTS(action_entries_snapshot
), &main_window
);
2070 ui
=gtk_ui_manager_new();
2071 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_normal
, 0);
2072 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_title
, 0);
2073 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_title_replay
, 0);
2074 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_game
, 0);
2075 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_snapshot
, 0);
2076 gtk_window_add_accel_group (GTK_WINDOW(main_window
.window
), gtk_ui_manager_get_accel_group (ui
));
2077 gtk_ui_manager_add_ui_from_string (ui
, ui_info
, -1, NULL
);
2078 main_window
.menubar
=gtk_ui_manager_get_widget (ui
, "/MenuBar");
2079 gtk_box_pack_start(GTK_BOX (vbox
), main_window
.menubar
, FALSE
, FALSE
, 0);
2080 main_window
.toolbar
=gtk_ui_manager_get_widget (ui
, "/ToolBar");
2081 gtk_toolbar_set_style(GTK_TOOLBAR(main_window
.toolbar
), GTK_TOOLBAR_BOTH_HORIZ
);
2082 gtk_box_pack_start(GTK_BOX (vbox
), main_window
.toolbar
, FALSE
, FALSE
, 0);
2083 gtk_toolbar_set_tooltips (GTK_TOOLBAR (main_window
.toolbar
), TRUE
);
2085 /* make a submenu, which contains the games compiled in. */
2087 menu
=gtk_menu_new();
2088 gtk_menu_item_set_submenu (GTK_MENU_ITEM (gtk_ui_manager_get_widget (ui
, "/MenuBar/FileMenu/LoadInternal")), menu
);
2089 names
=gd_caveset_get_internal_game_names ();
2091 GtkWidget
*menuitem
=gtk_menu_item_new_with_label(names
[i
]);
2093 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
2094 gtk_widget_show(menuitem
);
2095 g_signal_connect (G_OBJECT(menuitem
), "activate", G_CALLBACK(load_internal_cb
), GINT_TO_POINTER (i
));
2099 /* recent file chooser */
2100 recent_chooser
=gtk_recent_chooser_menu_new();
2101 recent_filter
=gtk_recent_filter_new();
2102 /* gdash file extensions */
2103 for (i
=0; gd_caveset_extensions
[i
]!=NULL
; i
++)
2104 gtk_recent_filter_add_pattern(recent_filter
, gd_caveset_extensions
[i
]);
2105 gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_chooser
), recent_filter
);
2106 gtk_recent_chooser_set_local_only(GTK_RECENT_CHOOSER(recent_chooser
), TRUE
);
2107 gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_chooser
), 10);
2108 gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_chooser
), GTK_RECENT_SORT_MRU
);
2109 gtk_menu_item_set_submenu (GTK_MENU_ITEM (gtk_ui_manager_get_widget (ui
, "/MenuBar/FileMenu/LoadRecent")), recent_chooser
);
2110 g_signal_connect(G_OBJECT(recent_chooser
), "item-activated", G_CALLBACK(recent_chooser_activated_cb
), NULL
);
2114 /* this hbox will contain the replay logo and the labels */
2115 hbox
=gtk_hbox_new(FALSE
, 6);
2116 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
2118 /* replay logo... */
2119 main_window
.replay_image_align
=gtk_alignment_new(0.5, 0.5, 0, 0);
2120 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.replay_image_align
, FALSE
, FALSE
, 0);
2121 gtk_container_add(GTK_CONTAINER(main_window
.replay_image_align
), gtk_image_new_from_stock(GD_ICON_REPLAY
, GTK_ICON_SIZE_LARGE_TOOLBAR
));
2124 main_window
.labels
=gtk_vbox_new(FALSE
, 0);
2125 gtk_box_pack_start_defaults(GTK_BOX(hbox
), main_window
.labels
);
2127 /* first hbox for labels ABOVE drawing */
2128 hbox
=gtk_hbox_new(FALSE
, 12);
2129 gtk_box_pack_start(GTK_BOX (main_window
.labels
), hbox
, FALSE
, FALSE
, 0);
2130 main_window
.label_topleft
=gtk_label_new(NULL
); /* NAME label */
2131 gtk_label_set_ellipsize(GTK_LABEL(main_window
.label_topleft
), PANGO_ELLIPSIZE_END
); /* enable ellipsize, as the cave name might be a long string */
2132 gtk_misc_set_alignment(GTK_MISC(main_window
.label_topleft
), 0, 0.5); /* as it will be expanded, we must set left justify (0) */
2133 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.label_topleft
, TRUE
, TRUE
, 0); /* should expand, as it is ellipsized!! */
2135 gtk_box_pack_end(GTK_BOX(hbox
), main_window
.label_topright
=gtk_label_new(NULL
), FALSE
, FALSE
, 0);
2137 /* second row of labels */
2138 hbox
=gtk_hbox_new(FALSE
, 12);
2139 gtk_box_pack_start(GTK_BOX(main_window
.labels
), hbox
, FALSE
, FALSE
, 0);
2140 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.label_bottomleft
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* diamonds label */
2141 gtk_box_pack_end(GTK_BOX(hbox
), main_window
.label_bottomright
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* time, score label */
2144 /* scroll window which contains the cave or the title image, so this is the main content of the window */
2145 main_window
.scroll_window
=gtk_scrolled_window_new(NULL
, NULL
);
2146 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(main_window
.scroll_window
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2147 gtk_box_pack_start_defaults(GTK_BOX (vbox
), main_window
.scroll_window
);
2149 main_window
.label_variables
=gtk_label_new(NULL
);
2150 gtk_box_pack_start(GTK_BOX (vbox
), main_window
.label_variables
, FALSE
, FALSE
, 0);
2152 hbox
=gtk_hbox_new(FALSE
, 6);
2153 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
2154 gtk_box_pack_start(GTK_BOX(hbox
), gtk_image_new_from_stock(GTK_STOCK_DIALOG_ERROR
, GTK_ICON_SIZE_MENU
), FALSE
, FALSE
, 0);
2155 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.error_label
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* error label */
2156 main_window
.error_hbox
=hbox
;
2158 gtk_widget_show_all(main_window
.window
);
2159 gtk_window_present(GTK_WINDOW(main_window
.window
));
2168 main(int argc
, char *argv
[])
2171 gboolean editor
=FALSE
;
2172 char *gallery_filename
=NULL
;
2173 char *png_filename
=NULL
, *png_size
=NULL
;
2174 char *save_cave_name
=NULL
;
2175 gboolean force_quit_no_gtk
;
2178 GOptionContext
*context
;
2179 GOptionEntry entries
[]={
2180 {"editor", 'e', 0, G_OPTION_ARG_NONE
, &editor
, N_("Start editor")},
2181 {"gallery", 'g', 0, G_OPTION_ARG_FILENAME
, &gallery_filename
, N_("Save caveset in a HTML gallery")},
2182 {"stylesheet", 's', 0, G_OPTION_ARG_STRING
/* not filename! */, &gd_html_stylesheet_filename
, N_("Link stylesheet from file to a HTML gallery, eg. \"../style.css\"")},
2183 {"favicon", 's', 0, G_OPTION_ARG_STRING
/* not filename! */, &gd_html_favicon_filename
, N_("Link shortcut icon to a HTML gallery, eg. \"../favicon.ico\"")},
2184 {"png", 'p', 0, G_OPTION_ARG_FILENAME
, &png_filename
, N_("Save cave C, level L in a PNG image. If no cave selected, uses a random one")},
2185 {"png_size", 'P', 0, G_OPTION_ARG_STRING
, &png_size
, N_("Set PNG image size. Default is 128x96, set to 0x0 for unscaled")},
2186 {"save", 'S', 0, G_OPTION_ARG_FILENAME
, &save_cave_name
, N_("Save caveset in a BDCFF file")},
2187 {"quit", 'q', 0, G_OPTION_ARG_NONE
, &quit
, N_("Batch mode: quit after specified tasks")},
2191 context
=gd_option_context_new();
2192 g_option_context_add_main_entries (context
, entries
, PACKAGE
); /* gdash (gtk version) parameters */
2193 g_option_context_add_group (context
, gtk_get_option_group(FALSE
)); /* add gtk parameters */
2194 g_option_context_parse (context
, &argc
, &argv
, &error
);
2195 g_option_context_free (context
);
2197 g_warning("%s", error
->message
);
2198 g_error_free(error
);
2202 if (gd_param_license
) {
2203 char *wrapped
=gd_wrap_text(gd_about_license
, 72);
2205 g_print("%s", wrapped
);
2210 gd_install_log_handler();
2212 gd_settings_init_dirs();
2217 gd_settings_set_locale();
2219 force_quit_no_gtk
=FALSE
;
2220 if (!gtk_init_check(&argc
, &argv
)) {
2221 force_quit_no_gtk
=TRUE
;
2224 gd_settings_init_translation();
2228 gd_cave_sound_db_init();
2229 gd_c64_import_init_tables();
2231 gd_caveset_clear(); /* this also creates the default cave */
2233 gd_clear_error_flag();
2235 gd_wait_before_game_over
=FALSE
;
2237 /* LOAD A CAVESET FROM A FILE, OR AN INTERNAL ONE */
2238 /* if remaining arguments, they are filenames */
2239 if (gd_param_cavenames
&& gd_param_cavenames
[0]) {
2240 /* load caveset, "ignore" errors. */
2241 if (!gd_open_caveset_in_ui(gd_param_cavenames
[0], gd_use_bdcff_highscore
))
2242 g_critical (_("Errors during loading caveset from file '%s'"), gd_param_cavenames
[0]);
2244 else if (gd_param_internal
) {
2245 /* if specified an internal caveset; if error, halt now */
2246 if (!gd_caveset_load_from_internal (gd_param_internal
-1, gd_user_config_dir
))
2247 g_critical (_("%d: no such internal caveset"), gd_param_internal
);
2250 /* if nothing requested, load default */
2251 gd_caveset_load_from_internal(0, gd_user_config_dir
);
2253 /* always load c64 graphics, as it is the builtin, and we need an icon for the theme selector. */
2254 gd_loadcells_default();
2255 gd_create_pixbuf_for_builtin_theme();
2257 /* load other theme, if specified in config. */
2258 if (gd_theme
!=NULL
&& !g_str_equal(gd_theme
, "")) {
2259 if (!gd_loadcells_file(gd_theme
)) {
2260 /* forget the theme if we are unable to load */
2261 g_warning("Cannot load theme %s, switching to built-in theme", gd_theme
);
2264 gd_loadcells_default(); /* load default gfx */
2268 /* after loading cells... see if generating a gallery. */
2269 /* but only if there are any caves at all. */
2270 if (gd_caveset
&& gallery_filename
)
2271 gd_save_html (gallery_filename
, NULL
);
2273 /* if cave or level values given, check range */
2275 if (gd_param_cave
<1 || gd_param_cave
>gd_caveset_count() || gd_param_level
<1 || gd_param_level
>5)
2276 g_critical (_("Invalid cave or level number!"));
2282 GdCave
*renderedcave
;
2283 unsigned int size_x
=128, size_y
=96; /* default size */
2285 if (gd_param_cave
== 0)
2286 gd_param_cave
=g_random_int_range(0, gd_caveset_count ())+1;
2288 if (png_size
&& (sscanf (png_size
, "%ux%u", &size_x
, &size_y
) != 2))
2289 g_critical (_("Invalid image size: %s"), png_size
);
2290 if (size_x
<1 || size_y
<1) {
2295 /* rendering cave for png: seed=0 */
2296 renderedcave
=gd_cave_new_from_caveset (gd_param_cave
-1, gd_param_level
-1, 0);
2297 pixbuf
=gd_drawcave_to_pixbuf(renderedcave
, size_x
, size_y
, TRUE
, FALSE
);
2298 if (!gdk_pixbuf_save (pixbuf
, png_filename
, "png", &error
, "compression", "9", NULL
))
2299 g_critical ("Error saving PNG image %s: %s", png_filename
, error
->message
);
2300 g_object_unref(pixbuf
);
2301 gd_cave_free (renderedcave
);
2303 /* avoid starting game */
2308 gd_caveset_save(save_cave_name
);
2310 /* if batch mode, quit now */
2313 if (force_quit_no_gtk
) {
2314 g_critical("Cannot initialize GUI");
2319 gd_register_stock_icons();
2320 create_main_window();
2321 gd_main_window_set_title();
2325 gd_sound_set_music_volume(gd_sound_music_volume_percent
);
2326 gd_sound_set_chunk_volumes(gd_sound_chunks_volume_percent
);
2328 main_window_init_title();
2330 #ifdef G_THREADS_ENABLED
2331 if (!g_thread_supported())
2332 g_thread_init(NULL
);
2335 if (gd_param_cave
) {
2336 /* if cave number given, start game */
2337 main_new_game(g_get_real_name(), gd_param_cave
-1, gd_param_level
-1);
2340 cave_editor_cb(NULL
, &main_window
);
2344 gd_save_highscore(gd_user_config_dir
);