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.
18 #include <glib/gi18n.h>
23 GdObjectLevels gd_levels_mask[] =
32 /* bdcff text description of object. caller should free string. */
33 char *gd_object_get_bdcff(const GdObject *object)
42 return g_strdup_printf("Point=%d %d %s",
43 object->x1, object->y1,
44 gd_elements[object->element].filename);
47 return g_strdup_printf("Line=%d %d %d %d %s",
48 object->x1, object->y1, object->x2, object->y2,
49 gd_elements[object->element].filename);
52 return g_strdup_printf("Rectangle=%d %d %d %d %s",
53 object->x1, object->y1, object->x2, object->y2,
54 gd_elements[object->element].filename);
56 case GD_FILLED_RECTANGLE:
57 /* if elements are not the same */
58 if (object->fill_element != object->element)
59 return g_strdup_printf("FillRect=%d %d %d %d %s %s",
60 object->x1, object->y1, object->x2, object->y2,
61 gd_elements[object->element].filename,
62 gd_elements[object->fill_element].filename);
64 /* they are the same */
65 return g_strdup_printf("FillRect=%d %d %d %d %s",
66 object->x1, object->y1, object->x2, object->y2,
67 gd_elements[object->element].filename);
70 return g_strdup_printf("Raster=%d %d %d %d %d %d %s",
71 object->x1, object->y1,
72 (object->x2 - object->x1) / object->dx + 1,
73 (object->y2 - object->y1) / object->dy + 1,
74 object->dx, object->dy,
75 gd_elements[object->element].filename);
78 return g_strdup_printf("Add=%d %d %s %s",
79 object->dx, object->dy,
80 gd_elements[object->element].filename,
81 gd_elements[object->fill_element].filename);
83 case GD_FLOODFILL_BORDER:
84 return g_strdup_printf("BoundaryFill=%d %d %s %s",
85 object->x1, object->y1,
86 gd_elements[object->fill_element].filename,
87 gd_elements[object->element].filename);
89 case GD_FLOODFILL_REPLACE:
90 return g_strdup_printf("FloodFill=%d %d %s %s",
91 object->x1, object->y1,
92 gd_elements[object->fill_element].filename,
93 gd_elements[object->element].filename);
96 case GD_MAZE_UNICURSAL:
100 case GD_MAZE: type = "perfect"; break;
101 case GD_MAZE_UNICURSAL: type = "unicursal"; break;
102 case GD_MAZE_BRAID: type = "braid"; break;
106 return g_strdup_printf("Maze=%d %d %d %d %d %d %d %d %d %d %d %d %s %s %s",
107 object->x1, object->y1, object->x2, object->y2,
108 object->dx, object->dy,
115 gd_elements[object->element].filename,
116 gd_elements[object->fill_element].filename, type);
119 str = g_string_new(NULL);
120 /* seed and initial fill */
121 g_string_append_printf(str, "%s=%d %d %d %d %d %d %d %d %d %s",
122 object->c64_random ? "RandomFillC64" : "RandomFill",
123 object->x1, object->y1, object->x2, object->y2,
129 gd_elements[object->fill_element].filename);
131 for (j = 0; j < 4; j++)
133 if (object->random_fill_probability[j] != 0)
134 g_string_append_printf(str, " %s %d",
135 gd_elements[object->random_fill[j]].filename,
136 object->random_fill_probability[j]);
139 if (object->element!=O_NONE)
140 g_string_append_printf(str, " %s",
141 gd_elements[object->element].filename);
143 /* free string but do not free char *; return char *. */
144 return g_string_free(str, FALSE);
147 return g_strdup_printf("CopyPaste=%d %d %d %d %d %d %s %s",
148 object->x1, object->y1, object->x2, object->y2,
149 object->dx, object->dy,
150 object->mirror ? "mirror" : "nomirror",
151 object->flip?"flip":"noflip");
160 /* create an INDIVIDUAL POINT CAVE OBJECT */
161 GdObject *gd_object_new_point(GdObjectLevels levels, int x, int y, GdElement elem)
163 GdObject *newobj = checked_calloc(sizeof(GdObject));
165 newobj->levels = levels;
166 newobj->type = GD_POINT;
169 newobj->element = elem;
174 /* create a LINE OBJECT */
175 GdObject *gd_object_new_line(GdObjectLevels levels, int x1, int y1, int x2, int y2,
178 GdObject *newobj = checked_calloc(sizeof(GdObject));
180 newobj->levels = levels;
181 newobj->type = GD_LINE;
186 newobj->element = elem;
191 /* create a RECTANGLE OBJECT */
192 GdObject *gd_object_new_rectangle(GdObjectLevels levels, int x1, int y1, int x2, int y2,
195 GdObject *newobj = checked_calloc(sizeof(GdObject));
197 newobj->levels = levels;
198 newobj->type = GD_RECTANGLE;
203 newobj->element = elem;
208 /* create a RECTANGLE OBJECT */
209 GdObject *gd_object_new_filled_rectangle(GdObjectLevels levels, int x1, int y1, int x2, int y2,
210 GdElement elem, GdElement fill_elem)
212 GdObject *newobj = checked_calloc(sizeof(GdObject));
214 newobj->levels = levels;
215 newobj->type = GD_FILLED_RECTANGLE;
220 newobj->element = elem;
221 newobj->fill_element = fill_elem;
226 /* create a raster object */
227 GdObject *gd_object_new_raster(GdObjectLevels levels, int x1, int y1, int x2, int y2,
228 int dx, int dy, GdElement elem)
230 GdObject *newobj = checked_calloc(sizeof(GdObject));
232 newobj->levels = levels;
233 newobj->type = GD_RASTER;
240 newobj->element = elem;
245 /* create a raster object */
246 GdObject *gd_object_new_join(GdObjectLevels levels, int dx, int dy,
247 GdElement search, GdElement replace)
249 GdObject *newobj = checked_calloc(sizeof(GdObject));
251 newobj->levels = levels;
252 newobj->type = GD_JOIN;
255 newobj->element = search;
256 newobj->fill_element = replace;
261 /* create a new boundary fill object */
262 GdObject *gd_object_new_floodfill_border(GdObjectLevels levels, int x1, int y1,
263 GdElement fill, GdElement border)
265 GdObject *newobj = checked_calloc(sizeof(GdObject));
267 newobj->levels = levels;
268 newobj->type = GD_FLOODFILL_BORDER;
271 newobj->element = border;
272 newobj->fill_element = fill;
277 GdObject *gd_object_new_floodfill_replace(GdObjectLevels levels, int x1, int y1,
278 GdElement fill, GdElement to_replace)
280 GdObject *newobj = checked_calloc(sizeof(GdObject));
282 newobj->levels = levels;
283 newobj->type = GD_FLOODFILL_REPLACE;
286 newobj->element = to_replace;
287 newobj->fill_element = fill;
292 GdObject *gd_object_new_maze(GdObjectLevels levels, int x1, int y1, int x2, int y2,
293 int wall_w, int path_w, GdElement wall_e, GdElement path_e,
294 int horiz_percent, const gint32 seed[5])
297 GdObject *newobj = checked_calloc(sizeof(GdObject));
299 newobj->levels = levels;
300 newobj->type = GD_MAZE;
307 newobj->element = wall_e;
308 newobj->fill_element = path_e;
309 newobj->horiz = horiz_percent;
311 for (i = 0; i < 5; ++i)
312 newobj->seed[i] = seed[i];
317 GdObject *gd_object_new_maze_unicursal(GdObjectLevels levels, int x1, int y1, int x2, int y2,
318 int wall_w, int path_w, GdElement wall_e, GdElement path_e,
319 int horiz_percent, const gint32 seed[5])
322 GdObject *newobj = checked_calloc(sizeof(GdObject));
324 newobj->levels = levels;
325 newobj->type = GD_MAZE_UNICURSAL;
332 newobj->element = wall_e;
333 newobj->fill_element = path_e;
334 newobj->horiz = horiz_percent;
336 for (i = 0; i < 5; ++i)
337 newobj->seed[i] = seed[i];
342 GdObject *gd_object_new_maze_braid(GdObjectLevels levels, int x1, int y1, int x2, int y2,
343 int wall_w, int path_w, GdElement wall_e, GdElement path_e,
344 int horiz_percent, const gint32 seed[5])
347 GdObject *newobj = checked_calloc(sizeof(GdObject));
349 newobj->levels = levels;
350 newobj->type = GD_MAZE_BRAID;
357 newobj->element = wall_e;
358 newobj->fill_element = path_e;
359 newobj->horiz = horiz_percent;
361 for (i = 0; i < 5; ++i)
362 newobj->seed[i] = seed[i];
367 GdObject *gd_object_new_random_fill(GdObjectLevels levels, int x1, int y1, int x2, int y2,
368 const gint32 seed[5], GdElement initial,
369 const GdElement random[4], const gint32 prob[4],
370 GdElement replace_only, boolean c64)
373 GdObject *newobj = checked_calloc(sizeof(GdObject));
375 newobj->levels = levels;
376 newobj->type = GD_RANDOM_FILL;
381 newobj->fill_element = initial;
383 for (i = 0; i < 5; ++i)
384 newobj->seed[i] = seed[i];
386 for (i = 0; i < 4; ++i)
388 newobj->random_fill[i] = random[i];
389 newobj->random_fill_probability[i] = prob[i];
392 newobj->element = replace_only;
393 newobj->c64_random = c64;
398 GdObject *gd_object_new_copy_paste(GdObjectLevels levels, int x1, int y1, int x2, int y2,
399 int dx, int dy, boolean mirror, boolean flip)
401 GdObject *newobj = checked_calloc(sizeof(GdObject));
403 newobj->levels = levels;
404 newobj->type = GD_COPY_PASTE;
411 newobj->mirror = mirror;
417 /* create new object from bdcff description.
418 return new object if ok; return null if failed.
420 GdObject *gd_object_new_from_string(char *str)
425 char elem0[100], elem1[100];
427 equalsign = strchr(str, '=');
431 /* split string by replacing the equal sign with zero */
434 param = equalsign + 1;
436 /* INDIVIDUAL POINT CAVE OBJECT */
437 if (strcasecmp(name, "Point") == 0)
439 object.type = GD_POINT;
440 if (sscanf(param, "%d %d %s", &object.x1, &object.y1, elem0) == 3)
442 object.element = gd_get_element_from_string(elem0);
444 return g_memdup(&object, sizeof (GdObject));
451 if (strcasecmp(name, "Line") == 0)
453 object.type = GD_LINE;
454 if (sscanf(param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0) == 5)
456 object.element = gd_get_element_from_string(elem0);
458 return g_memdup(&object, sizeof (GdObject));
464 /* RECTANGLE OBJECT */
465 if (strcasecmp(name, "Rectangle") == 0)
467 if (sscanf(param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0) == 5)
469 object.type = GD_RECTANGLE;
470 object.element = gd_get_element_from_string (elem0);
472 return g_memdup(&object, sizeof (GdObject));
478 /* FILLED RECTANGLE OBJECT */
479 if (strcasecmp(name, "FillRect") == 0)
483 paramcount = sscanf(param, "%d %d %d %d %s %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0, elem1);
484 object.type = GD_FILLED_RECTANGLE;
488 object.element = gd_get_element_from_string (elem0);
489 object.fill_element = gd_get_element_from_string (elem1);
491 return g_memdup(&object, sizeof (GdObject));
496 object.element = object.fill_element = gd_get_element_from_string (elem0);
498 return g_memdup(&object, sizeof (GdObject));
505 if (strcasecmp(name, "Raster") == 0)
509 if (sscanf(param, "%d %d %d %d %d %d %s", &object.x1, &object.y1, &nx, &ny, &object.dx, &object.dy, elem0) == 7)
513 object.x2 = object.x1 + nx * object.dx;
514 object.y2 = object.y1 + ny * object.dy;
515 object.type = GD_RASTER;
516 object.element = gd_get_element_from_string (elem0);
518 return g_memdup(&object, sizeof (GdObject));
525 if (strcasecmp(name, "Join") == 0 ||
526 strcasecmp(name, "Add") == 0)
528 if (sscanf(param, "%d %d %s %s", &object.dx, &object.dy, elem0, elem1) == 4)
530 object.type = GD_JOIN;
531 object.element = gd_get_element_from_string (elem0);
532 object.fill_element = gd_get_element_from_string (elem1);
534 return g_memdup(&object, sizeof (GdObject));
540 /* FILL TO BORDER OBJECT */
541 if (strcasecmp(name, "BoundaryFill") == 0)
543 if (sscanf(param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1) == 4)
545 object.type = GD_FLOODFILL_BORDER;
546 object.fill_element = gd_get_element_from_string (elem0);
547 object.element = gd_get_element_from_string (elem1);
549 return g_memdup(&object, sizeof (GdObject));
555 /* REPLACE FILL OBJECT */
556 if (strcasecmp(name, "FloodFill") == 0)
558 if (sscanf(param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1) == 4)
560 object.type = GD_FLOODFILL_REPLACE;
561 object.fill_element = gd_get_element_from_string (elem0);
562 object.element = gd_get_element_from_string (elem1);
564 return g_memdup(&object, sizeof (GdObject));
571 /* MAZE UNICURSAL OBJECT */
572 /* BRAID MAZE OBJECT */
573 if (strcasecmp(name, "Maze") == 0)
575 char type[100] = "perfect";
577 if (sscanf(param, "%d %d %d %d %d %d %d %d %d %d %d %d %s %s %s", &object.x1, &object.y1, &object.x2, &object.y2, &object.dx, &object.dy, &object.horiz, &object.seed[0], &object.seed[1], &object.seed[2], &object.seed[3], &object.seed[4], elem0, elem1, type) >= 14)
579 if (strcasecmp(type, "unicursal") == 0)
580 object.type = GD_MAZE_UNICURSAL;
581 else if (strcasecmp(type, "perfect") == 0)
582 object.type = GD_MAZE;
583 else if (strcasecmp(type, "braid") == 0)
584 object.type = GD_MAZE_BRAID;
587 Warn("unknown maze type: %s, defaulting to perfect", type);
588 object.type = GD_MAZE;
591 object.element = gd_get_element_from_string (elem0);
592 object.fill_element = gd_get_element_from_string (elem1);
594 return g_memdup(&object, sizeof (GdObject));
600 /* RANDOM FILL OBJECT */
601 if (strcasecmp(name, "RandomFill") == 0 ||
602 strcasecmp(name, "RandomFillC64") == 0)
604 static char **words = NULL;
607 object.type = GD_RANDOM_FILL;
608 if (strcasecmp(name, "RandomFillC64") == 0)
609 /* totally the same, but uses c64 random generator */
610 object.c64_random = TRUE;
612 object.c64_random = FALSE;
614 if (sscanf(param, "%d %d %d %d", &object.x1, &object.y1, &object.x2, &object.y2) != 4)
620 words = g_strsplit_set(param, " ", -1);
621 l = g_strv_length(words);
623 if (l < 10 || l > 19)
626 for (i = 0; i < 5; i++)
627 if (sscanf(words[4 + i], "%d", &object.seed[i]) != 1)
630 object.fill_element = gd_get_element_from_string(words[9]);
632 for (i = 0; i < 4; i++)
634 object.random_fill[i] = O_DIRT;
635 object.random_fill_probability[i] = 0;
638 for (i = 10; i < l - 1; i += 2)
640 object.random_fill[(i - 10) / 2] = gd_get_element_from_string(words[i]);
641 if (sscanf(words[i + 1], "%d", &object.random_fill_probability[(i - 10) / 2]) == 0)
645 object.element = O_NONE;
647 if (l > 10 && l % 2 == 1)
648 object.element = gd_get_element_from_string(words[l - 1]);
650 return g_memdup(&object, sizeof (GdObject));
653 /* COPY PASTE OBJECT */
654 if (strcasecmp(name, "CopyPaste") == 0)
656 char mirror[100] = "nomirror";
657 char flip[100] = "noflip";
658 object.type = GD_COPY_PASTE;
660 object.flip = object.mirror = FALSE;
662 if (sscanf(param, "%d %d %d %d %d %d %s %s", &object.x1, &object.y1, &object.x2, &object.y2, &object.dx, &object.dy, mirror, flip) < 6)
665 /* MIRROR PROPERTY */
666 if (strcasecmp(mirror, "mirror") == 0)
667 object.mirror = TRUE;
668 else if (strcasecmp(mirror, "nomirror") == 0)
669 object.mirror = FALSE;
671 Warn("invalid setting for copypaste mirror property: %s", mirror);
674 if (strcasecmp(flip, "flip") == 0)
676 else if (strcasecmp(flip, "noflip") == 0)
679 Warn("invalid setting for copypaste flip property: %s", flip);
681 return g_memdup(&object, sizeof(GdObject));
687 /** drawing a line, using bresenham's */
688 static void draw_line (GdCave *cave, const GdObject *object)
690 int x, y, x1, y1, x2, y2;
692 int error, dx, dy, ystep;
695 y1 = object->y1, x2 = object->x2;
697 steep = ABS (y2 - y1) > ABS (x2 - x1);
723 ystep = (y1 < y2) ? 1 : -1;
725 for (x = x1; x <= x2; x++)
728 gd_cave_store_rc (cave, y, x, object->element, object);
730 gd_cave_store_rc (cave, x, y, object->element, object);
744 static void draw_fill_replace_proc(GdCave *cave, int x, int y, const GdObject *object)
746 /* fill with border so we do not come back */
747 gd_cave_store_rc(cave, x, y, object->fill_element, object);
749 if (x > 0 && gd_cave_get_rc(cave, x - 1, y) == object->element)
750 draw_fill_replace_proc(cave, x - 1, y, object);
752 if (y > 0 && gd_cave_get_rc(cave, x, y - 1) == object->element)
753 draw_fill_replace_proc(cave, x, y - 1, object);
755 if (x < cave->w - 1 && gd_cave_get_rc(cave, x + 1, y) == object->element)
756 draw_fill_replace_proc(cave, x + 1, y, object);
758 if (y < cave->h - 1 && gd_cave_get_rc(cave, x, y + 1) == object->element)
759 draw_fill_replace_proc(cave, x, y + 1, object);
762 static void draw_fill_replace (GdCave *cave, const GdObject *object)
765 if (object->x1 < 0 ||
767 object->x1 >= cave->w ||
768 object->y1 >= cave->h)
771 if (object->element == object->fill_element)
774 /* this procedure fills the area with the object->element. */
775 draw_fill_replace_proc(cave, object->x1, object->y1, object);
778 static void draw_fill_border_proc (GdCave *cave, int x, int y, const GdObject *object)
780 /* fill with border so we do not come back */
781 gd_cave_store_rc(cave, x, y, object->element, object);
783 if (x > 0 && gd_cave_get_rc(cave, x - 1, y) != object->element)
784 draw_fill_border_proc(cave, x - 1, y, object);
786 if (y > 0 && gd_cave_get_rc(cave, x, y - 1) != object->element)
787 draw_fill_border_proc(cave, x, y - 1, object);
789 if (x < cave->w - 1 && gd_cave_get_rc(cave, x + 1, y) != object->element)
790 draw_fill_border_proc(cave, x + 1, y, object);
792 if (y < cave->h-1 && gd_cave_get_rc(cave, x, y + 1) != object->element)
793 draw_fill_border_proc(cave, x, y + 1, object);
796 static void draw_fill_border (GdCave *cave, const GdObject *object)
801 if (object->x1 < 0 ||
803 object->x1 >= cave->w ||
804 object->y1 >= cave->h)
807 /* this procedure fills the area with the object->element. */
808 draw_fill_border_proc(cave, object->x1, object->y1, object);
810 /* after the fill, we change all filled cells to the fill_element. */
811 /* we find those by looking at the object_order[][] */
812 for (y = 0; y < cave->h; y++)
813 for (x = 0; x < cave->w; x++)
814 if (cave->objects_order[y][x] == object)
815 cave->map[y][x] = object->fill_element;
818 /* rectangle, frame only */
819 static void draw_rectangle(GdCave *cave, const GdObject *object)
821 int x1, y1, x2, y2, x, y;
823 /* reorder coordinates if not drawing from northwest to southeast */
825 y1 = object->y1, x2 = object->x2;
842 for (x = x1; x <= x2; x++)
844 gd_cave_store_rc(cave, x, object->y1, object->element, object);
845 gd_cave_store_rc(cave, x, object->y2, object->element, object);
848 for (y = y1; y <= y2; y++)
850 gd_cave_store_rc(cave, object->x1, y, object->element, object);
851 gd_cave_store_rc(cave, object->x2, y, object->element, object);
855 /* rectangle, filled one */
856 static void draw_filled_rectangle(GdCave *cave, const GdObject *object)
858 int x1, y1, x2, y2, x, y;
860 /* reorder coordinates if not drawing from northwest to southeast */
862 y1 = object->y1, x2 = object->x2;
879 for (y = y1; y <= y2; y++)
880 for (x = x1; x <= x2; x++)
881 gd_cave_store_rc(cave, x, y, (y == object->y1 ||
884 x == object->x2) ? object->element : object->fill_element, object);
887 /* something like ordered fill, increment is dx and dy. */
888 static void draw_raster(GdCave *cave, const GdObject *object)
890 int x, y, x1, y1, x2, y2;
893 /* reorder coordinates if not drawing from northwest to southeast */
923 for (y = y1; y <= y2; y += dy)
924 for (x = x1; x <= x2; x += dx)
925 gd_cave_store_rc(cave, x, y, object->element, object);
928 /* find every object, and put fill_element next to it. relative coordinates dx,dy */
929 static void draw_join(GdCave *cave, const GdObject *object)
933 for (y = 0; y < cave->h; y++)
935 for (x = 0; x < cave->w; x++)
937 if (cave->map[y][x] == object->element)
939 int nx = x + object->dx;
940 int ny = y + object->dy;
941 /* this one implements wraparound for joins.
942 it is needed by many caves in profi boulder series */
943 while (nx >= cave->w)
946 gd_cave_store_rc(cave, nx, ny, object->fill_element, object);
952 /* create a maze in a boolean **maze. */
953 /* recursive algorithm. */
954 static void mazegen(GRand *rand, boolean **maze, int width, int height, int x, int y, int horiz)
964 dir = g_rand_int_range(rand, 0, 100) <horiz ? 2 : 0;
966 /* if no horizontal movement possible, choose vertical */
967 if (dir == 2 && (dirmask & 12) == 0)
969 else if (dir == 0 && (dirmask&3) == 0) /* and vice versa */
972 dir += g_rand_int_range(rand, 0, 2); /* dir */
973 if (dirmask & (1 << dir))
975 dirmask &= ~(1 << dir);
980 if (y >= 2 && !maze[y - 2][x])
982 maze[y - 1][x] = TRUE;
983 mazegen(rand, maze, width, height, x, y - 2, horiz);
988 if (y < height-2 && !maze[y + 2][x]) {
989 maze[y + 1][x] = TRUE;
990 mazegen(rand, maze, width, height, x, y + 2, horiz);
995 if (x >= 2 && !maze[y][x - 2]) {
996 maze[y][x - 1] = TRUE;
997 mazegen(rand, maze, width, height, x - 2, y, horiz);
1002 if (x < width - 2 && !maze[y][x + 2]) {
1003 maze[y][x + 1] = TRUE;
1004 mazegen(rand, maze, width, height, x + 2, y, horiz);
1015 static void braidmaze(GRand *rand, boolean **maze, int w, int h)
1019 for (y = 0; y < h; y += 2)
1021 for (x = 0; x < w; x += 2)
1023 int closed = 0, dirs = 0;
1026 /* if it is the edge of the map, OR no path carved, then we can't go in that direction. */
1027 if (x < 1 || !maze[y][x - 1])
1029 /* closed from this side. */
1032 /* if not the edge, we might open this wall (carve a path) to remove a dead end */
1034 closed_dirs[dirs++] = GD_MV_LEFT;
1037 /* other 3 directions similar */
1038 if (y < 1 || !maze[y - 1][x])
1042 closed_dirs[dirs++] = GD_MV_UP;
1045 if (x >= w - 1 || !maze[y][x + 1])
1049 closed_dirs[dirs++] = GD_MV_RIGHT;
1052 if (y >= h - 1 || !maze[y + 1][x]) {
1055 closed_dirs[dirs++] = GD_MV_DOWN;
1058 /* if closed from 3 sides, then it is a dead end. also check dirs != 0,
1059 that might fail for a 1x1 maze :) */
1060 if (closed == 3 && dirs != 0)
1062 /* make up a random direction, and open in that direction, so dead end is removed */
1063 int dir = closed_dirs[g_rand_int_range(rand, 0, dirs)];
1068 maze[y][x - 1] = TRUE; break;
1070 maze[y - 1][x] = TRUE; break;
1072 maze[y][x + 1] = TRUE; break;
1074 maze[y + 1][x] = TRUE; break;
1081 static void draw_maze(GdCave *cave, const GdObject *object, int level)
1085 int x1 = object->x1;
1086 int y1 = object->y1;
1087 int x2 = object->x2;
1088 int y2 = object->y2;
1089 int w, h, path, wall;
1094 /* change coordinates if not in correct order */
1117 /* calculate the width and height of the maze.
1118 n = number of passages, path = path width, wall = wall width, maze = maze width.
1119 if given the number of passages, the width of the maze is:
1121 n * path + (n - 1) * wall = maze
1122 n * path + n * wall - wall = maze
1123 n * (path + wall) = maze + wall
1124 n = (maze + wall) / (path + wall)
1127 /* number of passages for each side */
1128 w = (x2 - x1 + 1 + wall) / (path + wall);
1129 h = (y2 - y1 + 1 + wall) / (path + wall);
1131 /* and we calculate the size of the internal map */
1132 if (object->type == GD_MAZE_UNICURSAL)
1134 /* for unicursal maze, width and height must be mod2 = 0,
1135 and we will convert to paths & walls later */
1141 /* for normal maze */
1142 w = 2 * (w - 1) + 1;
1143 h = 2 * (h - 1) + 1;
1146 /* twodimensional boolean array to generate map in */
1147 map = checked_malloc((h) * sizeof(boolean *));
1148 for (y = 0; y < h; y++)
1149 map[y] = checked_calloc(w * sizeof(boolean));
1151 /* start generation, if map is big enough.
1152 otherwise the application would crash, as the editor places maze objects
1153 during mouse click & drag that have no sense */
1154 rand = g_rand_new_with_seed(object->seed[level] == -1 ?
1155 g_rand_int(cave->random) : object->seed[level]);
1157 if (w >= 1 && h >= 1)
1158 mazegen(rand, map, w, h, 0, 0, object->horiz);
1160 if (object->type == GD_MAZE_BRAID)
1161 braidmaze(rand, map, w, h);
1165 if (w >= 1 && h >= 1 && object->type == GD_MAZE_UNICURSAL)
1167 boolean **unicursal;
1169 /* convert to unicursal maze */
1185 unicursal = checked_malloc((h * 2 - 1) * sizeof(boolean *));
1187 for (y = 0; y < h * 2 - 1; y++)
1188 unicursal[y] = checked_calloc((w * 2 - 1) * sizeof(boolean));
1190 for (y = 0; y < h; y++)
1192 for(x = 0; x < w; x++)
1195 unicursal[y * 2][x * 2] = TRUE;
1196 unicursal[y * 2][x * 2 + 2] = TRUE;
1197 unicursal[y * 2 + 2][x * 2] = TRUE;
1198 unicursal[y * 2 + 2][x * 2 + 2] = TRUE;
1200 if (x < 1 || !map[y][x - 1]) unicursal[y * 2 + 1][x * 2] = TRUE;
1201 if (y < 1 || !map[y - 1][x]) unicursal[y * 2][x * 2 + 1] = TRUE;
1202 if (x >= w - 1 || !map[y][x + 1]) unicursal[y * 2 + 1][x * 2 + 2] = TRUE;
1203 if (y >= h - 1 || !map[y + 1][x]) unicursal[y * 2 + 2][x * 2 + 1] = TRUE;
1208 /* free original map */
1209 for (y = 0; y < h; y++)
1213 /* change to new map - the unicursal maze */
1219 /* copy map to cave with correct elements and size */
1220 /* now copy the map into the cave. the copying works like this...
1227 columns and rows denoted with "p" are to be drawn with path width,
1228 the others with wall width. */
1232 for (y = 0; y < h; y++)
1234 for (i = 0; i < (y % 2 == 0 ? path : wall); i++)
1238 for (x = 0; x < w; x++)
1239 for (j = 0; j < (x % 2 == 0 ? path : wall); j++)
1240 gd_cave_store_rc(cave, xk++, yk, map[y][x] ? object->fill_element : object->element, object);
1242 /* if width is smaller than requested, fill with wall */
1243 for(x = xk; x <= x2; x++)
1244 gd_cave_store_rc(cave, x, yk, object->element, object);
1250 /* if height is smaller than requested, fill with wall */
1251 for (y = yk; y <= y2; y++)
1252 for (x = x1; x <= x2; x++)
1253 gd_cave_store_rc(cave, x, y, object->element, object);
1256 for (y = 0; y < h; y++)
1261 static void draw_random_fill(GdCave *cave, const GdObject *object, int level)
1264 int x1 = object->x1;
1265 int y1 = object->y1;
1266 int x2 = object->x2;
1267 int y2 = object->y2;
1269 GdC64RandomGenerator c64_rand;
1272 /* -1 means that it should be different every time played. */
1273 if (object->seed[level] == -1)
1274 seed = g_rand_int(cave->random);
1276 seed = object->seed[level];
1278 rand = g_rand_new_with_seed(seed);
1279 /* for c64 random, use the 2*8 lsb. */
1280 gd_c64_random_set_seed(&c64_rand, seed / 256 % 256, seed % 256);
1282 /* change coordinates if not in correct order */
1297 for (y = y1; y <= y2; y++)
1299 for (x = x1; x <= x2; x++)
1304 if (object->c64_random)
1305 /* use c64 random generator */
1306 randm = gd_c64_random(&c64_rand);
1308 /* use the much better glib random generator */
1309 randm = g_rand_int_range(rand, 0, 256);
1311 element = object->fill_element;
1312 if (randm < object->random_fill_probability[0])
1313 element = object->random_fill[0];
1314 if (randm < object->random_fill_probability[1])
1315 element = object->random_fill[1];
1316 if (randm < object->random_fill_probability[2])
1317 element = object->random_fill[2];
1318 if (randm < object->random_fill_probability[3])
1319 element = object->random_fill[3];
1321 if (object->element==O_NONE ||
1322 gd_cave_get_rc(cave, x, y) == object->element)
1323 gd_cave_store_rc(cave, x, y, element, object);
1331 static void draw_copy_paste(GdCave *cave, const GdObject *object)
1333 int x1 = object->x1, y1 = object->y1, x2 = object->x2, y2 = object->y2;
1334 int x, y; /* iterators */
1336 GdElement *clipboard;
1338 /* reorder coordinates if not drawing from northwest to southeast */
1356 clipboard = checked_malloc((w * h) * sizeof(GdElement));
1358 /* copy to "clipboard" */
1359 for (y = 0; y < h; y++)
1360 for (x = 0; x < w; x++)
1361 clipboard[y * w + x] = gd_cave_get_rc(cave, x + x1, y + y1);
1363 for (y = 0; y < h; y++)
1367 ydest = object->flip ? h - 1 - y : y;
1369 for (x = 0; x < w; x++)
1373 xdest = object->mirror ? w - 1 - x : x;
1375 /* dx and dy are used here are "paste to" coordinates */
1376 gd_cave_store_rc(cave, object->dx + xdest, object->dy + ydest,
1377 clipboard[y * w + x], object);
1384 /* draw the specified game object into cave's data.
1385 also remember, which cell was set by which cave object. */
1386 void gd_cave_draw_object(GdCave *cave, const GdObject *object, int level)
1388 switch (object->type)
1392 gd_cave_store_rc(cave, object->x1, object->y1, object->element, object);
1396 draw_line(cave, object);
1400 draw_rectangle(cave, object);
1403 case GD_FILLED_RECTANGLE:
1404 draw_filled_rectangle(cave, object);
1408 draw_raster(cave, object);
1412 draw_join(cave, object);
1415 case GD_FLOODFILL_BORDER:
1416 draw_fill_border(cave, object);
1419 case GD_FLOODFILL_REPLACE:
1420 draw_fill_replace(cave, object);
1424 case GD_MAZE_UNICURSAL:
1426 draw_maze(cave, object, level);
1429 case GD_RANDOM_FILL:
1430 draw_random_fill(cave, object, level);
1434 draw_copy_paste(cave, object);
1441 Error("Unknown object %d", object->type);
1446 /* load cave to play... also can be called rendering the cave elements */
1447 GdCave *gd_cave_new_rendered(const GdCave *data, const int level, const guint32 seed)
1455 cave = gd_cave_new_from_cave(data);
1456 cave->rendered = level + 1;
1458 cave->render_seed = seed;
1459 cave->random = g_rand_new_with_seed(cave->render_seed);
1461 /* maps needed during drawing and gameplay */
1462 cave->objects_order = gd_cave_map_new(cave, gpointer);
1464 cave->time = data->level_time[level];
1465 cave->timevalue = data->level_timevalue[level];
1466 cave->diamonds_needed = data->level_diamonds[level];
1467 cave->magic_wall_time = data->level_magic_wall_time[level];
1468 cave->slime_permeability = data->level_slime_permeability[level];
1469 cave->slime_permeability_c64 = data->level_slime_permeability_c64[level];
1470 cave->time_bonus = data->level_bonus_time[level];
1471 cave->time_penalty = data->level_penalty_time[level];
1472 cave->amoeba_time = data->level_amoeba_time[level];
1473 cave->amoeba_max_count = data->level_amoeba_threshold[level];
1474 cave->amoeba_2_time = data->level_amoeba_2_time[level];
1475 cave->amoeba_2_max_count = data->level_amoeba_2_threshold[level];
1476 cave->hatching_delay_time = data->level_hatching_delay_time[level];
1477 cave->hatching_delay_frame = data->level_hatching_delay_frame[level];
1481 /* if we have no map, fill with predictable random generator. */
1482 cave->map = gd_cave_map_new(cave, GdElement);
1484 /* IF CAVE HAS NO MAP, USE THE RANDOM NUMBER GENERATOR */
1485 /* init c64 randomgenerator */
1486 if (data->level_rand[level] < 0)
1487 gd_cave_c64_random_set_seed(cave, g_rand_int_range(cave->random, 0, 256),
1488 g_rand_int_range(cave->random, 0, 256));
1490 gd_cave_c64_random_set_seed(cave, 0, data->level_rand[level]);
1492 /* generate random fill
1493 * start from row 1 (0 skipped), and fill also the borders on left and right hand side,
1494 * as c64 did. this way works the original random generator the right way.
1495 * also, do not fill last row, that is needed for the random seeds to be correct
1496 * after filling! predictable slime will use it. */
1497 for (y = 1; y < cave->h - 1; y++)
1499 for (x = 0; x < cave->w; x++)
1503 if (data->level_rand[level] < 0)
1504 /* use the much better glib random generator */
1505 randm = g_rand_int_range(cave->random, 0, 256);
1508 randm = gd_cave_c64_random(cave);
1510 element = data->initial_fill;
1511 if (randm < data->random_fill_probability[0])
1512 element = data->random_fill[0];
1513 if (randm < data->random_fill_probability[1])
1514 element = data->random_fill[1];
1515 if (randm < data->random_fill_probability[2])
1516 element = data->random_fill[2];
1517 if (randm < data->random_fill_probability[3])
1518 element = data->random_fill[3];
1520 gd_cave_store_rc(cave, x, y, element, NULL);
1524 /* draw initial border */
1525 for (y = 0; y < cave->h; y++)
1527 gd_cave_store_rc(cave, 0, y, cave->initial_border, NULL);
1528 gd_cave_store_rc(cave, cave->w - 1, y, cave->initial_border, NULL);
1531 for (x = 0; x < cave->w; x++)
1533 gd_cave_store_rc(cave, x, 0, cave->initial_border, NULL);
1534 gd_cave_store_rc(cave, x, cave->h - 1, cave->initial_border, NULL);
1539 /* IF CAVE HAS A MAP, SIMPLY USE IT... no need to fill with random elements */
1541 /* initialize c64 predictable random for slime.
1542 the values were taken from afl bd, see docs/internals.txt */
1543 gd_cave_c64_random_set_seed(cave, 0, 0x1e);
1546 if (data->level_slime_seed_c64[level] != -1)
1548 /* if a specific slime seed is requested, change it now. */
1550 gd_cave_c64_random_set_seed(cave,
1551 data->level_slime_seed_c64[level] / 256,
1552 data->level_slime_seed_c64[level] % 256);
1555 /* render cave objects above random data or map */
1556 for (iter = data->objects; iter; iter = g_list_next(iter))
1558 GdObject *object = (GdObject *)iter->data;
1560 if (object->levels & gd_levels_mask[level])
1561 gd_cave_draw_object(cave, iter->data, level);
1564 /* check if we use c64 ckdelay or milliseconds for timing */
1565 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
1566 cave->speed = data->level_speed[level]; /* exact timing */
1569 /* delay loop based timing... set something for first iteration,
1570 then later it will be calculated */
1573 /* this one may be used by iterate routine to calculate actual delay
1574 if c64scheduling is selected */
1575 cave->c64_timing = data->level_ckdelay[level];
1578 gd_cave_correct_visible_size(cave);
1584 render cave at specified level.
1585 copy result to the map; remove objects.
1586 the cave will be map-based.
1588 void gd_flatten_cave(GdCave *cave, const int level)
1595 /* render cave at specified level to obtain map. seed = 0 */
1596 rendered = gd_cave_new_rendered(cave, level, 0);
1598 /* forget old map without objects */
1599 gd_cave_map_free(cave->map);
1601 /* copy new map to cave */
1602 cave->map = gd_cave_map_dup(rendered, map);
1603 gd_cave_free(rendered);
1605 /* forget objects */
1606 g_list_foreach(cave->objects, (GFunc) free, NULL);
1607 cave->objects = NULL;