improved color handling when loading BDCFF file in BD engine
[rocksndiamonds.git] / src / game_bd / bd_caveobject.c
1 /*
2  * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
3  *
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.
7  *
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.
15  */
16
17 #include "main_bd.h"
18
19
20 GdObjectLevels gd_levels_mask[] =
21 {
22   GD_OBJECT_LEVEL1,
23   GD_OBJECT_LEVEL2,
24   GD_OBJECT_LEVEL3,
25   GD_OBJECT_LEVEL4,
26   GD_OBJECT_LEVEL5
27 };
28
29 /* create an INDIVIDUAL POINT CAVE OBJECT */
30 GdObject *gd_object_new_point(GdObjectLevels levels, int x, int y, GdElement elem)
31 {
32   GdObject *newobj = checked_calloc(sizeof(GdObject));
33
34   newobj->levels = levels;
35   newobj->type = GD_POINT;
36   newobj->x1 = x;
37   newobj->y1 = y;
38   newobj->element = elem;
39
40   return newobj;
41 }
42
43 /* create a LINE OBJECT */
44 GdObject *gd_object_new_line(GdObjectLevels levels, int x1, int y1, int x2, int y2,
45                              GdElement elem)
46 {
47   GdObject *newobj = checked_calloc(sizeof(GdObject));
48
49   newobj->levels = levels;
50   newobj->type = GD_LINE;
51   newobj->x1 = x1;
52   newobj->y1 = y1;
53   newobj->x2 = x2;
54   newobj->y2 = y2;
55   newobj->element = elem;
56
57   return newobj;
58 }
59
60 /* create a RECTANGLE OBJECT */
61 GdObject *gd_object_new_rectangle(GdObjectLevels levels, int x1, int y1, int x2, int y2,
62                                   GdElement elem)
63 {
64   GdObject *newobj = checked_calloc(sizeof(GdObject));
65
66   newobj->levels = levels;
67   newobj->type = GD_RECTANGLE;
68   newobj->x1 = x1;
69   newobj->y1 = y1;
70   newobj->x2 = x2;
71   newobj->y2 = y2;
72   newobj->element = elem;
73
74   return newobj;
75 }
76
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)
80 {
81   GdObject *newobj = checked_calloc(sizeof(GdObject));
82
83   newobj->levels = levels;
84   newobj->type = GD_FILLED_RECTANGLE;
85   newobj->x1 = x1;
86   newobj->y1 = y1;
87   newobj->x2 = x2;
88   newobj->y2 = y2;
89   newobj->element = elem;
90   newobj->fill_element = fill_elem;
91
92   return newobj;
93 }
94
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)
98 {
99   GdObject *newobj = checked_calloc(sizeof(GdObject));
100
101   newobj->levels = levels;
102   newobj->type = GD_RASTER;
103   newobj->x1 = x1;
104   newobj->y1 = y1;
105   newobj->x2 = x2;
106   newobj->y2 = y2;
107   newobj->dx = dx;
108   newobj->dy = dy;
109   newobj->element = elem;
110
111   return newobj;
112 }
113
114 /* create a raster object */
115 GdObject *gd_object_new_join(GdObjectLevels levels, int dx, int dy,
116                              GdElement search, GdElement replace)
117 {
118   GdObject *newobj = checked_calloc(sizeof(GdObject));
119
120   newobj->levels = levels;
121   newobj->type = GD_JOIN;
122   newobj->dx = dx;
123   newobj->dy = dy;
124   newobj->element = search;
125   newobj->fill_element = replace;
126
127   return newobj;
128 }
129
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)
133 {
134   GdObject *newobj = checked_calloc(sizeof(GdObject));
135
136   newobj->levels = levels;
137   newobj->type = GD_FLOODFILL_BORDER;
138   newobj->x1 = x1;
139   newobj->y1 = y1;
140   newobj->element = border;
141   newobj->fill_element = fill;
142
143   return newobj;
144 }
145
146 GdObject *gd_object_new_floodfill_replace(GdObjectLevels levels, int x1, int y1,
147                                           GdElement fill, GdElement to_replace)
148 {
149   GdObject *newobj = checked_calloc(sizeof(GdObject));
150
151   newobj->levels = levels;
152   newobj->type = GD_FLOODFILL_REPLACE;
153   newobj->x1 = x1;
154   newobj->y1 = y1;
155   newobj->element = to_replace;
156   newobj->fill_element = fill;
157
158   return newobj;
159 }
160
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])
164 {
165   int i;
166   GdObject *newobj = checked_calloc(sizeof(GdObject));
167
168   newobj->levels = levels;
169   newobj->type = GD_MAZE;
170   newobj->x1 = x1;
171   newobj->y1 = y1;
172   newobj->x2 = x2;
173   newobj->y2 = y2;
174   newobj->dx = wall_w;
175   newobj->dy = path_w;
176   newobj->element = wall_e;
177   newobj->fill_element = path_e;
178   newobj->horiz = horiz_percent;
179
180   for (i = 0; i < 5; ++i)
181     newobj->seed[i] = seed[i];
182
183   return newobj;
184 }
185
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])
189 {
190   int i;
191   GdObject *newobj = checked_calloc(sizeof(GdObject));
192
193   newobj->levels = levels;
194   newobj->type = GD_MAZE_UNICURSAL;
195   newobj->x1 = x1;
196   newobj->y1 = y1;
197   newobj->x2 = x2;
198   newobj->y2 = y2;
199   newobj->dx = wall_w;
200   newobj->dy = path_w;
201   newobj->element = wall_e;
202   newobj->fill_element = path_e;
203   newobj->horiz = horiz_percent;
204
205   for (i = 0; i < 5; ++i)
206     newobj->seed[i] = seed[i];
207
208   return newobj;
209 }
210
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])
214 {
215   int i;
216   GdObject *newobj = checked_calloc(sizeof(GdObject));
217
218   newobj->levels = levels;
219   newobj->type = GD_MAZE_BRAID;
220   newobj->x1 = x1;
221   newobj->y1 = y1;
222   newobj->x2 = x2;
223   newobj->y2 = y2;
224   newobj->dx = wall_w;
225   newobj->dy = path_w;
226   newobj->element = wall_e;
227   newobj->fill_element = path_e;
228   newobj->horiz = horiz_percent;
229
230   for (i = 0; i < 5; ++i)
231     newobj->seed[i] = seed[i];
232
233   return newobj;
234 }
235
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)
240 {
241   int i;
242   GdObject *newobj = checked_calloc(sizeof(GdObject));
243
244   newobj->levels = levels;
245   newobj->type = GD_RANDOM_FILL;
246   newobj->x1 = x1;
247   newobj->y1 = y1;
248   newobj->x2 = x2;
249   newobj->y2 = y2;
250   newobj->fill_element = initial;
251
252   for (i = 0; i < 5; ++i)
253     newobj->seed[i] = seed[i];
254
255   for (i = 0; i < 4; ++i)
256   {
257     newobj->random_fill[i] = random[i];
258     newobj->random_fill_probability[i] = prob[i];
259   }
260
261   newobj->element = replace_only;
262   newobj->c64_random = c64;
263
264   return newobj;
265 }
266
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)
269 {
270   GdObject *newobj = checked_calloc(sizeof(GdObject));
271
272   newobj->levels = levels;
273   newobj->type = GD_COPY_PASTE;
274   newobj->x1 = x1;
275   newobj->y1 = y1;
276   newobj->x2 = x2;
277   newobj->y2 = y2;
278   newobj->dx = dx;
279   newobj->dy = dy;
280   newobj->mirror = mirror;
281   newobj->flip = flip;
282
283   return newobj;
284 }
285
286 /* create new object from bdcff description.
287    return new object if ok; return null if failed.
288  */
289 GdObject *gd_object_new_from_string(char *str)
290 {
291   char *equalsign;
292   char *name, *param;
293   GdObject object;
294   char elem0[100], elem1[100];
295
296   equalsign = strchr(str, '=');
297   if (!equalsign)
298     return NULL;
299
300   /* split string by replacing the equal sign with zero */
301   *equalsign = '\0';
302   name = str;
303   param = equalsign + 1;
304
305   /* INDIVIDUAL POINT CAVE OBJECT */
306   if (strcasecmp(name, "Point") == 0)
307   {
308     object.type = GD_POINT;
309     if (sscanf(param, "%d %d %s", &object.x1, &object.y1, elem0) == 3)
310     {
311       object.element = gd_get_element_from_string(elem0);
312
313       return get_memcpy(&object, sizeof (GdObject));
314     }
315
316     return NULL;
317   }
318
319   /* LINE OBJECT */
320   if (strcasecmp(name, "Line") == 0)
321   {
322     object.type = GD_LINE;
323     if (sscanf(param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0) == 5)
324     {
325       object.element = gd_get_element_from_string(elem0);
326
327       return get_memcpy(&object, sizeof (GdObject));
328     }
329
330     return NULL;
331   }
332
333   /* RECTANGLE OBJECT */
334   if (strcasecmp(name, "Rectangle") == 0)
335   {
336     if (sscanf(param, "%d %d %d %d %s", &object.x1, &object.y1, &object.x2, &object.y2, elem0) == 5)
337     {
338       object.type = GD_RECTANGLE;
339       object.element = gd_get_element_from_string (elem0);
340
341       return get_memcpy(&object, sizeof (GdObject));
342     }
343
344     return NULL;
345   }
346
347   /* FILLED RECTANGLE OBJECT */
348   if (strcasecmp(name, "FillRect") == 0)
349   {
350     int paramcount;
351
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;
354
355     if (paramcount == 6)
356     {
357       object.element = gd_get_element_from_string (elem0);
358       object.fill_element = gd_get_element_from_string (elem1);
359
360       return get_memcpy(&object, sizeof (GdObject));
361     }
362
363     if (paramcount == 5)
364     {
365       object.element = object.fill_element = gd_get_element_from_string (elem0);
366
367       return get_memcpy(&object, sizeof (GdObject));
368     }
369
370     return NULL;
371   }
372
373   /* RASTER */
374   if (strcasecmp(name, "Raster") == 0)
375   {
376     int nx, ny;
377
378     if (sscanf(param, "%d %d %d %d %d %d %s", &object.x1, &object.y1, &nx, &ny, &object.dx, &object.dy, elem0) == 7)
379     {
380       nx--;
381       ny--;
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);
386
387       return get_memcpy(&object, sizeof (GdObject));
388     }
389
390     return NULL;
391   }
392
393   /* JOIN */
394   if (strcasecmp(name, "Join") == 0 ||
395       strcasecmp(name, "Add") == 0)
396   {
397     if (sscanf(param, "%d %d %s %s", &object.dx, &object.dy, elem0, elem1) == 4)
398     {
399       object.type = GD_JOIN;
400       object.element = gd_get_element_from_string (elem0);
401       object.fill_element = gd_get_element_from_string (elem1);
402
403       return get_memcpy(&object, sizeof (GdObject));
404     }
405
406     return NULL;
407   }
408
409   /* FILL TO BORDER OBJECT */
410   if (strcasecmp(name, "BoundaryFill") == 0)
411   {
412     if (sscanf(param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1) == 4)
413     {
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);
417
418       return get_memcpy(&object, sizeof (GdObject));
419     }
420
421     return NULL;
422   }
423
424   /* REPLACE FILL OBJECT */
425   if (strcasecmp(name, "FloodFill") == 0)
426   {
427     if (sscanf(param, "%d %d %s %s", &object.x1, &object.y1, elem0, elem1) == 4)
428     {
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);
432
433       return get_memcpy(&object, sizeof (GdObject));
434     }
435
436     return NULL;
437   }
438
439   /* MAZE OBJECT */
440   /* MAZE UNICURSAL OBJECT */
441   /* BRAID MAZE OBJECT */
442   if (strcasecmp(name, "Maze") == 0)
443   {
444     char type[100] = "perfect";
445
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)
447     {
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;
454       else
455       {
456         Warn("unknown maze type: %s, defaulting to perfect", type);
457         object.type = GD_MAZE;
458       }
459
460       object.element = gd_get_element_from_string (elem0);
461       object.fill_element = gd_get_element_from_string (elem1);
462
463       return get_memcpy(&object, sizeof (GdObject));
464     }
465
466     return NULL;
467   }
468
469   /* RANDOM FILL OBJECT */
470   if (strcasecmp(name, "RandomFill") == 0 ||
471       strcasecmp(name, "RandomFillC64") == 0)
472   {
473     static char **words = NULL;
474     int l, i;
475
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;
480     else
481       object.c64_random = FALSE;
482
483     if (sscanf(param, "%d %d %d %d", &object.x1, &object.y1, &object.x2, &object.y2) != 4)
484       return NULL;
485
486     if (words)
487       freeStringArray(words);
488
489     words = getSplitStringArray(param, " ", -1);
490     l = getStringArrayLength(words);
491
492     if (l < 10 || l > 19)
493       return NULL;
494
495     for (i = 0; i < 5; i++)
496       if (sscanf(words[4 + i], "%d", &object.seed[i]) != 1)
497         return NULL;
498
499     object.fill_element = gd_get_element_from_string(words[9]);
500
501     for (i = 0; i < 4; i++)
502     {
503       object.random_fill[i] = O_DIRT;
504       object.random_fill_probability[i] = 0;
505     }
506
507     for (i = 10; i < l - 1; i += 2)
508     {
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)
511         return NULL;
512     }
513
514     object.element = O_NONE;
515
516     if (l > 10 && l % 2 == 1)
517       object.element = gd_get_element_from_string(words[l - 1]);
518
519     return get_memcpy(&object, sizeof (GdObject));
520   }
521
522   /* COPY PASTE OBJECT */
523   if (strcasecmp(name, "CopyPaste") == 0)
524   {
525     char mirror[100] = "nomirror";
526     char flip[100] = "noflip";
527     object.type = GD_COPY_PASTE;
528
529     object.flip = object.mirror = FALSE;
530
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)
532       return NULL;
533
534     /* MIRROR PROPERTY */
535     if (strcasecmp(mirror, "mirror") == 0)
536       object.mirror = TRUE;
537     else if (strcasecmp(mirror, "nomirror") == 0)
538       object.mirror = FALSE;
539     else
540       Warn("invalid setting for copypaste mirror property: %s", mirror);
541
542     /* FLIP PROPERTY */
543     if (strcasecmp(flip, "flip") == 0)
544       object.flip = TRUE;
545     else if (strcasecmp(flip, "noflip") == 0)
546       object.flip = FALSE;
547     else
548       Warn("invalid setting for copypaste flip property: %s", flip);
549
550     return get_memcpy(&object, sizeof(GdObject));
551   }
552
553   return NULL;
554 }
555
556 /** drawing a line, using bresenham's */
557 static void draw_line (GdCave *cave, const GdObject *object)
558 {
559   int x, y, x1, y1, x2, y2;
560   boolean steep;
561   int error, dx, dy, ystep;
562
563   x1 = object->x1;
564   y1 = object->y1, x2 = object->x2;
565   y2 = object->y2;
566   steep = ABS (y2 - y1) > ABS (x2 - x1);
567
568   if (steep)
569   {
570     x = x1;
571     x1 = y1;
572     y1 = x;
573     x = x2;
574     x2 = y2;
575     y2 = x;
576   }
577
578   if (x1 > x2)
579   {
580     x = x1;
581     x1 = x2;
582     x2 = x;
583     x = y1;
584     y1 = y2;
585     y2 = x;
586   }
587
588   dx = x2 - x1;
589   dy = ABS (y2 - y1);
590   y = y1;
591   error = 0;
592   ystep = (y1 < y2) ? 1 : -1;
593
594   for (x = x1; x <= x2; x++)
595   {
596     if (steep)
597       gd_cave_store_rc (cave, y, x, object->element, object);
598     else
599       gd_cave_store_rc (cave, x, y, object->element, object);
600
601     error += dy;
602
603     if (error * 2 >= dx)
604     {
605       y += ystep;
606       error -= dx;
607     }
608   }
609 }
610
611
612
613 static void draw_fill_replace_proc(GdCave *cave, int x, int y, const GdObject *object)
614 {
615   /* fill with border so we do not come back */
616   gd_cave_store_rc(cave, x, y, object->fill_element, object);
617
618   if (x > 0 && gd_cave_get_rc(cave, x - 1, y) == object->element)
619     draw_fill_replace_proc(cave, x - 1, y, object);
620
621   if (y > 0 && gd_cave_get_rc(cave, x, y - 1) == object->element)
622     draw_fill_replace_proc(cave, x, y - 1, object);
623
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);
626
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);
629 }
630
631 static void draw_fill_replace (GdCave *cave, const GdObject *object)
632 {
633   /* check bounds */
634   if (object->x1 < 0 ||
635       object->y1 < 0 ||
636       object->x1 >= cave->w ||
637       object->y1 >= cave->h)
638     return;
639
640   if (object->element == object->fill_element)
641     return;
642
643   /* this procedure fills the area with the object->element. */
644   draw_fill_replace_proc(cave, object->x1, object->y1, object);
645 }
646
647 static void draw_fill_border_proc (GdCave *cave, int x, int y, const GdObject *object)
648 {
649   /* fill with border so we do not come back */
650   gd_cave_store_rc(cave, x, y, object->element, object);
651
652   if (x > 0 && gd_cave_get_rc(cave, x - 1, y) != object->element)
653     draw_fill_border_proc(cave, x - 1, y, object);
654
655   if (y > 0 && gd_cave_get_rc(cave, x, y - 1) != object->element)
656     draw_fill_border_proc(cave, x, y - 1, object);
657
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);
660
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);
663 }
664
665 static void draw_fill_border (GdCave *cave, const GdObject *object)
666 {
667   int x, y;
668
669   /* check bounds */
670   if (object->x1 < 0 ||
671       object->y1 < 0 ||
672       object->x1 >= cave->w ||
673       object->y1 >= cave->h)
674     return;
675
676   /* this procedure fills the area with the object->element. */
677   draw_fill_border_proc(cave, object->x1, object->y1, object);
678
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;
685 }
686
687 /* rectangle, frame only */
688 static void draw_rectangle(GdCave *cave, const GdObject *object)
689 {
690   int x1, y1, x2, y2, x, y;
691
692   /* reorder coordinates if not drawing from northwest to southeast */
693   x1 = object->x1;
694   y1 = object->y1, x2 = object->x2;
695   y2 = object->y2;
696
697   if (y1 > y2)
698   {
699     y = y1;
700     y1 = y2;
701     y2 = y;
702   }
703
704   if (x1 > x2)
705   {
706     x = x1;
707     x1 = x2;
708     x2 = x;
709   }
710
711   for (x = x1; x <= x2; x++)
712   {
713     gd_cave_store_rc(cave, x, object->y1, object->element, object);
714     gd_cave_store_rc(cave, x, object->y2, object->element, object);
715   }
716
717   for (y = y1; y <= y2; y++)
718   {
719     gd_cave_store_rc(cave, object->x1, y, object->element, object);
720     gd_cave_store_rc(cave, object->x2, y, object->element, object);
721   }
722 }
723
724 /* rectangle, filled one */
725 static void draw_filled_rectangle(GdCave *cave, const GdObject *object)
726 {
727   int x1, y1, x2, y2, x, y;
728
729   /* reorder coordinates if not drawing from northwest to southeast */
730   x1 = object->x1;
731   y1 = object->y1, x2 = object->x2;
732   y2 = object->y2;
733
734   if (y1 > y2)
735   {
736     y = y1;
737     y1 = y2;
738     y2 = y;
739   }
740
741   if (x1 > x2)
742   {
743     x = x1;
744     x1 = x2;
745     x2 = x;
746   }
747
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 ||
751                                     y == object->y2 ||
752                                     x == object->x1 ||
753                                     x == object->x2) ? object->element : object->fill_element, object);
754 }
755
756 /* something like ordered fill, increment is dx and dy. */
757 static void draw_raster(GdCave *cave, const GdObject *object)
758 {
759   int x, y, x1, y1, x2, y2;
760   int dx, dy;
761
762   /* reorder coordinates if not drawing from northwest to southeast */
763   x1 = object->x1;
764   y1 = object->y1;
765   x2 = object->x2;
766   y2 = object->y2;
767
768   if (y1 > y2)
769   {
770     y = y1;
771     y1 = y2;
772     y2 = y;
773   }
774
775   if (x1 > x2)
776   {
777     x = x1;
778     x1 = x2;
779     x2 = x;
780   }
781
782   dx = object->dx;
783
784   if (dx < 1)
785     dx = 1;
786
787   dy = object->dy;
788
789   if (dy < 1)
790     dy = 1;
791
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);
795 }
796
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)
799 {
800   int x, y;
801
802   for (y = 0; y < cave->h; y++)
803   {
804     for (x = 0; x < cave->w; x++)
805     {
806       if (cave->map[y][x] == object->element)
807       {
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)
813           nx -= cave->w, ny++;
814
815         gd_cave_store_rc(cave, nx, ny, object->fill_element, object);
816       }
817     }
818   }
819 }
820
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)
824 {
825   int dirmask = 15;
826
827   maze[y][x] = TRUE;
828   while (dirmask != 0)
829   {
830     int dir;
831
832     /* horiz or vert */
833     dir = gd_rand_int_range(rand, 0, 100) < horiz ? 2 : 0;
834
835     /* if no horizontal movement possible, choose vertical */
836     if (dir == 2 && (dirmask & 12) == 0)
837       dir = 0;
838     else if (dir == 0 && (dirmask & 3) == 0)    /* and vice versa */
839       dir = 2;
840
841     dir += gd_rand_int_range(rand, 0, 2);                /* dir */
842     if (dirmask & (1 << dir))
843     {
844       dirmask &= ~(1 << dir);
845
846       switch (dir)
847       {
848         case 0:    /* up */
849           if (y >= 2 && !maze[y - 2][x])
850           {
851             maze[y - 1][x] = TRUE;
852             mazegen(rand, maze, width, height, x, y - 2, horiz);
853           }
854           break;
855
856         case 1:    /* down */
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);
860           }
861           break;
862
863         case 2:    /* left */
864           if (x >= 2 && !maze[y][x - 2]) {
865             maze[y][x - 1] = TRUE;
866             mazegen(rand, maze, width, height, x - 2, y, horiz);
867           }
868           break;
869
870         case 3:    /* right */
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);
874           }
875           break;
876
877         default:
878           break;
879       }
880     }
881   }
882 }
883
884 static void braidmaze(GdRand *rand, boolean **maze, int w, int h)
885 {
886   int x, y;
887
888   for (y = 0; y < h; y += 2)
889   {
890     for (x = 0; x < w; x += 2)
891     {
892       int closed = 0, dirs = 0;
893       int closed_dirs[4];
894
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])
897       {
898         /* closed from this side. */
899         closed++;
900
901         /* if not the edge, we might open this wall (carve a path) to remove a dead end */
902         if (x > 0)
903           closed_dirs[dirs++] = GD_MV_LEFT;
904       }
905
906       /* other 3 directions similar */
907       if (y < 1 || !maze[y - 1][x])
908       {
909         closed++;
910         if (y > 0)
911           closed_dirs[dirs++] = GD_MV_UP;
912       }
913
914       if (x >= w - 1 || !maze[y][x + 1])
915       {
916         closed++;
917         if (x < w - 1)
918           closed_dirs[dirs++] = GD_MV_RIGHT;
919       }
920
921       if (y >= h - 1 || !maze[y + 1][x]) {
922         closed++;
923         if (y < h - 1)
924           closed_dirs[dirs++] = GD_MV_DOWN;
925       }
926
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)
930       {
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)];
933
934         switch (dir)
935         {
936           case GD_MV_LEFT:
937             maze[y][x - 1] = TRUE; break;
938           case GD_MV_UP:
939             maze[y - 1][x] = TRUE; break;
940           case GD_MV_RIGHT:
941             maze[y][x + 1] = TRUE; break;
942           case GD_MV_DOWN:
943             maze[y + 1][x] = TRUE; break;
944         }
945       }
946     }
947   }
948 }
949
950 static void draw_maze(GdCave *cave, const GdObject *object, int level)
951 {
952   int x, y;
953   boolean **map;
954   int x1 = object->x1;
955   int y1 = object->y1;
956   int x2 = object->x2;
957   int y2 = object->y2;
958   int w, h, path, wall;
959   int xk, yk;
960   GdRand *rand;
961   int i,j;
962
963   /* change coordinates if not in correct order */
964   if (y1 > y2)
965   {
966     y = y1;
967     y1 = y2;
968     y2 = y;
969   }
970
971   if (x1 > x2)
972   {
973     x = x1;
974     x1 = x2;
975     x2 = x;
976   }
977
978   wall = object->dx;
979   if (wall < 1)
980     wall = 1;
981
982   path = object->dy;
983   if (path < 1)
984     path = 1;
985
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:
989
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)
994   */
995
996   /* number of passages for each side */
997   w = (x2 - x1 + 1 + wall) / (path + wall);
998   h = (y2 - y1 + 1 + wall) / (path + wall);
999
1000   /* and we calculate the size of the internal map */
1001   if (object->type == GD_MAZE_UNICURSAL)
1002   {
1003     /* for unicursal maze, width and height must be mod2 = 0,
1004        and we will convert to paths & walls later */
1005     w = w / 2 * 2;
1006     h = h / 2 * 2;
1007   }
1008   else
1009   {
1010     /* for normal maze */
1011     w = 2 * (w - 1) + 1;
1012     h = 2 * (h - 1) + 1;
1013   }
1014
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));
1019
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]);
1025
1026   if (w >= 1 && h >= 1)
1027     mazegen(rand, map, w, h, 0, 0, object->horiz);
1028
1029   if (object->type == GD_MAZE_BRAID)
1030     braidmaze(rand, map, w, h);
1031
1032   gd_rand_free(rand);
1033
1034   if (w >= 1 && h >= 1 && object->type == GD_MAZE_UNICURSAL)
1035   {
1036     boolean **unicursal;
1037
1038     /* convert to unicursal maze */
1039     /* original:
1040         xxx x
1041           x x
1042         xxxxx
1043
1044         unicursal:
1045         xxxxxxx xxx
1046         x     x x x
1047         xxxxx x x x
1048             x x x x
1049         xxxxx xxx x
1050         x         x
1051         xxxxxxxxxxx
1052     */
1053
1054     unicursal = checked_malloc((h * 2 - 1) * sizeof(boolean *));
1055
1056     for (y = 0; y < h * 2 - 1; y++)
1057       unicursal[y] = checked_calloc((w * 2 - 1) * sizeof(boolean));
1058
1059     for (y = 0; y < h; y++)
1060     {
1061       for(x = 0; x < w; x++)
1062       {
1063         if (map[y][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;
1068
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;
1073         }
1074       }
1075     }
1076
1077     /* free original map */
1078     for (y = 0; y < h; y++)
1079       free(map[y]);
1080     free(map);
1081
1082     /* change to new map - the unicursal maze */
1083     map = unicursal;
1084     h = h * 2 - 1;
1085     w = w * 2 - 1;
1086   }
1087
1088   /* copy map to cave with correct elements and size */
1089   /* now copy the map into the cave. the copying works like this...
1090      pwpwp
1091      xxxxx p
1092      x x   w
1093      x xxx p
1094      x     w
1095      xxxxx p
1096      columns and rows denoted with "p" are to be drawn with path width,
1097      the others with wall width. */
1098
1099   yk = y1;
1100
1101   for (y = 0; y < h; y++)
1102   {
1103     for (i = 0; i < (y % 2 == 0 ? path : wall); i++)
1104     {
1105       xk = x1;
1106
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);
1110
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);
1114
1115       yk++;
1116     }
1117   }
1118
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);
1123
1124   /* free map */
1125   for (y = 0; y < h; y++)
1126     free(map[y]);
1127   free(map);
1128 }
1129
1130 static void draw_random_fill(GdCave *cave, const GdObject *object, int level)
1131 {
1132   int x, y;
1133   int x1 = object->x1;
1134   int y1 = object->y1;
1135   int x2 = object->x2;
1136   int y2 = object->y2;
1137   GdRand *rand;
1138   GdC64RandomGenerator c64_rand;
1139   unsigned int seed;
1140
1141   /* -1 means that it should be different every time played. */
1142   if (object->seed[level] == -1)
1143     seed = gd_rand_int(cave->random);
1144   else
1145     seed = object->seed[level];
1146
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);
1150
1151   /* change coordinates if not in correct order */
1152   if (y1 > y2)
1153   {
1154     y = y1;
1155     y1 = y2;
1156     y2 = y;
1157   }
1158
1159   if (x1 > x2)
1160   {
1161     x = x1;
1162     x1 = x2;
1163     x2 = x;
1164   }
1165
1166   for (y = y1; y <= y2; y++)
1167   {
1168     for (x = x1; x <= x2; x++)
1169     {
1170       unsigned int randm;
1171       GdElement element;
1172
1173       if (object->c64_random)
1174         /* use c64 random generator */
1175         randm = gd_c64_random(&c64_rand);
1176       else
1177         /* use the much better glib random generator */
1178         randm = gd_rand_int_range(rand, 0, 256);
1179
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];
1189
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);
1193     }
1194   }
1195
1196   gd_rand_free(rand);
1197 }
1198
1199
1200 static void draw_copy_paste(GdCave *cave, const GdObject *object)
1201 {
1202   int x1 = object->x1, y1 = object->y1, x2 = object->x2, y2 = object->y2;
1203   int x, y;    /* iterators */
1204   int w, h;
1205   GdElement *clipboard;
1206
1207   /* reorder coordinates if not drawing from northwest to southeast */
1208   if (x2 < x1)
1209   {
1210     x = x2;
1211     x2 = x1;
1212     x1 = x;
1213   }
1214
1215   if (y2 < y1)
1216   {
1217     y = y2;
1218     y2 = y1;
1219     y1 = y;
1220   }
1221
1222   w = x2 - x1 + 1;
1223   h = y2 - y1 + 1;
1224
1225   clipboard = checked_malloc((w * h) * sizeof(GdElement));
1226
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);
1231
1232   for (y = 0; y < h; y++)
1233   {
1234     int ydest;
1235
1236     ydest = object->flip ? h - 1 - y : y;
1237
1238     for (x = 0; x < w; x++)
1239     {
1240       int xdest;
1241
1242       xdest = object->mirror ? w - 1 - x : x;
1243
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);
1247     }
1248   }
1249
1250   free(clipboard);
1251 }
1252
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)
1256 {
1257   switch (object->type)
1258   {
1259     case GD_POINT:
1260       /* single point */
1261       gd_cave_store_rc(cave, object->x1, object->y1, object->element, object);
1262       break;
1263
1264     case GD_LINE:
1265       draw_line(cave, object);
1266       break;
1267
1268     case GD_RECTANGLE:
1269       draw_rectangle(cave, object);
1270       break;
1271
1272     case GD_FILLED_RECTANGLE:
1273       draw_filled_rectangle(cave, object);
1274       break;
1275
1276     case GD_RASTER:
1277       draw_raster(cave, object);
1278       break;
1279
1280     case GD_JOIN:
1281       draw_join(cave, object);
1282       break;
1283
1284     case GD_FLOODFILL_BORDER:
1285       draw_fill_border(cave, object);
1286       break;
1287
1288     case GD_FLOODFILL_REPLACE:
1289       draw_fill_replace(cave, object);
1290       break;
1291
1292     case GD_MAZE:
1293     case GD_MAZE_UNICURSAL:
1294     case GD_MAZE_BRAID:
1295       draw_maze(cave, object, level);
1296       break;
1297
1298     case GD_RANDOM_FILL:
1299       draw_random_fill(cave, object, level);
1300       break;
1301
1302     case GD_COPY_PASTE:
1303       draw_copy_paste(cave, object);
1304       break;
1305
1306     case NONE:
1307       break;
1308
1309     default:
1310       Error("Unknown object %d", object->type);
1311       break;
1312   }
1313 }
1314
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)
1317 {
1318   GdCave *cave;
1319   GdElement element;
1320   int x, y;
1321   List *iter;
1322
1323   /* make a copy */
1324   cave = gd_cave_new_from_cave(data);
1325   cave->rendered = level + 1;
1326
1327   cave->render_seed = seed;
1328   cave->random = gd_rand_new_with_seed(cave->render_seed);
1329
1330   /* maps needed during drawing and gameplay */
1331   cave->objects_order = gd_cave_map_new(cave, void *);
1332
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];
1347
1348   if (!cave->map)
1349   {
1350     /* if we have no map, fill with predictable random generator. */
1351     cave->map = gd_cave_map_new(cave, GdElement);
1352
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));
1358     else
1359       gd_cave_c64_random_set_seed(cave, 0, data->level_rand[level]);
1360
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++)
1367     {
1368       for (x = 0; x < cave->w; x++)
1369       {
1370         unsigned int randm;
1371
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);
1375         else
1376           /* use c64 */
1377           randm = gd_cave_c64_random(cave);
1378
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];
1388
1389         gd_cave_store_rc(cave, x, y, element, NULL);
1390       }
1391     }
1392
1393     /* draw initial border */
1394     for (y = 0; y < cave->h; y++)
1395     {
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);
1398     }
1399
1400     for (x = 0; x < cave->w; x++)
1401     {
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);
1404     }
1405   }
1406   else
1407   {
1408     /* IF CAVE HAS A MAP, SIMPLY USE IT... no need to fill with random elements */
1409
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);
1413   }
1414
1415   if (data->level_slime_seed_c64[level] != -1)
1416   {
1417     /* if a specific slime seed is requested, change it now. */
1418
1419     gd_cave_c64_random_set_seed(cave,
1420                                 data->level_slime_seed_c64[level] / 256,
1421                                 data->level_slime_seed_c64[level] % 256);
1422   }
1423
1424   /* render cave objects above random data or map */
1425   for (iter = data->objects; iter; iter = list_next(iter))
1426   {
1427     GdObject *object = (GdObject *)iter->data;
1428
1429     if (object->levels & gd_levels_mask[level])
1430       gd_cave_draw_object(cave, iter->data, level);
1431   }
1432
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 */
1436   else
1437   {
1438     /* delay loop based timing... set something for first iteration,
1439        then later it will be calculated */
1440     cave->speed = 120;
1441
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];
1445   }
1446
1447   gd_cave_correct_visible_size(cave);
1448
1449   return cave;
1450 }
1451
1452 /*
1453    render cave at specified level.
1454    copy result to the map; remove objects.
1455    the cave will be map-based.
1456  */
1457 void gd_flatten_cave(GdCave *cave, const int level)
1458 {
1459   GdCave *rendered;
1460
1461   if (cave == NULL)
1462     return;
1463
1464   /* render cave at specified level to obtain map. seed = 0 */
1465   rendered = gd_cave_new_rendered(cave, level, 0);
1466
1467   /* forget old map without objects */
1468   gd_cave_map_free(cave->map);
1469
1470   /* copy new map to cave */
1471   cave->map = gd_cave_map_dup(rendered, map);
1472   gd_cave_free(rendered);
1473
1474   /* forget objects */
1475   list_foreach(cave->objects, (list_fn) free, NULL);
1476   cave->objects = NULL;
1477 }