20081227
[gdash.git] / src / caveobject.c
blob74460ab5fa53475aab728a6a43ff2a1eb70c2a78
1 /*
2 * Copyright (c) 2007, 2008 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.
16 #include <glib.h>
17 #include <glib/gi18n.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include "cave.h"
21 #include "cavedb.h"
22 #include "caveobject.h"
26 /* description of coordinates, elements - used by editor properties dialog. */
27 GdObjectDescription gd_object_description[]={
28 { /* none */ NULL },
29 { /* point */ N_("Point"), N_("Coordinates"), NULL, NULL, NULL, N_("Element"), NULL, N_("Draw"), NULL },
30 { /* line */ N_("Line"), N_("Starting coordinates"), N_("Ending coordinates"), NULL, NULL, N_("Element"), NULL, N_("Draw"), NULL },
31 { /* rect */ N_("Outline"), N_("Starting coordinates"), N_("Ending coordinates"), NULL, NULL, N_("Element"), NULL, N_("Draw"), NULL },
32 { /* frect */ N_("Rectangle"), N_("Starting coordinates"), N_("Ending coordinates"), NULL, NULL, N_("Border element"), N_("Fill element"), N_("Draw"), N_("Fill") },
33 { /* rastr */ N_("Raster"), N_("Starting coordinates"), N_("Ending coordinates"), N_("Distance"), NULL, N_("Element"), NULL, N_("Draw"), NULL },
34 { /* join */ N_("Join"), NULL, NULL, N_("Distance"), NULL, N_("Search element"), N_("Add element"), N_("Find"), N_("Draw") },
35 { /* flodr */ N_("Replace fill"), N_("Starting coordinates"), NULL, NULL, NULL, N_("Search element"), N_("Fill element"), NULL, N_("Replace") },
36 { /* flodb */ N_("Fill to border"), N_("Starting coordinates"), NULL, NULL, NULL, N_("Border element"), N_("Fill element"), N_("Border"), N_("Fill") },
37 { /* maze */ N_("Maze"), N_("Starting coordinates"), N_("Ending coordinates"), N_("Wall and path"), N_("Random seed %d"), N_("Wall element"), N_("Path element"), N_("Wall"), N_("Path") , N_("Horizontal (%%)")},
38 { /* umaze */ N_("Unicursal maze"), N_("Starting coordinates"), N_("Ending coordinates"), N_("Wall and path"), N_("Random seed %d"), N_("Wall element"), N_("Path element"), N_("Wall"), N_("Path") , N_("Horizontal (%%)")},
39 { /* Bmaze */ N_("Braid maze"), N_("Starting coordinates"), N_("Ending coordinates"), N_("Wall and path"), N_("Random seed %d"), N_("Wall element"), N_("Path element"), N_("Wall"), N_("Path") , N_("Horizontal (%%)")},
40 { /* random */N_("Random fill"), N_("Starting coordinates"), N_("Ending coordinates"), NULL, N_("Random seed %d"), N_("Replace only this element"), NULL, N_("Random 1"), N_("Initial"), NULL, N_("C64 random numbers")},
41 { /* copyps */N_("Copy and paste"), N_("Starting coordinates"), N_("Width and height"), N_("Paste coordinates"), NULL, NULL, NULL, NULL, NULL, NULL, NULL, N_("Mirror"), N_("Flip")},
45 GdObjectLevels gd_levels_mask[]={GD_OBJECT_LEVEL1, GD_OBJECT_LEVEL2, GD_OBJECT_LEVEL3, GD_OBJECT_LEVEL4, GD_OBJECT_LEVEL5};
50 /* bdcff text description of object. caller should free string. */
51 char *
52 gd_object_to_bdcff(const GdObject *object)
54 GString *str;
55 int j;
56 const char *type;
58 switch (object->type) {
59 case POINT:
60 return g_strdup_printf("Point=%d %d %s", object->x1, object->y1, gd_elements[object->element].filename);
62 case LINE:
63 return g_strdup_printf("Line=%d %d %d %d %s", object->x1, object->y1, object->x2, object->y2, gd_elements[object->element].filename);
65 case RECTANGLE:
66 return g_strdup_printf("Rectangle=%d %d %d %d %s", object->x1, object->y1, object->x2, object->y2, gd_elements[object->element].filename);
68 case FILLED_RECTANGLE:
69 /* if elements are not the same */
70 if (object->fill_element!=object->element)
71 return g_strdup_printf("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);
72 /* they are the same */
73 return g_strdup_printf("FillRect=%d %d %d %d %s", object->x1, object->y1, object->x2, object->y2, gd_elements[object->element].filename);
75 case RASTER:
76 return g_strdup_printf("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);
78 case JOIN:
79 return g_strdup_printf("Add=%d %d %s %s", object->dx, object->dy, gd_elements[object->element].filename, gd_elements[object->fill_element].filename);
81 case FLOODFILL_BORDER:
82 return g_strdup_printf("BoundaryFill=%d %d %s %s", object->x1, object->y1, gd_elements[object->fill_element].filename, gd_elements[object->element].filename);
84 case FLOODFILL_REPLACE:
85 return g_strdup_printf("FloodFill=%d %d %s %s", object->x1, object->y1, gd_elements[object->fill_element].filename, gd_elements[object->element].filename);
87 case MAZE:
88 case MAZE_UNICURSAL:
89 case MAZE_BRAID:
90 switch(object->type) {
91 case MAZE: type="perfect"; break;
92 case MAZE_UNICURSAL: type="unicursal"; break;
93 case MAZE_BRAID: type="braid"; break;
94 default:
95 g_assert_not_reached();
97 return g_strdup_printf("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);
99 case RANDOM_FILL:
100 str=g_string_new(NULL);
101 g_string_append_printf(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); /* seed and initial fill */
102 for (j=0; j<4; j++)
103 if (object->random_fill_probability[j]!=0)
104 g_string_append_printf(str, " %s %d", gd_elements[object->random_fill[j]].filename, object->random_fill_probability[j]);
105 if (object->element!=O_NONE)
106 g_string_append_printf(str, " %s", gd_elements[object->element].filename);
108 /* free string but do not free char *; return char *. */
109 return g_string_free(str, FALSE);
111 case COPY_PASTE:
112 return g_strdup_printf("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");
115 case NONE:
116 g_assert_not_reached();
119 return NULL;
122 /* create new object from bdcff description.
123 return new object if ok; return null if failed.
125 GdObject *
126 gd_object_new_from_string(char *str)
128 char *equalsign;
129 char *name, *param;
130 GdObject object;
131 char elem0[100], elem1[100];
133 equalsign=strchr(str, '=');
134 if (!equalsign)
135 return NULL;
137 /* split string by replacing the equal sign with zero */
138 *equalsign='\0';
139 name=str;
140 param=equalsign+1;
142 /* INDIVIDUAL POINT CAVE OBJECT */
143 if (g_ascii_strcasecmp(name, "Point")==0) {
144 object.type=POINT;
145 if (sscanf(param, "%d %d %s", &object.x1, &object.y1, elem0)==3) {
146 object.element=gd_get_element_from_string(elem0);
147 return g_memdup(&object, sizeof (GdObject));
149 return NULL;
152 /* LINE OBJECT */
153 if (g_ascii_strcasecmp(name, "Line")==0) {
154 object.type=LINE;
155 if (sscanf(param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0)==5) {
156 object.element=gd_get_element_from_string(elem0);
157 return g_memdup(&object, sizeof (GdObject));
159 return NULL;
162 /* RECTANGLE OBJECT */
163 if (g_ascii_strcasecmp(name, "Rectangle")==0) {
164 if (sscanf (param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0)==5) {
165 object.type=RECTANGLE;
166 object.element=gd_get_element_from_string (elem0);
167 return g_memdup(&object, sizeof (GdObject));
169 return NULL;
172 /* FILLED RECTANGLE OBJECT */
173 if (g_ascii_strcasecmp(name, "FillRect")==0) {
174 int paramcount;
176 paramcount=sscanf(param, "%d %d %d %d %s %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0, elem1);
177 object.type=FILLED_RECTANGLE;
178 if (paramcount==6) {
179 object.element=gd_get_element_from_string (elem0);
180 object.fill_element=gd_get_element_from_string (elem1);
181 return g_memdup(&object, sizeof (GdObject));
183 if (paramcount==5) {
184 object.element=object.fill_element=gd_get_element_from_string (elem0);
185 return g_memdup(&object, sizeof (GdObject));
187 return NULL;
190 /* RASTER */
191 if (g_ascii_strcasecmp(name, "Raster")==0) {
192 int nx, ny;
194 if (sscanf (param, "%d %d %d %d %d %d %s", &object.x1, &object.y1, &nx, &ny, &object.dx, &object.dy, elem0)==7) {
195 nx--; ny--;
196 object.x2=object.x1 + nx*object.dx;
197 object.y2=object.y1 + ny*object.dy;
198 object.type=RASTER;
199 object.element=gd_get_element_from_string (elem0);
200 return g_memdup(&object, sizeof (GdObject));
202 return NULL;
205 /* JOIN */
206 if (g_ascii_strcasecmp(name, "Join")==0 || g_ascii_strcasecmp(name, "Add")==0) {
207 if (sscanf (param, "%d %d %s %s", &object.dx, &object.dy, elem0, elem1)==4) {
208 object.type=JOIN;
209 object.element=gd_get_element_from_string (elem0);
210 object.fill_element=gd_get_element_from_string (elem1);
211 return g_memdup(&object, sizeof (GdObject));
213 return NULL;
216 /* FILL TO BORDER OBJECT */
217 if (g_ascii_strcasecmp(name, "BoundaryFill")==0) {
218 if (sscanf (param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1)==4) {
219 object.type=FLOODFILL_BORDER;
220 object.fill_element=gd_get_element_from_string (elem0);
221 object.element=gd_get_element_from_string (elem1);
222 return g_memdup(&object, sizeof (GdObject));
224 return NULL;
227 /* REPLACE FILL OBJECT */
228 if (g_ascii_strcasecmp(name, "FloodFill")==0) {
229 if (sscanf (param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1)==4) {
230 object.type=FLOODFILL_REPLACE;
231 object.fill_element=gd_get_element_from_string (elem0);
232 object.element=gd_get_element_from_string (elem1);
233 return g_memdup(&object, sizeof (GdObject));
235 return NULL;
238 /* MAZE OBJECT */
239 /* MAZE UNICURSAL OBJECT */
240 /* BRAID MAZE OBJECT */
241 if (g_ascii_strcasecmp(name, "Maze")==0) {
242 char type[100]="perfect";
244 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) {
245 if (g_ascii_strcasecmp(type, "unicursal")==0)
246 object.type=MAZE_UNICURSAL;
247 else if (g_ascii_strcasecmp(type, "perfect")==0)
248 object.type=MAZE;
249 else if (g_ascii_strcasecmp(type, "braid")==0)
250 object.type=MAZE_BRAID;
251 else {
252 g_warning("unknown maze type: %s, defaulting to perfect", type);
253 object.type=MAZE;
255 object.element=gd_get_element_from_string (elem0);
256 object.fill_element=gd_get_element_from_string (elem1);
257 return g_memdup(&object, sizeof (GdObject));
259 return NULL;
262 /* RANDOM FILL OBJECT */
263 if (g_ascii_strcasecmp(name, "RandomFill")==0 || g_ascii_strcasecmp(name, "RandomFillC64")==0) {
264 static char **words=NULL;
265 int l, i;
267 object.type=RANDOM_FILL;
268 if (g_ascii_strcasecmp(name, "RandomFillC64")==0) /* totally the same, but uses c64 random generator */
269 object.c64_random=TRUE;
270 else
271 object.c64_random=FALSE;
272 if (sscanf(param, "%d %d %d %d", &object.x1, &object.y1, &object.x2, &object.y2)!=4)
273 return NULL;
274 if (words)
275 g_strfreev(words);
276 words=g_strsplit_set(param, " ", -1);
277 l=g_strv_length(words);
278 if (l<10 || l>19)
279 return NULL;
280 for (i=0; i<5; i++)
281 if (sscanf(words[4+i], "%d", &object.seed[i])!=1)
282 return NULL;
283 object.fill_element=gd_get_element_from_string(words[9]);
284 for (i=0; i<4; i++) {
285 object.random_fill[i]=O_DIRT;
286 object.random_fill_probability[i]=0;
288 for (i=10; i<l-1; i+=2) {
289 object.random_fill[(i-10)/2]=gd_get_element_from_string(words[i]);
290 if (sscanf(words[i+1], "%d", &object.random_fill_probability[(i-10)/2])==0)
291 return NULL;
293 object.element=O_NONE;
294 if (l>10 && l%2==1)
295 object.element=gd_get_element_from_string(words[l-1]);
297 return g_memdup(&object, sizeof (GdObject));
300 /* COPY PASTE OBJECT */
301 if (g_ascii_strcasecmp(name, "CopyPaste")==0) {
302 char mirror[100]="nomirror";
303 char flip[100]="noflip";
304 object.type=COPY_PASTE;
306 object.flip=object.mirror=FALSE;
308 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)
309 return NULL;
310 /* MIRROR PROPERTY */
311 if (g_ascii_strcasecmp(mirror, "mirror")==0)
312 object.mirror=TRUE;
313 else
314 if (g_ascii_strcasecmp(mirror, "nomirror")==0)
315 object.mirror=FALSE;
316 else
317 g_warning("invalid setting for copypaste mirror property: %s", mirror);
318 /* FLIP PROPERTY */
319 if (g_ascii_strcasecmp(flip, "flip")==0)
320 object.flip=TRUE;
321 else
322 if (g_ascii_strcasecmp(flip, "noflip")==0)
323 object.flip=FALSE;
324 else
325 g_warning("invalid setting for copypaste flip property: %s", flip);
327 return g_memdup(&object, sizeof(GdObject));
330 return NULL;
335 char *
336 gd_get_object_description_markup (GdObject *selected)
338 g_return_val_if_fail (selected != NULL, NULL);
340 switch (selected->type) {
341 case POINT:
342 return g_markup_printf_escaped(_("Point of <b>%s</b> at %d,%d"), gd_elements[selected->element].lowercase_name, selected->x1, selected->y1);
344 case LINE:
345 return g_markup_printf_escaped(_("Line from %d,%d to %d,%d of <b>%s</b>"), selected->x1, selected->y1, selected->x2, selected->y2, gd_elements[selected->element].lowercase_name);
347 case RECTANGLE:
348 return g_markup_printf_escaped(_("Rectangle from %d,%d to %d,%d of <b>%s</b>"), selected->x1, selected->y1, selected->x2, selected->y2, gd_elements[selected->element].lowercase_name);
350 case FILLED_RECTANGLE:
351 return g_markup_printf_escaped(_("Rectangle from %d,%d to %d,%d of <b>%s</b>, filled with <b>%s</b>"), selected->x1, selected->y1, selected->x2, selected->y2, gd_elements[selected->element].lowercase_name, gd_elements[selected->fill_element].lowercase_name);
353 case RASTER:
354 return g_markup_printf_escaped(_("Raster from %d,%d to %d,%d of <b>%s</b>, distance %+d,%+d"), selected->x1, selected->y1, selected->x2, selected->y2, gd_elements[selected->element].lowercase_name, selected->dx, selected->dy);
356 case JOIN:
357 return g_markup_printf_escaped(_("Join <b>%s</b> to every <b>%s</b>, distance %+d,%+d"), gd_elements[selected->fill_element].lowercase_name, gd_elements[selected->element].lowercase_name, selected->dx, selected->dy);
359 case FLOODFILL_BORDER:
360 return g_markup_printf_escaped(_("Floodfill from %d,%d of <b>%s</b>, border <b>%s</b>"), selected->x1, selected->y1, gd_elements[selected->fill_element].lowercase_name, gd_elements[selected->element].lowercase_name);
362 case FLOODFILL_REPLACE:
363 return g_markup_printf_escaped(_("Floodfill from %d,%d of <b>%s</b>, replacing <b>%s</b>"), selected->x1, selected->y1, gd_elements[selected->fill_element].lowercase_name, gd_elements[selected->element].lowercase_name);
365 case MAZE:
366 return g_markup_printf_escaped(_("Maze from %d,%d to %d,%d, wall <b>%s</b>, path <b>%s</b>"), selected->x1, selected->y1, selected->x2, selected->y2, gd_elements[selected->element].lowercase_name, gd_elements[selected->fill_element].lowercase_name);
368 case MAZE_UNICURSAL:
369 return g_markup_printf_escaped(_("Unicursal maze from %d,%d to %d,%d, wall <b>%s</b>, path <b>%s</b>"), selected->x1, selected->y1, selected->x2, selected->y2, gd_elements[selected->element].lowercase_name, gd_elements[selected->fill_element].lowercase_name);
371 case MAZE_BRAID:
372 return g_markup_printf_escaped(_("Braid maze from %d,%d to %d,%d, wall <b>%s</b>, path <b>%s</b>"), selected->x1, selected->y1, selected->x2, selected->y2, gd_elements[selected->element].lowercase_name, gd_elements[selected->fill_element].lowercase_name);
374 case RANDOM_FILL:
375 return g_markup_printf_escaped(_("Random fill from %d,%d to %d,%d, replacing %s"), selected->x1, selected->y1, selected->x2, selected->y2, gd_elements[selected->element].lowercase_name);
377 case COPY_PASTE:
378 return g_markup_printf_escaped(_("Copy from %d,%d-%d,%d, paste to %d,%d"), selected->x1, selected->y1, selected->x2, selected->y2, selected->dx, selected->dy);
381 case NONE:
382 g_assert_not_reached();
385 return NULL;
390 char *
391 gd_get_object_coordinates_text (GdObject *selected)
393 g_return_val_if_fail (selected!=NULL, NULL);
395 switch (selected->type) {
396 case POINT:
397 case FLOODFILL_BORDER:
398 case FLOODFILL_REPLACE:
399 return g_strdup_printf("%d,%d", selected->x1, selected->y1);
401 case LINE:
402 case RECTANGLE:
403 case FILLED_RECTANGLE:
404 case RANDOM_FILL:
405 return g_strdup_printf("%d,%d-%d,%d", selected->x1, selected->y1, selected->x2, selected->y2);
407 case RASTER:
408 case MAZE_UNICURSAL:
409 case MAZE_BRAID:
410 case MAZE:
411 return g_strdup_printf("%d,%d-%d,%d (%d,%d)", selected->x1, selected->y1, selected->x2, selected->y2, selected->dx, selected->dy);
413 case JOIN:
414 return g_strdup_printf("%+d,%+d", selected->dx, selected->dy);
416 case COPY_PASTE:
417 return g_strdup_printf("%d,%d-%d,%d, %d,%d", selected->x1, selected->y1, selected->x2, selected->y2, selected->dx, selected->dy);
419 case NONE:
420 g_assert_not_reached();
423 return NULL;
433 /** drawing a line, using bresenham's */
434 static void
435 draw_line (Cave *cave, const GdObject *object)
437 int x, y, x1, y1, x2, y2;
438 gboolean steep;
439 int error, dx, dy, ystep;
441 x1=object->x1;
442 y1=object->y1, x2=object->x2;
443 y2=object->y2;
444 steep=ABS (y2 - y1) > ABS (x2 - x1);
445 if (steep) {
446 x=x1;
447 x1=y1;
448 y1=x;
449 x=x2;
450 x2=y2;
451 y2=x;
453 if (x1 > x2) {
454 x=x1;
455 x1=x2;
456 x2=x;
457 x=y1;
458 y1=y2;
459 y2=x;
461 dx=x2 - x1;
462 dy=ABS (y2 - y1);
463 y=y1;
464 error=0;
465 ystep=(y1 < y2) ? 1 : -1;
466 for (x=x1; x <= x2; x++) {
467 if (steep)
468 gd_cave_store_rc (cave, y, x, object->element, object);
469 else
470 gd_cave_store_rc (cave, x, y, object->element, object);
471 error += dy;
472 if (error * 2 >= dx) {
473 y += ystep;
474 error -= dx;
481 static void
482 draw_fill_replace_proc(Cave *cave, int x, int y, const GdObject *object)
484 /* fill with border so we do not come back */
485 gd_cave_store_rc(cave, x, y, object->fill_element, object);
487 if (x>0 && gd_cave_get_rc(cave, x-1, y)==object->element) draw_fill_replace_proc(cave, x-1, y, object);
488 if (y>0 && gd_cave_get_rc(cave, x, y-1)==object->element) draw_fill_replace_proc(cave, x, y-1, object);
489 if (x<cave->w-1 && gd_cave_get_rc(cave, x+1, y)==object->element) draw_fill_replace_proc(cave, x+1, y, object);
490 if (y<cave->h-1 && gd_cave_get_rc(cave, x, y+1)==object->element) draw_fill_replace_proc(cave, x, y+1, object);
493 static void
494 draw_fill_replace (Cave *cave, const GdObject *object)
496 /* check bounds */
497 if (object->x1<0 || object->y1<0 || object->x1>=cave->w || object->y1>=cave->h)
498 return;
499 if (object->element==object->fill_element)
500 return;
501 /* this procedure fills the area with the object->element. */
502 draw_fill_replace_proc(cave, object->x1, object->y1, object);
507 static void
508 draw_fill_border_proc (Cave *cave, int x, int y, const GdObject *object)
510 /* fill with border so we do not come back */
511 gd_cave_store_rc(cave, x, y, object->element, object);
513 if (x>0 && gd_cave_get_rc(cave, x-1, y)!=object->element) draw_fill_border_proc(cave, x-1, y, object);
514 if (y>0 && gd_cave_get_rc(cave, x, y-1)!=object->element) draw_fill_border_proc(cave, x, y-1, object);
515 if (x<cave->w-1 && gd_cave_get_rc(cave, x+1, y)!=object->element) draw_fill_border_proc(cave, x+1, y, object);
516 if (y<cave->h-1 && gd_cave_get_rc(cave, x, y+1)!=object->element) draw_fill_border_proc(cave, x, y+1, object);
519 static void
520 draw_fill_border (Cave *cave, const GdObject *object)
522 int x, y;
524 /* check bounds */
525 if (object->x1<0 || object->y1<0 || object->x1>=cave->w || object->y1>=cave->h)
526 return;
528 /* this procedure fills the area with the object->element. */
529 draw_fill_border_proc(cave, object->x1, object->y1, object);
531 /* after the fill, we change all filled cells to the fill_element. */
532 /* we find those by looking at the object_order[][] */
533 for (y=0; y<cave->h; y++)
534 for (x=0; x<cave->w; x++)
535 if (cave->objects_order[y][x]==object)
536 cave->map[y][x]=object->fill_element;
541 /* rectangle, frame only */
542 static void
543 draw_rectangle(Cave *cave, const GdObject *object)
545 int x1, y1, x2, y2, x, y;
547 /* reorder coordinates if not drawing from northwest to southeast */
548 x1=object->x1;
549 y1=object->y1, x2=object->x2;
550 y2=object->y2;
551 if (y1 > y2) {
552 y=y1;
553 y1=y2;
554 y2=y;
556 if (x1 > x2) {
557 x=x1;
558 x1=x2;
559 x2=x;
561 for (x=x1; x <= x2; x++) {
562 gd_cave_store_rc (cave, x, object->y1, object->element, object);
563 gd_cave_store_rc (cave, x, object->y2, object->element, object);
565 for (y=y1; y <= y2; y++) {
566 gd_cave_store_rc (cave, object->x1, y, object->element, object);
567 gd_cave_store_rc (cave, object->x2, y, object->element, object);
573 /* rectangle, filled one */
574 static void
575 draw_filled_rectangle(Cave *cave, const GdObject *object)
577 int x1, y1, x2, y2, x, y;
579 /* reorder coordinates if not drawing from northwest to southeast */
580 x1=object->x1;
581 y1=object->y1, x2=object->x2;
582 y2=object->y2;
583 if (y1 > y2) {
584 y=y1;
585 y1=y2;
586 y2=y;
588 if (x1 > x2) {
589 x=x1;
590 x1=x2;
591 x2=x;
593 for (y=y1; y <= y2; y++)
594 for (x=x1; x <= x2; x++)
595 gd_cave_store_rc (cave, x, y, (y==object->y1 || y==object->y2 || x==object->x1 || x==object->x2) ? object->element : object->fill_element, object);
600 /* something like ordered fill, increment is dx and dy. */
601 static void
602 draw_raster(Cave *cave, const GdObject *object)
604 int x, y, x1, y1, x2, y2;
605 int dx, dy;
607 /* reorder coordinates if not drawing from northwest to southeast */
608 x1=object->x1;
609 y1=object->y1;
610 x2=object->x2;
611 y2=object->y2;
612 if (y1>y2) {
613 y=y1;
614 y1=y2;
615 y2=y;
617 if (x1>x2) {
618 x=x1;
619 x1=x2;
620 x2=x;
622 dx=object->dx;
623 dy=object->dy;
624 for (y=y1; y<=y2; y+=dy)
625 for (x=x1; x<=x2; x+=dx)
626 gd_cave_store_rc (cave, x, y, object->element, object);
631 /* find every object, and put fill_element next to it. relative coordinates dx,dy */
632 static void
633 draw_join(Cave *cave, const GdObject *object)
635 int x, y;
637 for (y=0; y<cave->h; y++)
638 for (x=0; x<cave->w; x++)
639 if (cave->map[y][x]==object->element) {
640 int nx=x + object->dx;
641 int ny=y + object->dy;
642 /* this one implements wraparound for joins. it is needed by many caves in profi boulder series */
643 while (nx>=cave->w)
644 nx-=cave->w, ny++;
645 gd_cave_store_rc (cave, nx, ny, object->fill_element, object);
650 /* create a maze in a gboolean **maze. */
651 /* recursive algorithm. */
652 static void
653 mazegen(GRand *rand, gboolean **maze, int width, int height, int x, int y, int horiz)
655 int dirmask=15;
657 maze[y][x]=TRUE;
658 while (dirmask!=0) {
659 int dir;
661 dir=g_rand_int_range(rand, 0, 100)<horiz?2:0; /* horiz or vert */
662 /* if no horizontal movement possible, choose vertical */
663 if (dir==2 && (dirmask&12)==0)
664 dir=0;
665 else if (dir==0 && (dirmask&3)==0) /* and vice versa */
666 dir=2;
667 dir+=g_rand_int_range(rand, 0, 2); /* dir */
668 if (dirmask&(1<<dir)) {
669 dirmask&=~(1<<dir);
671 switch(dir) {
672 case 0: /* up */
673 if (y>=2 && !maze[y-2][x]) {
674 maze[y-1][x]=TRUE;
675 mazegen(rand, maze, width, height, x, y-2, horiz);
677 break;
678 case 1: /* down */
679 if (y<height-2 && !maze[y+2][x]) {
680 maze[y+1][x]=TRUE;
681 mazegen(rand, maze, width, height, x, y+2, horiz);
683 break;
684 case 2: /* left */
685 if (x>=2 && !maze[y][x-2]) {
686 maze[y][x-1]=TRUE;
687 mazegen(rand, maze, width, height, x-2, y, horiz);
689 break;
690 case 3: /* right */
691 if (x<width-2 && !maze[y][x+2]) {
692 maze[y][x+1]=TRUE;
693 mazegen(rand, maze, width, height, x+2, y, horiz);
695 break;
696 default:
697 g_assert_not_reached();
704 #if 0
705 #define CELL_TO_POINTER(x,y) (GUINT_TO_POINTER(((y)<<16)+(x)))
706 #define X_FROM_POINTER(p) (GPOINTER_TO_UINT(p)&65535)
707 #define Y_FROM_POINTER(p) (GPOINTER_TO_UINT(p)>>16)
709 ptr_int_compare(gconstpointer p1, gconstpointer p2)
711 return GPOINTER_TO_INT(p1)-GPOINTER_TO_INT(p2);
714 /* maze generation algorithm from crli */
715 static void
716 mazegen(GRand *rand, gboolean **maze, int width, int height, int x, int y, int horiz)
718 GList *cells=NULL;
720 cells=g_list_append(cells, CELL_TO_POINTER(x,y));
721 maze[y][x]=TRUE;
722 while (cells!=NULL) {
723 GList *iter;
725 iter=cells;
726 while (iter!=NULL) {
727 GList *next;
728 int x, y;
729 gboolean possible_dirs[4];
731 x=X_FROM_POINTER(iter->data);
732 y=Y_FROM_POINTER(iter->data);
734 possible_dirs[0]=y>=2 && !maze[y-2][x];
735 possible_dirs[1]=x>=2 && !maze[y][x-2];
736 possible_dirs[2]=y<height-2 && !maze[y+2][x];
737 possible_dirs[3]=x<width-2 && !maze[y][x+2];
739 if (possible_dirs[0] || possible_dirs[1] || possible_dirs[2] || possible_dirs[3]) {
740 /* there is at least one direction, so choose one */
741 switch (g_rand_int_range(rand, 0, 4)) {
742 case 0:
743 if (possible_dirs[0]) {
744 maze[y-1][x]=TRUE;
745 maze[y-2][x]=TRUE;
746 cells=g_list_insert_sorted(cells, CELL_TO_POINTER(x, y-2), ptr_int_compare);
748 break;
749 case 1:
750 if (possible_dirs[1]) {
751 maze[y][x-1]=TRUE;
752 maze[y][x-2]=TRUE;
753 cells=g_list_insert_sorted(cells, CELL_TO_POINTER(x-2, y), ptr_int_compare);
755 break;
756 case 2:
757 if (possible_dirs[2]) {
758 maze[y+1][x]=TRUE;
759 maze[y+2][x]=TRUE;
760 cells=g_list_insert_sorted(cells, CELL_TO_POINTER(x, y+2), ptr_int_compare);
762 break;
763 case 3:
764 if (possible_dirs[3]) {
765 maze[y][x+1]=TRUE;
766 maze[y][x+2]=TRUE;
767 cells=g_list_insert_sorted(cells, CELL_TO_POINTER(x+2, y), ptr_int_compare);
769 break;
770 default:
771 g_assert_not_reached();
774 next=iter->next;
776 else {
777 next=iter->next;
779 cells=g_list_remove_link(cells, iter);
782 iter=next;
786 #undef CELL_TO_POINTER
787 #undef X_FROM_POINTER
788 #undef Y_FROM_POINTER
789 #endif
791 #if 0
792 #define CELL_TO_POINTER(x,y) (GUINT_TO_POINTER(((y)<<16)+(x)))
793 #define X_FROM_POINTER(p) (GPOINTER_TO_UINT(p)&65535)
794 #define Y_FROM_POINTER(p) (GPOINTER_TO_UINT(p)>>16)
795 /* growing tree maze generation algorithm. */
796 static void
797 mazegen(GRand *rand, gboolean **maze, int width, int height, int x, int y, int complexity)
799 GPtrArray *cells;
801 cells=g_ptr_array_sized_new(width*height/4);
802 g_ptr_array_add(cells, CELL_TO_POINTER(x,y));
803 maze[y][x]=TRUE;
804 while (cells->len!=0) {
805 int x, y;
806 int possible_dirs[4], dirnum;
807 int l;
808 int i;
810 if (complexity>=g_rand_int_range(rand, 0, 100))
811 l=cells->len-1;
812 else
813 l=g_rand_int_range(rand, 0, cells->len);
814 i=l;
815 x=X_FROM_POINTER(g_ptr_array_index(cells, i));
816 y=Y_FROM_POINTER(g_ptr_array_index(cells, i));
818 dirnum=0;
819 if (y>=2 && !maze[y-2][x])
820 possible_dirs[dirnum++]=0;
821 if (x>=2 && !maze[y][x-2])
822 possible_dirs[dirnum++]=1;
823 if (y<height-2 && !maze[y+2][x])
824 possible_dirs[dirnum++]=2;
825 if (x<width-2 && !maze[y][x+2])
826 possible_dirs[dirnum++]=3;
827 if (dirnum==0) /* no possible direction, remove from list */
828 g_ptr_array_remove_index(cells, i);
829 else {
830 /* there is at least one direction, so choose one */
831 int random_dir=possible_dirs[g_rand_int_range(rand, 0, dirnum)];
833 switch (random_dir) {
834 case 0:
835 maze[y-1][x]=TRUE;
836 maze[y-2][x]=TRUE;
837 g_ptr_array_add(cells, CELL_TO_POINTER(x, y-2));
838 break;
839 case 1:
840 maze[y][x-1]=TRUE;
841 maze[y][x-2]=TRUE;
842 g_ptr_array_add(cells, CELL_TO_POINTER(x-2, y));
843 break;
844 case 2:
845 maze[y+1][x]=TRUE;
846 maze[y+2][x]=TRUE;
847 g_ptr_array_add(cells, CELL_TO_POINTER(x, y+2));
848 break;
849 case 3:
850 maze[y][x+1]=TRUE;
851 maze[y][x+2]=TRUE;
852 g_ptr_array_add(cells, CELL_TO_POINTER(x+2, y));
853 break;
854 default:
855 g_assert_not_reached();
859 g_ptr_array_free(cells, TRUE);
861 #undef CELL_TO_POINTER
862 #undef X_FROM_POINTER
863 #undef Y_FROM_POINTER
864 #endif
866 static void
867 braidmaze(GRand *rand, gboolean **maze, int w, int h)
869 int x, y;
871 for (y=0; y<h; y+=2)
872 for (x=0; x<w; x+=2) {
873 int closed=0, dirs=0;
874 int closed_dirs[4];
876 /* if it is the edge of the map, OR no path carved, then we can't go in that direction. */
877 if (x<1 || !maze[y][x-1]) {
878 closed++; /* closed from this side. */
879 /* if not the edge, we might open this wall (carve a path) to remove a dead end */
880 if (x>0)
881 closed_dirs[dirs++]=MV_LEFT;
883 /* other 3 directions similar */
884 if (y<1 || !maze[y-1][x]) {
885 closed++;
886 if (y>0)
887 closed_dirs[dirs++]=MV_UP;
889 if (x>=w-1 || !maze[y][x+1]) {
890 closed++;
891 if (x<w-1)
892 closed_dirs[dirs++]=MV_RIGHT;
894 if (y>=h-1 || !maze[y+1][x]) {
895 closed++;
896 if (y<h-1)
897 closed_dirs[dirs++]=MV_DOWN;
900 /* if closed from 3 sides, then it is a dead end. also check dirs!=0, that might fail for a 1x1 maze :) */
901 if (closed==3 && dirs!=0) {
902 /* make up a random direction, and open in that direction, so dead end is removed */
903 int dir=closed_dirs[g_rand_int_range(rand, 0, dirs)];
905 switch(dir) {
906 case MV_LEFT:
907 maze[y][x-1]=TRUE; break;
908 case MV_UP:
909 maze[y-1][x]=TRUE; break;
910 case MV_RIGHT:
911 maze[y][x+1]=TRUE; break;
912 case MV_DOWN:
913 maze[y+1][x]=TRUE; break;
922 static void
923 draw_maze(Cave *cave, const GdObject *object, int level)
925 int x, y;
926 gboolean **map;
927 int x1=object->x1;
928 int y1=object->y1;
929 int x2=object->x2;
930 int y2=object->y2;
931 int w, h, path, wall;
932 int xk, yk;
933 GRand *rand;
934 int i,j;
936 /* change coordinates if not in correct order */
937 if (y1>y2) {
938 y=y1;
939 y1=y2;
940 y2=y;
942 if (x1>x2) {
943 x=x1;
944 x1=x2;
945 x2=x;
947 wall=object->dx;
948 if (wall<1)
949 wall=1;
950 path=object->dy;
951 if (path<1)
952 path=1;
954 /* calculate the width and height of the maze.
955 n=number of passages, path=path width, wall=wall width, maze=maze width.
956 if given the number of passages, the width of the maze is:
958 n*path+(n-1)*wall=maze
959 n*path+n*wall-wall=maze
960 n*(path+wall)=maze+wall
961 n=(maze+wall)/(path+wall)
963 /* number of passages for each side */
964 w=(x2-x1+1+wall)/(path+wall);
965 h=(y2-y1+1+wall)/(path+wall);
966 /* and we calculate the size of the internal map */
967 if (object->type==MAZE_UNICURSAL) {
968 /* for unicursal maze, width and height must be mod2=0, and we will convert to paths&walls later */
969 w=w/2*2;
970 h=h/2*2;
971 } else {
972 /* for normal maze */
973 w=2*(w-1)+1;
974 h=2*(h-1)+1;
977 /* twodimensional boolean array to generate map in */
978 map=g_new(gboolean *, h);
979 for (y=0; y<h; y++)
980 map[y]=g_new0(gboolean, w);
982 /* start generation, if map is big enough.
983 otherwise the application would crash, as the editor places maze objects during mouse click&drag that
984 have no sense */
985 rand=g_rand_new_with_seed(object->seed[level]==-1?g_rand_int(cave->random):object->seed[level]);
986 if (w>=1 && h>=1)
987 mazegen(rand, map, w, h, 0, 0, object->horiz);
988 if (object->type==MAZE_BRAID)
989 braidmaze(rand, map, w, h);
990 g_rand_free(rand);
992 if (w>=1 && h>=1 && object->type==MAZE_UNICURSAL) {
993 gboolean **unicursal;
995 /* convert to unicursal maze */
996 /* original:
997 xxx x
998 x x
999 xxxxx
1001 unicursal:
1002 xxxxxxx xxx
1003 x x x x
1004 xxxxx x x x
1005 x x x x
1006 xxxxx xxx x
1008 xxxxxxxxxxx
1011 unicursal=g_new(gboolean *, h*2-1);
1012 for (y=0; y<h*2-1; y++)
1013 unicursal[y]=g_new0(gboolean, w*2-1);
1015 for (y=0; y<h; y++)
1016 for(x=0; x<w; x++) {
1017 if (map[y][x]) {
1018 unicursal[y*2][x*2]=TRUE;
1019 unicursal[y*2][x*2+2]=TRUE;
1020 unicursal[y*2+2][x*2]=TRUE;
1021 unicursal[y*2+2][x*2+2]=TRUE;
1023 if (x<1 || !map[y][x-1]) unicursal[y*2+1][x*2]=TRUE;
1024 if (y<1 || !map[y-1][x]) unicursal[y*2][x*2+1]=TRUE;
1025 if (x>=w-1 || !map[y][x+1]) unicursal[y*2+1][x*2+2]=TRUE;
1026 if (y>=h-1 || !map[y+1][x]) unicursal[y*2+2][x*2+1]=TRUE;
1030 /* free original map */
1031 for (y=0; y<h; y++)
1032 g_free(map[y]);
1033 g_free(map);
1035 /* change to new map - the unicursal maze */
1036 map=unicursal;
1037 h=h*2-1;
1038 w=w*2-1;
1041 /* copy map to cave with correct elements and size */
1042 /* now copy the map into the cave. the copying works like this...
1043 pwpwp
1044 xxxxx p
1045 x x w
1046 x xxx p
1048 xxxxx p
1049 columns and rows denoted with "p" are to be drawn with path width, the others with wall width. */
1050 yk=y1;
1051 for (y=0; y<h; y++) {
1052 for (i=0; i<(y%2==0?path:wall); i++) {
1053 xk=x1;
1054 for (x=0; x<w; x++)
1055 for (j=0; j<(x%2==0?path:wall); j++)
1056 gd_cave_store_rc(cave, xk++, yk, map[y][x]?object->fill_element:object->element, object);
1058 /* if width is smaller than requested, fill with wall */
1059 for(x=xk; x<=x2; x++)
1060 gd_cave_store_rc(cave, x, yk, object->element, object);
1062 yk++;
1065 /* if height is smaller than requested, fill with wall */
1066 for (y=yk; y<=y2; y++)
1067 for (x=x1; x<=x2; x++)
1068 gd_cave_store_rc(cave, x, y, object->element, object);
1070 /* free map */
1071 for (y=0; y<h; y++)
1072 g_free(map[y]);
1073 g_free(map);
1077 static void
1078 draw_random_fill(Cave *cave, const GdObject *object, int level)
1080 int x, y;
1081 int x1=object->x1;
1082 int y1=object->y1;
1083 int x2=object->x2;
1084 int y2=object->y2;
1085 GRand *rand;
1086 GdC64RandomGenerator c64_rand;
1087 guint32 seed;
1089 /* -1 means that it should be different every time played. */
1090 if (object->seed[level]==-1)
1091 seed=g_rand_int(cave->random);
1092 else
1093 seed=object->seed[level];
1095 rand=g_rand_new_with_seed(seed);
1096 /* for c64 random, use the 2*8 lsb. */
1097 gd_c64_random_set_seed(&c64_rand, seed/256%256, seed%256);
1100 /* change coordinates if not in correct order */
1101 if (y1>y2) {
1102 y=y1;
1103 y1=y2;
1104 y2=y;
1106 if (x1>x2) {
1107 x=x1;
1108 x1=x2;
1109 x2=x;
1111 for (y=y1; y<=y2; y++)
1112 for (x=x1; x<=x2; x++) {
1113 unsigned int randm;
1114 GdElement element;
1116 if (object->c64_random) /* use c64 random generator */
1117 randm=gd_c64_random(&c64_rand);
1118 else /* use the much better glib random generator */
1119 randm=g_rand_int_range(rand, 0, 256);
1121 element=object->fill_element;
1122 if (randm<object->random_fill_probability[0])
1123 element=object->random_fill[0];
1124 if (randm<object->random_fill_probability[1])
1125 element=object->random_fill[1];
1126 if (randm<object->random_fill_probability[2])
1127 element=object->random_fill[2];
1128 if (randm<object->random_fill_probability[3])
1129 element=object->random_fill[3];
1131 if (object->element==O_NONE || gd_cave_get_rc(cave, x, y)==object->element)
1132 gd_cave_store_rc(cave, x, y, element, object);
1134 g_rand_free(rand);
1138 static void
1139 draw_copy_paste(Cave *cave, const GdObject *object)
1141 int x1=object->x1, y1=object->y1, x2=object->x2, y2=object->y2;
1142 int x, y; /* iterators */
1143 int w, h;
1144 GdElement *clipboard;
1146 /* reorder coordinates if not drawing from northwest to southeast */
1147 if (x2<x1) {
1148 x=x2;
1149 x2=x1;
1150 x1=x;
1152 if (y2<y1) {
1153 y=y2;
1154 y2=y1;
1155 y1=y;
1157 w=x2-x1+1;
1158 h=y2-y1+1;
1159 clipboard=g_new(GdElement, w*h);
1160 /* copy to "clipboard" */
1161 for (y=0; y<h; y++)
1162 for (x=0; x<w; x++)
1163 clipboard[y*w+x]=gd_cave_get_rc(cave, x+x1, y+y1);
1165 for (y=0; y<h; y++) {
1166 int ydest;
1168 ydest=object->flip?h-1-y:y;
1169 for (x=0; x<w; x++) {
1170 int xdest;
1172 xdest=object->mirror?w-1-x:x;
1173 /* dx and dy are used here are "paste to" coordinates */
1174 gd_cave_store_rc(cave, object->dx+xdest, object->dy+ydest, clipboard[y*w+x], object);
1178 g_free(clipboard);
1183 /* draw the specified game object into cave's data.
1184 also remember, which cell was set by which cave object. */
1185 void
1186 gd_cave_draw_object(Cave *cave, const GdObject *object, int level)
1188 g_assert (cave!=NULL);
1189 g_assert (cave->map!=NULL);
1190 g_assert (cave->objects_order!=NULL);
1191 g_assert (object!=NULL);
1192 g_assert (level==cave->rendered-1);
1194 switch (object->type) {
1195 case POINT:
1196 /* single point */
1197 gd_cave_store_rc(cave, object->x1, object->y1, object->element, object);
1198 break;
1200 case LINE:
1201 draw_line(cave, object);
1202 break;
1204 case RECTANGLE:
1205 draw_rectangle(cave, object);
1206 break;
1208 case FILLED_RECTANGLE:
1209 draw_filled_rectangle(cave, object);
1210 break;
1212 case RASTER:
1213 draw_raster(cave, object);
1214 break;
1216 case JOIN:
1217 draw_join(cave, object);
1218 break;
1220 case FLOODFILL_BORDER:
1221 draw_fill_border(cave, object);
1222 break;
1224 case FLOODFILL_REPLACE:
1225 draw_fill_replace(cave, object);
1226 break;
1228 case MAZE:
1229 case MAZE_UNICURSAL:
1230 case MAZE_BRAID:
1231 draw_maze(cave, object, level);
1232 break;
1234 case RANDOM_FILL:
1235 draw_random_fill(cave, object, level);
1236 break;
1238 case COPY_PASTE:
1239 draw_copy_paste(cave, object);
1240 break;
1242 case NONE:
1243 g_assert_not_reached();
1244 break;
1246 default:
1247 g_critical("Unknown object %d", object->type);
1248 break;
1256 /* load cave to play... also can be called rendering the cave elements */
1257 Cave *
1258 gd_cave_new_rendered (const Cave *data, const int level, const guint32 seed)
1260 Cave *cave;
1261 GdElement element;
1262 int x, y;
1263 GList *iter;
1265 /* make a copy */
1266 cave=gd_cave_new_from_cave (data);
1267 cave->rendered=level+1;
1269 cave->render_seed=seed;
1270 cave->random=g_rand_new_with_seed(cave->render_seed);
1272 /* maps needed during drawing and gameplay */
1273 cave->objects_order=gd_cave_map_new(cave, gpointer);
1275 cave->time=data->level_time[level];
1276 cave->timevalue=data->level_timevalue[level];
1277 cave->diamonds_needed=data->level_diamonds[level];
1278 cave->magic_wall_time=data->level_magic_wall_time[level];
1279 cave->slime_permeability=data->level_slime_permeability[level];
1280 cave->slime_permeability_c64=data->level_slime_permeability_c64[level];
1281 cave->time_bonus=data->level_bonus_time[level];
1282 cave->time_penalty=data->level_penalty_time[level];
1283 cave->amoeba_time=data->level_amoeba_time[level];
1284 cave->amoeba_max_count=data->level_amoeba_threshold[level];
1285 cave->amoeba_2_time=data->level_amoeba_2_time[level];
1286 cave->amoeba_2_max_count=data->level_amoeba_2_threshold[level];
1287 cave->hatching_delay_time=data->level_hatching_delay_time[level];
1288 cave->hatching_delay_frame=data->level_hatching_delay_frame[level];
1290 if (!cave->map) {
1291 /* if we have no map, fill with predictable random generator. */
1292 cave->map=gd_cave_map_new (cave, GdElement);
1293 /* IF CAVE HAS NO MAP, USE THE RANDOM NUMBER GENERATOR */
1294 /* init c64 randomgenerator */
1295 if (data->level_rand[level]<0)
1296 gd_cave_c64_random_set_seed(cave, g_rand_int_range(cave->random, 0, 256), g_rand_int_range(cave->random, 0, 256));
1297 else
1298 gd_cave_c64_random_set_seed(cave, 0, data->level_rand[level]);
1300 /* generate random fill
1301 * start from row 1 (0 skipped), and fill also the borders on left and right hand side,
1302 * as c64 did. this way works the original random generator the right way.
1303 * also, do not fill last row, that is needed for the random seeds to be correct
1304 * after filling! predictable slime will use it. */
1305 for (y=1; y<cave->h-1; y++) {
1306 for (x=0; x<cave->w; x++) {
1307 unsigned int randm;
1309 if (data->level_rand[level]<0)
1310 randm=g_rand_int_range(cave->random, 0, 256); /* use the much better glib random generator */
1311 else
1312 randm=gd_cave_c64_random(cave); /* use c64 */
1314 element=data->initial_fill;
1315 if (randm<data->random_fill_probability[0])
1316 element=data->random_fill[0];
1317 if (randm<data->random_fill_probability[1])
1318 element=data->random_fill[1];
1319 if (randm<data->random_fill_probability[2])
1320 element=data->random_fill[2];
1321 if (randm<data->random_fill_probability[3])
1322 element=data->random_fill[3];
1324 gd_cave_store_rc(cave, x, y, element, NULL);
1328 /* draw initial border */
1329 for (y=0; y<cave->h; y++) {
1330 gd_cave_store_rc(cave, 0, y, cave->initial_border, NULL);
1331 gd_cave_store_rc(cave, cave->w-1, y, cave->initial_border, NULL);
1333 for (x=0; x<cave->w; x++) {
1334 gd_cave_store_rc(cave, x, 0, cave->initial_border, NULL);
1335 gd_cave_store_rc(cave, x, cave->h-1, cave->initial_border, NULL);
1338 else {
1339 /* IF CAVE HAS A MAP, SIMPLY USE IT... no need to fill with random elements */
1341 /* initialize c64 predictable random for slime. the values were taken from afl bd, see docs/internals.txt */
1342 gd_cave_c64_random_set_seed(cave, 0, 0x1e);
1345 if (data->level_slime_seed_c64[level]!=-1) {
1346 /* if a specific slime seed is requested, change it now. */
1348 gd_cave_c64_random_set_seed(cave, data->level_slime_seed_c64[level]/256, data->level_slime_seed_c64[level]%256);
1351 /* render cave objects above random data or map */
1352 for (iter=data->objects; iter; iter=g_list_next (iter)) {
1353 GdObject *object=(GdObject *)iter->data;
1355 if (object->levels & gd_levels_mask[level])
1356 gd_cave_draw_object(cave, iter->data, level);
1359 /* check if we use c64 ckdelay or milliseconds for timing */
1360 if (cave->scheduling==GD_SCHEDULING_MILLISECONDS)
1361 cave->speed=data->level_speed[level]; /* exact timing */
1362 else {
1363 cave->speed=120; /* delay loop based timing... set something for first iteration, then later it will be calculated */
1364 cave->c64_timing=data->level_ckdelay[level]; /* this one may be used by iterate routine to calculate actual delay if c64scheduling is selected */
1367 gd_cave_correct_visible_size(cave);
1369 return cave;
1375 render cave at specified level.
1376 copy result to the map; remove objects.
1377 the cave will be map-based.
1379 void
1380 gd_flatten_cave(Cave *cave, const int level)
1382 Cave *rendered;
1384 g_return_if_fail(cave != NULL);
1386 /* render cave at specified level to obtain map. seed=0 */
1387 rendered=gd_cave_new_rendered(cave, level, 0);
1388 /* forget old map without objects */
1389 gd_cave_map_free(cave->map);
1390 /* copy new map to cave */
1391 cave->map=gd_cave_map_dup(rendered, map);
1392 gd_cave_free(rendered);
1394 /* forget objects */
1395 g_list_foreach(cave->objects, (GFunc) g_free, NULL);
1396 cave->objects=NULL;