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.
20 GdObjectLevels gd_levels_mask[] =
29 /* bdcff text description of object. caller should free string. */
30 char *gd_object_get_bdcff(const GdObject *object)
39 return getStringPrint("Point=%d %d %s", object->x1, object->y1, gd_elements[object->element].filename);
42 return getStringPrint("Line=%d %d %d %d %s", object->x1, object->y1, object->x2, object->y2, gd_elements[object->element].filename);
45 return getStringPrint("Rectangle=%d %d %d %d %s", object->x1, object->y1, object->x2, object->y2, gd_elements[object->element].filename);
47 case GD_FILLED_RECTANGLE:
48 /* if elements are not the same */
49 if (object->fill_element!=object->element)
50 return getStringPrint("FillRect=%d %d %d %d %s %s", object->x1, object->y1, object->x2, object->y2, gd_elements[object->element].filename, gd_elements[object->fill_element].filename);
51 /* they are the same */
52 return getStringPrint("FillRect=%d %d %d %d %s", object->x1, object->y1, object->x2, object->y2, gd_elements[object->element].filename);
55 return getStringPrint("Raster=%d %d %d %d %d %d %s", object->x1, object->y1, (object->x2-object->x1)/object->dx+1, (object->y2-object->y1)/object->dy+1, object->dx, object->dy, gd_elements[object->element].filename);
58 return getStringPrint("Add=%d %d %s %s", object->dx, object->dy, gd_elements[object->element].filename, gd_elements[object->fill_element].filename);
60 case GD_FLOODFILL_BORDER:
61 return getStringPrint("BoundaryFill=%d %d %s %s", object->x1, object->y1, gd_elements[object->fill_element].filename, gd_elements[object->element].filename);
63 case GD_FLOODFILL_REPLACE:
64 return getStringPrint("FloodFill=%d %d %s %s", object->x1, object->y1, gd_elements[object->fill_element].filename, gd_elements[object->element].filename);
67 case GD_MAZE_UNICURSAL:
75 case GD_MAZE_UNICURSAL:
88 return getStringPrint("Maze=%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], gd_elements[object->element].filename, gd_elements[object->fill_element].filename, type);
91 appendStringPrint(&str, "%s=%d %d %d %d %d %d %d %d %d %s", object->c64_random?"RandomFillC64":"RandomFill", object->x1, object->y1, object->x2, object->y2, object->seed[0], object->seed[1], object->seed[2], object->seed[3], object->seed[4], gd_elements[object->fill_element].filename);
93 /* seed and initial fill */
94 for (j = 0; j < 4; j++)
95 if (object->random_fill_probability[j] != 0)
96 appendStringPrint(&str, " %s %d", gd_elements[object->random_fill[j]].filename, object->random_fill_probability[j]);
98 if (object->element != O_NONE)
99 appendStringPrint(&str, " %s", gd_elements[object->element].filename);
104 return getStringPrint("CopyPaste=%d %d %d %d %d %d %s %s", object->x1, object->y1, object->x2, object->y2, object->dx, object->dy, object->mirror?"mirror":"nomirror", object->flip?"flip":"noflip");
114 /* create an INDIVIDUAL POINT CAVE OBJECT */
115 GdObject *gd_object_new_point(GdObjectLevels levels, int x, int y, GdElement elem)
117 GdObject *newobj = checked_calloc(sizeof(GdObject));
119 newobj->levels = levels;
120 newobj->type = GD_POINT;
123 newobj->element = elem;
128 /* create a LINE OBJECT */
129 GdObject *gd_object_new_line(GdObjectLevels levels, int x1, int y1, int x2, int y2,
132 GdObject *newobj = checked_calloc(sizeof(GdObject));
134 newobj->levels = levels;
135 newobj->type = GD_LINE;
140 newobj->element = elem;
145 /* create a RECTANGLE OBJECT */
146 GdObject *gd_object_new_rectangle(GdObjectLevels levels, int x1, int y1, int x2, int y2,
149 GdObject *newobj = checked_calloc(sizeof(GdObject));
151 newobj->levels = levels;
152 newobj->type = GD_RECTANGLE;
157 newobj->element = elem;
162 /* create a RECTANGLE OBJECT */
163 GdObject *gd_object_new_filled_rectangle(GdObjectLevels levels, int x1, int y1, int x2, int y2,
164 GdElement elem, GdElement fill_elem)
166 GdObject *newobj = checked_calloc(sizeof(GdObject));
168 newobj->levels = levels;
169 newobj->type = GD_FILLED_RECTANGLE;
174 newobj->element = elem;
175 newobj->fill_element = fill_elem;
180 /* create a raster object */
181 GdObject *gd_object_new_raster(GdObjectLevels levels, int x1, int y1, int x2, int y2,
182 int dx, int dy, GdElement elem)
184 GdObject *newobj = checked_calloc(sizeof(GdObject));
186 newobj->levels = levels;
187 newobj->type = GD_RASTER;
194 newobj->element = elem;
199 /* create a raster object */
200 GdObject *gd_object_new_join(GdObjectLevels levels, int dx, int dy,
201 GdElement search, GdElement replace)
203 GdObject *newobj = checked_calloc(sizeof(GdObject));
205 newobj->levels = levels;
206 newobj->type = GD_JOIN;
209 newobj->element = search;
210 newobj->fill_element = replace;
215 /* create a new boundary fill object */
216 GdObject *gd_object_new_floodfill_border(GdObjectLevels levels, int x1, int y1,
217 GdElement fill, GdElement border)
219 GdObject *newobj = checked_calloc(sizeof(GdObject));
221 newobj->levels = levels;
222 newobj->type = GD_FLOODFILL_BORDER;
225 newobj->element = border;
226 newobj->fill_element = fill;
231 GdObject *gd_object_new_floodfill_replace(GdObjectLevels levels, int x1, int y1,
232 GdElement fill, GdElement to_replace)
234 GdObject *newobj = checked_calloc(sizeof(GdObject));
236 newobj->levels = levels;
237 newobj->type = GD_FLOODFILL_REPLACE;
240 newobj->element = to_replace;
241 newobj->fill_element = fill;
246 GdObject *gd_object_new_maze(GdObjectLevels levels, int x1, int y1, int x2, int y2,
247 int wall_w, int path_w, GdElement wall_e, GdElement path_e,
248 int horiz_percent, const int seed[5])
251 GdObject *newobj = checked_calloc(sizeof(GdObject));
253 newobj->levels = levels;
254 newobj->type = GD_MAZE;
261 newobj->element = wall_e;
262 newobj->fill_element = path_e;
263 newobj->horiz = horiz_percent;
265 for (i = 0; i < 5; ++i)
266 newobj->seed[i] = seed[i];
271 GdObject *gd_object_new_maze_unicursal(GdObjectLevels levels, int x1, int y1, int x2, int y2,
272 int wall_w, int path_w, GdElement wall_e, GdElement path_e,
273 int horiz_percent, const int seed[5])
276 GdObject *newobj = checked_calloc(sizeof(GdObject));
278 newobj->levels = levels;
279 newobj->type = GD_MAZE_UNICURSAL;
286 newobj->element = wall_e;
287 newobj->fill_element = path_e;
288 newobj->horiz = horiz_percent;
290 for (i = 0; i < 5; ++i)
291 newobj->seed[i] = seed[i];
296 GdObject *gd_object_new_maze_braid(GdObjectLevels levels, int x1, int y1, int x2, int y2,
297 int wall_w, int path_w, GdElement wall_e, GdElement path_e,
298 int horiz_percent, const int seed[5])
301 GdObject *newobj = checked_calloc(sizeof(GdObject));
303 newobj->levels = levels;
304 newobj->type = GD_MAZE_BRAID;
311 newobj->element = wall_e;
312 newobj->fill_element = path_e;
313 newobj->horiz = horiz_percent;
315 for (i = 0; i < 5; ++i)
316 newobj->seed[i] = seed[i];
321 GdObject *gd_object_new_random_fill(GdObjectLevels levels, int x1, int y1, int x2, int y2,
322 const int seed[5], GdElement initial,
323 const GdElement random[4], const int prob[4],
324 GdElement replace_only, boolean c64)
327 GdObject *newobj = checked_calloc(sizeof(GdObject));
329 newobj->levels = levels;
330 newobj->type = GD_RANDOM_FILL;
335 newobj->fill_element = initial;
337 for (i = 0; i < 5; ++i)
338 newobj->seed[i] = seed[i];
340 for (i = 0; i < 4; ++i)
342 newobj->random_fill[i] = random[i];
343 newobj->random_fill_probability[i] = prob[i];
346 newobj->element = replace_only;
347 newobj->c64_random = c64;
352 GdObject *gd_object_new_copy_paste(GdObjectLevels levels, int x1, int y1, int x2, int y2,
353 int dx, int dy, boolean mirror, boolean flip)
355 GdObject *newobj = checked_calloc(sizeof(GdObject));
357 newobj->levels = levels;
358 newobj->type = GD_COPY_PASTE;
365 newobj->mirror = mirror;
371 /* create new object from bdcff description.
372 return new object if ok; return null if failed.
374 GdObject *gd_object_new_from_string(char *str)
379 char elem0[100], elem1[100];
381 equalsign = strchr(str, '=');
385 /* split string by replacing the equal sign with zero */
388 param = equalsign + 1;
390 /* INDIVIDUAL POINT CAVE OBJECT */
391 if (strcasecmp(name, "Point") == 0)
393 object.type = GD_POINT;
394 if (sscanf(param, "%d %d %s", &object.x1, &object.y1, elem0) == 3)
396 object.element = gd_get_element_from_string(elem0);
398 return get_memcpy(&object, sizeof (GdObject));
405 if (strcasecmp(name, "Line") == 0)
407 object.type = GD_LINE;
408 if (sscanf(param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0) == 5)
410 object.element = gd_get_element_from_string(elem0);
412 return get_memcpy(&object, sizeof (GdObject));
418 /* RECTANGLE OBJECT */
419 if (strcasecmp(name, "Rectangle") == 0)
421 if (sscanf(param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0) == 5)
423 object.type = GD_RECTANGLE;
424 object.element = gd_get_element_from_string (elem0);
426 return get_memcpy(&object, sizeof (GdObject));
432 /* FILLED RECTANGLE OBJECT */
433 if (strcasecmp(name, "FillRect") == 0)
437 paramcount = sscanf(param, "%d %d %d %d %s %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0, elem1);
438 object.type = GD_FILLED_RECTANGLE;
442 object.element = gd_get_element_from_string (elem0);
443 object.fill_element = gd_get_element_from_string (elem1);
445 return get_memcpy(&object, sizeof (GdObject));
450 object.element = object.fill_element = gd_get_element_from_string (elem0);
452 return get_memcpy(&object, sizeof (GdObject));
459 if (strcasecmp(name, "Raster") == 0)
463 if (sscanf(param, "%d %d %d %d %d %d %s", &object.x1, &object.y1, &nx, &ny, &object.dx, &object.dy, elem0) == 7)
467 object.x2 = object.x1 + nx * object.dx;
468 object.y2 = object.y1 + ny * object.dy;
469 object.type = GD_RASTER;
470 object.element = gd_get_element_from_string (elem0);
472 return get_memcpy(&object, sizeof (GdObject));
479 if (strcasecmp(name, "Join") == 0 ||
480 strcasecmp(name, "Add") == 0)
482 if (sscanf(param, "%d %d %s %s", &object.dx, &object.dy, elem0, elem1) == 4)
484 object.type = GD_JOIN;
485 object.element = gd_get_element_from_string (elem0);
486 object.fill_element = gd_get_element_from_string (elem1);
488 return get_memcpy(&object, sizeof (GdObject));
494 /* FILL TO BORDER OBJECT */
495 if (strcasecmp(name, "BoundaryFill") == 0)
497 if (sscanf(param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1) == 4)
499 object.type = GD_FLOODFILL_BORDER;
500 object.fill_element = gd_get_element_from_string (elem0);
501 object.element = gd_get_element_from_string (elem1);
503 return get_memcpy(&object, sizeof (GdObject));
509 /* REPLACE FILL OBJECT */
510 if (strcasecmp(name, "FloodFill") == 0)
512 if (sscanf(param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1) == 4)
514 object.type = GD_FLOODFILL_REPLACE;
515 object.fill_element = gd_get_element_from_string (elem0);
516 object.element = gd_get_element_from_string (elem1);
518 return get_memcpy(&object, sizeof (GdObject));
525 /* MAZE UNICURSAL OBJECT */
526 /* BRAID MAZE OBJECT */
527 if (strcasecmp(name, "Maze") == 0)
529 char type[100] = "perfect";
531 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)
533 if (strcasecmp(type, "unicursal") == 0)
534 object.type = GD_MAZE_UNICURSAL;
535 else if (strcasecmp(type, "perfect") == 0)
536 object.type = GD_MAZE;
537 else if (strcasecmp(type, "braid") == 0)
538 object.type = GD_MAZE_BRAID;
541 Warn("unknown maze type: %s, defaulting to perfect", type);
542 object.type = GD_MAZE;
545 object.element = gd_get_element_from_string (elem0);
546 object.fill_element = gd_get_element_from_string (elem1);
548 return get_memcpy(&object, sizeof (GdObject));
554 /* RANDOM FILL OBJECT */
555 if (strcasecmp(name, "RandomFill") == 0 ||
556 strcasecmp(name, "RandomFillC64") == 0)
558 static char **words = NULL;
561 object.type = GD_RANDOM_FILL;
562 if (strcasecmp(name, "RandomFillC64") == 0)
563 /* totally the same, but uses c64 random generator */
564 object.c64_random = TRUE;
566 object.c64_random = FALSE;
568 if (sscanf(param, "%d %d %d %d", &object.x1, &object.y1, &object.x2, &object.y2) != 4)
572 freeStringArray(words);
574 words = getSplitStringArray(param, " ", -1);
575 l = getStringArrayLength(words);
577 if (l < 10 || l > 19)
580 for (i = 0; i < 5; i++)
581 if (sscanf(words[4 + i], "%d", &object.seed[i]) != 1)
584 object.fill_element = gd_get_element_from_string(words[9]);
586 for (i = 0; i < 4; i++)
588 object.random_fill[i] = O_DIRT;
589 object.random_fill_probability[i] = 0;
592 for (i = 10; i < l - 1; i += 2)
594 object.random_fill[(i - 10) / 2] = gd_get_element_from_string(words[i]);
595 if (sscanf(words[i + 1], "%d", &object.random_fill_probability[(i - 10) / 2]) == 0)
599 object.element = O_NONE;
601 if (l > 10 && l % 2 == 1)
602 object.element = gd_get_element_from_string(words[l - 1]);
604 return get_memcpy(&object, sizeof (GdObject));
607 /* COPY PASTE OBJECT */
608 if (strcasecmp(name, "CopyPaste") == 0)
610 char mirror[100] = "nomirror";
611 char flip[100] = "noflip";
612 object.type = GD_COPY_PASTE;
614 object.flip = object.mirror = FALSE;
616 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)
619 /* MIRROR PROPERTY */
620 if (strcasecmp(mirror, "mirror") == 0)
621 object.mirror = TRUE;
622 else if (strcasecmp(mirror, "nomirror") == 0)
623 object.mirror = FALSE;
625 Warn("invalid setting for copypaste mirror property: %s", mirror);
628 if (strcasecmp(flip, "flip") == 0)
630 else if (strcasecmp(flip, "noflip") == 0)
633 Warn("invalid setting for copypaste flip property: %s", flip);
635 return get_memcpy(&object, sizeof(GdObject));
641 /** drawing a line, using bresenham's */
642 static void draw_line (GdCave *cave, const GdObject *object)
644 int x, y, x1, y1, x2, y2;
646 int error, dx, dy, ystep;
649 y1 = object->y1, x2 = object->x2;
651 steep = ABS (y2 - y1) > ABS (x2 - x1);
677 ystep = (y1 < y2) ? 1 : -1;
679 for (x = x1; x <= x2; x++)
682 gd_cave_store_rc (cave, y, x, object->element, object);
684 gd_cave_store_rc (cave, x, y, object->element, object);
698 static void draw_fill_replace_proc(GdCave *cave, int x, int y, const GdObject *object)
700 /* fill with border so we do not come back */
701 gd_cave_store_rc(cave, x, y, object->fill_element, object);
703 if (x > 0 && gd_cave_get_rc(cave, x - 1, y) == object->element)
704 draw_fill_replace_proc(cave, x - 1, y, object);
706 if (y > 0 && gd_cave_get_rc(cave, x, y - 1) == object->element)
707 draw_fill_replace_proc(cave, x, y - 1, object);
709 if (x < cave->w - 1 && gd_cave_get_rc(cave, x + 1, y) == object->element)
710 draw_fill_replace_proc(cave, x + 1, y, object);
712 if (y < cave->h - 1 && gd_cave_get_rc(cave, x, y + 1) == object->element)
713 draw_fill_replace_proc(cave, x, y + 1, object);
716 static void draw_fill_replace (GdCave *cave, const GdObject *object)
719 if (object->x1 < 0 ||
721 object->x1 >= cave->w ||
722 object->y1 >= cave->h)
725 if (object->element == object->fill_element)
728 /* this procedure fills the area with the object->element. */
729 draw_fill_replace_proc(cave, object->x1, object->y1, object);
732 static void draw_fill_border_proc (GdCave *cave, int x, int y, const GdObject *object)
734 /* fill with border so we do not come back */
735 gd_cave_store_rc(cave, x, y, object->element, object);
737 if (x > 0 && gd_cave_get_rc(cave, x - 1, y) != object->element)
738 draw_fill_border_proc(cave, x - 1, y, object);
740 if (y > 0 && gd_cave_get_rc(cave, x, y - 1) != object->element)
741 draw_fill_border_proc(cave, x, y - 1, object);
743 if (x < cave->w - 1 && gd_cave_get_rc(cave, x + 1, y) != object->element)
744 draw_fill_border_proc(cave, x + 1, y, object);
746 if (y < cave->h-1 && gd_cave_get_rc(cave, x, y + 1) != object->element)
747 draw_fill_border_proc(cave, x, y + 1, object);
750 static void draw_fill_border (GdCave *cave, const GdObject *object)
755 if (object->x1 < 0 ||
757 object->x1 >= cave->w ||
758 object->y1 >= cave->h)
761 /* this procedure fills the area with the object->element. */
762 draw_fill_border_proc(cave, object->x1, object->y1, object);
764 /* after the fill, we change all filled cells to the fill_element. */
765 /* we find those by looking at the object_order[][] */
766 for (y = 0; y < cave->h; y++)
767 for (x = 0; x < cave->w; x++)
768 if (cave->objects_order[y][x] == object)
769 cave->map[y][x] = object->fill_element;
772 /* rectangle, frame only */
773 static void draw_rectangle(GdCave *cave, const GdObject *object)
775 int x1, y1, x2, y2, x, y;
777 /* reorder coordinates if not drawing from northwest to southeast */
779 y1 = object->y1, x2 = object->x2;
796 for (x = x1; x <= x2; x++)
798 gd_cave_store_rc(cave, x, object->y1, object->element, object);
799 gd_cave_store_rc(cave, x, object->y2, object->element, object);
802 for (y = y1; y <= y2; y++)
804 gd_cave_store_rc(cave, object->x1, y, object->element, object);
805 gd_cave_store_rc(cave, object->x2, y, object->element, object);
809 /* rectangle, filled one */
810 static void draw_filled_rectangle(GdCave *cave, const GdObject *object)
812 int x1, y1, x2, y2, x, y;
814 /* reorder coordinates if not drawing from northwest to southeast */
816 y1 = object->y1, x2 = object->x2;
833 for (y = y1; y <= y2; y++)
834 for (x = x1; x <= x2; x++)
835 gd_cave_store_rc(cave, x, y, (y == object->y1 ||
838 x == object->x2) ? object->element : object->fill_element, object);
841 /* something like ordered fill, increment is dx and dy. */
842 static void draw_raster(GdCave *cave, const GdObject *object)
844 int x, y, x1, y1, x2, y2;
847 /* reorder coordinates if not drawing from northwest to southeast */
877 for (y = y1; y <= y2; y += dy)
878 for (x = x1; x <= x2; x += dx)
879 gd_cave_store_rc(cave, x, y, object->element, object);
882 /* find every object, and put fill_element next to it. relative coordinates dx,dy */
883 static void draw_join(GdCave *cave, const GdObject *object)
887 for (y = 0; y < cave->h; y++)
889 for (x = 0; x < cave->w; x++)
891 if (cave->map[y][x] == object->element)
893 int nx = x + object->dx;
894 int ny = y + object->dy;
895 /* this one implements wraparound for joins.
896 it is needed by many caves in profi boulder series */
897 while (nx >= cave->w)
900 gd_cave_store_rc(cave, nx, ny, object->fill_element, object);
906 /* create a maze in a boolean **maze. */
907 /* recursive algorithm. */
908 static void mazegen(GdRand *rand, boolean **maze, int width, int height, int x, int y, int horiz)
918 dir = gd_rand_int_range(rand, 0, 100) < horiz ? 2 : 0;
920 /* if no horizontal movement possible, choose vertical */
921 if (dir == 2 && (dirmask & 12) == 0)
923 else if (dir == 0 && (dirmask & 3) == 0) /* and vice versa */
926 dir += gd_rand_int_range(rand, 0, 2); /* dir */
927 if (dirmask & (1 << dir))
929 dirmask &= ~(1 << dir);
934 if (y >= 2 && !maze[y - 2][x])
936 maze[y - 1][x] = TRUE;
937 mazegen(rand, maze, width, height, x, y - 2, horiz);
942 if (y < height-2 && !maze[y + 2][x]) {
943 maze[y + 1][x] = TRUE;
944 mazegen(rand, maze, width, height, x, y + 2, horiz);
949 if (x >= 2 && !maze[y][x - 2]) {
950 maze[y][x - 1] = TRUE;
951 mazegen(rand, maze, width, height, x - 2, y, horiz);
956 if (x < width - 2 && !maze[y][x + 2]) {
957 maze[y][x + 1] = TRUE;
958 mazegen(rand, maze, width, height, x + 2, y, horiz);
969 static void braidmaze(GdRand *rand, boolean **maze, int w, int h)
973 for (y = 0; y < h; y += 2)
975 for (x = 0; x < w; x += 2)
977 int closed = 0, dirs = 0;
980 /* if it is the edge of the map, OR no path carved, then we can't go in that direction. */
981 if (x < 1 || !maze[y][x - 1])
983 /* closed from this side. */
986 /* if not the edge, we might open this wall (carve a path) to remove a dead end */
988 closed_dirs[dirs++] = GD_MV_LEFT;
991 /* other 3 directions similar */
992 if (y < 1 || !maze[y - 1][x])
996 closed_dirs[dirs++] = GD_MV_UP;
999 if (x >= w - 1 || !maze[y][x + 1])
1003 closed_dirs[dirs++] = GD_MV_RIGHT;
1006 if (y >= h - 1 || !maze[y + 1][x]) {
1009 closed_dirs[dirs++] = GD_MV_DOWN;
1012 /* if closed from 3 sides, then it is a dead end. also check dirs != 0,
1013 that might fail for a 1x1 maze :) */
1014 if (closed == 3 && dirs != 0)
1016 /* make up a random direction, and open in that direction, so dead end is removed */
1017 int dir = closed_dirs[gd_rand_int_range(rand, 0, dirs)];
1022 maze[y][x - 1] = TRUE; break;
1024 maze[y - 1][x] = TRUE; break;
1026 maze[y][x + 1] = TRUE; break;
1028 maze[y + 1][x] = TRUE; break;
1035 static void draw_maze(GdCave *cave, const GdObject *object, int level)
1039 int x1 = object->x1;
1040 int y1 = object->y1;
1041 int x2 = object->x2;
1042 int y2 = object->y2;
1043 int w, h, path, wall;
1048 /* change coordinates if not in correct order */
1071 /* calculate the width and height of the maze.
1072 n = number of passages, path = path width, wall = wall width, maze = maze width.
1073 if given the number of passages, the width of the maze is:
1075 n * path + (n - 1) * wall = maze
1076 n * path + n * wall - wall = maze
1077 n * (path + wall) = maze + wall
1078 n = (maze + wall) / (path + wall)
1081 /* number of passages for each side */
1082 w = (x2 - x1 + 1 + wall) / (path + wall);
1083 h = (y2 - y1 + 1 + wall) / (path + wall);
1085 /* and we calculate the size of the internal map */
1086 if (object->type == GD_MAZE_UNICURSAL)
1088 /* for unicursal maze, width and height must be mod2 = 0,
1089 and we will convert to paths & walls later */
1095 /* for normal maze */
1096 w = 2 * (w - 1) + 1;
1097 h = 2 * (h - 1) + 1;
1100 /* twodimensional boolean array to generate map in */
1101 map = checked_malloc((h) * sizeof(boolean *));
1102 for (y = 0; y < h; y++)
1103 map[y] = checked_calloc(w * sizeof(boolean));
1105 /* start generation, if map is big enough.
1106 otherwise the application would crash, as the editor places maze objects
1107 during mouse click & drag that have no sense */
1108 rand = gd_rand_new_with_seed(object->seed[level] == -1 ?
1109 gd_rand_int(cave->random) : object->seed[level]);
1111 if (w >= 1 && h >= 1)
1112 mazegen(rand, map, w, h, 0, 0, object->horiz);
1114 if (object->type == GD_MAZE_BRAID)
1115 braidmaze(rand, map, w, h);
1119 if (w >= 1 && h >= 1 && object->type == GD_MAZE_UNICURSAL)
1121 boolean **unicursal;
1123 /* convert to unicursal maze */
1139 unicursal = checked_malloc((h * 2 - 1) * sizeof(boolean *));
1141 for (y = 0; y < h * 2 - 1; y++)
1142 unicursal[y] = checked_calloc((w * 2 - 1) * sizeof(boolean));
1144 for (y = 0; y < h; y++)
1146 for(x = 0; x < w; x++)
1149 unicursal[y * 2][x * 2] = TRUE;
1150 unicursal[y * 2][x * 2 + 2] = TRUE;
1151 unicursal[y * 2 + 2][x * 2] = TRUE;
1152 unicursal[y * 2 + 2][x * 2 + 2] = TRUE;
1154 if (x < 1 || !map[y][x - 1]) unicursal[y * 2 + 1][x * 2] = TRUE;
1155 if (y < 1 || !map[y - 1][x]) unicursal[y * 2][x * 2 + 1] = TRUE;
1156 if (x >= w - 1 || !map[y][x + 1]) unicursal[y * 2 + 1][x * 2 + 2] = TRUE;
1157 if (y >= h - 1 || !map[y + 1][x]) unicursal[y * 2 + 2][x * 2 + 1] = TRUE;
1162 /* free original map */
1163 for (y = 0; y < h; y++)
1167 /* change to new map - the unicursal maze */
1173 /* copy map to cave with correct elements and size */
1174 /* now copy the map into the cave. the copying works like this...
1181 columns and rows denoted with "p" are to be drawn with path width,
1182 the others with wall width. */
1186 for (y = 0; y < h; y++)
1188 for (i = 0; i < (y % 2 == 0 ? path : wall); i++)
1192 for (x = 0; x < w; x++)
1193 for (j = 0; j < (x % 2 == 0 ? path : wall); j++)
1194 gd_cave_store_rc(cave, xk++, yk, map[y][x] ? object->fill_element : object->element, object);
1196 /* if width is smaller than requested, fill with wall */
1197 for(x = xk; x <= x2; x++)
1198 gd_cave_store_rc(cave, x, yk, object->element, object);
1204 /* if height is smaller than requested, fill with wall */
1205 for (y = yk; y <= y2; y++)
1206 for (x = x1; x <= x2; x++)
1207 gd_cave_store_rc(cave, x, y, object->element, object);
1210 for (y = 0; y < h; y++)
1215 static void draw_random_fill(GdCave *cave, const GdObject *object, int level)
1218 int x1 = object->x1;
1219 int y1 = object->y1;
1220 int x2 = object->x2;
1221 int y2 = object->y2;
1223 GdC64RandomGenerator c64_rand;
1226 /* -1 means that it should be different every time played. */
1227 if (object->seed[level] == -1)
1228 seed = gd_rand_int(cave->random);
1230 seed = object->seed[level];
1232 rand = gd_rand_new_with_seed(seed);
1233 /* for c64 random, use the 2*8 lsb. */
1234 gd_c64_random_set_seed(&c64_rand, seed / 256 % 256, seed % 256);
1236 /* change coordinates if not in correct order */
1251 for (y = y1; y <= y2; y++)
1253 for (x = x1; x <= x2; x++)
1258 if (object->c64_random)
1259 /* use c64 random generator */
1260 randm = gd_c64_random(&c64_rand);
1262 /* use the much better glib random generator */
1263 randm = gd_rand_int_range(rand, 0, 256);
1265 element = object->fill_element;
1266 if (randm < object->random_fill_probability[0])
1267 element = object->random_fill[0];
1268 if (randm < object->random_fill_probability[1])
1269 element = object->random_fill[1];
1270 if (randm < object->random_fill_probability[2])
1271 element = object->random_fill[2];
1272 if (randm < object->random_fill_probability[3])
1273 element = object->random_fill[3];
1275 if (object->element == O_NONE ||
1276 gd_cave_get_rc(cave, x, y) == object->element)
1277 gd_cave_store_rc(cave, x, y, element, object);
1285 static void draw_copy_paste(GdCave *cave, const GdObject *object)
1287 int x1 = object->x1, y1 = object->y1, x2 = object->x2, y2 = object->y2;
1288 int x, y; /* iterators */
1290 GdElement *clipboard;
1292 /* reorder coordinates if not drawing from northwest to southeast */
1310 clipboard = checked_malloc((w * h) * sizeof(GdElement));
1312 /* copy to "clipboard" */
1313 for (y = 0; y < h; y++)
1314 for (x = 0; x < w; x++)
1315 clipboard[y * w + x] = gd_cave_get_rc(cave, x + x1, y + y1);
1317 for (y = 0; y < h; y++)
1321 ydest = object->flip ? h - 1 - y : y;
1323 for (x = 0; x < w; x++)
1327 xdest = object->mirror ? w - 1 - x : x;
1329 /* dx and dy are used here are "paste to" coordinates */
1330 gd_cave_store_rc(cave, object->dx + xdest, object->dy + ydest,
1331 clipboard[y * w + x], object);
1338 /* draw the specified game object into cave's data.
1339 also remember, which cell was set by which cave object. */
1340 void gd_cave_draw_object(GdCave *cave, const GdObject *object, int level)
1342 switch (object->type)
1346 gd_cave_store_rc(cave, object->x1, object->y1, object->element, object);
1350 draw_line(cave, object);
1354 draw_rectangle(cave, object);
1357 case GD_FILLED_RECTANGLE:
1358 draw_filled_rectangle(cave, object);
1362 draw_raster(cave, object);
1366 draw_join(cave, object);
1369 case GD_FLOODFILL_BORDER:
1370 draw_fill_border(cave, object);
1373 case GD_FLOODFILL_REPLACE:
1374 draw_fill_replace(cave, object);
1378 case GD_MAZE_UNICURSAL:
1380 draw_maze(cave, object, level);
1383 case GD_RANDOM_FILL:
1384 draw_random_fill(cave, object, level);
1388 draw_copy_paste(cave, object);
1395 Error("Unknown object %d", object->type);
1400 /* load cave to play... also can be called rendering the cave elements */
1401 GdCave *gd_cave_new_rendered(const GdCave *data, const int level, const unsigned int seed)
1409 cave = gd_cave_new_from_cave(data);
1410 cave->rendered = level + 1;
1412 cave->render_seed = seed;
1413 cave->random = gd_rand_new_with_seed(cave->render_seed);
1415 /* maps needed during drawing and gameplay */
1416 cave->objects_order = gd_cave_map_new(cave, void *);
1418 cave->time = data->level_time[level];
1419 cave->timevalue = data->level_timevalue[level];
1420 cave->diamonds_needed = data->level_diamonds[level];
1421 cave->magic_wall_time = data->level_magic_wall_time[level];
1422 cave->slime_permeability = data->level_slime_permeability[level];
1423 cave->slime_permeability_c64 = data->level_slime_permeability_c64[level];
1424 cave->time_bonus = data->level_bonus_time[level];
1425 cave->time_penalty = data->level_penalty_time[level];
1426 cave->amoeba_time = data->level_amoeba_time[level];
1427 cave->amoeba_max_count = data->level_amoeba_threshold[level];
1428 cave->amoeba_2_time = data->level_amoeba_2_time[level];
1429 cave->amoeba_2_max_count = data->level_amoeba_2_threshold[level];
1430 cave->hatching_delay_time = data->level_hatching_delay_time[level];
1431 cave->hatching_delay_frame = data->level_hatching_delay_frame[level];
1435 /* if we have no map, fill with predictable random generator. */
1436 cave->map = gd_cave_map_new(cave, GdElement);
1438 /* IF CAVE HAS NO MAP, USE THE RANDOM NUMBER GENERATOR */
1439 /* init c64 randomgenerator */
1440 if (data->level_rand[level] < 0)
1441 gd_cave_c64_random_set_seed(cave, gd_rand_int_range(cave->random, 0, 256),
1442 gd_rand_int_range(cave->random, 0, 256));
1444 gd_cave_c64_random_set_seed(cave, 0, data->level_rand[level]);
1446 /* generate random fill
1447 * start from row 1 (0 skipped), and fill also the borders on left and right hand side,
1448 * as c64 did. this way works the original random generator the right way.
1449 * also, do not fill last row, that is needed for the random seeds to be correct
1450 * after filling! predictable slime will use it. */
1451 for (y = 1; y < cave->h - 1; y++)
1453 for (x = 0; x < cave->w; x++)
1457 if (data->level_rand[level] < 0)
1458 /* use the much better glib random generator */
1459 randm = gd_rand_int_range(cave->random, 0, 256);
1462 randm = gd_cave_c64_random(cave);
1464 element = data->initial_fill;
1465 if (randm < data->random_fill_probability[0])
1466 element = data->random_fill[0];
1467 if (randm < data->random_fill_probability[1])
1468 element = data->random_fill[1];
1469 if (randm < data->random_fill_probability[2])
1470 element = data->random_fill[2];
1471 if (randm < data->random_fill_probability[3])
1472 element = data->random_fill[3];
1474 gd_cave_store_rc(cave, x, y, element, NULL);
1478 /* draw initial border */
1479 for (y = 0; y < cave->h; y++)
1481 gd_cave_store_rc(cave, 0, y, cave->initial_border, NULL);
1482 gd_cave_store_rc(cave, cave->w - 1, y, cave->initial_border, NULL);
1485 for (x = 0; x < cave->w; x++)
1487 gd_cave_store_rc(cave, x, 0, cave->initial_border, NULL);
1488 gd_cave_store_rc(cave, x, cave->h - 1, cave->initial_border, NULL);
1493 /* IF CAVE HAS A MAP, SIMPLY USE IT... no need to fill with random elements */
1495 /* initialize c64 predictable random for slime.
1496 the values were taken from afl bd, see docs/internals.txt */
1497 gd_cave_c64_random_set_seed(cave, 0, 0x1e);
1500 if (data->level_slime_seed_c64[level] != -1)
1502 /* if a specific slime seed is requested, change it now. */
1504 gd_cave_c64_random_set_seed(cave,
1505 data->level_slime_seed_c64[level] / 256,
1506 data->level_slime_seed_c64[level] % 256);
1509 /* render cave objects above random data or map */
1510 for (iter = data->objects; iter; iter = list_next(iter))
1512 GdObject *object = (GdObject *)iter->data;
1514 if (object->levels & gd_levels_mask[level])
1515 gd_cave_draw_object(cave, iter->data, level);
1518 /* check if we use c64 ckdelay or milliseconds for timing */
1519 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
1520 cave->speed = data->level_speed[level]; /* exact timing */
1523 /* delay loop based timing... set something for first iteration,
1524 then later it will be calculated */
1527 /* this one may be used by iterate routine to calculate actual delay
1528 if c64scheduling is selected */
1529 cave->c64_timing = data->level_ckdelay[level];
1532 gd_cave_correct_visible_size(cave);
1538 render cave at specified level.
1539 copy result to the map; remove objects.
1540 the cave will be map-based.
1542 void gd_flatten_cave(GdCave *cave, const int level)
1549 /* render cave at specified level to obtain map. seed = 0 */
1550 rendered = gd_cave_new_rendered(cave, level, 0);
1552 /* forget old map without objects */
1553 gd_cave_map_free(cave->map);
1555 /* copy new map to cave */
1556 cave->map = gd_cave_map_dup(rendered, map);
1557 gd_cave_free(rendered);
1559 /* forget objects */
1560 list_foreach(cave->objects, (list_fn) free, NULL);
1561 cave->objects = NULL;