mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-20 23:51:29 -07:00
Configuration dialog box, on the GTK front end only as yet.
[originally from svn r4182]
This commit is contained in:
106
cube.c
106
cube.c
@ -11,6 +11,7 @@
|
|||||||
#include "puzzles.h"
|
#include "puzzles.h"
|
||||||
|
|
||||||
const char *const game_name = "Cube";
|
const char *const game_name = "Cube";
|
||||||
|
const int game_can_configure = TRUE;
|
||||||
|
|
||||||
#define MAXVERTICES 20
|
#define MAXVERTICES 20
|
||||||
#define MAXFACES 20
|
#define MAXFACES 20
|
||||||
@ -238,8 +239,8 @@ int game_fetch_preset(int i, char **name, game_params **params)
|
|||||||
case 1:
|
case 1:
|
||||||
str = "Tetrahedron";
|
str = "Tetrahedron";
|
||||||
ret->solid = TETRAHEDRON;
|
ret->solid = TETRAHEDRON;
|
||||||
ret->d1 = 2;
|
ret->d1 = 1;
|
||||||
ret->d2 = 1;
|
ret->d2 = 2;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
str = "Octahedron";
|
str = "Octahedron";
|
||||||
@ -324,12 +325,12 @@ static void enum_grid_squares(game_params *params,
|
|||||||
float theight = (float)(sqrt(3) / 2.0);
|
float theight = (float)(sqrt(3) / 2.0);
|
||||||
|
|
||||||
for (row = 0; row < params->d1 + params->d2; row++) {
|
for (row = 0; row < params->d1 + params->d2; row++) {
|
||||||
if (row < params->d1) {
|
if (row < params->d2) {
|
||||||
other = +1;
|
other = +1;
|
||||||
rowlen = row + params->d2;
|
rowlen = row + params->d1;
|
||||||
} else {
|
} else {
|
||||||
other = -1;
|
other = -1;
|
||||||
rowlen = 2*params->d1 + params->d2 - row;
|
rowlen = 2*params->d2 + params->d1 - row;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -415,7 +416,7 @@ static void enum_grid_squares(game_params *params,
|
|||||||
sq.flip = FALSE;
|
sq.flip = FALSE;
|
||||||
|
|
||||||
if (firstix < 0)
|
if (firstix < 0)
|
||||||
firstix = ix;
|
firstix = (ix - 1) & 3;
|
||||||
ix -= firstix;
|
ix -= firstix;
|
||||||
sq.tetra_class = ((row+(ix&1)) & 2) ^ (ix & 3);
|
sq.tetra_class = ((row+(ix&1)) & 2) ^ (ix & 3);
|
||||||
|
|
||||||
@ -443,6 +444,99 @@ static int grid_area(int d1, int d2, int order)
|
|||||||
return d1*d1 + d2*d2 + 4*d1*d2;
|
return d1*d1 + d2*d2 + 4*d1*d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config_item *game_configure(game_params *params)
|
||||||
|
{
|
||||||
|
config_item *ret = snewn(4, config_item);
|
||||||
|
char buf[80];
|
||||||
|
|
||||||
|
ret[0].name = "Type of solid";
|
||||||
|
ret[0].type = CHOICES;
|
||||||
|
ret[0].sval = ":Tetrahedron:Cube:Octahedron:Icosahedron";
|
||||||
|
ret[0].ival = params->solid;
|
||||||
|
|
||||||
|
ret[1].name = "Width / top";
|
||||||
|
ret[1].type = STRING;
|
||||||
|
sprintf(buf, "%d", params->d1);
|
||||||
|
ret[1].sval = dupstr(buf);
|
||||||
|
ret[1].ival = 0;
|
||||||
|
|
||||||
|
ret[2].name = "Height / bottom";
|
||||||
|
ret[2].type = STRING;
|
||||||
|
sprintf(buf, "%d", params->d2);
|
||||||
|
ret[2].sval = dupstr(buf);
|
||||||
|
ret[2].ival = 0;
|
||||||
|
|
||||||
|
ret[3].name = NULL;
|
||||||
|
ret[3].type = ENDCFG;
|
||||||
|
ret[3].sval = NULL;
|
||||||
|
ret[3].ival = 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
game_params *custom_params(config_item *cfg)
|
||||||
|
{
|
||||||
|
game_params *ret = snew(game_params);
|
||||||
|
|
||||||
|
ret->solid = cfg[0].ival;
|
||||||
|
ret->d1 = atoi(cfg[1].sval);
|
||||||
|
ret->d2 = atoi(cfg[2].sval);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void count_grid_square_callback(void *ctx, struct grid_square *sq)
|
||||||
|
{
|
||||||
|
int *classes = (int *)ctx;
|
||||||
|
int thisclass;
|
||||||
|
|
||||||
|
if (classes[4] == 4)
|
||||||
|
thisclass = sq->tetra_class;
|
||||||
|
else if (classes[4] == 2)
|
||||||
|
thisclass = sq->flip;
|
||||||
|
else
|
||||||
|
thisclass = 0;
|
||||||
|
|
||||||
|
classes[thisclass]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *validate_params(game_params *params)
|
||||||
|
{
|
||||||
|
int classes[5];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (params->solid < 0 || params->solid >= lenof(solids))
|
||||||
|
return "Unrecognised solid type";
|
||||||
|
|
||||||
|
if (solids[params->solid]->order == 4) {
|
||||||
|
if (params->d1 <= 0 || params->d2 <= 0)
|
||||||
|
return "Both grid dimensions must be greater than zero";
|
||||||
|
} else {
|
||||||
|
if (params->d1 <= 0 && params->d2 <= 0)
|
||||||
|
return "At least one grid dimension must be greater than zero";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
classes[i] = 0;
|
||||||
|
if (params->solid == TETRAHEDRON)
|
||||||
|
classes[4] = 4;
|
||||||
|
else if (params->solid == OCTAHEDRON)
|
||||||
|
classes[4] = 2;
|
||||||
|
else
|
||||||
|
classes[4] = 1;
|
||||||
|
enum_grid_squares(params, count_grid_square_callback, classes);
|
||||||
|
|
||||||
|
for (i = 0; i < classes[4]; i++)
|
||||||
|
if (classes[i] < solids[params->solid]->nfaces / classes[4])
|
||||||
|
return "Not enough grid space to place all blue faces";
|
||||||
|
|
||||||
|
if (grid_area(params->d1, params->d2, solids[params->solid]->order) <
|
||||||
|
solids[params->solid]->nfaces + 1)
|
||||||
|
return "Not enough space to place the solid on an empty square";
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct grid_data {
|
struct grid_data {
|
||||||
int *gridptrs[4];
|
int *gridptrs[4];
|
||||||
int nsquares[4];
|
int nsquares[4];
|
||||||
|
46
fifteen.c
46
fifteen.c
@ -11,6 +11,7 @@
|
|||||||
#include "puzzles.h"
|
#include "puzzles.h"
|
||||||
|
|
||||||
const char *const game_name = "Fifteen";
|
const char *const game_name = "Fifteen";
|
||||||
|
const int game_can_configure = TRUE;
|
||||||
|
|
||||||
#define TILE_SIZE 48
|
#define TILE_SIZE 48
|
||||||
#define BORDER (TILE_SIZE / 2)
|
#define BORDER (TILE_SIZE / 2)
|
||||||
@ -71,6 +72,51 @@ game_params *dup_params(game_params *params)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config_item *game_configure(game_params *params)
|
||||||
|
{
|
||||||
|
config_item *ret;
|
||||||
|
char buf[80];
|
||||||
|
|
||||||
|
ret = snewn(3, config_item);
|
||||||
|
|
||||||
|
ret[0].name = "Width";
|
||||||
|
ret[0].type = STRING;
|
||||||
|
sprintf(buf, "%d", params->w);
|
||||||
|
ret[0].sval = dupstr(buf);
|
||||||
|
ret[0].ival = 0;
|
||||||
|
|
||||||
|
ret[1].name = "Height";
|
||||||
|
ret[1].type = STRING;
|
||||||
|
sprintf(buf, "%d", params->h);
|
||||||
|
ret[1].sval = dupstr(buf);
|
||||||
|
ret[1].ival = 0;
|
||||||
|
|
||||||
|
ret[2].name = NULL;
|
||||||
|
ret[2].type = ENDCFG;
|
||||||
|
ret[2].sval = NULL;
|
||||||
|
ret[2].ival = 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
game_params *custom_params(config_item *cfg)
|
||||||
|
{
|
||||||
|
game_params *ret = snew(game_params);
|
||||||
|
|
||||||
|
ret->w = atoi(cfg[0].sval);
|
||||||
|
ret->h = atoi(cfg[1].sval);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *validate_params(game_params *params)
|
||||||
|
{
|
||||||
|
if (params->w < 2 && params->h < 2)
|
||||||
|
return "Width and height must both be at least two";
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int perm_parity(int *perm, int n)
|
int perm_parity(int *perm, int n)
|
||||||
{
|
{
|
||||||
int i, j, ret;
|
int i, j, ret;
|
||||||
|
277
gtk.c
277
gtk.c
@ -7,6 +7,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
#include <gdk/gdkkeysyms.h>
|
#include <gdk/gdkkeysyms.h>
|
||||||
@ -64,6 +65,9 @@ struct frontend {
|
|||||||
int timer_active, timer_id;
|
int timer_active, timer_id;
|
||||||
struct font *fonts;
|
struct font *fonts;
|
||||||
int nfonts, fontsize;
|
int nfonts, fontsize;
|
||||||
|
config_item *cfg;
|
||||||
|
int cfgret;
|
||||||
|
GtkWidget *cfgbox;
|
||||||
};
|
};
|
||||||
|
|
||||||
void frontend_default_colour(frontend *fe, float *output)
|
void frontend_default_colour(frontend *fe, float *output)
|
||||||
@ -370,6 +374,252 @@ void activate_timer(frontend *fe)
|
|||||||
fe->timer_active = TRUE;
|
fe->timer_active = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void window_destroy(GtkWidget *widget, gpointer data)
|
||||||
|
{
|
||||||
|
gtk_main_quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void errmsg_button_clicked(GtkButton *button, gpointer data)
|
||||||
|
{
|
||||||
|
gtk_widget_destroy(GTK_WIDGET(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_box(GtkWidget *parent, char *msg)
|
||||||
|
{
|
||||||
|
GtkWidget *window, *hbox, *text, *ok;
|
||||||
|
|
||||||
|
window = gtk_dialog_new();
|
||||||
|
text = gtk_label_new(msg);
|
||||||
|
gtk_misc_set_alignment(GTK_MISC(text), 0.0, 0.0);
|
||||||
|
hbox = gtk_hbox_new(FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, FALSE, 20);
|
||||||
|
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox),
|
||||||
|
hbox, FALSE, FALSE, 20);
|
||||||
|
gtk_widget_show(text);
|
||||||
|
gtk_widget_show(hbox);
|
||||||
|
gtk_window_set_title(GTK_WINDOW(window), "Error");
|
||||||
|
gtk_label_set_line_wrap(GTK_LABEL(text), TRUE);
|
||||||
|
ok = gtk_button_new_with_label("OK");
|
||||||
|
gtk_box_pack_end(GTK_BOX(GTK_DIALOG(window)->action_area),
|
||||||
|
ok, FALSE, FALSE, 0);
|
||||||
|
gtk_widget_show(ok);
|
||||||
|
GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT);
|
||||||
|
gtk_window_set_default(GTK_WINDOW(window), ok);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(ok), "clicked",
|
||||||
|
GTK_SIGNAL_FUNC(errmsg_button_clicked), window);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(window), "destroy",
|
||||||
|
GTK_SIGNAL_FUNC(window_destroy), NULL);
|
||||||
|
gtk_window_set_modal(GTK_WINDOW(window), TRUE);
|
||||||
|
gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(parent));
|
||||||
|
//set_transient_window_pos(parent, window);
|
||||||
|
gtk_widget_show(window);
|
||||||
|
gtk_main();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_ok_button_clicked(GtkButton *button, gpointer data)
|
||||||
|
{
|
||||||
|
frontend *fe = (frontend *)data;
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
err = midend_set_config(fe->me, fe->cfg);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
error_box(fe->cfgbox, err);
|
||||||
|
else {
|
||||||
|
fe->cfgret = TRUE;
|
||||||
|
gtk_widget_destroy(fe->cfgbox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_cancel_button_clicked(GtkButton *button, gpointer data)
|
||||||
|
{
|
||||||
|
frontend *fe = (frontend *)data;
|
||||||
|
|
||||||
|
gtk_widget_destroy(fe->cfgbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void editbox_changed(GtkEditable *ed, gpointer data)
|
||||||
|
{
|
||||||
|
config_item *i = (config_item *)data;
|
||||||
|
|
||||||
|
sfree(i->sval);
|
||||||
|
i->sval = dupstr(gtk_entry_get_text(GTK_ENTRY(ed)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void button_toggled(GtkToggleButton *tb, gpointer data)
|
||||||
|
{
|
||||||
|
config_item *i = (config_item *)data;
|
||||||
|
|
||||||
|
i->ival = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tb));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void droplist_sel(GtkMenuItem *item, gpointer data)
|
||||||
|
{
|
||||||
|
config_item *i = (config_item *)data;
|
||||||
|
|
||||||
|
i->ival = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item),
|
||||||
|
"user-data"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_config(frontend *fe)
|
||||||
|
{
|
||||||
|
GtkWidget *w, *table;
|
||||||
|
config_item *i;
|
||||||
|
int y;
|
||||||
|
|
||||||
|
fe->cfg = midend_get_config(fe->me);
|
||||||
|
fe->cfgret = FALSE;
|
||||||
|
|
||||||
|
fe->cfgbox = gtk_dialog_new();
|
||||||
|
gtk_window_set_title(GTK_WINDOW(fe->cfgbox), "Configure");
|
||||||
|
|
||||||
|
w = gtk_button_new_with_label("OK");
|
||||||
|
gtk_box_pack_end(GTK_BOX(GTK_DIALOG(fe->cfgbox)->action_area),
|
||||||
|
w, FALSE, FALSE, 0);
|
||||||
|
gtk_widget_show(w);
|
||||||
|
GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
|
||||||
|
gtk_window_set_default(GTK_WINDOW(fe->cfgbox), w);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(w), "clicked",
|
||||||
|
GTK_SIGNAL_FUNC(config_ok_button_clicked), fe);
|
||||||
|
|
||||||
|
w = gtk_button_new_with_label("Cancel");
|
||||||
|
gtk_box_pack_end(GTK_BOX(GTK_DIALOG(fe->cfgbox)->action_area),
|
||||||
|
w, FALSE, FALSE, 0);
|
||||||
|
gtk_widget_show(w);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(w), "clicked",
|
||||||
|
GTK_SIGNAL_FUNC(config_cancel_button_clicked), fe);
|
||||||
|
|
||||||
|
table = gtk_table_new(1, 2, FALSE);
|
||||||
|
y = 0;
|
||||||
|
gtk_box_pack_end(GTK_BOX(GTK_DIALOG(fe->cfgbox)->vbox),
|
||||||
|
table, FALSE, FALSE, 0);
|
||||||
|
gtk_widget_show(table);
|
||||||
|
|
||||||
|
for (i = fe->cfg; i->type != ENDCFG; i++) {
|
||||||
|
gtk_table_resize(GTK_TABLE(table), y+1, 2);
|
||||||
|
|
||||||
|
switch (i->type) {
|
||||||
|
case STRING:
|
||||||
|
/*
|
||||||
|
* Edit box with a label beside it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
w = gtk_label_new(i->name);
|
||||||
|
gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5);
|
||||||
|
gtk_table_attach(GTK_TABLE(table), w, 0, 1, y, y+1,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
3, 3);
|
||||||
|
gtk_widget_show(w);
|
||||||
|
|
||||||
|
w = gtk_entry_new();
|
||||||
|
gtk_table_attach(GTK_TABLE(table), w, 1, 2, y, y+1,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
3, 3);
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(w), i->sval);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(w), "changed",
|
||||||
|
GTK_SIGNAL_FUNC(editbox_changed), i);
|
||||||
|
gtk_widget_show(w);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BOOLEAN:
|
||||||
|
/*
|
||||||
|
* Simple checkbox.
|
||||||
|
*/
|
||||||
|
w = gtk_check_button_new_with_label(i->name);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(w), "toggled",
|
||||||
|
GTK_SIGNAL_FUNC(button_toggled), i);
|
||||||
|
gtk_table_attach(GTK_TABLE(table), w, 0, 2, y, y+1,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
3, 3);
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), i->ival);
|
||||||
|
gtk_widget_show(w);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CHOICES:
|
||||||
|
/*
|
||||||
|
* Drop-down list (GtkOptionMenu).
|
||||||
|
*/
|
||||||
|
|
||||||
|
w = gtk_label_new(i->name);
|
||||||
|
gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5);
|
||||||
|
gtk_table_attach(GTK_TABLE(table), w, 0, 1, y, y+1,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
3, 3);
|
||||||
|
gtk_widget_show(w);
|
||||||
|
|
||||||
|
w = gtk_option_menu_new();
|
||||||
|
gtk_table_attach(GTK_TABLE(table), w, 1, 2, y, y+1,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
3, 3);
|
||||||
|
gtk_widget_show(w);
|
||||||
|
|
||||||
|
{
|
||||||
|
int c, val;
|
||||||
|
char *p, *q, *name;
|
||||||
|
GtkWidget *menuitem;
|
||||||
|
GtkWidget *menu = gtk_menu_new();
|
||||||
|
|
||||||
|
gtk_option_menu_set_menu(GTK_OPTION_MENU(w), menu);
|
||||||
|
|
||||||
|
c = *i->sval;
|
||||||
|
p = i->sval+1;
|
||||||
|
val = 0;
|
||||||
|
|
||||||
|
while (*p) {
|
||||||
|
q = p;
|
||||||
|
while (*q && *q != c)
|
||||||
|
q++;
|
||||||
|
|
||||||
|
name = snewn(q-p+1, char);
|
||||||
|
strncpy(name, p, q-p);
|
||||||
|
name[q-p] = '\0';
|
||||||
|
|
||||||
|
if (*q) q++; /* eat delimiter */
|
||||||
|
|
||||||
|
menuitem = gtk_menu_item_new_with_label(name);
|
||||||
|
gtk_container_add(GTK_CONTAINER(menu), menuitem);
|
||||||
|
gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
|
||||||
|
GINT_TO_POINTER(val));
|
||||||
|
gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
|
||||||
|
GTK_SIGNAL_FUNC(droplist_sel), i);
|
||||||
|
gtk_widget_show(menuitem);
|
||||||
|
|
||||||
|
val++;
|
||||||
|
|
||||||
|
p = q;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_option_menu_set_history(GTK_OPTION_MENU(w), i->ival);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_signal_connect(GTK_OBJECT(fe->cfgbox), "destroy",
|
||||||
|
GTK_SIGNAL_FUNC(window_destroy), NULL);
|
||||||
|
gtk_window_set_modal(GTK_WINDOW(fe->cfgbox), TRUE);
|
||||||
|
gtk_window_set_transient_for(GTK_WINDOW(fe->cfgbox),
|
||||||
|
GTK_WINDOW(fe->window));
|
||||||
|
//set_transient_window_pos(fe->window, fe->cfgbox);
|
||||||
|
gtk_widget_show(fe->cfgbox);
|
||||||
|
gtk_main();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIXME: free fe->cfg
|
||||||
|
*/
|
||||||
|
|
||||||
|
return fe->cfgret;
|
||||||
|
}
|
||||||
|
|
||||||
static void menu_key_event(GtkMenuItem *menuitem, gpointer data)
|
static void menu_key_event(GtkMenuItem *menuitem, gpointer data)
|
||||||
{
|
{
|
||||||
frontend *fe = (frontend *)data;
|
frontend *fe = (frontend *)data;
|
||||||
@ -394,6 +644,21 @@ static void menu_preset_event(GtkMenuItem *menuitem, gpointer data)
|
|||||||
fe->h = y;
|
fe->h = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void menu_config_event(GtkMenuItem *menuitem, gpointer data)
|
||||||
|
{
|
||||||
|
frontend *fe = (frontend *)data;
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
if (!get_config(fe))
|
||||||
|
return;
|
||||||
|
|
||||||
|
midend_new_game(fe->me, NULL);
|
||||||
|
midend_size(fe->me, &x, &y);
|
||||||
|
gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
|
||||||
|
fe->w = x;
|
||||||
|
fe->h = y;
|
||||||
|
}
|
||||||
|
|
||||||
static GtkWidget *add_menu_item_with_key(frontend *fe, GtkContainer *cont,
|
static GtkWidget *add_menu_item_with_key(frontend *fe, GtkContainer *cont,
|
||||||
char *text, int key)
|
char *text, int key)
|
||||||
{
|
{
|
||||||
@ -451,12 +716,12 @@ static frontend *new_window(void)
|
|||||||
add_menu_item_with_key(fe, GTK_CONTAINER(menu), "New", 'n');
|
add_menu_item_with_key(fe, GTK_CONTAINER(menu), "New", 'n');
|
||||||
add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Restart", 'r');
|
add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Restart", 'r');
|
||||||
|
|
||||||
if ((n = midend_num_presets(fe->me)) > 0) {
|
if ((n = midend_num_presets(fe->me)) > 0 || game_can_configure) {
|
||||||
GtkWidget *submenu;
|
GtkWidget *submenu;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
menuitem = gtk_menu_item_new_with_label("Type");
|
menuitem = gtk_menu_item_new_with_label("Type");
|
||||||
gtk_container_add(GTK_CONTAINER(menu), menuitem);
|
gtk_container_add(GTK_CONTAINER(menubar), menuitem);
|
||||||
gtk_widget_show(menuitem);
|
gtk_widget_show(menuitem);
|
||||||
|
|
||||||
submenu = gtk_menu_new();
|
submenu = gtk_menu_new();
|
||||||
@ -475,6 +740,14 @@ static frontend *new_window(void)
|
|||||||
GTK_SIGNAL_FUNC(menu_preset_event), fe);
|
GTK_SIGNAL_FUNC(menu_preset_event), fe);
|
||||||
gtk_widget_show(menuitem);
|
gtk_widget_show(menuitem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (game_can_configure) {
|
||||||
|
menuitem = gtk_menu_item_new_with_label("Custom...");
|
||||||
|
gtk_container_add(GTK_CONTAINER(submenu), menuitem);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
|
||||||
|
GTK_SIGNAL_FUNC(menu_config_event), fe);
|
||||||
|
gtk_widget_show(menuitem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
add_menu_separator(GTK_CONTAINER(menu));
|
add_menu_separator(GTK_CONTAINER(menu));
|
||||||
|
24
midend.c
24
midend.c
@ -299,3 +299,27 @@ int midend_wants_statusbar(midend_data *me)
|
|||||||
{
|
{
|
||||||
return game_wants_statusbar();
|
return game_wants_statusbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config_item *midend_get_config(midend_data *me)
|
||||||
|
{
|
||||||
|
return game_configure(me->params);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *midend_set_config(midend_data *me, config_item *cfg)
|
||||||
|
{
|
||||||
|
char *error;
|
||||||
|
game_params *params;
|
||||||
|
|
||||||
|
params = custom_params(cfg);
|
||||||
|
error = validate_params(params);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
free_params(params);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_params(me->params);
|
||||||
|
me->params = params;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
68
net.c
68
net.c
@ -12,6 +12,7 @@
|
|||||||
#include "tree234.h"
|
#include "tree234.h"
|
||||||
|
|
||||||
const char *const game_name = "Net";
|
const char *const game_name = "Net";
|
||||||
|
const int game_can_configure = TRUE;
|
||||||
|
|
||||||
#define PI 3.141592653589793238462643383279502884197169399
|
#define PI 3.141592653589793238462643383279502884197169399
|
||||||
|
|
||||||
@ -182,6 +183,73 @@ game_params *dup_params(game_params *params)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config_item *game_configure(game_params *params)
|
||||||
|
{
|
||||||
|
config_item *ret;
|
||||||
|
char buf[80];
|
||||||
|
|
||||||
|
ret = snewn(5, config_item);
|
||||||
|
|
||||||
|
ret[0].name = "Width";
|
||||||
|
ret[0].type = STRING;
|
||||||
|
sprintf(buf, "%d", params->width);
|
||||||
|
ret[0].sval = dupstr(buf);
|
||||||
|
ret[0].ival = 0;
|
||||||
|
|
||||||
|
ret[1].name = "Height";
|
||||||
|
ret[1].type = STRING;
|
||||||
|
sprintf(buf, "%d", params->height);
|
||||||
|
ret[1].sval = dupstr(buf);
|
||||||
|
ret[1].ival = 0;
|
||||||
|
|
||||||
|
ret[2].name = "Walls wrap around";
|
||||||
|
ret[2].type = BOOLEAN;
|
||||||
|
ret[2].sval = NULL;
|
||||||
|
ret[2].ival = params->wrapping;
|
||||||
|
|
||||||
|
ret[3].name = "Barrier probability";
|
||||||
|
ret[3].type = STRING;
|
||||||
|
sprintf(buf, "%g", params->barrier_probability);
|
||||||
|
ret[3].sval = dupstr(buf);
|
||||||
|
ret[3].ival = 0;
|
||||||
|
|
||||||
|
ret[4].name = NULL;
|
||||||
|
ret[4].type = ENDCFG;
|
||||||
|
ret[4].sval = NULL;
|
||||||
|
ret[4].ival = 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
game_params *custom_params(config_item *cfg)
|
||||||
|
{
|
||||||
|
game_params *ret = snew(game_params);
|
||||||
|
|
||||||
|
ret->width = atoi(cfg[0].sval);
|
||||||
|
ret->height = atoi(cfg[1].sval);
|
||||||
|
ret->wrapping = cfg[2].ival;
|
||||||
|
ret->barrier_probability = atof(cfg[3].sval);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *validate_params(game_params *params)
|
||||||
|
{
|
||||||
|
if (params->width <= 0 && params->height <= 0)
|
||||||
|
return "Width and height must both be greater than zero";
|
||||||
|
if (params->width <= 0)
|
||||||
|
return "Width must be greater than zero";
|
||||||
|
if (params->height <= 0)
|
||||||
|
return "Height must be greater than zero";
|
||||||
|
if (params->width <= 1 && params->height <= 1)
|
||||||
|
return "At least one of width and height must be greater than one";
|
||||||
|
if (params->barrier_probability < 0)
|
||||||
|
return "Barrier probability may not be negative";
|
||||||
|
if (params->barrier_probability > 1)
|
||||||
|
return "Barrier probability may not be greater than 1";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------
|
/* ----------------------------------------------------------------------
|
||||||
* Randomly select a new game seed.
|
* Randomly select a new game seed.
|
||||||
*/
|
*/
|
||||||
|
16
nullgame.c
16
nullgame.c
@ -20,6 +20,7 @@
|
|||||||
#include "puzzles.h"
|
#include "puzzles.h"
|
||||||
|
|
||||||
const char *const game_name = "Null Game";
|
const char *const game_name = "Null Game";
|
||||||
|
const int game_can_configure = FALSE;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
COL_BACKGROUND,
|
COL_BACKGROUND,
|
||||||
@ -60,6 +61,21 @@ game_params *dup_params(game_params *params)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config_item *game_configure(game_params *params)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
game_params *custom_params(config_item *cfg)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *validate_params(game_params *params)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
char *new_game_seed(game_params *params)
|
char *new_game_seed(game_params *params)
|
||||||
{
|
{
|
||||||
return dupstr("FIXME");
|
return dupstr("FIXME");
|
||||||
|
39
puzzles.h
39
puzzles.h
@ -31,6 +31,7 @@ enum {
|
|||||||
#define IGNOREARG(x) ( (x) = (x) )
|
#define IGNOREARG(x) ( (x) = (x) )
|
||||||
|
|
||||||
typedef struct frontend frontend;
|
typedef struct frontend frontend;
|
||||||
|
typedef struct config_item config_item;
|
||||||
typedef struct midend_data midend_data;
|
typedef struct midend_data midend_data;
|
||||||
typedef struct random_state random_state;
|
typedef struct random_state random_state;
|
||||||
typedef struct game_params game_params;
|
typedef struct game_params game_params;
|
||||||
@ -47,6 +48,38 @@ typedef struct game_drawstate game_drawstate;
|
|||||||
#define FONT_FIXED 0
|
#define FONT_FIXED 0
|
||||||
#define FONT_VARIABLE 1
|
#define FONT_VARIABLE 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure used to pass configuration data between frontend and
|
||||||
|
* game
|
||||||
|
*/
|
||||||
|
enum { STRING, CHOICES, BOOLEAN, ENDCFG };
|
||||||
|
struct config_item {
|
||||||
|
/*
|
||||||
|
* `name' is never dynamically allocated.
|
||||||
|
*/
|
||||||
|
char *name;
|
||||||
|
/*
|
||||||
|
* `type' contains one of the above values.
|
||||||
|
*/
|
||||||
|
int type;
|
||||||
|
/*
|
||||||
|
* For STRING, `sval' is always dynamically allocated and
|
||||||
|
* non-NULL. For BOOLEAN and ENDCFG, `sval' is always NULL. For
|
||||||
|
* CHOICES, `sval' is non-NULL, _not_ dynamically allocated,
|
||||||
|
* and contains a set of option strings separated by a
|
||||||
|
* delimiter. The delimeter is also the first character in the
|
||||||
|
* string, so for example ":Foo:Bar:Baz" gives three options
|
||||||
|
* `Foo', `Bar' and `Baz'.
|
||||||
|
*/
|
||||||
|
char *sval;
|
||||||
|
/*
|
||||||
|
* For BOOLEAN, this is TRUE or FALSE. For CHOICES, it
|
||||||
|
* indicates the chosen index from the `sval' list. In the
|
||||||
|
* above example, 0==Foo, 1==Bar and 2==Baz.
|
||||||
|
*/
|
||||||
|
int ival;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Platform routines
|
* Platform routines
|
||||||
*/
|
*/
|
||||||
@ -84,6 +117,8 @@ int midend_num_presets(midend_data *me);
|
|||||||
void midend_fetch_preset(midend_data *me, int n,
|
void midend_fetch_preset(midend_data *me, int n,
|
||||||
char **name, game_params **params);
|
char **name, game_params **params);
|
||||||
int midend_wants_statusbar(midend_data *me);
|
int midend_wants_statusbar(midend_data *me);
|
||||||
|
config_item *midend_get_config(midend_data *me);
|
||||||
|
char *midend_set_config(midend_data *me, config_item *cfg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* malloc.c
|
* malloc.c
|
||||||
@ -115,10 +150,14 @@ void random_free(random_state *state);
|
|||||||
* Game-specific routines
|
* Game-specific routines
|
||||||
*/
|
*/
|
||||||
extern const char *const game_name;
|
extern const char *const game_name;
|
||||||
|
const int game_can_configure;
|
||||||
game_params *default_params(void);
|
game_params *default_params(void);
|
||||||
int game_fetch_preset(int i, char **name, game_params **params);
|
int game_fetch_preset(int i, char **name, game_params **params);
|
||||||
void free_params(game_params *params);
|
void free_params(game_params *params);
|
||||||
game_params *dup_params(game_params *params);
|
game_params *dup_params(game_params *params);
|
||||||
|
config_item *game_configure(game_params *params);
|
||||||
|
game_params *custom_params(config_item *cfg);
|
||||||
|
char *validate_params(game_params *params);
|
||||||
char *new_game_seed(game_params *params);
|
char *new_game_seed(game_params *params);
|
||||||
game_state *new_game(game_params *params, char *seed);
|
game_state *new_game(game_params *params, char *seed);
|
||||||
game_state *dup_game(game_state *state);
|
game_state *dup_game(game_state *state);
|
||||||
|
57
sixteen.c
57
sixteen.c
@ -13,6 +13,7 @@
|
|||||||
#include "puzzles.h"
|
#include "puzzles.h"
|
||||||
|
|
||||||
const char *const game_name = "Sixteen";
|
const char *const game_name = "Sixteen";
|
||||||
|
const int game_can_configure = TRUE;
|
||||||
|
|
||||||
#define TILE_SIZE 48
|
#define TILE_SIZE 48
|
||||||
#define BORDER TILE_SIZE /* big border to fill with arrows */
|
#define BORDER TILE_SIZE /* big border to fill with arrows */
|
||||||
@ -44,6 +45,7 @@ struct game_state {
|
|||||||
int *tiles;
|
int *tiles;
|
||||||
int completed;
|
int completed;
|
||||||
int movecount;
|
int movecount;
|
||||||
|
int last_movement_sense;
|
||||||
};
|
};
|
||||||
|
|
||||||
game_params *default_params(void)
|
game_params *default_params(void)
|
||||||
@ -90,6 +92,51 @@ game_params *dup_params(game_params *params)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config_item *game_configure(game_params *params)
|
||||||
|
{
|
||||||
|
config_item *ret;
|
||||||
|
char buf[80];
|
||||||
|
|
||||||
|
ret = snewn(3, config_item);
|
||||||
|
|
||||||
|
ret[0].name = "Width";
|
||||||
|
ret[0].type = STRING;
|
||||||
|
sprintf(buf, "%d", params->w);
|
||||||
|
ret[0].sval = dupstr(buf);
|
||||||
|
ret[0].ival = 0;
|
||||||
|
|
||||||
|
ret[1].name = "Height";
|
||||||
|
ret[1].type = STRING;
|
||||||
|
sprintf(buf, "%d", params->h);
|
||||||
|
ret[1].sval = dupstr(buf);
|
||||||
|
ret[1].ival = 0;
|
||||||
|
|
||||||
|
ret[2].name = NULL;
|
||||||
|
ret[2].type = ENDCFG;
|
||||||
|
ret[2].sval = NULL;
|
||||||
|
ret[2].ival = 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
game_params *custom_params(config_item *cfg)
|
||||||
|
{
|
||||||
|
game_params *ret = snew(game_params);
|
||||||
|
|
||||||
|
ret->w = atoi(cfg[0].sval);
|
||||||
|
ret->h = atoi(cfg[1].sval);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *validate_params(game_params *params)
|
||||||
|
{
|
||||||
|
if (params->w < 2 && params->h < 2)
|
||||||
|
return "Width and height must both be at least two";
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int perm_parity(int *perm, int n)
|
int perm_parity(int *perm, int n)
|
||||||
{
|
{
|
||||||
int i, j, ret;
|
int i, j, ret;
|
||||||
@ -233,6 +280,7 @@ game_state *new_game(game_params *params, char *seed)
|
|||||||
assert(!*p);
|
assert(!*p);
|
||||||
|
|
||||||
state->completed = state->movecount = 0;
|
state->completed = state->movecount = 0;
|
||||||
|
state->last_movement_sense = 0;
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@ -248,6 +296,7 @@ game_state *dup_game(game_state *state)
|
|||||||
memcpy(ret->tiles, state->tiles, state->w * state->h * sizeof(int));
|
memcpy(ret->tiles, state->tiles, state->w * state->h * sizeof(int));
|
||||||
ret->completed = state->completed;
|
ret->completed = state->completed;
|
||||||
ret->movecount = state->movecount;
|
ret->movecount = state->movecount;
|
||||||
|
ret->last_movement_sense = state->last_movement_sense;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -291,6 +340,8 @@ game_state *make_move(game_state *from, int x, int y, int button)
|
|||||||
|
|
||||||
ret->movecount++;
|
ret->movecount++;
|
||||||
|
|
||||||
|
ret->last_movement_sense = -(dx+dy);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* See if the game has been completed.
|
* See if the game has been completed.
|
||||||
*/
|
*/
|
||||||
@ -551,13 +602,15 @@ void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
|
|||||||
y0 = COORD(Y(state, j));
|
y0 = COORD(Y(state, j));
|
||||||
|
|
||||||
dx = (x1 - x0);
|
dx = (x1 - x0);
|
||||||
if (abs(dx) > TILE_SIZE) {
|
if (dx != 0 &&
|
||||||
|
dx != TILE_SIZE * state->last_movement_sense) {
|
||||||
dx = (dx < 0 ? dx + TILE_SIZE * state->w :
|
dx = (dx < 0 ? dx + TILE_SIZE * state->w :
|
||||||
dx - TILE_SIZE * state->w);
|
dx - TILE_SIZE * state->w);
|
||||||
assert(abs(dx) == TILE_SIZE);
|
assert(abs(dx) == TILE_SIZE);
|
||||||
}
|
}
|
||||||
dy = (y1 - y0);
|
dy = (y1 - y0);
|
||||||
if (abs(dy) > TILE_SIZE) {
|
if (dy != 0 &&
|
||||||
|
dy != TILE_SIZE * state->last_movement_sense) {
|
||||||
dy = (dy < 0 ? dy + TILE_SIZE * state->h :
|
dy = (dy < 0 ? dy + TILE_SIZE * state->h :
|
||||||
dy - TILE_SIZE * state->h);
|
dy - TILE_SIZE * state->h);
|
||||||
assert(abs(dy) == TILE_SIZE);
|
assert(abs(dy) == TILE_SIZE);
|
||||||
|
Reference in New Issue
Block a user