/*
* GLOP: Gnomified Lights Out Puzzles
* Copyright (C) 2001 Andreas T. Hagli <[email protected]>
*
* 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 <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <dirent.h>
#include <stdio.h>
#include <unistd.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <libintl.h>
#include <gpe/init.h>
#include <gpe/pixmaps.h>
#include <gpe/picturebutton.h>
#include <gpe/question.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#define _(x) gettext(x)
#define LIGHT_SIZE 40
#define MAX_WIDTH 12
#define MAX_HEIGHT 9
#define GAME_EVENTS (GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK| GDK_BUTTON_RELEASE_MASK)
enum game_types { CLASSIC, TWOK, DELUX, KEYCHAIN, CUBE, MERLIN };
enum light_state { NO, ON, HALF, OFF };
static GtkWidget *moves_label, *lights_off, *lights_label;
static GtkWidget *vb, *status_box;
static GtkWidget *align, *draw_area;
static GtkWidget *pref_dialog;
static GdkPixbuf *lights;
static gchar *fname;
static int x_dir[8] = { 1, 1, 0,-1,-1,-1,0,1 };
static int y_dir[8] = { 0,-1,-1,-1, 0, 1,1,1 };
static int game_type;
static int moves;
static gboolean randomize;
static gboolean theme_default;
static char *theme;
static char *pref_theme;
static gboolean pref_theme_default;
static gboolean pref_randomize;
static int pref_game_type;
static struct game_values {
int width;
int height;
gboolean cubeform;
gboolean consistent;
int states;
/* From east to south east (like radians; 0 -> 2PI). */
gboolean neighbours[8];
} game;
enum game_types grid[MAX_WIDTH][MAX_HEIGHT];
static void new_game (void);
static void
draw_light (int x, int y)
{
int bx, by;
if (grid[x][y] != NO)
{
by = 0;
bx = LIGHT_SIZE * (grid[x][y] - 1);
gdk_draw_pixbuf (draw_area->window,
draw_area->style->black_gc, lights,
bx, by, x * LIGHT_SIZE, y * LIGHT_SIZE,
LIGHT_SIZE, LIGHT_SIZE, GDK_RGB_DITHER_NORMAL, 0, 0);
}
else
gdk_window_clear_area (draw_area->window, x * LIGHT_SIZE, y * LIGHT_SIZE,
LIGHT_SIZE, LIGHT_SIZE);
}
static void
paint (GdkRectangle *area)
{
int x1, y1, x2, y2, x, y;
x1 = area->x / LIGHT_SIZE;
y1 = area->y / LIGHT_SIZE;
x2 = (area->x + area->width) / LIGHT_SIZE;
y2 = (area->y + area->height) / LIGHT_SIZE;
for (x = x1; x <= x2; x++){
for (y = y1; y <= y2; y++){
draw_light (x, y);
}
}
}
static void
game_over (void)
{
gchar *msg;
int r;
msg = g_strdup_printf (_("Congratulations!\n\nYou made it with %d moves.\n\nPlay again?"), moves);
r = gpe_question_ask (msg, _("Game over"), "icon", "!gtk-yes", NULL, "!gtk-no", NULL, NULL, NULL);
g_free (msg);
if (r == 0)
new_game ();
}
static int
count_lights_on (void)
{
int x, y;
int counter = 0;
for (x = 0; x < (game.cubeform ? 4 : 1) * game.width; x++)
for (y = 0; y < (game.cubeform ? 3 : 1) * game.height; y++)
if (grid[x][y] == ON || grid[x][y] == HALF)
counter++;
return counter;
}
static void
change_light (int x, int y)
{
if (grid[x][y] == ON && game.states == 2)
grid[x][y] = OFF;
else if (grid[x][y] == ON && game.states == 3)
grid[x][y] = HALF;
else if (grid[x][y] == HALF)
grid[x][y] = OFF;
else if (grid[x][y] == OFF)
grid[x][y] = ON;
draw_light (x, y);
}
static void
set_lights_off (void)
{
char buf [10];
int nlo;
nlo = game.width * game.height * (game.cubeform ? 6 : 1) - count_lights_on ();
sprintf (buf, "%.2d", nlo);
gtk_label_set (GTK_LABEL(lights_off), buf);
}
static void
set_moves (int new_moves)
{
char buf [10];
moves = new_moves;
sprintf (buf, "%.2d ", moves);
gtk_label_set (GTK_LABEL(moves_label), buf);
}
static void
select_light (int x, int y)
{
int i;
if (grid[x][y] == NO)
return;
change_light (x, y);
if (game_type == MERLIN)
{
if (x != 0)
change_light (x-1, y);
if (x != 2)
change_light (x+1, y);
if (y != 0)
change_light (x, y-1);
if (y != 2)
change_light (x, y+1);
if (!(x == 1 && y == 1))
change_light (1, 1);
}
else
{
for (i = 0; i < 8; i++)
if (game.neighbours[i])
{
if (game.cubeform)
{
int tmp_x, tmp_y;
tmp_x = x + x_dir[i];
tmp_y = y + y_dir[i];
if (tmp_y < 0)
{
tmp_y = game.height;
tmp_x = 5*game.width - tmp_x - 1;
}
else if (y < game.height && tmp_x < game.width)
{
tmp_x = tmp_y;
tmp_y = game.height;
}
else if (y < game.height && tmp_x >= 2*game.width)
{
tmp_x += game.width - tmp_y - 1;
tmp_y = game.height;
}
else if (tmp_y < game.height && tmp_x < game.width)
{
tmp_y = tmp_x;
tmp_x = game.width;
}
else if (tmp_y < game.height && tmp_x >= 3*game.width)
{
tmp_y = 0;
tmp_x = 5*game.width - tmp_x - 1;
}
else if (tmp_y < game.height && tmp_x >= 2*game.width)
{
tmp_y = 3*game.width - tmp_x - 1;
tmp_x = 2*game.width - 1;
}
else if (tmp_x < 0)
tmp_x = 4*game.width - 1;
else if (tmp_x >= 4*game.width)
tmp_x = 0;
else if (y >= 2*game.height && tmp_x < game.width)
{
tmp_x = 3*game.height - tmp_y - 1;
tmp_y = 2*game.height - 1;
}
else if (y >= 2*game.height && tmp_x >= 2*game.width)
{
tmp_x = tmp_y;
tmp_y = 2*game.height - 1;
}
else if (tmp_y >= 2*game.height && tmp_x < game.width)
{
tmp_y = 3*game.height - tmp_x - 1;
tmp_x = game.width;
}
else if (tmp_y >= 2*game.height && tmp_x >= 3*game.width)
{
tmp_y = 3*game.height - 1;
tmp_x = 5*game.width - tmp_x - 1;
}
else if (tmp_y >= 2*game.height && tmp_x >= 2*game.width)
{
tmp_y = tmp_x;
tmp_x = 2*game.width - 1;
}
else if (tmp_y >= 3*game.height)
tmp_y = 0;
change_light (tmp_x, tmp_y);
}
else if (game.consistent)
{
int tmp_x, tmp_y;
if ((x_dir[i] + x) < 0)
tmp_x = x + x_dir[i] + game.width;
else if ((x_dir[i] + x) >= game.width)
tmp_x = x + x_dir[i] - game.width;
else
tmp_x = x + x_dir[i];
if ((y_dir[i] + y) < 0)
tmp_y = y + y_dir[i] + game.height;
else if ((y_dir[i] + y) >= game.height)
tmp_y = y + y_dir[i] - game.height;
else
tmp_y = y + y_dir[i];
change_light (tmp_x, tmp_y);
}
else if ((x_dir[i] + x) >= 0 && (x_dir[i] + x) < game.width &&
(y_dir[i] + y) >= 0 && (y_dir[i] + y) < game.height)
change_light (x_dir[i] + x, y_dir[i] + y);
}
}
}
static void
click_light (int x, int y)
{
if (!count_lights_on ())
return;
select_light (x, y);
if (grid[x][y] != NO)
set_moves (++moves);
set_lights_off ();
gtk_widget_draw (draw_area, NULL);
if (count_lights_on () == 0)
game_over ();
}
static gint
area_event (GtkWidget *widget, GdkEvent *event, void *d)
{
switch (event->type){
case GDK_EXPOSE: {
GdkEventExpose *e = (GdkEventExpose *) event;
paint (&e->area);
return TRUE;
}
case GDK_BUTTON_