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 /* create an INDIVIDUAL POINT CAVE OBJECT */
30 GdObject *gd_object_new_point(GdObjectLevels levels, int x, int y, GdElement elem)
32 GdObject *newobj = checked_calloc(sizeof(GdObject));
34 newobj->levels = levels;
35 newobj->type = GD_POINT;
38 newobj->element = elem;
43 /* create a LINE OBJECT */
44 GdObject *gd_object_new_line(GdObjectLevels levels, int x1, int y1, int x2, int y2,
47 GdObject *newobj = checked_calloc(sizeof(GdObject));
49 newobj->levels = levels;
50 newobj->type = GD_LINE;
55 newobj->element = elem;
60 /* create a RECTANGLE OBJECT */
61 GdObject *gd_object_new_rectangle(GdObjectLevels levels, int x1, int y1, int x2, int y2,
64 GdObject *newobj = checked_calloc(sizeof(GdObject));
66 newobj->levels = levels;
67 newobj->type = GD_RECTANGLE;
72 newobj->element = elem;
77 /* create a RECTANGLE OBJECT */
78 GdObject *gd_object_new_filled_rectangle(GdObjectLevels levels, int x1, int y1, int x2, int y2,
79 GdElement elem, GdElement fill_elem)
81 GdObject *newobj = checked_calloc(sizeof(GdObject));
83 newobj->levels = levels;
84 newobj->type = GD_FILLED_RECTANGLE;
89 newobj->element = elem;
90 newobj->fill_element = fill_elem;
95 /* create a raster object */
96 GdObject *gd_object_new_raster(GdObjectLevels levels, int x1, int y1, int x2, int y2,
97 int dx, int dy, GdElement elem)
99 GdObject *newobj = checked_calloc(sizeof(GdObject));
101 newobj->levels = levels;
102 newobj->type = GD_RASTER;
109 newobj->element = elem;
114 /* create a raster object */
115 GdObject *gd_object_new_join(GdObjectLevels levels, int dx, int dy,
116 GdElement search, GdElement replace)
118 GdObject *newobj = checked_calloc(sizeof(GdObject));
120 newobj->levels = levels;
121 newobj->type = GD_JOIN;
124 newobj->element = search;
125 newobj->fill_element = replace;
130 /* create a new boundary fill object */
131 GdObject *gd_object_new_floodfill_border(GdObjectLevels levels, int x1, int y1,
132 GdElement fill, GdElement border)
134 GdObject *newobj = checked_calloc(sizeof(GdObject));
136 newobj->levels = levels;
137 newobj->type = GD_FLOODFILL_BORDER;
140 newobj->element = border;
141 newobj->fill_element = fill;
146 GdObject *gd_object_new_floodfill_replace(GdObjectLevels levels, int x1, int y1,
147 GdElement fill, GdElement to_replace)
149 GdObject *newobj = checked_calloc(sizeof(GdObject));
151 newobj->levels = levels;
152 newobj->type = GD_FLOODFILL_REPLACE;
155 newobj->element = to_replace;
156 newobj->fill_element = fill;
161 GdObject *gd_object_new_maze(GdObjectLevels levels, int x1, int y1, int x2, int y2,
162 int wall_w, int path_w, GdElement wall_e, GdElement path_e,
163 int horiz_percent, const int seed[5])
166 GdObject *newobj = checked_calloc(sizeof(GdObject));
168 newobj->levels = levels;
169 newobj->type = GD_MAZE;
176 newobj->element = wall_e;
177 newobj->fill_element = path_e;
178 newobj->horiz = horiz_percent;
180 for (i = 0; i < 5; ++i)
181 newobj->seed[i] = seed[i];
186 GdObject *gd_object_new_maze_unicursal(GdObjectLevels levels, int x1, int y1, int x2, int y2,
187 int wall_w, int path_w, GdElement wall_e, GdElement path_e,
188 int horiz_percent, const int seed[5])
191 GdObject *newobj = checked_calloc(sizeof(GdObject));
193 newobj->levels = levels;
194 newobj->type = GD_MAZE_UNICURSAL;
201 newobj->element = wall_e;
202 newobj->fill_element = path_e;
203 newobj->horiz = horiz_percent;
205 for (i = 0; i < 5; ++i)
206 newobj->seed[i] = seed[i];
211 GdObject *gd_object_new_maze_braid(GdObjectLevels levels, int x1, int y1, int x2, int y2,
212 int wall_w, int path_w, GdElement wall_e, GdElement path_e,
213 int horiz_percent, const int seed[5])
216 GdObject *newobj = checked_calloc(sizeof(GdObject));
218 newobj->levels = levels;
219 newobj->type = GD_MAZE_BRAID;
226 newobj->element = wall_e;
227 newobj->fill_element = path_e;
228 newobj->horiz = horiz_percent;
230 for (i = 0; i < 5; ++i)
231 newobj->seed[i] = seed[i];
236 GdObject *gd_object_new_random_fill(GdObjectLevels levels, int x1, int y1, int x2, int y2,
237 const int seed[5], GdElement initial,
238 const GdElement random[4], const int prob[4],
239 GdElement replace_only, boolean c64)
242 GdObject *newobj = checked_calloc(sizeof(GdObject));
244 newobj->levels = levels;
245 newobj->type = GD_RANDOM_FILL;
250 newobj->fill_element = initial;
252 for (i = 0; i < 5; ++i)
253 newobj->seed[i] = seed[i];
255 for (i = 0; i < 4; ++i)
257 newobj->random_fill[i] = random[i];
258 newobj->random_fill_probability[i] = prob[i];
261 newobj->element = replace_only;
262 newobj->c64_random = c64;
267 GdObject *gd_object_new_copy_paste(GdObjectLevels levels, int x1, int y1, int x2, int y2,
268 int dx, int dy, boolean mirror, boolean flip)
270 GdObject *newobj = checked_calloc(sizeof(GdObject));
272 newobj->levels = levels;
273 newobj->type = GD_COPY_PASTE;
280 newobj->mirror = mirror;
286 /* create new object from bdcff description.
287 return new object if ok; return null if failed.
289 GdObject *gd_object_new_from_string(char *str)
294 char elem0[100], elem1[100];
296 equalsign = strchr(str, '=');
300 /* split string by replacing the equal sign with zero */
303 param = equalsign + 1;
305 /* INDIVIDUAL POINT CAVE OBJECT */
306 if (strcasecmp(name, "Point") == 0)
308 object.type = GD_POINT;
309 if (sscanf(param, "%d %d %s", &object.x1, &object.y1, elem0) == 3)
311 object.element = gd_get_element_from_string(elem0);
313 return get_memcpy(&object, sizeof (GdObject));
320 if (strcasecmp(name, "Line") == 0)
322 object.type = GD_LINE;
323 if (sscanf(param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0) == 5)
325 object.element = gd_get_element_from_string(elem0);
327 return get_memcpy(&object, sizeof (GdObject));
333 /* RECTANGLE OBJECT */
334 if (strcasecmp(name, "Rectangle") == 0)
336 if (sscanf(param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0) == 5)
338 object.type = GD_RECTANGLE;
339 object.element = gd_get_element_from_string (elem0);
341 return get_memcpy(&object, sizeof (GdObject));
347 /* FILLED RECTANGLE OBJECT */
348 if (strcasecmp(name, "FillRect") == 0)
352 paramcount = sscanf(param, "%d %d %d %d %s %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0, elem1);
353 object.type = GD_FILLED_RECTANGLE;
357 object.element = gd_get_element_from_string (elem0);
358 object.fill_element = gd_get_element_from_string (elem1);
360 return get_memcpy(&object, sizeof (GdObject));
365 object.element = object.fill_element = gd_get_element_from_string (elem0);
367 return get_memcpy(&object, sizeof (GdObject));
374 if (strcasecmp(name, "Raster") == 0)
378 if (sscanf(param, "%d %d %d %d %d %d %s", &object.x1, &object.y1, &nx, &ny, &object.dx, &object.dy, elem0) == 7)
382 object.x2 = object.x1 + nx * object.dx;
383 object.y2 = object.y1 + ny * object.dy;
384 object.type = GD_RASTER;
385 object.element = gd_get_element_from_string (elem0);
387 return get_memcpy(&object, sizeof (GdObject));
394 if (strcasecmp(name, "Join") == 0 ||
395 strcasecmp(name, "Add") == 0)
397 if (sscanf(param, "%d %d %s %s", &object.dx, &object.dy, elem0, elem1) == 4)
399 object.type = GD_JOIN;
400 object.element = gd_get_element_from_string (elem0);
401 object.fill_element = gd_get_element_from_string (elem1);
403 return get_memcpy(&object, sizeof (GdObject));
409 /* FILL TO BORDER OBJECT */
410 if (strcasecmp(name, "BoundaryFill") == 0)
412 if (sscanf(param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1) == 4)
414 object.type = GD_FLOODFILL_BORDER;
415 object.fill_element = gd_get_element_from_string (elem0);
416 object.element = gd_get_element_from_string (elem1);
418 return get_memcpy(&object, sizeof (GdObject));
424 /* REPLACE FILL OBJECT */
425 if (strcasecmp(name, "FloodFill") == 0)
427 if (sscanf(param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1) == 4)
429 object.type = GD_FLOODFILL_REPLACE;
430 object.fill_element = gd_get_element_from_string (elem0);
431 object.element = gd_get_element_from_string (elem1);
433 return get_memcpy(&object, sizeof (GdObject));
440 /* MAZE UNICURSAL OBJECT */
441 /* BRAID MAZE OBJECT */
442 if (strcasecmp(name, "Maze") == 0)
444 char type[100] = "perfect";
446 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)
448 if (strcasecmp(type, "unicursal") == 0)
449 object.type = GD_MAZE_UNICURSAL;
450 else if (strcasecmp(type, "perfect") == 0)
451 object.type = GD_MAZE;
452 else if (strcasecmp(type, "braid") == 0)
453 object.type = GD_MAZE_BRAID;
456 Warn("unknown maze type: %s, defaulting to perfect", type);
457 object.type = GD_MAZE;
460 object.element = gd_get_element_from_string (elem0);
461 object.fill_element = gd_get_element_from_string (elem1);
463 return get_memcpy(&object, sizeof (GdObject));
469 /* RANDOM FILL OBJECT */
470 if (strcasecmp(name, "RandomFill") == 0 ||
471 strcasecmp(name, "RandomFillC64") == 0)
473 static char **words = NULL;
476 object.type = GD_RANDOM_FILL;
477 if (strcasecmp(name, "RandomFillC64") == 0)
478 /* totally the same, but uses c64 random generator */
479 object.c64_random = TRUE;
481 object.c64_random = FALSE;
483 if (sscanf(param, "%d %d %d %d", &object.x1, &object.y1, &object.x2, &object.y2) != 4)
487 freeStringArray(words);
489 words = getSplitStringArray(param, " ", -1);
490 l = getStringArrayLength(words);
492 if (l < 10 || l > 19)
495 for (i = 0; i < 5; i++)
496 if (sscanf(words[4 + i], "%d", &object.seed[i]) != 1)
499 object.fill_element = gd_get_element_from_string(words[9]);
501 for (i = 0; i < 4; i++)
503 object.random_fill[i] = O_DIRT;
504 object.random_fill_probability[i] = 0;
507 for (i = 10; i < l - 1; i += 2)
509 object.random_fill[(i - 10) / 2] = gd_get_element_from_string(words[i]);
510 if (sscanf(words[i + 1], "%d", &object.random_fill_probability[(i - 10) / 2]) == 0)
514 object.element = O_NONE;
516 if (l > 10 && l % 2 == 1)
517 object.element = gd_get_element_from_string(words[l - 1]);
519 return get_memcpy(&object, sizeof (GdObject));
522 /* COPY PASTE OBJECT */
523 if (strcasecmp(name, "CopyPaste") == 0)
525 char mirror[100] = "nomirror";
526 char flip[100] = "noflip";
527 object.type = GD_COPY_PASTE;
529 object.flip = object.mirror = FALSE;
531 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)
534 /* MIRROR PROPERTY */
535 if (strcasecmp(mirror, "mirror") == 0)
536 object.mirror = TRUE;
537 else if (strcasecmp(mirror, "nomirror") == 0)
538 object.mirror = FALSE;
540 Warn("invalid setting for copypaste mirror property: %s", mirror);
543 if (strcasecmp(flip, "flip") == 0)
545 else if (strcasecmp(flip, "noflip") == 0)
548 Warn("invalid setting for copypaste flip property: %s", flip);
550 return get_memcpy(&object, sizeof(GdObject));
556 /** drawing a line, using bresenham's */
557 static void draw_line (GdCave *cave, const GdObject *object)
559 int x, y, x1, y1, x2, y2;
561 int error, dx, dy, ystep;
564 y1 = object->y1, x2 = object->x2;
566 steep = ABS (y2 - y1) > ABS (x2 - x1);
592 ystep = (y1 < y2) ? 1 : -1;
594 for (x = x1; x <= x2; x++)
597 gd_cave_store_rc (cave, y, x, object->element, object);
599 gd_cave_store_rc (cave, x, y, object->element, object);
613 static void draw_fill_replace_proc(GdCave *cave, int x, int y, const GdObject *object)
615 /* fill with border so we do not come back */
616 gd_cave_store_rc(cave, x, y, object->fill_element, object);
618 if (x > 0 && gd_cave_get_rc(cave, x - 1, y) == object->element)
619 draw_fill_replace_proc(cave, x - 1, y, object);
621 if (y > 0 && gd_cave_get_rc(cave, x, y - 1) == object->element)
622 draw_fill_replace_proc(cave, x, y - 1, object);
624 if (x < cave->w - 1 && gd_cave_get_rc(cave, x + 1, y) == object->element)
625 draw_fill_replace_proc(cave, x + 1, y, object);
627 if (y < cave->h - 1 && gd_cave_get_rc(cave, x, y + 1) == object->element)
628 draw_fill_replace_proc(cave, x, y + 1, object);
631 static void draw_fill_replace (GdCave *cave, const GdObject *object)
634 if (object->x1 < 0 ||
636 object->x1 >= cave->w ||
637 object->y1 >= cave->h)
640 if (object->element == object->fill_element)
643 /* this procedure fills the area with the object->element. */
644 draw_fill_replace_proc(cave, object->x1, object->y1, object);
647 static void draw_fill_border_proc (GdCave *cave, int x, int y, const GdObject *object)
649 /* fill with border so we do not come back */
650 gd_cave_store_rc(cave, x, y, object->element, object);
652 if (x > 0 && gd_cave_get_rc(cave, x - 1, y) != object->element)
653 draw_fill_border_proc(cave, x - 1, y, object);
655 if (y > 0 && gd_cave_get_rc(cave, x, y - 1) != object->element)
656 draw_fill_border_proc(cave, x, y - 1, object);
658 if (x < cave->w - 1 && gd_cave_get_rc(cave, x + 1, y) != object->element)
659 draw_fill_border_proc(cave, x + 1, y, object);
661 if (y < cave->h-1 && gd_cave_get_rc(cave, x, y + 1) != object->element)
662 draw_fill_border_proc(cave, x, y + 1, object);
665 static void draw_fill_border (GdCave *cave, const GdObject *object)
670 if (object->x1 < 0 ||
672 object->x1 >= cave->w ||
673 object->y1 >= cave->h)
676 /* this procedure fills the area with the object->element. */
677 draw_fill_border_proc(cave, object->x1, object->y1, object);
679 /* after the fill, we change all filled cells to the fill_element. */
680 /* we find those by looking at the object_order[][] */
681 for (y = 0; y < cave->h; y++)
682 for (x = 0; x < cave->w; x++)
683 if (cave->objects_order[y][x] == object)
684 cave->map[y][x] = object->fill_element;
687 /* rectangle, frame only */
688 static void draw_rectangle(GdCave *cave, const GdObject *object)
690 int x1, y1, x2, y2, x, y;
692 /* reorder coordinates if not drawing from northwest to southeast */
694 y1 = object->y1, x2 = object->x2;
711 for (x = x1; x <= x2; x++)
713 gd_cave_store_rc(cave, x, object->y1, object->element, object);
714 gd_cave_store_rc(cave, x, object->y2, object->element, object);
717 for (y = y1; y <= y2; y++)
719 gd_cave_store_rc(cave, object->x1, y, object->element, object);
720 gd_cave_store_rc(cave, object->x2, y, object->element, object);
724 /* rectangle, filled one */
725 static void draw_filled_rectangle(GdCave *cave, const GdObject *object)
727 int x1, y1, x2, y2, x, y;
729 /* reorder coordinates if not drawing from northwest to southeast */
731 y1 = object->y1, x2 = object->x2;
748 for (y = y1; y <= y2; y++)
749 for (x = x1; x <= x2; x++)
750 gd_cave_store_rc(cave, x, y, (y == object->y1 ||
753 x == object->x2) ? object->element : object->fill_element, object);
756 /* something like ordered fill, increment is dx and dy. */
757 static void draw_raster(GdCave *cave, const GdObject *object)
759 int x, y, x1, y1, x2, y2;
762 /* reorder coordinates if not drawing from northwest to southeast */
792 for (y = y1; y <= y2; y += dy)
793 for (x = x1; x <= x2; x += dx)
794 gd_cave_store_rc(cave, x, y, object->element, object);
797 /* find every object, and put fill_element next to it. relative coordinates dx,dy */
798 static void draw_join(GdCave *cave, const GdObject *object)
802 for (y = 0; y < cave->h; y++)
804 for (x = 0; x < cave->w; x++)
806 if (cave->map[y][x] == object->element)
808 int nx = x + object->dx;
809 int ny = y + object->dy;
810 /* this one implements wraparound for joins.
811 it is needed by many caves in profi boulder series */
812 while (nx >= cave->w)
815 gd_cave_store_rc(cave, nx, ny, object->fill_element, object);
821 /* create a maze in a boolean **maze. */
822 /* recursive algorithm. */
823 static void mazegen(GdRand *rand, boolean **maze, int width, int height, int x, int y, int horiz)
833 dir = gd_rand_int_range(rand, 0, 100) < horiz ? 2 : 0;
835 /* if no horizontal movement possible, choose vertical */
836 if (dir == 2 && (dirmask & 12) == 0)
838 else if (dir == 0 && (dirmask & 3) == 0) /* and vice versa */
841 dir += gd_rand_int_range(rand, 0, 2); /* dir */
842 if (dirmask & (1 << dir))
844 dirmask &= ~(1 << dir);
849 if (y >= 2 && !maze[y - 2][x])
851 maze[y - 1][x] = TRUE;
852 mazegen(rand, maze, width, height, x, y - 2, horiz);
857 if (y < height-2 && !maze[y + 2][x]) {
858 maze[y + 1][x] = TRUE;
859 mazegen(rand, maze, width, height, x, y + 2, horiz);
864 if (x >= 2 && !maze[y][x - 2]) {
865 maze[y][x - 1] = TRUE;
866 mazegen(rand, maze, width, height, x - 2, y, horiz);
871 if (x < width - 2 && !maze[y][x + 2]) {
872 maze[y][x + 1] = TRUE;
873 mazegen(rand, maze, width, height, x + 2, y, horiz);
884 static void braidmaze(GdRand *rand, boolean **maze, int w, int h)
888 for (y = 0; y < h; y += 2)
890 for (x = 0; x < w; x += 2)
892 int closed = 0, dirs = 0;
895 /* if it is the edge of the map, OR no path carved, then we can't go in that direction. */
896 if (x < 1 || !maze[y][x - 1])
898 /* closed from this side. */
901 /* if not the edge, we might open this wall (carve a path) to remove a dead end */
903 closed_dirs[dirs++] = GD_MV_LEFT;
906 /* other 3 directions similar */
907 if (y < 1 || !maze[y - 1][x])
911 closed_dirs[dirs++] = GD_MV_UP;
914 if (x >= w - 1 || !maze[y][x + 1])
918 closed_dirs[dirs++] = GD_MV_RIGHT;
921 if (y >= h - 1 || !maze[y + 1][x]) {
924 closed_dirs[dirs++] = GD_MV_DOWN;
927 /* if closed from 3 sides, then it is a dead end. also check dirs != 0,
928 that might fail for a 1x1 maze :) */
929 if (closed == 3 && dirs != 0)
931 /* make up a random direction, and open in that direction, so dead end is removed */
932 int dir = closed_dirs[gd_rand_int_range(rand, 0, dirs)];
937 maze[y][x - 1] = TRUE; break;
939 maze[y - 1][x] = TRUE; break;
941 maze[y][x + 1] = TRUE; break;
943 maze[y + 1][x] = TRUE; break;
950 static void draw_maze(GdCave *cave, const GdObject *object, int level)
958 int w, h, path, wall;
963 /* change coordinates if not in correct order */
986 /* calculate the width and height of the maze.
987 n = number of passages, path = path width, wall = wall width, maze = maze width.
988 if given the number of passages, the width of the maze is:
990 n * path + (n - 1) * wall = maze
991 n * path + n * wall - wall = maze
992 n * (path + wall) = maze + wall
993 n = (maze + wall) / (path + wall)
996 /* number of passages for each side */
997 w = (x2 - x1 + 1 + wall) / (path + wall);
998 h = (y2 - y1 + 1 + wall) / (path + wall);
1000 /* and we calculate the size of the internal map */
1001 if (object->type == GD_MAZE_UNICURSAL)
1003 /* for unicursal maze, width and height must be mod2 = 0,
1004 and we will convert to paths & walls later */
1010 /* for normal maze */
1011 w = 2 * (w - 1) + 1;
1012 h = 2 * (h - 1) + 1;
1015 /* twodimensional boolean array to generate map in */
1016 map = checked_malloc((h) * sizeof(boolean *));
1017 for (y = 0; y < h; y++)
1018 map[y] = checked_calloc(w * sizeof(boolean));
1020 /* start generation, if map is big enough.
1021 otherwise the application would crash, as the editor places maze objects
1022 during mouse click & drag that have no sense */
1023 rand = gd_rand_new_with_seed(object->seed[level] == -1 ?
1024 gd_rand_int(cave->random) : object->seed[level]);
1026 if (w >= 1 && h >= 1)
1027 mazegen(rand, map, w, h, 0, 0, object->horiz);
1029 if (object->type == GD_MAZE_BRAID)
1030 braidmaze(rand, map, w, h);
1034 if (w >= 1 && h >= 1 && object->type == GD_MAZE_UNICURSAL)
1036 boolean **unicursal;
1038 /* convert to unicursal maze */
1054 unicursal = checked_malloc((h * 2 - 1) * sizeof(boolean *));
1056 for (y = 0; y < h * 2 - 1; y++)
1057 unicursal[y] = checked_calloc((w * 2 - 1) * sizeof(boolean));
1059 for (y = 0; y < h; y++)
1061 for(x = 0; x < w; x++)
1064 unicursal[y * 2][x * 2] = TRUE;
1065 unicursal[y * 2][x * 2 + 2] = TRUE;
1066 unicursal[y * 2 + 2][x * 2] = TRUE;
1067 unicursal[y * 2 + 2][x * 2 + 2] = TRUE;
1069 if (x < 1 || !map[y][x - 1]) unicursal[y * 2 + 1][x * 2] = TRUE;
1070 if (y < 1 || !map[y - 1][x]) unicursal[y * 2][x * 2 + 1] = TRUE;
1071 if (x >= w - 1 || !map[y][x + 1]) unicursal[y * 2 + 1][x * 2 + 2] = TRUE;
1072 if (y >= h - 1 || !map[y + 1][x]) unicursal[y * 2 + 2][x * 2 + 1] = TRUE;
1077 /* free original map */
1078 for (y = 0; y < h; y++)
1082 /* change to new map - the unicursal maze */
1088 /* copy map to cave with correct elements and size */
1089 /* now copy the map into the cave. the copying works like this...
1096 columns and rows denoted with "p" are to be drawn with path width,
1097 the others with wall width. */
1101 for (y = 0; y < h; y++)
1103 for (i = 0; i < (y % 2 == 0 ? path : wall); i++)
1107 for (x = 0; x < w; x++)
1108 for (j = 0; j < (x % 2 == 0 ? path : wall); j++)
1109 gd_cave_store_rc(cave, xk++, yk, map[y][x] ? object->fill_element : object->element, object);
1111 /* if width is smaller than requested, fill with wall */
1112 for(x = xk; x <= x2; x++)
1113 gd_cave_store_rc(cave, x, yk, object->element, object);
1119 /* if height is smaller than requested, fill with wall */
1120 for (y = yk; y <= y2; y++)
1121 for (x = x1; x <= x2; x++)
1122 gd_cave_store_rc(cave, x, y, object->element, object);
1125 for (y = 0; y < h; y++)
1130 static void draw_random_fill(GdCave *cave, const GdObject *object, int level)
1133 int x1 = object->x1;
1134 int y1 = object->y1;
1135 int x2 = object->x2;
1136 int y2 = object->y2;
1138 GdC64RandomGenerator c64_rand;
1141 /* -1 means that it should be different every time played. */
1142 if (object->seed[level] == -1)
1143 seed = gd_rand_int(cave->random);
1145 seed = object->seed[level];
1147 rand = gd_rand_new_with_seed(seed);
1148 /* for c64 random, use the 2*8 lsb. */
1149 gd_c64_random_set_seed(&c64_rand, seed / 256 % 256, seed % 256);
1151 /* change coordinates if not in correct order */
1166 for (y = y1; y <= y2; y++)
1168 for (x = x1; x <= x2; x++)
1173 if (object->c64_random)
1174 /* use c64 random generator */
1175 randm = gd_c64_random(&c64_rand);
1177 /* use the much better glib random generator */
1178 randm = gd_rand_int_range(rand, 0, 256);
1180 element = object->fill_element;
1181 if (randm < object->random_fill_probability[0])
1182 element = object->random_fill[0];
1183 if (randm < object->random_fill_probability[1])
1184 element = object->random_fill[1];
1185 if (randm < object->random_fill_probability[2])
1186 element = object->random_fill[2];
1187 if (randm < object->random_fill_probability[3])
1188 element = object->random_fill[3];
1190 if (object->element == O_NONE ||
1191 gd_cave_get_rc(cave, x, y) == object->element)
1192 gd_cave_store_rc(cave, x, y, element, object);
1200 static void draw_copy_paste(GdCave *cave, const GdObject *object)
1202 int x1 = object->x1, y1 = object->y1, x2 = object->x2, y2 = object->y2;
1203 int x, y; /* iterators */
1205 GdElement *clipboard;
1207 /* reorder coordinates if not drawing from northwest to southeast */
1225 clipboard = checked_malloc((w * h) * sizeof(GdElement));
1227 /* copy to "clipboard" */
1228 for (y = 0; y < h; y++)
1229 for (x = 0; x < w; x++)
1230 clipboard[y * w + x] = gd_cave_get_rc(cave, x + x1, y + y1);
1232 for (y = 0; y < h; y++)
1236 ydest = object->flip ? h - 1 - y : y;
1238 for (x = 0; x < w; x++)
1242 xdest = object->mirror ? w - 1 - x : x;
1244 /* dx and dy are used here are "paste to" coordinates */
1245 gd_cave_store_rc(cave, object->dx + xdest, object->dy + ydest,
1246 clipboard[y * w + x], object);
1253 /* draw the specified game object into cave's data.
1254 also remember, which cell was set by which cave object. */
1255 void gd_cave_draw_object(GdCave *cave, const GdObject *object, int level)
1257 switch (object->type)
1261 gd_cave_store_rc(cave, object->x1, object->y1, object->element, object);
1265 draw_line(cave, object);
1269 draw_rectangle(cave, object);
1272 case GD_FILLED_RECTANGLE:
1273 draw_filled_rectangle(cave, object);
1277 draw_raster(cave, object);
1281 draw_join(cave, object);
1284 case GD_FLOODFILL_BORDER:
1285 draw_fill_border(cave, object);
1288 case GD_FLOODFILL_REPLACE:
1289 draw_fill_replace(cave, object);
1293 case GD_MAZE_UNICURSAL:
1295 draw_maze(cave, object, level);
1298 case GD_RANDOM_FILL:
1299 draw_random_fill(cave, object, level);
1303 draw_copy_paste(cave, object);
1310 Error("Unknown object %d", object->type);
1315 /* load cave to play... also can be called rendering the cave elements */
1316 GdCave *gd_cave_new_rendered(const GdCave *data, const int level, const unsigned int seed)
1324 cave = gd_cave_new_from_cave(data);
1325 cave->rendered = level + 1;
1327 cave->render_seed = seed;
1328 cave->random = gd_rand_new_with_seed(cave->render_seed);
1330 /* maps needed during drawing and gameplay */
1331 cave->objects_order = gd_cave_map_new(cave, void *);
1333 cave->time = data->level_time[level];
1334 cave->timevalue = data->level_timevalue[level];
1335 cave->diamonds_needed = data->level_diamonds[level];
1336 cave->magic_wall_time = data->level_magic_wall_time[level];
1337 cave->slime_permeability = data->level_slime_permeability[level];
1338 cave->slime_permeability_c64 = data->level_slime_permeability_c64[level];
1339 cave->time_bonus = data->level_bonus_time[level];
1340 cave->time_penalty = data->level_penalty_time[level];
1341 cave->amoeba_time = data->level_amoeba_time[level];
1342 cave->amoeba_max_count = data->level_amoeba_threshold[level];
1343 cave->amoeba_2_time = data->level_amoeba_2_time[level];
1344 cave->amoeba_2_max_count = data->level_amoeba_2_threshold[level];
1345 cave->hatching_delay_time = data->level_hatching_delay_time[level];
1346 cave->hatching_delay_frame = data->level_hatching_delay_frame[level];
1350 /* if we have no map, fill with predictable random generator. */
1351 cave->map = gd_cave_map_new(cave, GdElement);
1353 /* IF CAVE HAS NO MAP, USE THE RANDOM NUMBER GENERATOR */
1354 /* init c64 randomgenerator */
1355 if (data->level_rand[level] < 0)
1356 gd_cave_c64_random_set_seed(cave, gd_rand_int_range(cave->random, 0, 256),
1357 gd_rand_int_range(cave->random, 0, 256));
1359 gd_cave_c64_random_set_seed(cave, 0, data->level_rand[level]);
1361 /* generate random fill
1362 * start from row 1 (0 skipped), and fill also the borders on left and right hand side,
1363 * as c64 did. this way works the original random generator the right way.
1364 * also, do not fill last row, that is needed for the random seeds to be correct
1365 * after filling! predictable slime will use it. */
1366 for (y = 1; y < cave->h - 1; y++)
1368 for (x = 0; x < cave->w; x++)
1372 if (data->level_rand[level] < 0)
1373 /* use the much better glib random generator */
1374 randm = gd_rand_int_range(cave->random, 0, 256);
1377 randm = gd_cave_c64_random(cave);
1379 element = data->initial_fill;
1380 if (randm < data->random_fill_probability[0])
1381 element = data->random_fill[0];
1382 if (randm < data->random_fill_probability[1])
1383 element = data->random_fill[1];
1384 if (randm < data->random_fill_probability[2])
1385 element = data->random_fill[2];
1386 if (randm < data->random_fill_probability[3])
1387 element = data->random_fill[3];
1389 gd_cave_store_rc(cave, x, y, element, NULL);
1393 /* draw initial border */
1394 for (y = 0; y < cave->h; y++)
1396 gd_cave_store_rc(cave, 0, y, cave->initial_border, NULL);
1397 gd_cave_store_rc(cave, cave->w - 1, y, cave->initial_border, NULL);
1400 for (x = 0; x < cave->w; x++)
1402 gd_cave_store_rc(cave, x, 0, cave->initial_border, NULL);
1403 gd_cave_store_rc(cave, x, cave->h - 1, cave->initial_border, NULL);
1408 /* IF CAVE HAS A MAP, SIMPLY USE IT... no need to fill with random elements */
1410 /* initialize c64 predictable random for slime.
1411 the values were taken from afl bd, see docs/internals.txt */
1412 gd_cave_c64_random_set_seed(cave, 0, 0x1e);
1415 if (data->level_slime_seed_c64[level] != -1)
1417 /* if a specific slime seed is requested, change it now. */
1419 gd_cave_c64_random_set_seed(cave,
1420 data->level_slime_seed_c64[level] / 256,
1421 data->level_slime_seed_c64[level] % 256);
1424 /* render cave objects above random data or map */
1425 for (iter = data->objects; iter; iter = list_next(iter))
1427 GdObject *object = (GdObject *)iter->data;
1429 if (object->levels & gd_levels_mask[level])
1430 gd_cave_draw_object(cave, iter->data, level);
1433 /* check if we use c64 ckdelay or milliseconds for timing */
1434 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
1435 cave->speed = data->level_speed[level]; /* exact timing */
1438 /* delay loop based timing... set something for first iteration,
1439 then later it will be calculated */
1442 /* this one may be used by iterate routine to calculate actual delay
1443 if c64scheduling is selected */
1444 cave->c64_timing = data->level_ckdelay[level];
1447 gd_cave_correct_visible_size(cave);
1453 render cave at specified level.
1454 copy result to the map; remove objects.
1455 the cave will be map-based.
1457 void gd_flatten_cave(GdCave *cave, const int level)
1464 /* render cave at specified level to obtain map. seed = 0 */
1465 rendered = gd_cave_new_rendered(cave, level, 0);
1467 /* forget old map without objects */
1468 gd_cave_map_free(cave->map);
1470 /* copy new map to cave */
1471 cave->map = gd_cave_map_dup(rendered, map);
1472 gd_cave_free(rendered);
1474 /* forget objects */
1475 list_foreach(cave->objects, (list_fn) free, NULL);
1476 cave->objects = NULL;