2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <glib/gi18n.h>
23 GdObjectLevels gd_levels_mask[] =
32 /* create an INDIVIDUAL POINT CAVE OBJECT */
33 GdObject *gd_object_new_point(GdObjectLevels levels, int x, int y, GdElement elem)
35 GdObject *newobj = checked_calloc(sizeof(GdObject));
37 newobj->levels = levels;
38 newobj->type = GD_POINT;
41 newobj->element = elem;
46 /* create a LINE OBJECT */
47 GdObject *gd_object_new_line(GdObjectLevels levels, int x1, int y1, int x2, int y2,
50 GdObject *newobj = checked_calloc(sizeof(GdObject));
52 newobj->levels = levels;
53 newobj->type = GD_LINE;
58 newobj->element = elem;
63 /* create a RECTANGLE OBJECT */
64 GdObject *gd_object_new_rectangle(GdObjectLevels levels, int x1, int y1, int x2, int y2,
67 GdObject *newobj = checked_calloc(sizeof(GdObject));
69 newobj->levels = levels;
70 newobj->type = GD_RECTANGLE;
75 newobj->element = elem;
80 /* create a RECTANGLE OBJECT */
81 GdObject *gd_object_new_filled_rectangle(GdObjectLevels levels, int x1, int y1, int x2, int y2,
82 GdElement elem, GdElement fill_elem)
84 GdObject *newobj = checked_calloc(sizeof(GdObject));
86 newobj->levels = levels;
87 newobj->type = GD_FILLED_RECTANGLE;
92 newobj->element = elem;
93 newobj->fill_element = fill_elem;
98 /* create a raster object */
99 GdObject *gd_object_new_raster(GdObjectLevels levels, int x1, int y1, int x2, int y2,
100 int dx, int dy, GdElement elem)
102 GdObject *newobj = checked_calloc(sizeof(GdObject));
104 newobj->levels = levels;
105 newobj->type = GD_RASTER;
112 newobj->element = elem;
117 /* create a raster object */
118 GdObject *gd_object_new_join(GdObjectLevels levels, int dx, int dy,
119 GdElement search, GdElement replace)
121 GdObject *newobj = checked_calloc(sizeof(GdObject));
123 newobj->levels = levels;
124 newobj->type = GD_JOIN;
127 newobj->element = search;
128 newobj->fill_element = replace;
133 /* create a new boundary fill object */
134 GdObject *gd_object_new_floodfill_border(GdObjectLevels levels, int x1, int y1,
135 GdElement fill, GdElement border)
137 GdObject *newobj = checked_calloc(sizeof(GdObject));
139 newobj->levels = levels;
140 newobj->type = GD_FLOODFILL_BORDER;
143 newobj->element = border;
144 newobj->fill_element = fill;
149 GdObject *gd_object_new_floodfill_replace(GdObjectLevels levels, int x1, int y1,
150 GdElement fill, GdElement to_replace)
152 GdObject *newobj = checked_calloc(sizeof(GdObject));
154 newobj->levels = levels;
155 newobj->type = GD_FLOODFILL_REPLACE;
158 newobj->element = to_replace;
159 newobj->fill_element = fill;
164 GdObject *gd_object_new_maze(GdObjectLevels levels, int x1, int y1, int x2, int y2,
165 int wall_w, int path_w, GdElement wall_e, GdElement path_e,
166 int horiz_percent, const gint32 seed[5])
169 GdObject *newobj = checked_calloc(sizeof(GdObject));
171 newobj->levels = levels;
172 newobj->type = GD_MAZE;
179 newobj->element = wall_e;
180 newobj->fill_element = path_e;
181 newobj->horiz = horiz_percent;
183 for (i = 0; i < 5; ++i)
184 newobj->seed[i] = seed[i];
189 GdObject *gd_object_new_maze_unicursal(GdObjectLevels levels, int x1, int y1, int x2, int y2,
190 int wall_w, int path_w, GdElement wall_e, GdElement path_e,
191 int horiz_percent, const gint32 seed[5])
194 GdObject *newobj = checked_calloc(sizeof(GdObject));
196 newobj->levels = levels;
197 newobj->type = GD_MAZE_UNICURSAL;
204 newobj->element = wall_e;
205 newobj->fill_element = path_e;
206 newobj->horiz = horiz_percent;
208 for (i = 0; i < 5; ++i)
209 newobj->seed[i] = seed[i];
214 GdObject *gd_object_new_maze_braid(GdObjectLevels levels, int x1, int y1, int x2, int y2,
215 int wall_w, int path_w, GdElement wall_e, GdElement path_e,
216 int horiz_percent, const gint32 seed[5])
219 GdObject *newobj = checked_calloc(sizeof(GdObject));
221 newobj->levels = levels;
222 newobj->type = GD_MAZE_BRAID;
229 newobj->element = wall_e;
230 newobj->fill_element = path_e;
231 newobj->horiz = horiz_percent;
233 for (i = 0; i < 5; ++i)
234 newobj->seed[i] = seed[i];
239 GdObject *gd_object_new_random_fill(GdObjectLevels levels, int x1, int y1, int x2, int y2,
240 const gint32 seed[5], GdElement initial,
241 const GdElement random[4], const gint32 prob[4],
242 GdElement replace_only, boolean c64)
245 GdObject *newobj = checked_calloc(sizeof(GdObject));
247 newobj->levels = levels;
248 newobj->type = GD_RANDOM_FILL;
253 newobj->fill_element = initial;
255 for (i = 0; i < 5; ++i)
256 newobj->seed[i] = seed[i];
258 for (i = 0; i < 4; ++i)
260 newobj->random_fill[i] = random[i];
261 newobj->random_fill_probability[i] = prob[i];
264 newobj->element = replace_only;
265 newobj->c64_random = c64;
270 GdObject *gd_object_new_copy_paste(GdObjectLevels levels, int x1, int y1, int x2, int y2,
271 int dx, int dy, boolean mirror, boolean flip)
273 GdObject *newobj = checked_calloc(sizeof(GdObject));
275 newobj->levels = levels;
276 newobj->type = GD_COPY_PASTE;
283 newobj->mirror = mirror;
289 /* create new object from bdcff description.
290 return new object if ok; return null if failed.
292 GdObject *gd_object_new_from_string(char *str)
297 char elem0[100], elem1[100];
299 equalsign = strchr(str, '=');
303 /* split string by replacing the equal sign with zero */
306 param = equalsign + 1;
308 /* INDIVIDUAL POINT CAVE OBJECT */
309 if (strcasecmp(name, "Point") == 0)
311 object.type = GD_POINT;
312 if (sscanf(param, "%d %d %s", &object.x1, &object.y1, elem0) == 3)
314 object.element = gd_get_element_from_string(elem0);
316 return g_memdup(&object, sizeof (GdObject));
323 if (strcasecmp(name, "Line") == 0)
325 object.type = GD_LINE;
326 if (sscanf(param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0) == 5)
328 object.element = gd_get_element_from_string(elem0);
330 return g_memdup(&object, sizeof (GdObject));
336 /* RECTANGLE OBJECT */
337 if (strcasecmp(name, "Rectangle") == 0)
339 if (sscanf(param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0) == 5)
341 object.type = GD_RECTANGLE;
342 object.element = gd_get_element_from_string (elem0);
344 return g_memdup(&object, sizeof (GdObject));
350 /* FILLED RECTANGLE OBJECT */
351 if (strcasecmp(name, "FillRect") == 0)
355 paramcount = sscanf(param, "%d %d %d %d %s %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0, elem1);
356 object.type = GD_FILLED_RECTANGLE;
360 object.element = gd_get_element_from_string (elem0);
361 object.fill_element = gd_get_element_from_string (elem1);
363 return g_memdup(&object, sizeof (GdObject));
368 object.element = object.fill_element = gd_get_element_from_string (elem0);
370 return g_memdup(&object, sizeof (GdObject));
377 if (strcasecmp(name, "Raster") == 0)
381 if (sscanf(param, "%d %d %d %d %d %d %s", &object.x1, &object.y1, &nx, &ny, &object.dx, &object.dy, elem0) == 7)
385 object.x2 = object.x1 + nx * object.dx;
386 object.y2 = object.y1 + ny * object.dy;
387 object.type = GD_RASTER;
388 object.element = gd_get_element_from_string (elem0);
390 return g_memdup(&object, sizeof (GdObject));
397 if (strcasecmp(name, "Join") == 0 ||
398 strcasecmp(name, "Add") == 0)
400 if (sscanf(param, "%d %d %s %s", &object.dx, &object.dy, elem0, elem1) == 4)
402 object.type = GD_JOIN;
403 object.element = gd_get_element_from_string (elem0);
404 object.fill_element = gd_get_element_from_string (elem1);
406 return g_memdup(&object, sizeof (GdObject));
412 /* FILL TO BORDER OBJECT */
413 if (strcasecmp(name, "BoundaryFill") == 0)
415 if (sscanf(param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1) == 4)
417 object.type = GD_FLOODFILL_BORDER;
418 object.fill_element = gd_get_element_from_string (elem0);
419 object.element = gd_get_element_from_string (elem1);
421 return g_memdup(&object, sizeof (GdObject));
427 /* REPLACE FILL OBJECT */
428 if (strcasecmp(name, "FloodFill") == 0)
430 if (sscanf(param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1) == 4)
432 object.type = GD_FLOODFILL_REPLACE;
433 object.fill_element = gd_get_element_from_string (elem0);
434 object.element = gd_get_element_from_string (elem1);
436 return g_memdup(&object, sizeof (GdObject));
443 /* MAZE UNICURSAL OBJECT */
444 /* BRAID MAZE OBJECT */
445 if (strcasecmp(name, "Maze") == 0)
447 char type[100] = "perfect";
449 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)
451 if (strcasecmp(type, "unicursal") == 0)
452 object.type = GD_MAZE_UNICURSAL;
453 else if (strcasecmp(type, "perfect") == 0)
454 object.type = GD_MAZE;
455 else if (strcasecmp(type, "braid") == 0)
456 object.type = GD_MAZE_BRAID;
459 Warn("unknown maze type: %s, defaulting to perfect", type);
460 object.type = GD_MAZE;
463 object.element = gd_get_element_from_string (elem0);
464 object.fill_element = gd_get_element_from_string (elem1);
466 return g_memdup(&object, sizeof (GdObject));
472 /* RANDOM FILL OBJECT */
473 if (strcasecmp(name, "RandomFill") == 0 ||
474 strcasecmp(name, "RandomFillC64") == 0)
476 static char **words = NULL;
479 object.type = GD_RANDOM_FILL;
480 if (strcasecmp(name, "RandomFillC64") == 0)
481 /* totally the same, but uses c64 random generator */
482 object.c64_random = TRUE;
484 object.c64_random = FALSE;
486 if (sscanf(param, "%d %d %d %d", &object.x1, &object.y1, &object.x2, &object.y2) != 4)
490 freeStringArray(words);
492 words = getSplitStringArray(param, " ", -1);
493 l = getStringArrayLength(words);
495 if (l < 10 || l > 19)
498 for (i = 0; i < 5; i++)
499 if (sscanf(words[4 + i], "%d", &object.seed[i]) != 1)
502 object.fill_element = gd_get_element_from_string(words[9]);
504 for (i = 0; i < 4; i++)
506 object.random_fill[i] = O_DIRT;
507 object.random_fill_probability[i] = 0;
510 for (i = 10; i < l - 1; i += 2)
512 object.random_fill[(i - 10) / 2] = gd_get_element_from_string(words[i]);
513 if (sscanf(words[i + 1], "%d", &object.random_fill_probability[(i - 10) / 2]) == 0)
517 object.element = O_NONE;
519 if (l > 10 && l % 2 == 1)
520 object.element = gd_get_element_from_string(words[l - 1]);
522 return g_memdup(&object, sizeof (GdObject));
525 /* COPY PASTE OBJECT */
526 if (strcasecmp(name, "CopyPaste") == 0)
528 char mirror[100] = "nomirror";
529 char flip[100] = "noflip";
530 object.type = GD_COPY_PASTE;
532 object.flip = object.mirror = FALSE;
534 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)
537 /* MIRROR PROPERTY */
538 if (strcasecmp(mirror, "mirror") == 0)
539 object.mirror = TRUE;
540 else if (strcasecmp(mirror, "nomirror") == 0)
541 object.mirror = FALSE;
543 Warn("invalid setting for copypaste mirror property: %s", mirror);
546 if (strcasecmp(flip, "flip") == 0)
548 else if (strcasecmp(flip, "noflip") == 0)
551 Warn("invalid setting for copypaste flip property: %s", flip);
553 return g_memdup(&object, sizeof(GdObject));
559 /** drawing a line, using bresenham's */
560 static void draw_line (GdCave *cave, const GdObject *object)
562 int x, y, x1, y1, x2, y2;
564 int error, dx, dy, ystep;
567 y1 = object->y1, x2 = object->x2;
569 steep = ABS (y2 - y1) > ABS (x2 - x1);
595 ystep = (y1 < y2) ? 1 : -1;
597 for (x = x1; x <= x2; x++)
600 gd_cave_store_rc (cave, y, x, object->element, object);
602 gd_cave_store_rc (cave, x, y, object->element, object);
616 static void draw_fill_replace_proc(GdCave *cave, int x, int y, const GdObject *object)
618 /* fill with border so we do not come back */
619 gd_cave_store_rc(cave, x, y, object->fill_element, object);
621 if (x > 0 && gd_cave_get_rc(cave, x - 1, y) == object->element)
622 draw_fill_replace_proc(cave, x - 1, y, object);
624 if (y > 0 && gd_cave_get_rc(cave, x, y - 1) == object->element)
625 draw_fill_replace_proc(cave, x, y - 1, object);
627 if (x < cave->w - 1 && gd_cave_get_rc(cave, x + 1, y) == object->element)
628 draw_fill_replace_proc(cave, x + 1, y, object);
630 if (y < cave->h - 1 && gd_cave_get_rc(cave, x, y + 1) == object->element)
631 draw_fill_replace_proc(cave, x, y + 1, object);
634 static void draw_fill_replace (GdCave *cave, const GdObject *object)
637 if (object->x1 < 0 ||
639 object->x1 >= cave->w ||
640 object->y1 >= cave->h)
643 if (object->element == object->fill_element)
646 /* this procedure fills the area with the object->element. */
647 draw_fill_replace_proc(cave, object->x1, object->y1, object);
650 static void draw_fill_border_proc (GdCave *cave, int x, int y, const GdObject *object)
652 /* fill with border so we do not come back */
653 gd_cave_store_rc(cave, x, y, object->element, object);
655 if (x > 0 && gd_cave_get_rc(cave, x - 1, y) != object->element)
656 draw_fill_border_proc(cave, x - 1, y, object);
658 if (y > 0 && gd_cave_get_rc(cave, x, y - 1) != object->element)
659 draw_fill_border_proc(cave, x, y - 1, object);
661 if (x < cave->w - 1 && gd_cave_get_rc(cave, x + 1, y) != object->element)
662 draw_fill_border_proc(cave, x + 1, y, object);
664 if (y < cave->h-1 && gd_cave_get_rc(cave, x, y + 1) != object->element)
665 draw_fill_border_proc(cave, x, y + 1, object);
668 static void draw_fill_border (GdCave *cave, const GdObject *object)
673 if (object->x1 < 0 ||
675 object->x1 >= cave->w ||
676 object->y1 >= cave->h)
679 /* this procedure fills the area with the object->element. */
680 draw_fill_border_proc(cave, object->x1, object->y1, object);
682 /* after the fill, we change all filled cells to the fill_element. */
683 /* we find those by looking at the object_order[][] */
684 for (y = 0; y < cave->h; y++)
685 for (x = 0; x < cave->w; x++)
686 if (cave->objects_order[y][x] == object)
687 cave->map[y][x] = object->fill_element;
690 /* rectangle, frame only */
691 static void draw_rectangle(GdCave *cave, const GdObject *object)
693 int x1, y1, x2, y2, x, y;
695 /* reorder coordinates if not drawing from northwest to southeast */
697 y1 = object->y1, x2 = object->x2;
714 for (x = x1; x <= x2; x++)
716 gd_cave_store_rc(cave, x, object->y1, object->element, object);
717 gd_cave_store_rc(cave, x, object->y2, object->element, object);
720 for (y = y1; y <= y2; y++)
722 gd_cave_store_rc(cave, object->x1, y, object->element, object);
723 gd_cave_store_rc(cave, object->x2, y, object->element, object);
727 /* rectangle, filled one */
728 static void draw_filled_rectangle(GdCave *cave, const GdObject *object)
730 int x1, y1, x2, y2, x, y;
732 /* reorder coordinates if not drawing from northwest to southeast */
734 y1 = object->y1, x2 = object->x2;
751 for (y = y1; y <= y2; y++)
752 for (x = x1; x <= x2; x++)
753 gd_cave_store_rc(cave, x, y, (y == object->y1 ||
756 x == object->x2) ? object->element : object->fill_element, object);
759 /* something like ordered fill, increment is dx and dy. */
760 static void draw_raster(GdCave *cave, const GdObject *object)
762 int x, y, x1, y1, x2, y2;
765 /* reorder coordinates if not drawing from northwest to southeast */
795 for (y = y1; y <= y2; y += dy)
796 for (x = x1; x <= x2; x += dx)
797 gd_cave_store_rc(cave, x, y, object->element, object);
800 /* find every object, and put fill_element next to it. relative coordinates dx,dy */
801 static void draw_join(GdCave *cave, const GdObject *object)
805 for (y = 0; y < cave->h; y++)
807 for (x = 0; x < cave->w; x++)
809 if (cave->map[y][x] == object->element)
811 int nx = x + object->dx;
812 int ny = y + object->dy;
813 /* this one implements wraparound for joins.
814 it is needed by many caves in profi boulder series */
815 while (nx >= cave->w)
818 gd_cave_store_rc(cave, nx, ny, object->fill_element, object);
824 /* create a maze in a boolean **maze. */
825 /* recursive algorithm. */
826 static void mazegen(GRand *rand, boolean **maze, int width, int height, int x, int y, int horiz)
836 dir = g_rand_int_range(rand, 0, 100) <horiz ? 2 : 0;
838 /* if no horizontal movement possible, choose vertical */
839 if (dir == 2 && (dirmask & 12) == 0)
841 else if (dir == 0 && (dirmask&3) == 0) /* and vice versa */
844 dir += g_rand_int_range(rand, 0, 2); /* dir */
845 if (dirmask & (1 << dir))
847 dirmask &= ~(1 << dir);
852 if (y >= 2 && !maze[y - 2][x])
854 maze[y - 1][x] = TRUE;
855 mazegen(rand, maze, width, height, x, y - 2, horiz);
860 if (y < height-2 && !maze[y + 2][x]) {
861 maze[y + 1][x] = TRUE;
862 mazegen(rand, maze, width, height, x, y + 2, horiz);
867 if (x >= 2 && !maze[y][x - 2]) {
868 maze[y][x - 1] = TRUE;
869 mazegen(rand, maze, width, height, x - 2, y, horiz);
874 if (x < width - 2 && !maze[y][x + 2]) {
875 maze[y][x + 1] = TRUE;
876 mazegen(rand, maze, width, height, x + 2, y, horiz);
887 static void braidmaze(GRand *rand, boolean **maze, int w, int h)
891 for (y = 0; y < h; y += 2)
893 for (x = 0; x < w; x += 2)
895 int closed = 0, dirs = 0;
898 /* if it is the edge of the map, OR no path carved, then we can't go in that direction. */
899 if (x < 1 || !maze[y][x - 1])
901 /* closed from this side. */
904 /* if not the edge, we might open this wall (carve a path) to remove a dead end */
906 closed_dirs[dirs++] = GD_MV_LEFT;
909 /* other 3 directions similar */
910 if (y < 1 || !maze[y - 1][x])
914 closed_dirs[dirs++] = GD_MV_UP;
917 if (x >= w - 1 || !maze[y][x + 1])
921 closed_dirs[dirs++] = GD_MV_RIGHT;
924 if (y >= h - 1 || !maze[y + 1][x]) {
927 closed_dirs[dirs++] = GD_MV_DOWN;
930 /* if closed from 3 sides, then it is a dead end. also check dirs != 0,
931 that might fail for a 1x1 maze :) */
932 if (closed == 3 && dirs != 0)
934 /* make up a random direction, and open in that direction, so dead end is removed */
935 int dir = closed_dirs[g_rand_int_range(rand, 0, dirs)];
940 maze[y][x - 1] = TRUE; break;
942 maze[y - 1][x] = TRUE; break;
944 maze[y][x + 1] = TRUE; break;
946 maze[y + 1][x] = TRUE; break;
953 static void draw_maze(GdCave *cave, const GdObject *object, int level)
961 int w, h, path, wall;
966 /* change coordinates if not in correct order */
989 /* calculate the width and height of the maze.
990 n = number of passages, path = path width, wall = wall width, maze = maze width.
991 if given the number of passages, the width of the maze is:
993 n * path + (n - 1) * wall = maze
994 n * path + n * wall - wall = maze
995 n * (path + wall) = maze + wall
996 n = (maze + wall) / (path + wall)
999 /* number of passages for each side */
1000 w = (x2 - x1 + 1 + wall) / (path + wall);
1001 h = (y2 - y1 + 1 + wall) / (path + wall);
1003 /* and we calculate the size of the internal map */
1004 if (object->type == GD_MAZE_UNICURSAL)
1006 /* for unicursal maze, width and height must be mod2 = 0,
1007 and we will convert to paths & walls later */
1013 /* for normal maze */
1014 w = 2 * (w - 1) + 1;
1015 h = 2 * (h - 1) + 1;
1018 /* twodimensional boolean array to generate map in */
1019 map = checked_malloc((h) * sizeof(boolean *));
1020 for (y = 0; y < h; y++)
1021 map[y] = checked_calloc(w * sizeof(boolean));
1023 /* start generation, if map is big enough.
1024 otherwise the application would crash, as the editor places maze objects
1025 during mouse click & drag that have no sense */
1026 rand = g_rand_new_with_seed(object->seed[level] == -1 ?
1027 g_rand_int(cave->random) : object->seed[level]);
1029 if (w >= 1 && h >= 1)
1030 mazegen(rand, map, w, h, 0, 0, object->horiz);
1032 if (object->type == GD_MAZE_BRAID)
1033 braidmaze(rand, map, w, h);
1037 if (w >= 1 && h >= 1 && object->type == GD_MAZE_UNICURSAL)
1039 boolean **unicursal;
1041 /* convert to unicursal maze */
1057 unicursal = checked_malloc((h * 2 - 1) * sizeof(boolean *));
1059 for (y = 0; y < h * 2 - 1; y++)
1060 unicursal[y] = checked_calloc((w * 2 - 1) * sizeof(boolean));
1062 for (y = 0; y < h; y++)
1064 for(x = 0; x < w; x++)
1067 unicursal[y * 2][x * 2] = TRUE;
1068 unicursal[y * 2][x * 2 + 2] = TRUE;
1069 unicursal[y * 2 + 2][x * 2] = TRUE;
1070 unicursal[y * 2 + 2][x * 2 + 2] = TRUE;
1072 if (x < 1 || !map[y][x - 1]) unicursal[y * 2 + 1][x * 2] = TRUE;
1073 if (y < 1 || !map[y - 1][x]) unicursal[y * 2][x * 2 + 1] = TRUE;
1074 if (x >= w - 1 || !map[y][x + 1]) unicursal[y * 2 + 1][x * 2 + 2] = TRUE;
1075 if (y >= h - 1 || !map[y + 1][x]) unicursal[y * 2 + 2][x * 2 + 1] = TRUE;
1080 /* free original map */
1081 for (y = 0; y < h; y++)
1085 /* change to new map - the unicursal maze */
1091 /* copy map to cave with correct elements and size */
1092 /* now copy the map into the cave. the copying works like this...
1099 columns and rows denoted with "p" are to be drawn with path width,
1100 the others with wall width. */
1104 for (y = 0; y < h; y++)
1106 for (i = 0; i < (y % 2 == 0 ? path : wall); i++)
1110 for (x = 0; x < w; x++)
1111 for (j = 0; j < (x % 2 == 0 ? path : wall); j++)
1112 gd_cave_store_rc(cave, xk++, yk, map[y][x] ? object->fill_element : object->element, object);
1114 /* if width is smaller than requested, fill with wall */
1115 for(x = xk; x <= x2; x++)
1116 gd_cave_store_rc(cave, x, yk, object->element, object);
1122 /* if height is smaller than requested, fill with wall */
1123 for (y = yk; y <= y2; y++)
1124 for (x = x1; x <= x2; x++)
1125 gd_cave_store_rc(cave, x, y, object->element, object);
1128 for (y = 0; y < h; y++)
1133 static void draw_random_fill(GdCave *cave, const GdObject *object, int level)
1136 int x1 = object->x1;
1137 int y1 = object->y1;
1138 int x2 = object->x2;
1139 int y2 = object->y2;
1141 GdC64RandomGenerator c64_rand;
1144 /* -1 means that it should be different every time played. */
1145 if (object->seed[level] == -1)
1146 seed = g_rand_int(cave->random);
1148 seed = object->seed[level];
1150 rand = g_rand_new_with_seed(seed);
1151 /* for c64 random, use the 2*8 lsb. */
1152 gd_c64_random_set_seed(&c64_rand, seed / 256 % 256, seed % 256);
1154 /* change coordinates if not in correct order */
1169 for (y = y1; y <= y2; y++)
1171 for (x = x1; x <= x2; x++)
1176 if (object->c64_random)
1177 /* use c64 random generator */
1178 randm = gd_c64_random(&c64_rand);
1180 /* use the much better glib random generator */
1181 randm = g_rand_int_range(rand, 0, 256);
1183 element = object->fill_element;
1184 if (randm < object->random_fill_probability[0])
1185 element = object->random_fill[0];
1186 if (randm < object->random_fill_probability[1])
1187 element = object->random_fill[1];
1188 if (randm < object->random_fill_probability[2])
1189 element = object->random_fill[2];
1190 if (randm < object->random_fill_probability[3])
1191 element = object->random_fill[3];
1193 if (object->element == O_NONE ||
1194 gd_cave_get_rc(cave, x, y) == object->element)
1195 gd_cave_store_rc(cave, x, y, element, object);
1203 static void draw_copy_paste(GdCave *cave, const GdObject *object)
1205 int x1 = object->x1, y1 = object->y1, x2 = object->x2, y2 = object->y2;
1206 int x, y; /* iterators */
1208 GdElement *clipboard;
1210 /* reorder coordinates if not drawing from northwest to southeast */
1228 clipboard = checked_malloc((w * h) * sizeof(GdElement));
1230 /* copy to "clipboard" */
1231 for (y = 0; y < h; y++)
1232 for (x = 0; x < w; x++)
1233 clipboard[y * w + x] = gd_cave_get_rc(cave, x + x1, y + y1);
1235 for (y = 0; y < h; y++)
1239 ydest = object->flip ? h - 1 - y : y;
1241 for (x = 0; x < w; x++)
1245 xdest = object->mirror ? w - 1 - x : x;
1247 /* dx and dy are used here are "paste to" coordinates */
1248 gd_cave_store_rc(cave, object->dx + xdest, object->dy + ydest,
1249 clipboard[y * w + x], object);
1256 /* draw the specified game object into cave's data.
1257 also remember, which cell was set by which cave object. */
1258 void gd_cave_draw_object(GdCave *cave, const GdObject *object, int level)
1260 switch (object->type)
1264 gd_cave_store_rc(cave, object->x1, object->y1, object->element, object);
1268 draw_line(cave, object);
1272 draw_rectangle(cave, object);
1275 case GD_FILLED_RECTANGLE:
1276 draw_filled_rectangle(cave, object);
1280 draw_raster(cave, object);
1284 draw_join(cave, object);
1287 case GD_FLOODFILL_BORDER:
1288 draw_fill_border(cave, object);
1291 case GD_FLOODFILL_REPLACE:
1292 draw_fill_replace(cave, object);
1296 case GD_MAZE_UNICURSAL:
1298 draw_maze(cave, object, level);
1301 case GD_RANDOM_FILL:
1302 draw_random_fill(cave, object, level);
1306 draw_copy_paste(cave, object);
1313 Error("Unknown object %d", object->type);
1318 /* load cave to play... also can be called rendering the cave elements */
1319 GdCave *gd_cave_new_rendered(const GdCave *data, const int level, const guint32 seed)
1327 cave = gd_cave_new_from_cave(data);
1328 cave->rendered = level + 1;
1330 cave->render_seed = seed;
1331 cave->random = g_rand_new_with_seed(cave->render_seed);
1333 /* maps needed during drawing and gameplay */
1334 cave->objects_order = gd_cave_map_new(cave, gpointer);
1336 cave->time = data->level_time[level];
1337 cave->timevalue = data->level_timevalue[level];
1338 cave->diamonds_needed = data->level_diamonds[level];
1339 cave->magic_wall_time = data->level_magic_wall_time[level];
1340 cave->slime_permeability = data->level_slime_permeability[level];
1341 cave->slime_permeability_c64 = data->level_slime_permeability_c64[level];
1342 cave->time_bonus = data->level_bonus_time[level];
1343 cave->time_penalty = data->level_penalty_time[level];
1344 cave->amoeba_time = data->level_amoeba_time[level];
1345 cave->amoeba_max_count = data->level_amoeba_threshold[level];
1346 cave->amoeba_2_time = data->level_amoeba_2_time[level];
1347 cave->amoeba_2_max_count = data->level_amoeba_2_threshold[level];
1348 cave->hatching_delay_time = data->level_hatching_delay_time[level];
1349 cave->hatching_delay_frame = data->level_hatching_delay_frame[level];
1353 /* if we have no map, fill with predictable random generator. */
1354 cave->map = gd_cave_map_new(cave, GdElement);
1356 /* IF CAVE HAS NO MAP, USE THE RANDOM NUMBER GENERATOR */
1357 /* init c64 randomgenerator */
1358 if (data->level_rand[level] < 0)
1359 gd_cave_c64_random_set_seed(cave, g_rand_int_range(cave->random, 0, 256),
1360 g_rand_int_range(cave->random, 0, 256));
1362 gd_cave_c64_random_set_seed(cave, 0, data->level_rand[level]);
1364 /* generate random fill
1365 * start from row 1 (0 skipped), and fill also the borders on left and right hand side,
1366 * as c64 did. this way works the original random generator the right way.
1367 * also, do not fill last row, that is needed for the random seeds to be correct
1368 * after filling! predictable slime will use it. */
1369 for (y = 1; y < cave->h - 1; y++)
1371 for (x = 0; x < cave->w; x++)
1375 if (data->level_rand[level] < 0)
1376 /* use the much better glib random generator */
1377 randm = g_rand_int_range(cave->random, 0, 256);
1380 randm = gd_cave_c64_random(cave);
1382 element = data->initial_fill;
1383 if (randm < data->random_fill_probability[0])
1384 element = data->random_fill[0];
1385 if (randm < data->random_fill_probability[1])
1386 element = data->random_fill[1];
1387 if (randm < data->random_fill_probability[2])
1388 element = data->random_fill[2];
1389 if (randm < data->random_fill_probability[3])
1390 element = data->random_fill[3];
1392 gd_cave_store_rc(cave, x, y, element, NULL);
1396 /* draw initial border */
1397 for (y = 0; y < cave->h; y++)
1399 gd_cave_store_rc(cave, 0, y, cave->initial_border, NULL);
1400 gd_cave_store_rc(cave, cave->w - 1, y, cave->initial_border, NULL);
1403 for (x = 0; x < cave->w; x++)
1405 gd_cave_store_rc(cave, x, 0, cave->initial_border, NULL);
1406 gd_cave_store_rc(cave, x, cave->h - 1, cave->initial_border, NULL);
1411 /* IF CAVE HAS A MAP, SIMPLY USE IT... no need to fill with random elements */
1413 /* initialize c64 predictable random for slime.
1414 the values were taken from afl bd, see docs/internals.txt */
1415 gd_cave_c64_random_set_seed(cave, 0, 0x1e);
1418 if (data->level_slime_seed_c64[level] != -1)
1420 /* if a specific slime seed is requested, change it now. */
1422 gd_cave_c64_random_set_seed(cave,
1423 data->level_slime_seed_c64[level] / 256,
1424 data->level_slime_seed_c64[level] % 256);
1427 /* render cave objects above random data or map */
1428 for (iter = data->objects; iter; iter = g_list_next(iter))
1430 GdObject *object = (GdObject *)iter->data;
1432 if (object->levels & gd_levels_mask[level])
1433 gd_cave_draw_object(cave, iter->data, level);
1436 /* check if we use c64 ckdelay or milliseconds for timing */
1437 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
1438 cave->speed = data->level_speed[level]; /* exact timing */
1441 /* delay loop based timing... set something for first iteration,
1442 then later it will be calculated */
1445 /* this one may be used by iterate routine to calculate actual delay
1446 if c64scheduling is selected */
1447 cave->c64_timing = data->level_ckdelay[level];
1450 gd_cave_correct_visible_size(cave);
1456 render cave at specified level.
1457 copy result to the map; remove objects.
1458 the cave will be map-based.
1460 void gd_flatten_cave(GdCave *cave, const int level)
1467 /* render cave at specified level to obtain map. seed = 0 */
1468 rendered = gd_cave_new_rendered(cave, level, 0);
1470 /* forget old map without objects */
1471 gd_cave_map_free(cave->map);
1473 /* copy new map to cave */
1474 cave->map = gd_cave_map_dup(rendered, map);
1475 gd_cave_free(rendered);
1477 /* forget objects */
1478 g_list_foreach(cave->objects, (GFunc) free, NULL);
1479 cave->objects = NULL;