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);
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.
373 GdObject *gd_object_new_from_string(char *str)
378 char elem0[100], elem1[100];
380 equalsign = strchr(str, '=');
384 // split string by replacing the equal sign with zero
387 param = equalsign + 1;
389 // INDIVIDUAL POINT CAVE OBJECT
390 if (strcasecmp(name, "Point") == 0)
392 object.type = GD_POINT;
393 if (sscanf(param, "%d %d %s", &object.x1, &object.y1, elem0) == 3)
395 object.element = gd_get_element_from_string(elem0);
397 return get_memcpy(&object, sizeof (GdObject));
404 if (strcasecmp(name, "Line") == 0)
406 object.type = GD_LINE;
407 if (sscanf(param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0) == 5)
409 object.element = gd_get_element_from_string(elem0);
411 return get_memcpy(&object, sizeof (GdObject));
418 if (strcasecmp(name, "Rectangle") == 0)
420 if (sscanf(param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0) == 5)
422 object.type = GD_RECTANGLE;
423 object.element = gd_get_element_from_string (elem0);
425 return get_memcpy(&object, sizeof (GdObject));
431 // FILLED RECTANGLE OBJECT
432 if (strcasecmp(name, "FillRect") == 0)
436 paramcount = sscanf(param, "%d %d %d %d %s %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0, elem1);
437 object.type = GD_FILLED_RECTANGLE;
441 object.element = gd_get_element_from_string (elem0);
442 object.fill_element = gd_get_element_from_string (elem1);
444 return get_memcpy(&object, sizeof (GdObject));
449 object.element = object.fill_element = gd_get_element_from_string (elem0);
451 return get_memcpy(&object, sizeof (GdObject));
458 if (strcasecmp(name, "Raster") == 0)
462 if (sscanf(param, "%d %d %d %d %d %d %s", &object.x1, &object.y1, &nx, &ny, &object.dx, &object.dy, elem0) == 7)
466 object.x2 = object.x1 + nx * object.dx;
467 object.y2 = object.y1 + ny * object.dy;
468 object.type = GD_RASTER;
469 object.element = gd_get_element_from_string (elem0);
471 return get_memcpy(&object, sizeof (GdObject));
478 if (strcasecmp(name, "Join") == 0 ||
479 strcasecmp(name, "Add") == 0)
481 if (sscanf(param, "%d %d %s %s", &object.dx, &object.dy, elem0, elem1) == 4)
483 object.type = GD_JOIN;
484 object.element = gd_get_element_from_string (elem0);
485 object.fill_element = gd_get_element_from_string (elem1);
487 return get_memcpy(&object, sizeof (GdObject));
493 // FILL TO BORDER OBJECT
494 if (strcasecmp(name, "BoundaryFill") == 0)
496 if (sscanf(param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1) == 4)
498 object.type = GD_FLOODFILL_BORDER;
499 object.fill_element = gd_get_element_from_string (elem0);
500 object.element = gd_get_element_from_string (elem1);
502 return get_memcpy(&object, sizeof (GdObject));
508 // REPLACE FILL OBJECT
509 if (strcasecmp(name, "FloodFill") == 0)
511 if (sscanf(param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1) == 4)
513 object.type = GD_FLOODFILL_REPLACE;
514 object.fill_element = gd_get_element_from_string (elem0);
515 object.element = gd_get_element_from_string (elem1);
517 return get_memcpy(&object, sizeof (GdObject));
524 // MAZE UNICURSAL OBJECT
526 if (strcasecmp(name, "Maze") == 0)
528 char type[100] = "perfect";
530 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)
532 if (strcasecmp(type, "unicursal") == 0)
533 object.type = GD_MAZE_UNICURSAL;
534 else if (strcasecmp(type, "perfect") == 0)
535 object.type = GD_MAZE;
536 else if (strcasecmp(type, "braid") == 0)
537 object.type = GD_MAZE_BRAID;
540 Warn("unknown maze type: %s, defaulting to perfect", type);
541 object.type = GD_MAZE;
544 object.element = gd_get_element_from_string (elem0);
545 object.fill_element = gd_get_element_from_string (elem1);
547 return get_memcpy(&object, sizeof (GdObject));
553 // RANDOM FILL OBJECT
554 if (strcasecmp(name, "RandomFill") == 0 ||
555 strcasecmp(name, "RandomFillC64") == 0)
557 static char **words = NULL;
560 object.type = GD_RANDOM_FILL;
561 if (strcasecmp(name, "RandomFillC64") == 0)
562 // totally the same, but uses c64 random generator
563 object.c64_random = TRUE;
565 object.c64_random = FALSE;
567 if (sscanf(param, "%d %d %d %d", &object.x1, &object.y1, &object.x2, &object.y2) != 4)
571 freeStringArray(words);
573 words = getSplitStringArray(param, " ", -1);
574 l = getStringArrayLength(words);
576 if (l < 10 || l > 19)
579 for (i = 0; i < 5; i++)
580 if (sscanf(words[4 + i], "%d", &object.seed[i]) != 1)
583 object.fill_element = gd_get_element_from_string(words[9]);
585 for (i = 0; i < 4; i++)
587 object.random_fill[i] = O_DIRT;
588 object.random_fill_probability[i] = 0;
591 for (i = 10; i < l - 1; i += 2)
593 object.random_fill[(i - 10) / 2] = gd_get_element_from_string(words[i]);
594 if (sscanf(words[i + 1], "%d", &object.random_fill_probability[(i - 10) / 2]) == 0)
598 object.element = O_NONE;
600 if (l > 10 && l % 2 == 1)
601 object.element = gd_get_element_from_string(words[l - 1]);
603 return get_memcpy(&object, sizeof (GdObject));
607 if (strcasecmp(name, "CopyPaste") == 0)
609 char mirror[100] = "nomirror";
610 char flip[100] = "noflip";
611 object.type = GD_COPY_PASTE;
613 object.flip = object.mirror = FALSE;
615 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 if (strcasecmp(mirror, "mirror") == 0)
620 object.mirror = TRUE;
621 else if (strcasecmp(mirror, "nomirror") == 0)
622 object.mirror = FALSE;
624 Warn("invalid setting for copypaste mirror property: %s", mirror);
627 if (strcasecmp(flip, "flip") == 0)
629 else if (strcasecmp(flip, "noflip") == 0)
632 Warn("invalid setting for copypaste flip property: %s", flip);
634 return get_memcpy(&object, sizeof(GdObject));
640 // drawing a line, using bresenham's
641 static void draw_line (GdCave *cave, const GdObject *object)
643 int x, y, x1, y1, x2, y2;
645 int error, dx, dy, ystep;
648 y1 = object->y1, x2 = object->x2;
650 steep = ABS (y2 - y1) > ABS (x2 - x1);
676 ystep = (y1 < y2) ? 1 : -1;
678 for (x = x1; x <= x2; x++)
681 gd_cave_store_rc (cave, y, x, object->element, object);
683 gd_cave_store_rc (cave, x, y, object->element, object);
697 static void draw_fill_replace_proc(GdCave *cave, int x, int y, const GdObject *object)
699 // fill with border so we do not come back
700 gd_cave_store_rc(cave, x, y, object->fill_element, object);
702 if (x > 0 && gd_cave_get_rc(cave, x - 1, y) == object->element)
703 draw_fill_replace_proc(cave, x - 1, y, object);
705 if (y > 0 && gd_cave_get_rc(cave, x, y - 1) == object->element)
706 draw_fill_replace_proc(cave, x, y - 1, object);
708 if (x < cave->w - 1 && gd_cave_get_rc(cave, x + 1, y) == object->element)
709 draw_fill_replace_proc(cave, x + 1, y, object);
711 if (y < cave->h - 1 && gd_cave_get_rc(cave, x, y + 1) == object->element)
712 draw_fill_replace_proc(cave, x, y + 1, object);
715 static void draw_fill_replace (GdCave *cave, const GdObject *object)
718 if (object->x1 < 0 ||
720 object->x1 >= cave->w ||
721 object->y1 >= cave->h)
724 if (object->element == object->fill_element)
727 // this procedure fills the area with the object->element.
728 draw_fill_replace_proc(cave, object->x1, object->y1, object);
731 static void draw_fill_border_proc (GdCave *cave, int x, int y, const GdObject *object)
733 // fill with border so we do not come back
734 gd_cave_store_rc(cave, x, y, object->element, object);
736 if (x > 0 && gd_cave_get_rc(cave, x - 1, y) != object->element)
737 draw_fill_border_proc(cave, x - 1, y, object);
739 if (y > 0 && gd_cave_get_rc(cave, x, y - 1) != object->element)
740 draw_fill_border_proc(cave, x, y - 1, object);
742 if (x < cave->w - 1 && gd_cave_get_rc(cave, x + 1, y) != object->element)
743 draw_fill_border_proc(cave, x + 1, y, object);
745 if (y < cave->h-1 && gd_cave_get_rc(cave, x, y + 1) != object->element)
746 draw_fill_border_proc(cave, x, y + 1, object);
749 static void draw_fill_border (GdCave *cave, const GdObject *object)
754 if (object->x1 < 0 ||
756 object->x1 >= cave->w ||
757 object->y1 >= cave->h)
760 // this procedure fills the area with the object->element.
761 draw_fill_border_proc(cave, object->x1, object->y1, object);
763 // after the fill, we change all filled cells to the fill_element.
764 // we find those by looking at the object_order[][]
765 for (y = 0; y < cave->h; y++)
766 for (x = 0; x < cave->w; x++)
767 if (cave->objects_order[y][x] == object)
768 cave->map[y][x] = object->fill_element;
771 // rectangle, frame only
772 static void draw_rectangle(GdCave *cave, const GdObject *object)
774 int x1, y1, x2, y2, x, y;
776 // reorder coordinates if not drawing from northwest to southeast
778 y1 = object->y1, x2 = object->x2;
795 for (x = x1; x <= x2; x++)
797 gd_cave_store_rc(cave, x, object->y1, object->element, object);
798 gd_cave_store_rc(cave, x, object->y2, object->element, object);
801 for (y = y1; y <= y2; y++)
803 gd_cave_store_rc(cave, object->x1, y, object->element, object);
804 gd_cave_store_rc(cave, object->x2, y, object->element, object);
808 // rectangle, filled one
809 static void draw_filled_rectangle(GdCave *cave, const GdObject *object)
811 int x1, y1, x2, y2, x, y;
813 // reorder coordinates if not drawing from northwest to southeast
815 y1 = object->y1, x2 = object->x2;
832 for (y = y1; y <= y2; y++)
833 for (x = x1; x <= x2; x++)
834 gd_cave_store_rc(cave, x, y, (y == object->y1 ||
837 x == object->x2) ? object->element : object->fill_element, object);
840 // something like ordered fill, increment is dx and dy.
841 static void draw_raster(GdCave *cave, const GdObject *object)
843 int x, y, x1, y1, x2, y2;
846 // reorder coordinates if not drawing from northwest to southeast
876 for (y = y1; y <= y2; y += dy)
877 for (x = x1; x <= x2; x += dx)
878 gd_cave_store_rc(cave, x, y, object->element, object);
881 // find every object, and put fill_element next to it. relative coordinates dx,dy
882 static void draw_join(GdCave *cave, const GdObject *object)
886 for (y = 0; y < cave->h; y++)
888 for (x = 0; x < cave->w; x++)
890 if (cave->map[y][x] == object->element)
892 int nx = x + object->dx;
893 int ny = y + object->dy;
894 // this one implements wraparound for joins.
895 // it is needed by many caves in profi boulder series
896 while (nx >= cave->w)
899 gd_cave_store_rc(cave, nx, ny, object->fill_element, object);
905 // create a maze in a boolean **maze.
906 // recursive algorithm.
907 static void mazegen(GdRand *rand, boolean **maze, int width, int height, int x, int y, int horiz)
917 dir = gd_rand_int_range(rand, 0, 100) < horiz ? 2 : 0;
919 // if no horizontal movement possible, choose vertical
920 if (dir == 2 && (dirmask & 12) == 0)
922 else if (dir == 0 && (dirmask & 3) == 0) // and vice versa
925 dir += gd_rand_int_range(rand, 0, 2); // dir
926 if (dirmask & (1 << dir))
928 dirmask &= ~(1 << dir);
933 if (y >= 2 && !maze[y - 2][x])
935 maze[y - 1][x] = TRUE;
936 mazegen(rand, maze, width, height, x, y - 2, horiz);
941 if (y < height-2 && !maze[y + 2][x]) {
942 maze[y + 1][x] = TRUE;
943 mazegen(rand, maze, width, height, x, y + 2, horiz);
948 if (x >= 2 && !maze[y][x - 2]) {
949 maze[y][x - 1] = TRUE;
950 mazegen(rand, maze, width, height, x - 2, y, horiz);
955 if (x < width - 2 && !maze[y][x + 2]) {
956 maze[y][x + 1] = TRUE;
957 mazegen(rand, maze, width, height, x + 2, y, horiz);
968 static void braidmaze(GdRand *rand, boolean **maze, int w, int h)
972 for (y = 0; y < h; y += 2)
974 for (x = 0; x < w; x += 2)
976 int closed = 0, dirs = 0;
979 // if it is the edge of the map, OR no path carved, then we can't go in that direction.
980 if (x < 1 || !maze[y][x - 1])
982 // closed from this side.
985 // if not the edge, we might open this wall (carve a path) to remove a dead end
987 closed_dirs[dirs++] = GD_MV_LEFT;
990 // other 3 directions similar
991 if (y < 1 || !maze[y - 1][x])
995 closed_dirs[dirs++] = GD_MV_UP;
998 if (x >= w - 1 || !maze[y][x + 1])
1002 closed_dirs[dirs++] = GD_MV_RIGHT;
1005 if (y >= h - 1 || !maze[y + 1][x]) {
1008 closed_dirs[dirs++] = GD_MV_DOWN;
1011 // if closed from 3 sides, then it is a dead end. also check dirs != 0,
1012 // that might fail for a 1x1 maze :)
1013 if (closed == 3 && dirs != 0)
1015 // make up a random direction, and open in that direction, so dead end is removed
1016 int dir = closed_dirs[gd_rand_int_range(rand, 0, dirs)];
1021 maze[y][x - 1] = TRUE; break;
1023 maze[y - 1][x] = TRUE; break;
1025 maze[y][x + 1] = TRUE; break;
1027 maze[y + 1][x] = TRUE; break;
1034 static void draw_maze(GdCave *cave, const GdObject *object, int level)
1038 int x1 = object->x1;
1039 int y1 = object->y1;
1040 int x2 = object->x2;
1041 int y2 = object->y2;
1042 int w, h, path, wall;
1047 // 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
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
1448 // start from row 1 (0 skipped), and fill also the borders on left and right hand side,
1449 // as c64 did. this way works the original random generator the right way.
1450 // also, do not fill last row, that is needed for the random seeds to be correct
1451 // after filling! predictable slime will use it.
1452 for (y = 1; y < cave->h - 1; y++)
1454 for (x = 0; x < cave->w; x++)
1458 if (data->level_rand[level] < 0)
1459 // use the much better glib random generator
1460 randm = gd_rand_int_range(cave->random, 0, 256);
1463 randm = gd_cave_c64_random(cave);
1465 element = data->initial_fill;
1466 if (randm < data->random_fill_probability[0])
1467 element = data->random_fill[0];
1468 if (randm < data->random_fill_probability[1])
1469 element = data->random_fill[1];
1470 if (randm < data->random_fill_probability[2])
1471 element = data->random_fill[2];
1472 if (randm < data->random_fill_probability[3])
1473 element = data->random_fill[3];
1475 gd_cave_store_rc(cave, x, y, element, NULL);
1479 // draw initial border
1480 for (y = 0; y < cave->h; y++)
1482 gd_cave_store_rc(cave, 0, y, cave->initial_border, NULL);
1483 gd_cave_store_rc(cave, cave->w - 1, y, cave->initial_border, NULL);
1486 for (x = 0; x < cave->w; x++)
1488 gd_cave_store_rc(cave, x, 0, cave->initial_border, NULL);
1489 gd_cave_store_rc(cave, x, cave->h - 1, cave->initial_border, NULL);
1492 // store if random number generator needs correction for static random seed
1493 cave->slime_correct_random = (data->level_rand[level] >= 0);
1497 // IF CAVE HAS A MAP, SIMPLY USE IT... no need to fill with random elements
1499 // initialize c64 predictable random for slime.
1500 // the values were taken from afl bd, see docs/internals.txt
1501 gd_cave_c64_random_set_seed(cave, 0, 0x1e);
1503 // correct random number generator if cave was rendered with static random seed
1504 if (cave->slime_correct_random)
1508 // set static random seed used when rendering the cave
1509 gd_cave_c64_random_set_seed(cave, 0, data->level_rand[level]);
1511 for (i = 0; i < cave->w * (cave->h - 2); i++)
1512 gd_cave_c64_random(cave);
1516 if (data->level_slime_seed_c64[level] != -1)
1518 // if a specific slime seed is requested, change it now.
1520 gd_cave_c64_random_set_seed(cave,
1521 data->level_slime_seed_c64[level] / 256,
1522 data->level_slime_seed_c64[level] % 256);
1525 // render cave objects above random data or map
1526 for (iter = data->objects; iter; iter = list_next(iter))
1528 GdObject *object = (GdObject *)iter->data;
1530 if (object->levels & gd_levels_mask[level])
1531 gd_cave_draw_object(cave, iter->data, level);
1534 // check if we use c64 ckdelay or milliseconds for timing
1535 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
1536 cave->speed = data->level_speed[level]; // exact timing
1539 // delay loop based timing... set something for first iteration,
1540 // then later it will be calculated
1543 // this one may be used by iterate routine to calculate actual delay
1544 // if c64scheduling is selected
1545 cave->c64_timing = data->level_ckdelay[level];
1548 gd_cave_correct_visible_size(cave);
1554 render cave at specified level.
1555 copy result to the map; remove objects.
1556 the cave will be map-based.
1558 void gd_flatten_cave(GdCave *cave, const int level)
1565 // render cave at specified level to obtain map. seed = 0
1566 rendered = gd_cave_new_rendered(cave, level, 0);
1568 // forget old map without objects
1569 gd_cave_map_free(cave->map);
1571 // copy new map to cave
1572 cave->map = gd_cave_map_dup(rendered, map);
1573 gd_cave_free(rendered);
1576 list_foreach(cave->objects, (list_fn) free, NULL);
1577 cave->objects = NULL;