changed most comments from C to C++ style for BD engine code
[rocksndiamonds.git] / src / game_bd / bd_cave.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 // arrays for movements
21 // also no1 and bd2 cave data import helpers; line direction coordinates
22 const int gd_dx[] =
23 {
24   0, 0, 1, 1, 1, 0, -1, -1, -1, 0, 2, 2, 2, 0, -2, -2, -2
25 };
26 const int gd_dy[] =
27 {
28   0, -1, -1, 0, 1, 1, 1, 0, -1, -2, -2, 0, 2, 2, 2, 0, -2
29 };
30
31 // TRANSLATORS:
32 // None here means "no direction to move"; when there is no gravity while stirring the pot.
33 static const char* direction_name[] =
34 {
35   N_("None"),
36   N_("Up"),
37   N_("Up+right"),
38   N_("Right"),
39   N_("Down+right"),
40   N_("Down"),
41   N_("Down+left"),
42   N_("Left"),
43   N_("Up+left")
44 };
45
46 static const char* direction_filename[] =
47 {
48   "none",
49   "up",
50   "upright",
51   "right",
52   "downright",
53   "down",
54   "downleft",
55   "left",
56   "upleft"
57 };
58
59 static const char* scheduling_name[] =
60 {
61   N_("Milliseconds"),
62   "BD1",
63   "BD2",
64   "Construction Kit",
65   "Crazy Dream 7",
66   "Atari BD1",
67   "Atari BD2/Construction Kit"
68 };
69
70 static const char* scheduling_filename[] =
71 {
72   "ms",
73   "bd1",
74   "bd2",
75   "plck",
76   "crdr7",
77   "bd1atari",
78   "bd2ckatari"
79 };
80
81 static HashTable *name_to_element;
82 GdElement gd_char_to_element[256];
83
84 // color of flashing the screen, gate opening to exit
85 const GdColor gd_flash_color = 0xFFFFC0;
86
87 // selected object in editor
88 const GdColor gd_select_color = 0x8080FF;
89
90 // direction to string and vice versa
91 const char *gd_direction_get_visible_name(GdDirection dir)
92 {
93   return direction_name[dir];
94 }
95
96 const char *gd_direction_get_filename(GdDirection dir)
97 {
98   return direction_filename[dir];
99 }
100
101 GdDirection gd_direction_from_string(const char *str)
102 {
103   int i;
104
105   for (i = 1; i < ARRAY_SIZE(direction_filename); i++)
106     if (strcasecmp(str, direction_filename[i]) == 0)
107       return (GdDirection) i;
108
109   Warn("invalid direction name '%s', defaulting to down", str);
110   return GD_MV_DOWN;
111 }
112
113 // scheduling name to string and vice versa
114 const char *gd_scheduling_get_filename(GdScheduling sched)
115 {
116   return scheduling_filename[sched];
117 }
118
119 const char *gd_scheduling_get_visible_name(GdScheduling sched)
120 {
121   return scheduling_name[sched];
122 }
123
124 GdScheduling gd_scheduling_from_string(const char *str)
125 {
126   int i;
127
128   for (i = 0; i < ARRAY_SIZE(scheduling_filename); i++)
129     if (strcasecmp(str, scheduling_filename[i]) == 0)
130       return (GdScheduling) i;
131
132   Warn("invalid scheduling name '%s', defaulting to plck", str);
133
134   return GD_SCHEDULING_PLCK;
135 }
136
137 /*
138   fill a given struct with default properties.
139   "str" is the struct (data),
140   "properties" describes the structure and its pointers,
141   "defaults" are the pieces of data which will be copied to str.
142 */
143 void gd_struct_set_defaults_from_array(void *str,
144                                        const GdStructDescriptor *properties,
145                                        GdPropertyDefault *defaults)
146 {
147   int i;
148
149   for (i = 0; defaults[i].offset != -1; i++)
150   {
151     void *pvalue = STRUCT_MEMBER_P(str, defaults[i].offset);
152     // these point to the same, but to avoid the awkward cast syntax
153     int *ivalue = pvalue;
154     GdElement *evalue = pvalue;
155     GdDirection *dvalue = pvalue;
156     GdScheduling *svalue = pvalue;
157     boolean *bvalue = pvalue;
158     GdColor *cvalue = pvalue;
159     int j, n;
160
161     // check which property we are talking about: find it in gd_cave_properties.
162     n = defaults[i].property_index;
163     if (n == 0)
164     {
165       while (properties[n].identifier != NULL &&
166              properties[n].offset != defaults[i].offset)
167         n++;
168
169       // remember so we will be fast later
170       defaults[i].property_index = n;
171     }
172
173     // some properties are arrays. this loop fills all with the same values
174     for (j = 0; j < properties[n].count; j++)
175     {
176       switch (properties[n].type)
177       {
178         // these are for the gui; do nothing
179         case GD_TAB:
180         case GD_LABEL:
181           // no default value for strings
182         case GD_TYPE_STRING:
183         case GD_TYPE_LONGSTRING:
184           break;
185
186         case GD_TYPE_RATIO:
187           // this is also an integer, difference is only when saving to bdcff
188         case GD_TYPE_INT:
189           if (defaults[i].defval < properties[n].min ||
190               defaults[i].defval > properties[n].max)
191             Warn("integer property %s out of range", properties[n].identifier);
192           ivalue[j] = defaults[i].defval;
193           break;
194
195         case GD_TYPE_PROBABILITY:
196           // floats are stored as integer, /million; but are integers
197           if (defaults[i].defval < 0 ||
198               defaults[i].defval > 1000000)
199             Warn("integer property %s out of range", properties[n].identifier);
200           ivalue[j] = defaults[i].defval;
201           break;
202
203         case GD_TYPE_BOOLEAN:
204           bvalue[j] = defaults[i].defval != 0;
205           break;
206
207         case GD_TYPE_ELEMENT:
208         case GD_TYPE_EFFECT:
209           evalue[j] = (GdElement) defaults[i].defval;
210           break;
211
212         case GD_TYPE_COLOR:
213           cvalue[j] = gd_c64_color(defaults[i].defval);
214           break;
215
216         case GD_TYPE_DIRECTION:
217           dvalue[j] = (GdDirection) defaults[i].defval;
218           break;
219
220         case GD_TYPE_SCHEDULING:
221           svalue[j] = (GdScheduling) defaults[i].defval;
222           break;
223       }
224     }
225   }
226 }
227
228 /*
229   creates the character->element conversion table; using
230   the fixed-in-the-bdcff characters. later, this table
231   may be filled with more elements.
232 */
233 void gd_create_char_to_element_table(void)
234 {
235   int i;
236
237   // fill all with unknown
238   for (i = 0; i < ARRAY_SIZE(gd_char_to_element); i++)
239     gd_char_to_element[i] = O_UNKNOWN;
240
241   // then set fixed characters
242   for (i = 0; i < O_MAX; i++)
243   {
244     int c = gd_elements[i].character;
245
246     if (c)
247     {
248       if (gd_char_to_element[c] != O_UNKNOWN)
249         Warn("Character %c already used for element %x", c, gd_char_to_element[c]);
250
251       gd_char_to_element[c] = i;
252     }
253   }
254 }
255
256 // search the element database for the specified character, and return the element.
257 GdElement gd_get_element_from_character (byte character)
258 {
259   if (gd_char_to_element[character] != O_UNKNOWN)
260     return gd_char_to_element[character];
261
262   Warn ("Invalid character representing element: %c", character);
263
264   return O_UNKNOWN;
265 }
266
267 /*
268   do some init; this function is to be called at the start of the application
269 */
270 void gd_cave_init(void)
271 {
272   int i;
273
274   // put names to a hash table
275   // this is a helper for file read operations
276   // maps copied strings to elements (integers)
277   name_to_element = create_hashtable(gd_str_case_hash, gd_str_case_equal, NULL, NULL);
278
279   for (i = 0; i < O_MAX; i++)
280   {
281     char *key;
282
283     key = getStringToUpper(gd_elements[i].filename);
284
285     if (hashtable_exists(name_to_element, key))         // hash value may be 0
286       Warn("Name %s already used for element %x", key, i);
287
288     hashtable_insert(name_to_element, key, INT_TO_PTR(i));
289     // ^^^ do not free "key", as hash table needs it during the whole time!
290
291     key = getStringCat2("SCANNED_", key);               // new string
292
293     hashtable_insert(name_to_element, key, INT_TO_PTR(i));
294     // once again, do not free "key" ^^^
295   }
296
297   // for compatibility with tim stridmann's memorydump->bdcff converter... .... ...
298   hashtable_insert(name_to_element, "HEXPANDING_WALL", INT_TO_PTR(O_H_EXPANDING_WALL));
299   hashtable_insert(name_to_element, "FALLING_DIAMOND", INT_TO_PTR(O_DIAMOND_F));
300   hashtable_insert(name_to_element, "FALLING_BOULDER", INT_TO_PTR(O_STONE_F));
301   hashtable_insert(name_to_element, "EXPLOSION1S", INT_TO_PTR(O_EXPLODE_1));
302   hashtable_insert(name_to_element, "EXPLOSION2S", INT_TO_PTR(O_EXPLODE_2));
303   hashtable_insert(name_to_element, "EXPLOSION3S", INT_TO_PTR(O_EXPLODE_3));
304   hashtable_insert(name_to_element, "EXPLOSION4S", INT_TO_PTR(O_EXPLODE_4));
305   hashtable_insert(name_to_element, "EXPLOSION5S", INT_TO_PTR(O_EXPLODE_5));
306   hashtable_insert(name_to_element, "EXPLOSION1D", INT_TO_PTR(O_PRE_DIA_1));
307   hashtable_insert(name_to_element, "EXPLOSION2D", INT_TO_PTR(O_PRE_DIA_2));
308   hashtable_insert(name_to_element, "EXPLOSION3D", INT_TO_PTR(O_PRE_DIA_3));
309   hashtable_insert(name_to_element, "EXPLOSION4D", INT_TO_PTR(O_PRE_DIA_4));
310   hashtable_insert(name_to_element, "EXPLOSION5D", INT_TO_PTR(O_PRE_DIA_5));
311   hashtable_insert(name_to_element, "WALL2", INT_TO_PTR(O_STEEL_EXPLODABLE));
312
313   // compatibility with old bd-faq (pre disassembly of bladder)
314   hashtable_insert(name_to_element, "BLADDERd9", INT_TO_PTR(O_BLADDER_8));
315
316   // create table to show errors at the start of the application
317   gd_create_char_to_element_table();
318 }
319
320 // search the element database for the specified name, and return the element
321 GdElement gd_get_element_from_string (const char *string)
322 {
323   char *upper = getStringToUpper(string);
324   void *value;
325   boolean found;
326
327   if (!string)
328   {
329     Warn("Invalid string representing element: (null)");
330     return O_UNKNOWN;
331   }
332
333   found = hashtable_exists(name_to_element, upper);     // hash value may be 0
334   if (found)
335     value = hashtable_search(name_to_element, upper);
336   free(upper);
337   if (found)
338     return (GdElement) (PTR_TO_INT(value));
339
340   Warn("Invalid string representing element: '%s'", string);
341
342   return O_UNKNOWN;
343 }
344
345 void gd_cave_set_defaults_from_array(GdCave* cave, GdPropertyDefault *defaults)
346 {
347   gd_struct_set_defaults_from_array(cave, gd_cave_properties, defaults);
348 }
349
350 /*
351   load default values from description array
352   these are default for gdash and bdcff.
353 */
354 void gd_cave_set_gdash_defaults(GdCave* cave)
355 {
356   int i;
357
358   gd_cave_set_defaults_from_array(cave, gd_cave_defaults_gdash);
359
360   // these did not fit into the descriptor array
361   for (i = 0; i < 5; i++)
362   {
363     cave->level_rand[i] = i;
364     cave->level_timevalue[i] = i + 1;
365   }
366 }
367
368 // for quicksort. compares two highscores.
369 int gd_highscore_compare(const void *a, const void *b)
370 {
371   const GdHighScore *ha = a;
372   const GdHighScore *hb = b;
373   return hb->score - ha->score;
374 }
375
376 void gd_clear_highscore(GdHighScore *hs)
377 {
378   int i;
379
380   for (i = 0; i < GD_HIGHSCORE_NUM; i++)
381   {
382     strcpy(hs[i].name, "");
383     hs[i].score = 0;
384   }
385 }
386
387 boolean gd_has_highscore(GdHighScore *hs)
388 {
389   return hs[0].score > 0;
390 }
391
392 // return true if score achieved is a highscore
393 boolean gd_is_highscore(GdHighScore *scores, int score)
394 {
395   // if score is above zero AND bigger than the last one
396   if (score > 0 && score > scores[GD_HIGHSCORE_NUM-1].score)
397     return TRUE;
398
399   return FALSE;
400 }
401
402 int gd_add_highscore(GdHighScore *highscores, const char *name, int score)
403 {
404   int i;
405
406   if (!gd_is_highscore(highscores, score))
407     return -1;
408
409   // overwrite the last one
410   gd_strcpy(highscores[GD_HIGHSCORE_NUM-1].name, name);
411   highscores[GD_HIGHSCORE_NUM-1].score = score;
412
413   // and sort
414   qsort(highscores, GD_HIGHSCORE_NUM, sizeof(GdHighScore), gd_highscore_compare);
415
416   for (i = 0; i < GD_HIGHSCORE_NUM; i++)
417     if (strEqual(highscores[i].name, name) && highscores[i].score == score)
418       return i;
419
420   return -1;
421 }
422
423 // for the case-insensitive hash keys
424 int gd_str_case_equal(void *s1, void *s2)
425 {
426   return strcasecmp(s1, s2) == 0;
427 }
428
429 unsigned int gd_str_case_hash(void *v)
430 {
431   char *upper = getStringToUpper(v);
432   unsigned int hash = get_hash_from_string(upper);
433
434   free(upper);
435
436   return hash;
437 }
438
439 /*
440   create new cave with default values.
441   sets every value, also default size, diamond value etc.
442 */
443 GdCave *gd_cave_new(void)
444 {
445   GdCave *cave;
446
447   cave = checked_calloc(sizeof(GdCave));
448
449   // hash table which stores unknown tags as strings.
450   cave->tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
451
452   gd_cave_set_gdash_defaults(cave);
453
454   return cave;
455 }
456
457 /*
458   cave maps.
459   cave maps are continuous areas in memory. the allocated memory
460   is width * height * bytes_per_cell long.
461   the cave map[0] stores the pointer given by g_malloc().
462   the map itself is also an allocated array of pointers to the
463   beginning of rows.
464   therefore:
465   rows = new (pointers to rows);
466   rows[0] = new map
467   rows[1..h-1] = rows[0] + width * bytes
468
469   freeing this:
470   free(rows[0])
471   free(rows)
472 */
473
474 /*
475   allocate a cave map-like array, and initialize to zero.
476   one cell is cell_size bytes long.
477 */
478 void *gd_cave_map_new_for_cave(const GdCave *cave, const int cell_size)
479 {
480   void **rows;                // this is void**, pointer to array of ...
481   int y;
482
483   rows = checked_malloc((cave->h) * sizeof(void *));
484   rows[0] = checked_calloc(cell_size * cave->w * cave->h);
485
486   for (y = 1; y < cave->h; y++)
487     // base pointer + num_of_bytes_per_element * width * number_of_row; as sizeof(char) = 1
488     rows[y] = (char *)rows[0] + cell_size * cave->w * y;
489
490   return rows;
491 }
492
493 /*
494   duplicate map
495
496   if map is null, this also returns null.
497 */
498 void *gd_cave_map_dup_size(const GdCave *cave, const void *map, const int cell_size)
499 {
500   void **rows;
501   void **maplines = (void **)map;
502   int y;
503
504   if (!map)
505     return NULL;
506
507   rows = checked_malloc((cave->h) * sizeof(void *));
508   rows[0] = get_memcpy (maplines[0], cell_size * cave->w * cave->h);
509
510   for (y = 1; y < cave->h; y++)
511     rows[y] = (char *)rows[0] + cell_size * cave->w * y;
512
513   return rows;
514 }
515
516 void gd_cave_map_free(void *map)
517 {
518   void **maplines = (void **) map;
519
520   if (!map)
521     return;
522
523   free(maplines[0]);
524   free(map);
525 }
526
527 /*
528   frees memory associated to cave
529 */
530 void gd_cave_free(GdCave *cave)
531 {
532   int i;
533
534   if (!cave)
535     return;
536
537   if (cave->tags)
538     hashtable_destroy(cave->tags);
539
540   if (cave->random)    // random generator is a GdRand *
541     gd_rand_free(cave->random);
542
543   // free strings
544   for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
545     if (gd_cave_properties[i].type == GD_TYPE_LONGSTRING)
546       checked_free(STRUCT_MEMBER(char *, cave, gd_cave_properties[i].offset));
547
548   // map
549   gd_cave_map_free(cave->map);
550
551   // rendered data
552   gd_cave_map_free(cave->objects_order);
553
554   // hammered walls to reappear data
555   gd_cave_map_free(cave->hammered_reappear);
556
557   // free objects
558   list_foreach(cave->objects, (list_fn) free, NULL);
559   list_free(cave->objects);
560
561   // free replays
562   list_foreach(cave->replays, (list_fn) gd_replay_free, NULL);
563   list_free(cave->replays);
564
565   // freeing main pointer
566   free (cave);
567 }
568
569 static void hash_copy_foreach(const char *key, const char *value, HashTable *dest)
570 {
571   hashtable_insert(dest, getStringCopy(key), getStringCopy(value));
572 }
573
574 // copy cave from src to destination, with duplicating dynamically allocated data
575 void gd_cave_copy(GdCave *dest, const GdCave *src)
576 {
577   int i;
578
579   // copy entire data
580   memmove(dest, src, sizeof(GdCave));
581
582   // but duplicate dynamic data
583   dest->tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
584
585   if (src->tags)
586     hashtable_foreach(src->tags, (hashtable_fn)hash_copy_foreach, dest->tags);
587
588   dest->map = gd_cave_map_dup(src, map);
589   dest->hammered_reappear = gd_cave_map_dup(src, hammered_reappear);
590
591   // for longstrings
592   for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
593     if (gd_cave_properties[i].type == GD_TYPE_LONGSTRING)
594       STRUCT_MEMBER(char *, dest, gd_cave_properties[i].offset) =
595         getStringCopy(STRUCT_MEMBER(char *, src, gd_cave_properties[i].offset));
596
597   // no reason to copy this
598   dest->objects_order = NULL;
599
600   // copy objects list
601   if (src->objects)
602   {
603     List *iter;
604
605     dest->objects = NULL;    // new empty list
606     for (iter = src->objects; iter != NULL; iter = iter->next) // do a deep copy
607       dest->objects = list_append(dest->objects, get_memcpy (iter->data, sizeof (GdObject)));
608   }
609
610   // copy replays
611   if (src->replays)
612   {
613     List *iter;
614
615     dest->replays = NULL;
616     for (iter = src->replays; iter != NULL; iter = iter->next) // do a deep copy
617       dest->replays = list_append(dest->replays, gd_replay_new_from_replay(iter->data));
618   }
619
620   // copy random number generator
621   if (src->random)
622     dest->random = gd_rand_copy(src->random);
623 }
624
625 // create new cave, which is a copy of the cave given.
626 GdCave *gd_cave_new_from_cave(const GdCave *orig)
627 {
628   GdCave *cave;
629
630   cave = gd_cave_new();
631   gd_cave_copy(cave, orig);
632
633   return cave;
634 }
635
636 /*
637   Put an object to the specified position.
638   Performs range checking.
639   If wraparound objects are selected, wraps around x coordinates, with or without lineshift.
640   (The y coordinate is not wrapped, as it did not work like that on the c64)
641   order is a pointer to the GdObject describing this object. Thus the editor can identify
642   which cell was created by which object.
643 */
644 void gd_cave_store_rc(GdCave *cave, int x, int y, const GdElement element, const void *order)
645 {
646   // if we do not need to draw, exit now
647   if (element == O_NONE)
648     return;
649
650   // check bounds
651   if (cave->wraparound_objects)
652   {
653     if (cave->lineshift)
654     {
655       // fit x coordinate within range, with correcting y at the same time
656       while (x < 0)
657       {
658         x += cave->w;    // out of bounds on the left...
659         y--;             // previous row
660       }
661
662       while (x >= cave->w)
663       {
664         x -= cave->w;
665         y++;
666       }
667
668       // lineshifting does not fix the y coordinates.
669       // if out of bounds, element will not be displayed.
670       // if such an object appeared in the c64 game, well, it was a buffer overrun.
671     }
672     else
673     {
674       // non lineshifting: changing x does not change y coordinate.
675       while (x < 0)
676         x += cave->w;
677
678       while (x >= cave->w)
679         x -= cave->w;
680
681       // after that, fix y coordinate
682       while (y < 0)
683         y += cave->h;
684
685       while (y >= cave->h)
686         y -= cave->h;
687     }
688   }
689
690   // if the above wraparound code fixed the coordinates, this will always be true.
691   // but see the above comment for lineshifting y coordinate
692   if (x >= 0 && x < cave->w && y >= 0 && y < cave->h)
693   {
694     cave->map[y][x] = element;
695     cave->objects_order[y][x] = (void *)order;
696   }
697 }
698
699 GdElement gd_cave_get_rc(const GdCave *cave, int x, int y)
700 {
701   // always fix coordinates as if cave was wraparound.
702
703   // fix x coordinate
704   if (cave->lineshift)
705   {
706     // fit x coordinate within range, with correcting y at the same time
707     while (x < 0)
708     {
709       x += cave->w;    // out of bounds on the left...
710       y--;             // previous row
711     }
712     while (x >= cave->w)
713     {
714       x -= cave->w;
715       y++;
716     }
717   }
718   else
719   {
720     // non lineshifting: changing x does not change y coordinate.
721     while (x < 0)
722       x += cave->w;
723
724     while (x >= cave->w)
725       x -= cave->w;
726   }
727
728   // after that, fix y coordinate
729   while (y < 0)
730     y += cave->h;
731
732   while (y >= cave->h)
733     y -= cave->h;
734
735   return cave->map[y][x];
736 }
737
738 unsigned int gd_c64_random(GdC64RandomGenerator *rand)
739 {
740   unsigned int temp_rand_1, temp_rand_2, carry, result;
741
742   temp_rand_1 = (rand->rand_seed_1 & 0x0001) << 7;
743   temp_rand_2 = (rand->rand_seed_2 >> 1) & 0x007F;
744   result = (rand->rand_seed_2) + ((rand->rand_seed_2 & 0x0001) << 7);
745   carry = (result >> 8);
746   result = result & 0x00FF;
747   result = result + carry + 0x13;
748   carry = (result >> 8);
749   rand->rand_seed_2 = result & 0x00FF;
750   result = rand->rand_seed_1 + carry + temp_rand_1;
751   carry = (result >> 8);
752   result = result & 0x00FF;
753   result = result + carry + temp_rand_2;
754   rand->rand_seed_1 = result & 0x00FF;
755
756   return rand->rand_seed_1;
757 }
758
759 /*
760   C64 BD predictable random number generator.
761   Used to load the original caves imported from c64 files.
762   Also by the predictable slime.
763 */
764 unsigned int gd_cave_c64_random(GdCave *cave)
765 {
766   return gd_c64_random(&cave->c64_rand);
767 }
768
769 void gd_c64_random_set_seed(GdC64RandomGenerator *rand, int seed1, int seed2)
770 {
771   rand->rand_seed_1 = seed1;
772   rand->rand_seed_2 = seed2;
773 }
774
775 void gd_cave_c64_random_set_seed(GdCave *cave, int seed1, int seed2)
776 {
777   gd_c64_random_set_seed(&cave->c64_rand, seed1, seed2);
778 }
779
780 void gd_cave_set_random_c64_colors(GdCave *cave)
781 {
782   const int bright_colors[] = { 1, 3, 7 };
783   const int dark_colors[] = { 2, 6, 8, 9, 11 };
784
785   // always black
786   cave->colorb = gd_c64_color(0);
787   cave->color0 = gd_c64_color(0);
788
789   // choose some bright color for brick
790   cave->color3 = gd_c64_color(bright_colors[gd_random_int_range(0, ARRAY_SIZE(bright_colors))]);
791
792   // choose a dark color for dirt, but should not be == color of brick
793   do
794   {
795     cave->color1 = gd_c64_color(dark_colors[gd_random_int_range(0, ARRAY_SIZE(dark_colors))]);
796   }
797   while (cave->color1 == cave->color3);    // so it is not the same as color 1
798
799   // choose any but black for steel wall, but should not be == brick or dirt
800   do
801   {
802     // between 1 and 15 - do not use black for this.
803     cave->color2 = gd_c64_color(gd_random_int_range(1, 16));
804   }
805   while (cave->color1 == cave->color2 || cave->color2 == cave->color3);    // so colors are not the same
806
807   // copy amoeba and slime color
808   cave->color4 = cave->color3;
809   cave->color5 = cave->color1;
810 }
811
812 /*
813   shrink cave
814   if last line or last row is just steel wall (or (invisible) outbox).
815   used after loading a game for playing.
816   after this, ew and eh will contain the effective width and height.
817 */
818 void gd_cave_auto_shrink(GdCave *cave)
819 {
820
821   int x, y;
822   enum
823   {
824     STEEL_ONLY,
825     STEEL_OR_OTHER,
826     NO_SHRINK
827   }
828   empty;
829
830   // set to maximum size, then try to shrink
831   cave->x1 = 0;
832   cave->y1 = 0;
833   cave->x2 = cave->w - 1;
834   cave->y2 = cave->h - 1;
835
836   // search for empty, steel-wall-only last rows.
837   // clear all lines, which are only steel wall.
838   // and clear only one line, which is steel wall, but also has a player or an outbox.
839   empty = STEEL_ONLY;
840
841   do
842   {
843     for (y = cave->y2 - 1; y <= cave->y2; y++)
844     {
845       for (x = cave->x1; x <= cave->x2; x++)
846       {
847         switch (gd_cave_get_rc (cave, x, y))
848         {
849           // if steels only, this is to be deleted.
850           case O_STEEL:
851             break;
852
853           case O_PRE_OUTBOX:
854           case O_PRE_INVIS_OUTBOX:
855           case O_INBOX:
856             if (empty == STEEL_OR_OTHER)
857               empty = NO_SHRINK;
858
859             // if this, delete only this one, and exit.
860             if (empty == STEEL_ONLY)
861               empty = STEEL_OR_OTHER;
862             break;
863
864           default:
865             // anything else, that should be left in the cave.
866             empty = NO_SHRINK;
867             break;
868         }
869       }
870     }
871
872     // shrink if full steel or steel and player/outbox.
873     if (empty != NO_SHRINK)
874       cave->y2--;            // one row shorter
875   }
876   while (empty == STEEL_ONLY);    // if found just steels, repeat.
877
878   // search for empty, steel-wall-only first rows.
879   empty = STEEL_ONLY;
880
881   do
882   {
883     for (y = cave->y1; y <= cave->y1 + 1; y++)
884     {
885       for (x = cave->x1; x <= cave->x2; x++)
886       {
887         switch (gd_cave_get_rc (cave, x, y))
888         {
889           case O_STEEL:
890             break;
891
892           case O_PRE_OUTBOX:
893           case O_PRE_INVIS_OUTBOX:
894           case O_INBOX:
895             // shrink only lines, which have only ONE player or outbox.
896             // this is for bd4 intermission 2, for example.
897             if (empty == STEEL_OR_OTHER)
898               empty = NO_SHRINK;
899             if (empty == STEEL_ONLY)
900               empty = STEEL_OR_OTHER;
901             break;
902
903           default:
904             empty = NO_SHRINK;
905             break;
906         }
907       }
908     }
909
910     if (empty != NO_SHRINK)
911       cave->y1++;
912   }
913   while (empty == STEEL_ONLY);    // if found one, repeat.
914
915   // empty last columns.
916   empty = STEEL_ONLY;
917
918   do
919   {
920     for (y = cave->y1; y <= cave->y2; y++)
921     {
922       for (x = cave->x2 - 1; x <= cave->x2; x++)
923       {
924         switch (gd_cave_get_rc (cave, x, y))
925         {
926           case O_STEEL:
927             break;
928
929           case O_PRE_OUTBOX:
930           case O_PRE_INVIS_OUTBOX:
931           case O_INBOX:
932             if (empty == STEEL_OR_OTHER)
933               empty = NO_SHRINK;
934             if (empty == STEEL_ONLY)
935               empty = STEEL_OR_OTHER;
936             break;
937
938           default:
939             empty = NO_SHRINK;
940             break;
941         }
942       }
943     }
944
945     // just remember that one column shorter.
946     // free will know the size of memchunk, no need to realloc!
947     if (empty != NO_SHRINK)
948       cave->x2--;
949   }
950   while (empty == STEEL_ONLY);    // if found one, repeat.
951
952   // empty first columns.
953   empty = STEEL_ONLY;
954
955   do
956   {
957     for (y = cave->y1; y <= cave->y2; y++)
958     {
959       for (x = cave->x1; x <= cave->x1 + 1; x++)
960       {
961         switch (gd_cave_get_rc (cave, x, y))
962         {
963           case O_STEEL:
964             break;
965
966           case O_PRE_OUTBOX:
967           case O_PRE_INVIS_OUTBOX:
968           case O_INBOX:
969             if (empty == STEEL_OR_OTHER)
970               empty = NO_SHRINK;
971             if (empty == STEEL_ONLY)
972               empty = STEEL_OR_OTHER;
973             break;
974
975           default:
976             empty = NO_SHRINK;
977             break;
978         }
979       }
980     }
981
982     if (empty != NO_SHRINK)
983       cave->x1++;
984   }
985   while (empty == STEEL_ONLY);    // if found one, repeat.
986 }
987
988 /*
989   check if cave visible part coordinates
990   are outside cave sizes, or not in the right order.
991   correct them if needed.
992 */
993 void gd_cave_correct_visible_size(GdCave *cave)
994 {
995   // change visible coordinates if they do not point to upperleft and lowerright
996   if (cave->x2 < cave->x1)
997   {
998     int t = cave->x2;
999     cave->x2 = cave->x1;
1000     cave->x1 = t;
1001   }
1002
1003   if (cave->y2 < cave->y1)
1004   {
1005     int t = cave->y2;
1006     cave->y2 = cave->y1;
1007     cave->y1 = t;
1008   }
1009
1010   if (cave->x1 < 0)
1011     cave->x1 = 0;
1012
1013   if (cave->y1 < 0)
1014     cave->y1 = 0;
1015
1016   if (cave->x2 > cave->w - 1)
1017     cave->x2 = cave->w - 1;
1018
1019   if (cave->y2 > cave->h - 1)
1020     cave->y2 = cave->h - 1;
1021 }
1022
1023 /*
1024   bd1 and similar engines had animation bits in cave data, to set which elements to animate
1025   (firefly, butterfly, amoeba).
1026   animating an element also caused some delay each frame; according to my measurements,
1027   around 2.6 ms/element.
1028 */
1029 static void cave_set_ckdelay_extra_for_animation(GdCave *cave)
1030 {
1031   int x, y;
1032   boolean has_amoeba = FALSE, has_firefly = FALSE, has_butterfly = FALSE;
1033
1034   for (y = 0; y < cave->h; y++)
1035   {
1036     for (x = 0; x < cave->w; x++)
1037     {
1038       switch (cave->map[y][x] & ~SCANNED)
1039       {
1040         case O_FIREFLY_1:
1041         case O_FIREFLY_2:
1042         case O_FIREFLY_3:
1043         case O_FIREFLY_4:
1044           has_firefly = TRUE;
1045           break;
1046
1047         case O_BUTTER_1:
1048         case O_BUTTER_2:
1049         case O_BUTTER_3:
1050         case O_BUTTER_4:
1051           has_butterfly = TRUE;
1052           break;
1053
1054         case O_AMOEBA:
1055           has_amoeba = TRUE;
1056           break;
1057       }
1058     }
1059   }
1060
1061   cave->ckdelay_extra_for_animation = 0;
1062   if (has_amoeba)
1063     cave->ckdelay_extra_for_animation += 2600;
1064   if (has_firefly)
1065     cave->ckdelay_extra_for_animation += 2600;
1066   if (has_butterfly)
1067     cave->ckdelay_extra_for_animation += 2600;
1068   if (has_amoeba)
1069     cave->ckdelay_extra_for_animation += 2600;
1070 }
1071
1072 // do some init - setup some cave variables before the game.
1073 void gd_cave_setup_for_game(GdCave *cave)
1074 {
1075   int x, y;
1076
1077   cave_set_ckdelay_extra_for_animation(cave);
1078
1079   // find the player which will be the one to scroll to at the beginning of the game
1080   // (before the player's birth)
1081   if (cave->active_is_first_found)
1082   {
1083     // uppermost player is active
1084     for (y = cave->h - 1; y >= 0; y--)
1085     { 
1086      for (x = cave->w - 1; x >= 0; x--)
1087      {
1088         if (cave->map[y][x] == O_INBOX)
1089         {
1090           cave->player_x = x;
1091           cave->player_y = y;
1092         }
1093      }
1094     }
1095   }
1096   else
1097   {
1098     // lowermost player is active
1099     for (y = 0; y < cave->h; y++)
1100     {
1101       for (x = 0; x < cave->w; x++)
1102       {
1103         if (cave->map[y][x] == O_INBOX)
1104         {
1105           cave->player_x = x;
1106           cave->player_y = y;
1107         }
1108       }
1109     }
1110   }
1111
1112   // select number of milliseconds (for pal and ntsc)
1113   cave->timing_factor = cave->pal_timing ? 1200 : 1000;
1114
1115   cave->time                    *= cave->timing_factor;
1116   cave->magic_wall_time         *= cave->timing_factor;
1117   cave->amoeba_time             *= cave->timing_factor;
1118   cave->amoeba_2_time           *= cave->timing_factor;
1119   cave->hatching_delay_time     *= cave->timing_factor;
1120
1121   if (cave->hammered_walls_reappear)
1122     cave->hammered_reappear = gd_cave_map_new(cave, int);
1123 }
1124
1125 // cave diamonds needed can be set to n<=0.
1126 // if so, count the diamonds at the time of the hatching, and decrement that value from
1127 // the number of diamonds found.
1128 // of course, this function is to be called from the cave engine, at the exact time of hatching.
1129 void gd_cave_count_diamonds(GdCave *cave)
1130 {
1131   int x, y;
1132
1133   // if automatically counting diamonds. if this was negative,
1134   // the sum will be this less than the number of all the diamonds in the cave
1135   if (cave->diamonds_needed <= 0)
1136   {
1137     for (y = 0; y < cave->h; y++)
1138       for (x = 0; x < cave->w; x++)
1139         if (cave->map[y][x] == O_DIAMOND)
1140           cave->diamonds_needed++;
1141
1142     // if still below zero, let this be 0, so gate will be open immediately
1143     if (cave->diamonds_needed < 0)
1144       cave->diamonds_needed = 0;
1145   }
1146 }
1147
1148 /*
1149   takes a cave and a gfx buffer, and fills the buffer with cell indexes.
1150   the indexes might change if bonus life flash is active (small lines in
1151   "SPACE" cells),
1152   for the paused state (which is used in gdash but not in sdash) - yellowish
1153   color.
1154   also one can select the animation frame (0..7) to draw the cave on. so the
1155   caller manages
1156   increasing that.
1157
1158   if a cell is changed, it is flagged with GD_REDRAW; the flag can be cleared
1159   by the caller.
1160 */
1161 void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer,
1162                       boolean bonus_life_flash, int animcycle, boolean hate_invisible_outbox)
1163 {
1164   static int player_blinking = 0;
1165   static int player_tapping = 0;
1166   int elemmapping[O_MAX];
1167   int elemdrawing[O_MAX];
1168   int x, y, map, draw;
1169
1170   if (cave->last_direction)
1171   {
1172     // he is moving, so stop blinking and tapping.
1173     player_blinking = 0;
1174     player_tapping = 0;
1175   }
1176   else
1177   {
1178     // he is idle, so animations can be done.
1179     if (animcycle == 0)
1180     {
1181       // blinking and tapping is started at the beginning of animation sequences.
1182       // 1/4 chance of blinking, every sequence.
1183       player_blinking = gd_random_int_range(0, 4) == 0;
1184
1185       // 1/16 chance of starting or stopping tapping.
1186       if (gd_random_int_range(0, 16) == 0)
1187         player_tapping = !player_tapping;
1188     }
1189   }
1190
1191   for (x = 0; x < O_MAX; x++)
1192   {
1193     elemmapping[x] = x;
1194     elemdrawing[x] = gd_elements[x].image_game;
1195   }
1196
1197   if (bonus_life_flash)
1198   {
1199     elemmapping[O_SPACE] = O_FAKE_BONUS;
1200     elemdrawing[O_SPACE] = gd_elements[O_FAKE_BONUS].image_game;
1201   }
1202
1203   elemmapping[O_MAGIC_WALL] = (cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK);
1204   elemdrawing[O_MAGIC_WALL] = gd_elements[cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK].image_game;
1205
1206   elemmapping[O_CREATURE_SWITCH] = (cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH);
1207   elemdrawing[O_CREATURE_SWITCH] = gd_elements[cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH].image_game;
1208
1209   elemmapping[O_EXPANDING_WALL_SWITCH] = (cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ);
1210   elemdrawing[O_EXPANDING_WALL_SWITCH] = gd_elements[cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ].image_game;
1211
1212   elemmapping[O_GRAVITY_SWITCH] = (cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH);
1213   elemdrawing[O_GRAVITY_SWITCH] = gd_elements[cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH].image_game;
1214
1215   elemmapping[O_REPLICATOR_SWITCH] = (cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF);
1216   elemdrawing[O_REPLICATOR_SWITCH] = gd_elements[cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF].image_game;
1217
1218   if (cave->replicators_active)
1219     // if the replicators are active, animate them.
1220     elemmapping[O_REPLICATOR] = O_REPLICATOR_ACTIVE;
1221
1222   if (!cave->replicators_active)
1223     // if the replicators are inactive, do not animate them.
1224     elemdrawing[O_REPLICATOR] = ABS(elemdrawing[O_REPLICATOR]);
1225
1226   elemmapping[O_CONVEYOR_SWITCH] = (cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF);
1227   elemdrawing[O_CONVEYOR_SWITCH] = gd_elements[cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF].image_game;
1228
1229   if (cave->conveyor_belts_direction_changed)
1230   {
1231     // if direction is changed, animation is changed.
1232     int temp;
1233
1234     elemmapping[O_CONVEYOR_LEFT] = O_CONVEYOR_RIGHT;
1235     elemmapping[O_CONVEYOR_RIGHT] = O_CONVEYOR_LEFT;
1236
1237     temp = elemdrawing[O_CONVEYOR_LEFT];
1238     elemdrawing[O_CONVEYOR_LEFT] = elemdrawing[O_CONVEYOR_RIGHT];
1239     elemdrawing[O_CONVEYOR_RIGHT] = temp;
1240
1241     elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_CHANGED;
1242     elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_CHANGED].image_game;
1243   }
1244   else
1245   {
1246     elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_NORMAL;
1247     elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_NORMAL].image_game;
1248   }
1249
1250   if (cave->conveyor_belts_active)
1251   {
1252     // keep potentially changed direction
1253     int offset = (O_CONVEYOR_LEFT_ACTIVE - O_CONVEYOR_LEFT);
1254
1255     // if they are running, animate them.
1256     elemmapping[O_CONVEYOR_LEFT]  += offset;
1257     elemmapping[O_CONVEYOR_RIGHT] += offset;
1258   }
1259   if (!cave->conveyor_belts_active)
1260   {
1261     // if they are not running, do not animate them.
1262     elemdrawing[O_CONVEYOR_LEFT] = ABS(elemdrawing[O_CONVEYOR_LEFT]);
1263     elemdrawing[O_CONVEYOR_RIGHT] = ABS(elemdrawing[O_CONVEYOR_RIGHT]);
1264   }
1265
1266   if (animcycle & 2)
1267   {
1268     // also a hack, like biter_switch
1269     elemdrawing[O_PNEUMATIC_ACTIVE_LEFT]  += 2;
1270     elemdrawing[O_PNEUMATIC_ACTIVE_RIGHT] += 2;
1271     elemdrawing[O_PLAYER_PNEUMATIC_LEFT]  += 2;
1272     elemdrawing[O_PLAYER_PNEUMATIC_RIGHT] += 2;
1273   }
1274
1275   if ((cave->last_direction) == GD_MV_STILL)
1276   {
1277     // player is idle.
1278     if (player_blinking && player_tapping)
1279     {
1280       map = O_PLAYER_TAP_BLINK;
1281       draw = gd_elements[O_PLAYER_TAP_BLINK].image_game;
1282     }
1283     else if (player_blinking)
1284     {
1285       map = O_PLAYER_BLINK;
1286       draw = gd_elements[O_PLAYER_BLINK].image_game;
1287     }
1288     else if (player_tapping)
1289     {
1290       map = O_PLAYER_TAP;
1291       draw = gd_elements[O_PLAYER_TAP].image_game;
1292     }
1293     else
1294     {
1295       map = O_PLAYER;
1296       draw = gd_elements[O_PLAYER].image_game;
1297     }
1298   }
1299   else if (cave->last_horizontal_direction == GD_MV_LEFT)
1300   {
1301     map = O_PLAYER_LEFT;
1302     draw = gd_elements[O_PLAYER_LEFT].image_game;
1303   }
1304   else
1305   {
1306     // of course this is GD_MV_RIGHT.
1307     map = O_PLAYER_RIGHT;
1308     draw = gd_elements[O_PLAYER_RIGHT].image_game;
1309   }
1310
1311   elemmapping[O_PLAYER] = map;
1312   elemmapping[O_PLAYER_GLUED] = map;
1313
1314   elemdrawing[O_PLAYER] = draw;
1315   elemdrawing[O_PLAYER_GLUED] = draw;
1316
1317   // player with bomb does not blink or tap - no graphics drawn for that.
1318   // running is drawn using w/o bomb cells */
1319   if (cave->last_direction != GD_MV_STILL)
1320   {
1321     elemmapping[O_PLAYER_BOMB] = map;
1322     elemdrawing[O_PLAYER_BOMB] = draw;
1323   }
1324
1325   elemmapping[O_INBOX] = (cave->inbox_flash_toggle ? O_INBOX_OPEN : O_INBOX_CLOSED);
1326   elemdrawing[O_INBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1327
1328   elemmapping[O_OUTBOX] = (cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED);
1329   elemdrawing[O_OUTBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1330
1331   // hack, not fit into gd_elements
1332   elemmapping[O_BITER_SWITCH] = O_BITER_SWITCH_1 + cave->biter_delay_frame;
1333   // hack, not fit into gd_elements
1334   elemdrawing[O_BITER_SWITCH] = gd_elements[O_BITER_SWITCH].image_game + cave->biter_delay_frame;
1335
1336   // visual effects
1337   elemmapping[O_DIRT] = cave->dirt_looks_like;
1338   elemmapping[O_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1339   elemmapping[O_V_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1340   elemmapping[O_H_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1341   elemmapping[O_AMOEBA_2] = cave->amoeba_2_looks_like;
1342
1343   // visual effects
1344   elemdrawing[O_DIRT] = elemdrawing[cave->dirt_looks_like];
1345   elemdrawing[O_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1346   elemdrawing[O_V_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1347   elemdrawing[O_H_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1348   elemdrawing[O_AMOEBA_2] = elemdrawing[cave->amoeba_2_looks_like];
1349
1350   // change only graphically
1351   if (hate_invisible_outbox)
1352   {
1353     elemmapping[O_PRE_INVIS_OUTBOX] = O_PRE_OUTBOX;
1354     elemmapping[O_INVIS_OUTBOX] = O_OUTBOX;
1355   }
1356
1357   if (hate_invisible_outbox)
1358   {
1359     elemdrawing[O_PRE_INVIS_OUTBOX] = elemdrawing[O_PRE_OUTBOX];
1360     elemdrawing[O_INVIS_OUTBOX] = elemdrawing[O_OUTBOX];
1361   }
1362
1363   for (y = cave->y1; y <= cave->y2; y++)
1364   {
1365     for (x = cave->x1; x <= cave->x2; x++)
1366     {
1367       GdElement actual = cave->map[y][x];
1368
1369       // if covered, real element is not important
1370       if (actual & COVERED)
1371         map = O_COVERED;
1372       else
1373         map = elemmapping[actual];
1374
1375       // if covered, real element is not important
1376       if (actual & COVERED)
1377         draw = gd_elements[O_COVERED].image_game;
1378       else
1379         draw = elemdrawing[actual];
1380
1381       // if negative, animated.
1382       if (draw < 0)
1383         draw = -draw + animcycle;
1384
1385       // flash
1386       if (cave->gate_open_flash)
1387         draw += GD_NUM_OF_CELLS;
1388
1389       // set to buffer, with caching
1390       if (element_buffer[y][x] != map)
1391         element_buffer[y][x] = map;
1392
1393       if (gfx_buffer[y][x] != draw)
1394         gfx_buffer[y][x] = draw | GD_REDRAW;
1395     }
1396   }
1397 }
1398
1399 /*
1400   cave time is rounded _UP_ to seconds. so at the exact moment when it
1401   changes from
1402   2sec remaining to 1sec remaining, the player has exactly one second.
1403   when it changes
1404   to zero, it is the exact moment of timeout.
1405
1406   internal time is milliseconds (or 1200 milliseconds for pal timing).
1407 */
1408 int gd_cave_time_show(const GdCave *cave, int internal_time)
1409 {
1410   return (internal_time + cave->timing_factor - 1) / cave->timing_factor;
1411 }
1412
1413 GdReplay *gd_replay_new(void)
1414 {
1415   GdReplay *rep;
1416
1417   rep = checked_calloc(sizeof(GdReplay));
1418   rep->movements = checked_calloc(sizeof(GdReplayMovements));
1419
1420   return rep;
1421 }
1422
1423 GdReplay *gd_replay_new_from_replay(GdReplay *orig)
1424 {
1425   GdReplay *rep;
1426
1427   rep = get_memcpy(orig, sizeof(GdReplay));
1428
1429   // replicate dynamic data
1430   rep->comment = getStringCopy(orig->comment);
1431   rep->movements = get_memcpy(orig->movements, sizeof(GdReplayMovements));
1432
1433   return rep;
1434 }
1435
1436 void gd_replay_free(GdReplay *replay)
1437 {
1438   checked_free(replay->movements);
1439   checked_free(replay->comment);
1440   free(replay);
1441 }
1442
1443 // store movement in a replay
1444 void gd_replay_store_movement(GdReplay *replay, GdDirection player_move,
1445                               boolean player_fire, boolean suicide)
1446 {
1447   byte data[1];
1448
1449   data[0] = ((player_move) |
1450              (player_fire ? GD_REPLAY_FIRE_MASK : 0) |
1451              (suicide ? GD_REPLAY_SUICIDE_MASK : 0));
1452
1453   if (replay->movements->len < MAX_REPLAY_LEN)
1454   {
1455     replay->movements->data[replay->movements->len++] = data[0];
1456
1457     if (replay->movements->len == MAX_REPLAY_LEN)
1458       Warn("BD replay truncated: size exceeds maximum replay size %d", MAX_REPLAY_LEN);
1459   }
1460 }
1461
1462 // calculate adler checksum for a rendered cave; this can be used for more caves.
1463 void gd_cave_adler_checksum_more(GdCave *cave, unsigned int *a, unsigned int *b)
1464 {
1465   int x, y;
1466
1467   for (y = 0; y < cave->h; y++)
1468     for (x = 0; x < cave->w; x++)
1469     {
1470       *a += gd_elements[cave->map[y][x]].character;
1471       *b += *a;
1472
1473       *a %= 65521;
1474       *b %= 65521;
1475     }
1476 }
1477
1478 // calculate adler checksum for a single rendered cave.
1479 unsigned int gd_cave_adler_checksum(GdCave *cave)
1480 {
1481   unsigned int a = 1;
1482   unsigned int b = 0;
1483
1484   gd_cave_adler_checksum_more(cave, &a, &b);
1485   return (b << 16) + a;
1486 }