/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* * Gnome Nibbles: Gnome Worm Game * Written by Sean MacIsaac , Ian Peters * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "main.h" #include "gnibbles.h" #include "worm.h" #include "boni.h" #include "bonus.h" #include "warpmanager.h" #include "properties.h" #include "scoreboard.h" GnibblesWorm *worms[NUMWORMS]; GnibblesBoni *boni = NULL; GnibblesWarpManager *warpmanager; GdkPixmap *buffer_pixmap = NULL; GdkPixmap *gnibbles_pixmap = NULL; GdkPixmap *logo_pixmap = NULL; extern GtkWidget *drawing_area; extern gchar board[BOARDWIDTH][BOARDHEIGHT]; extern GnibblesProperties *properties; extern GnibblesScoreboard *scoreboard; /* extern guint properties->tilesize, properties->tilesize; */ static void gnibbles_error (GtkWidget *window, gchar *message) { GtkWidget *w = gtk_message_dialog_new (GTK_WINDOW (window), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, message); gtk_dialog_run (GTK_DIALOG (w)); gtk_widget_destroy (GTK_WIDGET (w)); exit (1); } static void gnibbles_copy_pixmap (GdkDrawable *drawable, gint which, gint x, gint y, gboolean big) { gint w = properties->tilesize * (big ? 2 : 1), h = properties->tilesize * (big ? 2 : 1); guint nh = 10 / (big ? 2 : 1), nv = 10 / (big ? 2 : 1); gdk_draw_drawable (GDK_DRAWABLE (drawable), drawing_area->style->fg_gc[GTK_WIDGET_STATE (drawing_area)], gnibbles_pixmap, (which % nh) * w, ((big ? 3 : 0) + which / nv) * h, x * properties->tilesize, y * properties->tilesize, w, h); } void gnibbles_draw_pixmap (gint which, gint x, gint y) { gnibbles_copy_pixmap (drawing_area->window, which, x, y, FALSE); gnibbles_copy_pixmap (buffer_pixmap, which, x, y, FALSE); } void gnibbles_draw_big_pixmap (gint which, gint x, gint y) { gnibbles_copy_pixmap (drawing_area->window, which, x, y, TRUE); gnibbles_copy_pixmap (buffer_pixmap, which, x, y, TRUE); } void gnibbles_draw_pixmap_buffer (gint which, gint x, gint y) { gnibbles_copy_pixmap (buffer_pixmap, which, x, y, FALSE); } void gnibbles_draw_big_pixmap_buffer (gint which, gint x, gint y) { gnibbles_copy_pixmap (buffer_pixmap, which, x, y, TRUE); } void gnibbles_load_pixmap (GtkWidget *window) { GdkPixbuf *image; GdkPixbuf *tmp; gchar *filename; filename = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_DATADIR, "pixmaps/gnibbles/gnibbles.png", TRUE, NULL); if (!filename) { char *message = g_strdup_printf (_("Gnibbles couldn't find pixmap file:\n%s\n\n" "Please check your Gnibbles installation"), filename); gnibbles_error (window, message); } image = gdk_pixbuf_new_from_file (filename, NULL); if (gnibbles_pixmap) g_object_unref (G_OBJECT (gnibbles_pixmap)); tmp = gdk_pixbuf_scale_simple (image, 10 * properties->tilesize, 10 * properties->tilesize, GDK_INTERP_TILES); gdk_pixbuf_render_pixmap_and_mask (tmp, &gnibbles_pixmap, NULL, 127); g_object_unref (G_OBJECT (image)); g_object_unref (G_OBJECT (tmp)); g_free (filename); filename = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_DATADIR, "pixmaps/gnibbles/gnibbles-logo.png", TRUE, NULL); if (!filename) { char *message = g_strdup_printf (_("Gnibbles Couldn't find pixmap file:\n%s\n\n" "Please check your Gnibbles instalation"), filename); gnibbles_error (window, message); } image = gdk_pixbuf_new_from_file (filename, NULL); tmp = gdk_pixbuf_scale_simple (image, BOARDWIDTH * properties->tilesize, BOARDHEIGHT * properties->tilesize, GDK_INTERP_TILES); if (logo_pixmap) g_object_unref (G_OBJECT (logo_pixmap)); gdk_pixbuf_render_pixmap_and_mask (tmp, &logo_pixmap, NULL, 127); g_object_unref (G_OBJECT (image)); g_object_unref (G_OBJECT (tmp)); g_free (filename); } void gnibbles_load_level (GtkWidget *window, gint level) { gchar *tmp = NULL; gchar *filename; FILE *in; gchar tmpboard[BOARDWIDTH + 1]; gint i, j; gint count = 0; tmp = g_strdup_printf ("gnibbles/level%03d.gnl", level); filename = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_DATADIR, tmp, TRUE, NULL); g_free (tmp); if ((in = fopen (filename, "r")) == NULL) { char *message = g_strdup_printf ( _("Gnibbles couldn't load level file:\n%s\n\n" "Please check your Gnibbles installation"), filename); gnibbles_error (window, message); } g_free (filename); if (warpmanager) gnibbles_warpmanager_destroy (warpmanager); warpmanager = gnibbles_warpmanager_new (); if (boni) gnibbles_boni_destroy (boni); boni = gnibbles_boni_new (); for (i = 0; i < BOARDHEIGHT; i++) { fgets (tmpboard, 255, in); for (j = 0; j < BOARDWIDTH; j++) { board[j][i] = tmpboard[j]; switch (board[j][i]) { case 'm': board[j][i] = 'a'; if (count < properties->numworms) gnibbles_worm_set_start (worms[count++], j, i, WORMUP); break; case 'n': board[j][i] = 'a'; if (count < properties->numworms) gnibbles_worm_set_start (worms[count++], j, i, WORMLEFT); break; case 'o': board[j][i] = 'a'; if (count < properties->numworms) gnibbles_worm_set_start (worms[count++], j, i, WORMDOWN); break; case 'p': board[j][i] = 'a'; if (count < properties->numworms) gnibbles_worm_set_start (worms[count++], j, i, WORMRIGHT); break; case 'Q': gnibbles_warpmanager_add_warp (warpmanager, j - 1, i - 1, -1, -1); break; case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': gnibbles_warpmanager_add_warp (warpmanager, j - 1, i - 1, -board[j][i], 0); break; case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': gnibbles_warpmanager_add_warp (warpmanager, -(board[j][i]-'a'+'A'), 0, j, i); board[j][i] = EMPTYCHAR; break; } gnibbles_draw_pixmap_buffer (board[j][i]-'a', j, i); } } gdk_draw_drawable (GDK_DRAWABLE (drawing_area->window), drawing_area->style->fg_gc [GTK_WIDGET_STATE (drawing_area)], buffer_pixmap, 0, 0, 0, 0, BOARDWIDTH * 10, BOARDHEIGHT * 10); fclose (in); } void gnibbles_init (void) { gint i; for (i = 0; i < properties->numworms; i++) if (worms[i]) gnibbles_worm_destroy (worms[i]); gnibbles_scoreboard_clear (scoreboard); for (i = 0; i < properties->numworms; i++) { worms[i] = gnibbles_worm_new (i); gnibbles_scoreboard_register (scoreboard, worms[i]); } gnibbles_scoreboard_update (scoreboard); } void gnibbles_add_bonus (gint regular) { gint x, y, good; if (regular) { good = 0; } else { good = rand() % 50; if (good) return; } do { good = 1; x = rand() % (BOARDWIDTH - 1); y = rand() % (BOARDHEIGHT - 1); if (board[x][y] != EMPTYCHAR) good = 0; if (board[x + 1][y] != EMPTYCHAR) good = 0; if (board[x][y + 1] != EMPTYCHAR) good = 0; if (board[x + 1][y + 1] != EMPTYCHAR) good = 0; } while (!good); if (regular) { if ((rand() % 7 == 0) && properties->fakes) gnibbles_boni_add_bonus (boni, x, y, BONUSREGULAR, 1, 300); good = 0; while (!good) { good = 1; x = rand() % (BOARDWIDTH - 1); y = rand() % (BOARDHEIGHT - 1); if (board[x][y] != EMPTYCHAR) good = 0; if (board[x + 1][y] != EMPTYCHAR) good = 0; if (board[x][y + 1] != EMPTYCHAR) good = 0; if (board[x + 1][y + 1] != EMPTYCHAR) good = 0; } gnibbles_boni_add_bonus (boni, x, y, BONUSREGULAR, 0, 300); } else if (boni->missed <= MAXMISSED) { good = rand () % 7; if (good) good = 0; else good = 1; if (good && !properties->fakes) return; switch (rand () % 21) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: gnibbles_boni_add_bonus (boni, x, y, BONUSHALF, good, 200); break; case 10: case 11: case 12: case 13: case 14: gnibbles_boni_add_bonus (boni, x, y, BONUSDOUBLE, good, 150); break; case 15: gnibbles_boni_add_bonus (boni, x, y, BONUSLIFE, good, 100); break; case 16: case 17: case 18: case 19: case 20: if (properties->numworms > 1) gnibbles_boni_add_bonus (boni, x, y, BONUSREVERSE, good, 150); break; } } } gint gnibbles_move_worms () { gint i, j, status = 1; gint *dead = g_new (gint, properties->numworms); if (boni->missed > MAXMISSED) for (i = 0; i < properties->numworms; i++) if (worms[i]->score) worms[i]->score--; for (i = 0; i < boni->numbonuses; i++) { if (!(boni->bonuses[i]->countdown--)) { if (boni->bonuses[i]->type == BONUSREGULAR && !boni->bonuses[i]->fake) { gnibbles_boni_remove_bonus (boni, boni->bonuses[i]->x, boni->bonuses[i]->y); boni->missed++; gnibbles_add_bonus (1); } else { gnibbles_boni_remove_bonus (boni, boni->bonuses[i]->x, boni->bonuses[i]->y); } } } for (i = 0; i < properties->numworms; i++) { gnibbles_worm_erase_tail (worms[i]); } for (i = 0; i < properties->numworms; i++) { dead[i] = !gnibbles_worm_test_move_head (worms[i]); status &= !dead[i]; } /* If one worm has died, me must make sure that an earlier worm was not supposed to die as well. */ if (!status) for (i = 0; i < properties->numworms; i++) if (!dead[i]) for (j = 0; j < properties->numworms; j++) { if (i != j && worms[i]->xhead == worms[j]->xhead && worms[i]->yhead == worms[j]->yhead) dead[i] = TRUE; gnibbles_draw_pixmap (BLANKPIXMAP, worms[i]->xtail, worms[i]->ytail); gnibbles_draw_pixmap (properties->wormprops[i]->color, worms[i]->xhead, worms[i]->yhead); } for (i = 0; i < properties->numworms; i++) if (dead[i]) { if (properties->numworms > 1) worms[i]->score *= .7; status |= gnibbles_worm_lose_life (worms[i]) << 1; } for (i = 0; i < properties->numworms; i++) if (!dead[i]) gnibbles_worm_move_tail (worms[i]); for (i = 0; i < properties->numworms; i++) if (!dead[i]) gnibbles_worm_draw_head (worms[i]); if (status & GAMEOVER) { gnibbles_play_sound ("crash"); gnibbles_play_sound ("gameover"); return (GAMEOVER); } if (status) return (CONTINUE); gnibbles_play_sound ("crash"); g_free (dead); return (NEWROUND); } gint gnibbles_keypress_worms (guint keyval) { gint i; for (i = 0; i < properties->numworms; i++) if (gnibbles_worm_handle_keypress (worms[i], keyval)) return TRUE; return FALSE; } void gnibbles_undraw_worms (gint data) { gint i; for (i = 0; i < properties->numworms; i++) gnibbles_worm_undraw_nth (worms[i], data); } void gnibbles_play_sound (const char *which) { if (properties->sound) gnome_triggers_do (NULL, NULL, "gnibbles", which, NULL); } void gnibbles_show_scores (GtkWidget *window, gint pos) { GtkWidget *dialog; gchar *buf = NULL; buf = g_strdup_printf ("%d.%d", properties->gamespeed, properties->fakes); dialog = gnome_scores_display ("Gnibbles", "gnibbles", buf, pos); g_free (buf); if (dialog != NULL) { gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window)); gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); } } void gnibbles_log_score (GtkWidget *window) { gchar *buf = NULL; gint pos; if (properties->numworms > 1) return; if (properties->startlevel != 1) return; if (!worms[0]->score) return; buf = g_strdup_printf ("%d.%d", properties->gamespeed, properties->fakes); pos = gnome_score_log (worms[0]->score, buf, TRUE); g_free (buf); update_score_state (); gnibbles_show_scores (window, pos); }