/* bubbles.c - frying pan / soft drink in a glass simulation */
-/*$Id: bubbles.c,v 1.16 1998/11/19 07:25:01 jwz Exp $*/
+/*$Id: bubbles.c,v 1.18 2002/01/17 02:16:04 jwz Exp $*/
/*
* Copyright (C) 1995-1996 James Macnicol
* and things are _much_ nicer.)
*
* Author: James Macnicol
- * Internet E-mail : J.Macnicol@student.anu.edu.au
+ * Internet E-mail : j-macnicol@adfa.edu.au
*/
#include <math.h>
#include "screenhack.h"
-#include "bubbles.h"
#include <limits.h>
# include <unistd.h>
#endif
#include "yarandom.h"
+#include "bubbles.h"
+#include "xpm-pixmap.h"
-#ifdef HAVE_XPM
-# include <X11/xpm.h>
+#if defined(HAVE_GDK_PIXBUF) || defined(HAVE_XPM)
+# define FANCY_BUBBLES
#endif
/*
extern void init_default_bubbles(void);
extern int num_default_bubbles;
extern char **default_bubbles[];
+static int drop_bubble( Bubble *bb );
char *progclass = "Bubbles";
"*delay: 800",
"*quiet: false",
"*nodelay: false",
+ "*drop: false",
+ "*trails: false",
"*3D: false",
0
};
XrmOptionDescRec options [] = {
{ "-simple", ".simple", XrmoptionNoArg, "true" },
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
{ "-broken", ".broken", XrmoptionNoArg, "true" },
-#endif /* HAVE_XPM */
+#endif
{ "-quiet", ".quiet", XrmoptionNoArg, "true" },
{ "-nodelay", ".nodelay", XrmoptionNoArg, "true" },
{ "-3D", ".3D", XrmoptionNoArg, "true" },
{ "-delay", ".delay", XrmoptionSepArg, 0 },
+ { "-drop", ".drop", XrmoptionNoArg, "true" },
+ { "-rise", ".rise", XrmoptionNoArg, "true" },
+ { "-trails", ".trails", XrmoptionNoArg, "true" },
{ 0, 0, 0, 0 }
};
static int bubble_min_radius;
static int bubble_max_radius;
static long *bubble_areas;
+static int *bubble_droppages;
static GC draw_gc, erase_gc;
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
static int num_bubble_pixmaps;
static Bubble_Step **step_pixmaps;
-#endif /* HAVE_XPM */
+#endif
/* Options stuff */
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
static Bool simple = False;
#else
static Bool simple = True;
static Bool broken = False;
static Bool quiet = False;
static Bool threed = False;
+static Bool drop = False;
+static Bool trails = False;
+static int drop_dir;
static int delay;
/*
bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
die_bad_bubble(bb);
}
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
} else {
if ((bb->x < 0) || (bb->x > screen_width) ||
(bb->y < 0) || (bb->y > screen_height) ||
bb->radius, bb->x, bb->y, bb->magic, bb->cell_index);
die_bad_bubble(bb);
}
-#endif /* HAVE_XPM */
+#endif
}
#endif /* DEBUG */
return 0;
long factor;
int i;
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
if (simple)
maxarea = bubble_areas[bubble_max_radius+1];
else
maxarea = step_pixmaps[num_bubble_pixmaps]->area;
-#else
+#else /* !FANCY_BUBBLES */
maxarea = bubble_areas[bubble_max_radius+1];
-#endif /* HAVE_XPM */
+#endif /* !FANCY_BUBBLES */
maxvalue = (double)screen_width * 2.0 * (double)maxarea;
factor = (long)ceil(maxvalue / (double)LONG_MAX);
if (factor > 1) {
/* Overflow will occur in weighted_mean(). We must divide areas
each by factor so it will never do so. */
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
if (simple) {
for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
bubble_areas[i] /= factor;
#endif /* DEBUG */
}
}
-#else
+#else /* !FANCY_BUBBLES */
for (i = bubble_min_radius; i <= bubble_max_radius+1; i++) {
bubble_areas[i] /= factor;
if (bubble_areas[i] == 0)
bubble_areas[i] = 1;
}
-#endif /* HAVE_XPM */
+#endif /* !FANCY_BUBBLES */
}
#ifdef DEBUG
printf("maxarea = %ld\n", maxarea);
if (simple) {
rv->radius = bubble_min_radius;
rv->area = bubble_areas[bubble_min_radius];
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
} else {
rv->step = 0;
rv->radius = step_pixmaps[0]->radius;
rv->area = step_pixmaps[0]->area;
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
}
rv->visible = 0;
rv->magic = BUBBLE_MAGIC;
(bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
360*64);
} else {
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
XSetClipOrigin(defdsp, step_pixmaps[bb->step]->draw_gc,
(bb->x - bb->radius),
(bb->y - bb->radius));
(bb->radius * 2),
(bb->x - bb->radius),
(bb->y - bb->radius));
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
}
}
}
(bb->y - bb->radius), bb->radius*2, bb->radius*2, 0,
360*64);
} else {
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
if (! broken) {
XSetClipOrigin(defdsp, step_pixmaps[bb->step]->erase_gc,
(bb->x - bb->radius), (bb->y - bb->radius));
(bb->radius * 2),
(bb->radius * 2));
}
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
}
}
}
diner->area += food->area;
delete_bubble_in_mesh(food, DELETE_BUBBLE);
- if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
- delete_bubble_in_mesh(diner, DELETE_BUBBLE);
- return 0;
+ if (drop) {
+ if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
+ diner->area = bubble_areas[bubble_max_radius];
+ }
+#ifdef FANCY_BUBBLES
+ if ((! simple) && (diner->area > step_pixmaps[num_bubble_pixmaps]->area)) {
+ diner->area = step_pixmaps[num_bubble_pixmaps]->area;
+ }
+#endif /* FANCY_BUBBLES */
}
-#ifdef HAVE_XPM
- if ((! simple) && (diner->area >
- step_pixmaps[num_bubble_pixmaps]->area)) {
- delete_bubble_in_mesh(diner, DELETE_BUBBLE);
- return 0;
+ else {
+ if ((simple) && (diner->area > bubble_areas[bubble_max_radius])) {
+ delete_bubble_in_mesh(diner, DELETE_BUBBLE);
+ return 0;
+ }
+#ifdef FANCY_BUBBLES
+ if ((! simple) && (diner->area >
+ step_pixmaps[num_bubble_pixmaps]->area)) {
+ delete_bubble_in_mesh(diner, DELETE_BUBBLE);
+ return 0;
+ }
+#endif /* FANCY_BUBBLES */
}
-#endif /* HAVE_XPM */
if (simple) {
if (diner->area > bubble_areas[diner->radius + 1]) {
/* Move the bubble to a new radius */
i = diner->radius;
- while (diner->area > bubble_areas[i+1])
- ++i;
+ while ((i < bubble_max_radius - 1) && (diner->area > bubble_areas[i+1]))
+ ++i;
diner->radius = i;
}
show_bubble(diner);
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
} else {
if (diner->area > step_pixmaps[diner->step+1]->area) {
i = diner->step;
- while (diner->area > step_pixmaps[i+1]->area)
- ++i;
+ while ((i < num_bubble_pixmaps - 1) && (diner->area > step_pixmaps[i+1]->area))
+ ++i;
diner->step = i;
diner->radius = step_pixmaps[diner->step]->radius;
}
show_bubble(diner);
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
}
/* Now adjust locations and cells if need be */
nextbub = tmp;
touch = get_closest_bubble(nextbub);
- while (! null_bubble(touch)) {
- switch (merge_bubbles(nextbub, touch)) {
- case 2:
- /* touch ate nextbub and survived */
- nextbub = touch;
- break;
- case 1:
- /* nextbub ate touch and survived */
- break;
- case 0:
- /* somebody ate someone else but they exploded */
- nextbub = (Bubble *)NULL;
- break;
- default:
- /* something went wrong */
- fprintf(stderr, "Error occurred in insert_new_bubble()\n");
- exit(1);
- }
- /* Check to see if there are any other bubbles still in the area
- and if we need to do this all over again for them. */
- if (! null_bubble(nextbub))
- touch = get_closest_bubble(nextbub);
- else
- touch = (Bubble *)NULL;
+ if (null_bubble(touch))
+ return;
+
+ while (1) {
+
+ /* Merge all touching bubbles */
+ while (! null_bubble(touch)) {
+ switch (merge_bubbles(nextbub, touch)) {
+ case 2:
+ /* touch ate nextbub and survived */
+ nextbub = touch;
+ break;
+ case 1:
+ /* nextbub ate touch and survived */
+ break;
+ case 0:
+ /* somebody ate someone else but they exploded */
+ nextbub = (Bubble *)NULL;
+ break;
+ default:
+ /* something went wrong */
+ fprintf(stderr, "Error occurred in insert_new_bubble()\n");
+ exit(1);
+ }
+
+ /* Check to see if any bubble survived. */
+ if (null_bubble(nextbub))
+ break;
+
+ /* Check to see if there are any other bubbles still in the area
+ and if we need to do this all over again for them. */
+ touch = get_closest_bubble(nextbub);
+ }
+
+ if (null_bubble(nextbub))
+ break;
+
+ /* Shift bubble down. Break if we run off the screen. */
+ if (drop) {
+ if (drop_bubble( nextbub ) == -1)
+ break;
+ }
+
+ /* Check to see if there are any other bubbles still in the area
+ and if we need to do this all over again for them. */
+ touch = get_closest_bubble(nextbub);
+ if (null_bubble(touch)) {
+ /* We also continue every so often if we're dropping and the bubble is at max size */
+ if (drop) {
+ if (simple) {
+ if ((nextbub->area >= bubble_areas[bubble_max_radius - 1]) && (random() % 2 == 0))
+ continue;
+ }
+#ifdef FANCY_BUBBLES
+ else {
+ if ((nextbub->step >= num_bubble_pixmaps - 1) && (random() % 2 == 0))
+ continue;
+ }
+#endif /* FANCY_BUBBLES */
+ }
+ break;
+ }
+
}
}
+
+static void
+leave_trail( Bubble *bb )
+{
+ Bubble *tmp;
+
+ tmp = new_bubble();
+ tmp->x = bb->x;
+ tmp->y = bb->y - ((bb->radius + 10) * drop_dir);
+ tmp->cell_index = pixel_to_mesh(tmp->x, tmp->y);
+ add_to_mesh(tmp);
+ insert_new_bubble(tmp);
+ show_bubble( tmp );
+}
+
+
+static int
+drop_bubble( Bubble *bb )
+{
+ int newmi;
+
+ hide_bubble( bb );
+
+ if (simple)
+ (bb->y) += (bubble_droppages[bb->radius] * drop_dir);
+#ifdef FANCY_BUBBLES
+ else
+ (bb->y) += (step_pixmaps[bb->step]->droppage * drop_dir);
+#endif /* FANCY_BUBBLES */
+ if ((bb->y < 0) || (bb->y > screen_height)) {
+ delete_bubble_in_mesh( bb, DELETE_BUBBLE );
+ return -1;
+ }
+
+ show_bubble( bb );
+
+ /* Now adjust locations and cells if need be */
+ newmi = pixel_to_mesh(bb->x, bb->y);
+ if (newmi != bb->cell_index) {
+ delete_bubble_in_mesh(bb, KEEP_BUBBLE);
+ bb->cell_index = newmi;
+ add_to_mesh(bb);
+ }
+
+ if (trails) {
+ if (simple) {
+ if ((bb->area >= bubble_areas[bubble_max_radius - 1]) && (random() % 2 == 0))
+ leave_trail( bb );
+ }
+#ifdef FANCY_BUBBLES
+ else {
+ if ((bb->step >= num_bubble_pixmaps - 1) && (random() % 2 == 0))
+ leave_trail( bb );
+ }
+#endif /* FANCY_BUBBLES */
+ }
+
+ return 0;
+}
+
+
#ifdef DEBUG
static int
get_length_of_bubble_list(Bubble *bb)
* still check for XPM, though!
*/
-#ifdef HAVE_XPM
+#ifdef FANCY_BUBBLES
/*
* Pixmaps without file I/O (but do have XPM)
prevrad = step_pixmaps[ind]->radius;
}
#endif /* DEBUG */
+
+ /* Now populate the droppage values */
+ for (ind = 0; ind < num_bubble_pixmaps; ind++)
+ step_pixmaps[ind]->droppage = MAX_DROPPAGE * ind / num_bubble_pixmaps;
}
static void
This is virtually copied verbatim from make_pixmap_from_file() above and
changes made to either should be propagated onwards! */
{
- int result;
XGCValues gcv;
#ifdef DEBUG
exit(1);
}
- bl->xpmattrs.valuemask = 0;
-
-#ifdef XpmCloseness
- bl->xpmattrs.valuemask |= XpmCloseness;
- bl->xpmattrs.closeness = 40000;
-#endif
-#ifdef XpmVisual
- bl->xpmattrs.valuemask |= XpmVisual;
- bl->xpmattrs.visual = defvisual;
-#endif
-#ifdef XpmDepth
- bl->xpmattrs.valuemask |= XpmDepth;
- bl->xpmattrs.depth = screen_depth;
-#endif
-#ifdef XpmColormap
- bl->xpmattrs.valuemask |= XpmColormap;
- bl->xpmattrs.colormap = defcmap;
-#endif
-
-
- /* This is the only line which is different from make_pixmap_from_file() */
- result = XpmCreatePixmapFromData(defdsp, defwin, pixmap_data, &bl->ball,
- &bl->shape_mask, &bl->xpmattrs);
-
- switch(result) {
- case XpmColorError:
- fprintf(stderr, "xpm: color substitution performed\n");
- /* fall through */
- case XpmSuccess:
- bl->radius = MAX(bl->xpmattrs.width, bl->xpmattrs.height) / 2;
+#ifdef FANCY_BUBBLES
+ {
+ int w, h;
+ bl->ball = xpm_data_to_pixmap (defdsp, defwin, (char **) pixmap_data,
+ &w, &h, &bl->shape_mask);
+ bl->radius = MAX(w, h) / 2;
bl->area = calc_bubble_area(bl->radius);
- break;
- case XpmColorFailed:
- fprintf(stderr, "xpm: color allocation failed\n");
- exit(1);
- case XpmNoMemory:
- fprintf(stderr, "xpm: out of memory\n");
- exit(1);
- default:
- fprintf(stderr, "xpm: unknown error code %d\n", result);
- exit(1);
}
-
+#endif /* FANCY_BUBBLES */
+
gcv.plane_mask = AllPlanes;
gcv.foreground = default_fg_pixel;
gcv.function = GXcopy;
make_pixmap_array(pixmap_list);
}
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
/*
get_resources(Display *dpy, Window window)
/* Get the appropriate X resources and warn about any inconsistencies. */
{
- Bool nodelay;
+ Bool nodelay, rise;
XWindowAttributes xgwa;
Colormap cmap;
XGetWindowAttributes (dpy, window, &xgwa);
if (delay < 0)
delay = 0;
+ drop = get_boolean_resource("drop", "Boolean");
+ rise = get_boolean_resource("rise", "Boolean");
+ trails = get_boolean_resource("trails", "Boolean");
+ if (drop && rise) {
+ fprintf( stderr, "Sorry, bubbles can't both drop and rise\n" );
+ exit(1);
+ }
+ drop_dir = (drop ? 1 : -1);
+ if (drop || rise)
+ drop = 1;
+
default_fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy,
cmap);
default_bg_pixel = get_pixel_resource ("background", "Background", dpy,
if (! quiet)
fprintf(stderr, "-broken not available in simple mode\n");
} else {
-#ifndef HAVE_XPM
+#ifndef FANCY_BUBBLES
simple = 1;
-#else
+#else /* FANCY_BUBBLES */
broken = get_boolean_resource("broken", "Boolean");
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
}
}
for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
bubble_areas[i] = calc_bubble_area(i);
+ /* Now populate the droppage values */
+ bubble_droppages = (int *)xmalloc((bubble_max_radius + 2) * sizeof(int));
+ for (i = 0; i < bubble_min_radius; i++)
+ bubble_droppages[i] = 0;
+ for (i = bubble_min_radius; i <= (bubble_max_radius+1); i++)
+ bubble_droppages[i] = MAX_DROPPAGE * (i - bubble_min_radius) / (bubble_max_radius - bubble_min_radius);
+
mesh_length = (2 * bubble_max_radius) + 3;
} else {
-#ifndef HAVE_XPM
+#ifndef FANCY_BUBBLES
fprintf(stderr,
- "Bug: simple mode code not set but HAVE_XPM not defined\n");
+ "Bug: simple mode code not set but FANCY_BUBBLES not defined\n");
exit(1);
-#else
+#else /* FANCY_BUBBLES */
/* Make sure all #ifdef sort of things have been taken care of in
get_resources(). */
default_to_pixmaps();
/* Set mesh length */
mesh_length = (2 * step_pixmaps[num_bubble_pixmaps-1]->radius) + 3;
-#endif /* HAVE_XPM */
+#endif /* FANCY_BUBBLES */
/* Am I missing something in here??? */
}