2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 // arrays for movements
21 // also no1 and bd2 cave data import helpers; line direction coordinates
24 0, 0, 1, 1, 1, 0, -1, -1, -1, 0, 2, 2, 2, 0, -2, -2, -2
28 0, -1, -1, 0, 1, 1, 1, 0, -1, -2, -2, 0, 2, 2, 2, 0, -2
32 // None here means "no direction to move"; when there is no gravity while stirring the pot.
33 static const char* direction_name[] =
46 static const char* direction_filename[] =
59 static const char* scheduling_name[] =
67 "Atari BD2/Construction Kit"
70 static const char* scheduling_filename[] =
81 static HashTable *name_to_element;
82 GdElement gd_char_to_element[256];
84 // color of flashing the screen, gate opening to exit
85 const GdColor gd_flash_color = 0xFFFFC0;
87 // selected object in editor
88 const GdColor gd_select_color = 0x8080FF;
90 // direction to string and vice versa
91 const char *gd_direction_get_visible_name(GdDirection dir)
93 return direction_name[dir];
96 const char *gd_direction_get_filename(GdDirection dir)
98 return direction_filename[dir];
101 GdDirection gd_direction_from_string(const char *str)
105 for (i = 1; i < ARRAY_SIZE(direction_filename); i++)
106 if (strcasecmp(str, direction_filename[i]) == 0)
107 return (GdDirection) i;
109 Warn("invalid direction name '%s', defaulting to down", str);
113 // scheduling name to string and vice versa
114 const char *gd_scheduling_get_filename(GdScheduling sched)
116 return scheduling_filename[sched];
119 const char *gd_scheduling_get_visible_name(GdScheduling sched)
121 return scheduling_name[sched];
124 GdScheduling gd_scheduling_from_string(const char *str)
128 for (i = 0; i < ARRAY_SIZE(scheduling_filename); i++)
129 if (strcasecmp(str, scheduling_filename[i]) == 0)
130 return (GdScheduling) i;
132 Warn("invalid scheduling name '%s', defaulting to plck", str);
134 return GD_SCHEDULING_PLCK;
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.
143 void gd_struct_set_defaults_from_array(void *str,
144 const GdStructDescriptor *properties,
145 GdPropertyDefault *defaults)
149 for (i = 0; defaults[i].offset != -1; i++)
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;
161 // check which property we are talking about: find it in gd_cave_properties.
162 n = defaults[i].property_index;
165 while (properties[n].identifier != NULL &&
166 properties[n].offset != defaults[i].offset)
169 // remember so we will be fast later
170 defaults[i].property_index = n;
173 // some properties are arrays. this loop fills all with the same values
174 for (j = 0; j < properties[n].count; j++)
176 switch (properties[n].type)
178 // these are for the gui; do nothing
181 // no default value for strings
183 case GD_TYPE_LONGSTRING:
187 // this is also an integer, difference is only when saving to bdcff
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;
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;
203 case GD_TYPE_BOOLEAN:
204 bvalue[j] = defaults[i].defval != 0;
207 case GD_TYPE_ELEMENT:
209 evalue[j] = (GdElement) defaults[i].defval;
213 cvalue[j] = gd_c64_color(defaults[i].defval);
216 case GD_TYPE_DIRECTION:
217 dvalue[j] = (GdDirection) defaults[i].defval;
220 case GD_TYPE_SCHEDULING:
221 svalue[j] = (GdScheduling) defaults[i].defval;
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.
233 void gd_create_char_to_element_table(void)
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;
241 // then set fixed characters
242 for (i = 0; i < O_MAX; i++)
244 int c = gd_elements[i].character;
248 if (gd_char_to_element[c] != O_UNKNOWN)
249 Warn("Character %c already used for element %x", c, gd_char_to_element[c]);
251 gd_char_to_element[c] = i;
256 // search the element database for the specified character, and return the element.
257 GdElement gd_get_element_from_character (byte character)
259 if (gd_char_to_element[character] != O_UNKNOWN)
260 return gd_char_to_element[character];
262 Warn ("Invalid character representing element: %c", character);
268 do some init; this function is to be called at the start of the application
270 void gd_cave_init(void)
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);
279 for (i = 0; i < O_MAX; i++)
283 key = getStringToUpper(gd_elements[i].filename);
285 if (hashtable_exists(name_to_element, key)) // hash value may be 0
286 Warn("Name %s already used for element %x", key, i);
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!
291 key = getStringCat2("SCANNED_", key); // new string
293 hashtable_insert(name_to_element, key, INT_TO_PTR(i));
294 // once again, do not free "key" ^^^
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));
313 // compatibility with old bd-faq (pre disassembly of bladder)
314 hashtable_insert(name_to_element, "BLADDERd9", INT_TO_PTR(O_BLADDER_8));
316 // create table to show errors at the start of the application
317 gd_create_char_to_element_table();
320 // search the element database for the specified name, and return the element
321 GdElement gd_get_element_from_string (const char *string)
323 char *upper = getStringToUpper(string);
329 Warn("Invalid string representing element: (null)");
333 found = hashtable_exists(name_to_element, upper); // hash value may be 0
335 value = hashtable_search(name_to_element, upper);
338 return (GdElement) (PTR_TO_INT(value));
340 Warn("Invalid string representing element: '%s'", string);
345 void gd_cave_set_defaults_from_array(GdCave* cave, GdPropertyDefault *defaults)
347 gd_struct_set_defaults_from_array(cave, gd_cave_properties, defaults);
351 load default values from description array
352 these are default for gdash and bdcff.
354 void gd_cave_set_gdash_defaults(GdCave* cave)
358 gd_cave_set_defaults_from_array(cave, gd_cave_defaults_gdash);
360 // these did not fit into the descriptor array
361 for (i = 0; i < 5; i++)
363 cave->level_rand[i] = i;
364 cave->level_timevalue[i] = i + 1;
368 // for quicksort. compares two highscores.
369 int gd_highscore_compare(const void *a, const void *b)
371 const GdHighScore *ha = a;
372 const GdHighScore *hb = b;
373 return hb->score - ha->score;
376 void gd_clear_highscore(GdHighScore *hs)
380 for (i = 0; i < GD_HIGHSCORE_NUM; i++)
382 strcpy(hs[i].name, "");
387 boolean gd_has_highscore(GdHighScore *hs)
389 return hs[0].score > 0;
392 // return true if score achieved is a highscore
393 boolean gd_is_highscore(GdHighScore *scores, int score)
395 // if score is above zero AND bigger than the last one
396 if (score > 0 && score > scores[GD_HIGHSCORE_NUM-1].score)
402 int gd_add_highscore(GdHighScore *highscores, const char *name, int score)
406 if (!gd_is_highscore(highscores, score))
409 // overwrite the last one
410 gd_strcpy(highscores[GD_HIGHSCORE_NUM-1].name, name);
411 highscores[GD_HIGHSCORE_NUM-1].score = score;
414 qsort(highscores, GD_HIGHSCORE_NUM, sizeof(GdHighScore), gd_highscore_compare);
416 for (i = 0; i < GD_HIGHSCORE_NUM; i++)
417 if (strEqual(highscores[i].name, name) && highscores[i].score == score)
423 // for the case-insensitive hash keys
424 int gd_str_case_equal(void *s1, void *s2)
426 return strcasecmp(s1, s2) == 0;
429 unsigned int gd_str_case_hash(void *v)
431 char *upper = getStringToUpper(v);
432 unsigned int hash = get_hash_from_string(upper);
440 create new cave with default values.
441 sets every value, also default size, diamond value etc.
443 GdCave *gd_cave_new(void)
447 cave = checked_calloc(sizeof(GdCave));
449 // hash table which stores unknown tags as strings.
450 cave->tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
452 gd_cave_set_gdash_defaults(cave);
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
465 rows = new (pointers to rows);
467 rows[1..h-1] = rows[0] + width * bytes
475 allocate a cave map-like array, and initialize to zero.
476 one cell is cell_size bytes long.
478 void *gd_cave_map_new_for_cave(const GdCave *cave, const int cell_size)
480 void **rows; // this is void**, pointer to array of ...
483 rows = checked_malloc((cave->h) * sizeof(void *));
484 rows[0] = checked_calloc(cell_size * cave->w * cave->h);
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;
496 if map is null, this also returns null.
498 void *gd_cave_map_dup_size(const GdCave *cave, const void *map, const int cell_size)
501 void **maplines = (void **)map;
507 rows = checked_malloc((cave->h) * sizeof(void *));
508 rows[0] = get_memcpy (maplines[0], cell_size * cave->w * cave->h);
510 for (y = 1; y < cave->h; y++)
511 rows[y] = (char *)rows[0] + cell_size * cave->w * y;
516 void gd_cave_map_free(void *map)
518 void **maplines = (void **) map;
528 frees memory associated to cave
530 void gd_cave_free(GdCave *cave)
538 hashtable_destroy(cave->tags);
540 if (cave->random) // random generator is a GdRand *
541 gd_rand_free(cave->random);
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));
549 gd_cave_map_free(cave->map);
552 gd_cave_map_free(cave->objects_order);
554 // hammered walls to reappear data
555 gd_cave_map_free(cave->hammered_reappear);
558 list_foreach(cave->objects, (list_fn) free, NULL);
559 list_free(cave->objects);
562 list_foreach(cave->replays, (list_fn) gd_replay_free, NULL);
563 list_free(cave->replays);
565 // freeing main pointer
569 static void hash_copy_foreach(const char *key, const char *value, HashTable *dest)
571 hashtable_insert(dest, getStringCopy(key), getStringCopy(value));
574 // copy cave from src to destination, with duplicating dynamically allocated data
575 void gd_cave_copy(GdCave *dest, const GdCave *src)
580 memmove(dest, src, sizeof(GdCave));
582 // but duplicate dynamic data
583 dest->tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
586 hashtable_foreach(src->tags, (hashtable_fn)hash_copy_foreach, dest->tags);
588 dest->map = gd_cave_map_dup(src, map);
589 dest->hammered_reappear = gd_cave_map_dup(src, hammered_reappear);
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));
597 // no reason to copy this
598 dest->objects_order = NULL;
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)));
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));
620 // copy random number generator
622 dest->random = gd_rand_copy(src->random);
625 // create new cave, which is a copy of the cave given.
626 GdCave *gd_cave_new_from_cave(const GdCave *orig)
630 cave = gd_cave_new();
631 gd_cave_copy(cave, orig);
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.
644 void gd_cave_store_rc(GdCave *cave, int x, int y, const GdElement element, const void *order)
646 // if we do not need to draw, exit now
647 if (element == O_NONE)
651 if (cave->wraparound_objects)
655 // fit x coordinate within range, with correcting y at the same time
658 x += cave->w; // out of bounds on the left...
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.
674 // non lineshifting: changing x does not change y coordinate.
681 // after that, fix y coordinate
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)
694 cave->map[y][x] = element;
695 cave->objects_order[y][x] = (void *)order;
699 GdElement gd_cave_get_rc(const GdCave *cave, int x, int y)
701 // always fix coordinates as if cave was wraparound.
706 // fit x coordinate within range, with correcting y at the same time
709 x += cave->w; // out of bounds on the left...
720 // non lineshifting: changing x does not change y coordinate.
728 // after that, fix y coordinate
735 return cave->map[y][x];
738 unsigned int gd_c64_random(GdC64RandomGenerator *rand)
740 unsigned int temp_rand_1, temp_rand_2, carry, result;
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;
756 return rand->rand_seed_1;
760 C64 BD predictable random number generator.
761 Used to load the original caves imported from c64 files.
762 Also by the predictable slime.
764 unsigned int gd_cave_c64_random(GdCave *cave)
766 return gd_c64_random(&cave->c64_rand);
769 void gd_c64_random_set_seed(GdC64RandomGenerator *rand, int seed1, int seed2)
771 rand->rand_seed_1 = seed1;
772 rand->rand_seed_2 = seed2;
775 void gd_cave_c64_random_set_seed(GdCave *cave, int seed1, int seed2)
777 gd_c64_random_set_seed(&cave->c64_rand, seed1, seed2);
781 select random colors for a given cave.
782 this function will select colors so that they should look somewhat nice; for example
783 brick walls won't be the darkest color, for example.
785 static inline void swap(int *i1, int *i2)
793 void gd_cave_set_random_c64_colors(GdCave *cave)
795 const int bright_colors[] = { 1, 3, 7 };
796 const int dark_colors[] = { 2, 6, 8, 9, 11 };
799 cave->colorb = gd_c64_color(0);
800 cave->color0 = gd_c64_color(0);
802 // choose some bright color for brick
803 cave->color3 = gd_c64_color(bright_colors[gd_random_int_range(0, ARRAY_SIZE(bright_colors))]);
805 // choose a dark color for dirt, but should not be == color of brick
808 cave->color1 = gd_c64_color(dark_colors[gd_random_int_range(0, ARRAY_SIZE(dark_colors))]);
810 while (cave->color1 == cave->color3); // so it is not the same as color 1
812 // choose any but black for steel wall, but should not be == brick or dirt
815 // between 1 and 15 - do not use black for this.
816 cave->color2 = gd_c64_color(gd_random_int_range(1, 16));
818 while (cave->color1 == cave->color2 || cave->color2 == cave->color3); // so colors are not the same
820 // copy amoeba and slime color
821 cave->color4 = cave->color3;
822 cave->color5 = cave->color1;
825 static void cave_set_random_indexed_colors(GdCave *cave, GdColor (*color_indexer_func) (int, int))
827 int hue = gd_random_int_range(0, 15);
828 int hue_spread = gd_random_int_range(1, 6); // 1..5
830 // we only use 0..6, as saturation 15 is too bright (almost always white)
831 // also, saturation 0..1..2 is too dark. the color0=black is there for dark.
832 // so this is also 1..5. when hue spread is low, brightness spread is high
833 int bri_spread = 6 - hue_spread;
834 int bri1 = 8, bri2 = 8 - bri_spread, bri3 = 8 + bri_spread;
836 // there are 15 valid choices for hue, so we do a %15
837 int col1 = hue, col2 = (hue + hue_spread + 15) % 15, col3 = (hue - hue_spread + 15) % 15;
839 // this makes up a random color, and selects a color triad by hue+5 and hue+10.
840 // also creates a random saturation.
841 // color of brick is 8+sat, so it is always a bright color.
842 // another two are 8-sat and 8.
843 // order of colors is also changed randomly.
844 if (gd_random_boolean()) swap(&bri1, &bri2);
846 // we do not touch bri3 (8+sat), as it should be a bright color
847 if (gd_random_boolean()) swap(&col1, &col2);
848 if (gd_random_boolean()) swap(&col2, &col3);
849 if (gd_random_boolean()) swap(&col1, &col3);
851 cave->colorb = color_indexer_func(0, 0);
852 cave->color0 = color_indexer_func(0, 0);
853 cave->color1 = color_indexer_func(col1 + 1, bri1);
854 cave->color2 = color_indexer_func(col2 + 1, bri2);
855 cave->color3 = color_indexer_func(col3 + 1, bri3);
856 // amoeba and slime are different
858 cave->color4 = color_indexer_func(gd_random_int_range(11, 13), gd_random_int_range(6, 12));
859 // some blueish thing
860 cave->color5 = color_indexer_func(gd_random_int_range(7, 10), gd_random_int_range(0, 6));
863 static void gd_cave_set_random_atari_colors(GdCave *cave)
865 cave_set_random_indexed_colors(cave, gd_atari_color_huesat);
868 static void gd_cave_set_random_c64dtv_colors(GdCave *cave)
870 cave_set_random_indexed_colors(cave, gd_c64dtv_color_huesat);
873 static inline void swapd(double *i1, double *i2)
881 static void gd_cave_set_random_rgb_colors(GdCave *cave)
883 const double hue_max = 10.0 / 30.0;
885 double hue = gd_random_double();
886 // hue 360 degress=1. hue spread is min. 24 degrees, max 120 degrees (1/3)
887 double hue_spread = gd_random_double_range(2.0 / 30.0, hue_max);
888 double h1 = hue, h2 = hue + hue_spread, h3 = hue + 2 * hue_spread;
892 if (gd_random_boolean())
894 // when hue spread is low, brightness(saturation) spread is high
895 // this formula gives a number (x) between 0.1 and 0.4,
896 // which will be 0.5-x and 0.5+x, so the range is 0.1->0.9
897 double spread = 0.1 + 0.3 * (1 - hue_spread / hue_max);
898 v1 = 0.6; // brightness variation, too
901 s1 = 0.5; // saturation is different
907 // when hue spread is low, brightness(saturation) spread is high
908 // this formula gives a number (x) between 0.1 and 0.25,
909 // which will be 0.5+x and 0.5+2x, so the range is 0.5->0.9
910 double spread = 0.1 + 0.15 * (1 - hue_spread / hue_max);
911 v1 = 0.5; // brightness is different
913 v3 = 0.5 + 2 * spread;
914 s1 = 0.7; // saturation is same - a not fully saturated one
919 // randomly change values, but do not touch v3, as cave->color3 should be a bright color
920 if (gd_random_boolean()) swapd(&v1, &v2);
922 // randomly change hues and saturations
923 if (gd_random_boolean()) swapd(&h1, &h2);
924 if (gd_random_boolean()) swapd(&h2, &h3);
925 if (gd_random_boolean()) swapd(&h1, &h3);
926 if (gd_random_boolean()) swapd(&s1, &s2);
927 if (gd_random_boolean()) swapd(&s2, &s3);
928 if (gd_random_boolean()) swapd(&s1, &s3);
934 cave->colorb = gd_color_get_from_hsv(0, 0, 0);
935 cave->color0 = gd_color_get_from_hsv(0, 0, 0); // black for background
936 cave->color1 = gd_color_get_from_hsv(h1, s1, v1); // dirt
937 cave->color2 = gd_color_get_from_hsv(h2, s2, v2); // steel
938 cave->color3 = gd_color_get_from_hsv(h3, s3, v3); // brick
939 // green(120+-20) with the saturation and brightness of brick
940 cave->color4 = gd_color_get_from_hsv(gd_random_int_range(100, 140), s2, v2);
941 // blue(240+-20) with saturation and brightness of dirt
942 cave->color5 = gd_color_get_from_hsv(gd_random_int_range(220, 260), s1, v1);
945 void gd_cave_set_random_colors(GdCave *cave, GdColorType type)
949 case GD_COLOR_TYPE_RGB:
950 gd_cave_set_random_rgb_colors(cave);
953 case GD_COLOR_TYPE_C64:
954 gd_cave_set_random_c64_colors(cave);
957 case GD_COLOR_TYPE_C64DTV:
958 gd_cave_set_random_c64dtv_colors(cave);
961 case GD_COLOR_TYPE_ATARI:
962 gd_cave_set_random_atari_colors(cave);
972 if last line or last row is just steel wall (or (invisible) outbox).
973 used after loading a game for playing.
974 after this, ew and eh will contain the effective width and height.
976 void gd_cave_auto_shrink(GdCave *cave)
988 // set to maximum size, then try to shrink
991 cave->x2 = cave->w - 1;
992 cave->y2 = cave->h - 1;
994 // search for empty, steel-wall-only last rows.
995 // clear all lines, which are only steel wall.
996 // and clear only one line, which is steel wall, but also has a player or an outbox.
1001 for (y = cave->y2 - 1; y <= cave->y2; y++)
1003 for (x = cave->x1; x <= cave->x2; x++)
1005 switch (gd_cave_get_rc (cave, x, y))
1007 // if steels only, this is to be deleted.
1012 case O_PRE_INVIS_OUTBOX:
1014 if (empty == STEEL_OR_OTHER)
1017 // if this, delete only this one, and exit.
1018 if (empty == STEEL_ONLY)
1019 empty = STEEL_OR_OTHER;
1023 // anything else, that should be left in the cave.
1030 // shrink if full steel or steel and player/outbox.
1031 if (empty != NO_SHRINK)
1032 cave->y2--; // one row shorter
1034 while (empty == STEEL_ONLY); // if found just steels, repeat.
1036 // search for empty, steel-wall-only first rows.
1041 for (y = cave->y1; y <= cave->y1 + 1; y++)
1043 for (x = cave->x1; x <= cave->x2; x++)
1045 switch (gd_cave_get_rc (cave, x, y))
1051 case O_PRE_INVIS_OUTBOX:
1053 // shrink only lines, which have only ONE player or outbox.
1054 // this is for bd4 intermission 2, for example.
1055 if (empty == STEEL_OR_OTHER)
1057 if (empty == STEEL_ONLY)
1058 empty = STEEL_OR_OTHER;
1068 if (empty != NO_SHRINK)
1071 while (empty == STEEL_ONLY); // if found one, repeat.
1073 // empty last columns.
1078 for (y = cave->y1; y <= cave->y2; y++)
1080 for (x = cave->x2 - 1; x <= cave->x2; x++)
1082 switch (gd_cave_get_rc (cave, x, y))
1088 case O_PRE_INVIS_OUTBOX:
1090 if (empty == STEEL_OR_OTHER)
1092 if (empty == STEEL_ONLY)
1093 empty = STEEL_OR_OTHER;
1103 // just remember that one column shorter.
1104 // free will know the size of memchunk, no need to realloc!
1105 if (empty != NO_SHRINK)
1108 while (empty == STEEL_ONLY); // if found one, repeat.
1110 // empty first columns.
1115 for (y = cave->y1; y <= cave->y2; y++)
1117 for (x = cave->x1; x <= cave->x1 + 1; x++)
1119 switch (gd_cave_get_rc (cave, x, y))
1125 case O_PRE_INVIS_OUTBOX:
1127 if (empty == STEEL_OR_OTHER)
1129 if (empty == STEEL_ONLY)
1130 empty = STEEL_OR_OTHER;
1140 if (empty != NO_SHRINK)
1143 while (empty == STEEL_ONLY); // if found one, repeat.
1147 check if cave visible part coordinates
1148 are outside cave sizes, or not in the right order.
1149 correct them if needed.
1151 void gd_cave_correct_visible_size(GdCave *cave)
1153 // change visible coordinates if they do not point to upperleft and lowerright
1154 if (cave->x2 < cave->x1)
1157 cave->x2 = cave->x1;
1161 if (cave->y2 < cave->y1)
1164 cave->y2 = cave->y1;
1174 if (cave->x2 > cave->w - 1)
1175 cave->x2 = cave->w - 1;
1177 if (cave->y2 > cave->h - 1)
1178 cave->y2 = cave->h - 1;
1182 bd1 and similar engines had animation bits in cave data, to set which elements to animate
1183 (firefly, butterfly, amoeba).
1184 animating an element also caused some delay each frame; according to my measurements,
1185 around 2.6 ms/element.
1187 static void cave_set_ckdelay_extra_for_animation(GdCave *cave)
1190 boolean has_amoeba = FALSE, has_firefly = FALSE, has_butterfly = FALSE;
1192 for (y = 0; y < cave->h; y++)
1194 for (x = 0; x < cave->w; x++)
1196 switch (cave->map[y][x] & ~SCANNED)
1209 has_butterfly = TRUE;
1219 cave->ckdelay_extra_for_animation = 0;
1221 cave->ckdelay_extra_for_animation += 2600;
1223 cave->ckdelay_extra_for_animation += 2600;
1225 cave->ckdelay_extra_for_animation += 2600;
1227 cave->ckdelay_extra_for_animation += 2600;
1230 // do some init - setup some cave variables before the game.
1231 void gd_cave_setup_for_game(GdCave *cave)
1235 cave_set_ckdelay_extra_for_animation(cave);
1237 // find the player which will be the one to scroll to at the beginning of the game
1238 // (before the player's birth)
1239 if (cave->active_is_first_found)
1241 // uppermost player is active
1242 for (y = cave->h - 1; y >= 0; y--)
1244 for (x = cave->w - 1; x >= 0; x--)
1246 if (cave->map[y][x] == O_INBOX)
1256 // lowermost player is active
1257 for (y = 0; y < cave->h; y++)
1259 for (x = 0; x < cave->w; x++)
1261 if (cave->map[y][x] == O_INBOX)
1270 // select number of milliseconds (for pal and ntsc)
1271 cave->timing_factor = cave->pal_timing ? 1200 : 1000;
1273 cave->time *= cave->timing_factor;
1274 cave->magic_wall_time *= cave->timing_factor;
1275 cave->amoeba_time *= cave->timing_factor;
1276 cave->amoeba_2_time *= cave->timing_factor;
1277 cave->hatching_delay_time *= cave->timing_factor;
1279 if (cave->hammered_walls_reappear)
1280 cave->hammered_reappear = gd_cave_map_new(cave, int);
1283 // cave diamonds needed can be set to n<=0.
1284 // if so, count the diamonds at the time of the hatching, and decrement that value from
1285 // the number of diamonds found.
1286 // of course, this function is to be called from the cave engine, at the exact time of hatching.
1287 void gd_cave_count_diamonds(GdCave *cave)
1291 // if automatically counting diamonds. if this was negative,
1292 // the sum will be this less than the number of all the diamonds in the cave
1293 if (cave->diamonds_needed <= 0)
1295 for (y = 0; y < cave->h; y++)
1296 for (x = 0; x < cave->w; x++)
1297 if (cave->map[y][x] == O_DIAMOND)
1298 cave->diamonds_needed++;
1300 // if still below zero, let this be 0, so gate will be open immediately
1301 if (cave->diamonds_needed < 0)
1302 cave->diamonds_needed = 0;
1307 takes a cave and a gfx buffer, and fills the buffer with cell indexes.
1308 the indexes might change if bonus life flash is active (small lines in
1310 for the paused state (which is used in gdash but not in sdash) - yellowish
1312 also one can select the animation frame (0..7) to draw the cave on. so the
1316 if a cell is changed, it is flagged with GD_REDRAW; the flag can be cleared
1319 void gd_drawcave_game(const GdCave *cave,
1320 int **element_buffer, int **last_element_buffer, int **gfx_buffer,
1321 boolean bonus_life_flash, int animcycle, boolean hate_invisible_outbox)
1323 static int player_blinking = 0;
1324 static int player_tapping = 0;
1325 int elemmapping[O_MAX_ALL];
1326 int elemdrawing[O_MAX_ALL];
1327 int x, y, map, draw;
1329 if (cave->last_direction)
1331 // he is moving, so stop blinking and tapping.
1332 player_blinking = 0;
1337 // he is idle, so animations can be done.
1340 // blinking and tapping is started at the beginning of animation sequences.
1341 // 1/4 chance of blinking, every sequence.
1342 player_blinking = gd_random_int_range(0, 4) == 0;
1344 // 1/16 chance of starting or stopping tapping.
1345 if (gd_random_int_range(0, 16) == 0)
1346 player_tapping = !player_tapping;
1350 for (x = 0; x < O_MAX_ALL; x++)
1353 elemdrawing[x] = gd_elements[x].image_game;
1356 if (bonus_life_flash)
1358 elemmapping[O_SPACE] = O_FAKE_BONUS;
1359 elemdrawing[O_SPACE] = gd_elements[O_FAKE_BONUS].image_game;
1362 elemmapping[O_MAGIC_WALL] = (cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK);
1363 elemdrawing[O_MAGIC_WALL] = gd_elements[cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK].image_game;
1365 elemmapping[O_CREATURE_SWITCH] = (cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH);
1366 elemdrawing[O_CREATURE_SWITCH] = gd_elements[cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH].image_game;
1368 elemmapping[O_EXPANDING_WALL_SWITCH] = (cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ);
1369 elemdrawing[O_EXPANDING_WALL_SWITCH] = gd_elements[cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ].image_game;
1371 elemmapping[O_GRAVITY_SWITCH] = (cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH);
1372 elemdrawing[O_GRAVITY_SWITCH] = gd_elements[cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH].image_game;
1374 elemmapping[O_REPLICATOR_SWITCH] = (cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF);
1375 elemdrawing[O_REPLICATOR_SWITCH] = gd_elements[cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF].image_game;
1377 if (cave->replicators_active)
1378 // if the replicators are active, animate them.
1379 elemmapping[O_REPLICATOR] = O_REPLICATOR_ACTIVE;
1381 if (!cave->replicators_active)
1382 // if the replicators are inactive, do not animate them.
1383 elemdrawing[O_REPLICATOR] = ABS(elemdrawing[O_REPLICATOR]);
1385 elemmapping[O_CONVEYOR_SWITCH] = (cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF);
1386 elemdrawing[O_CONVEYOR_SWITCH] = gd_elements[cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF].image_game;
1388 if (cave->conveyor_belts_direction_changed)
1390 // if direction is changed, animation is changed.
1393 elemmapping[O_CONVEYOR_LEFT] = O_CONVEYOR_RIGHT;
1394 elemmapping[O_CONVEYOR_RIGHT] = O_CONVEYOR_LEFT;
1396 temp = elemdrawing[O_CONVEYOR_LEFT];
1397 elemdrawing[O_CONVEYOR_LEFT] = elemdrawing[O_CONVEYOR_RIGHT];
1398 elemdrawing[O_CONVEYOR_RIGHT] = temp;
1400 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_CHANGED;
1401 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_CHANGED].image_game;
1405 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_NORMAL;
1406 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_NORMAL].image_game;
1409 if (cave->conveyor_belts_active)
1411 // keep potentially changed direction
1412 int offset = (O_CONVEYOR_LEFT_ACTIVE - O_CONVEYOR_LEFT);
1414 // if they are running, animate them.
1415 elemmapping[O_CONVEYOR_LEFT] += offset;
1416 elemmapping[O_CONVEYOR_RIGHT] += offset;
1418 if (!cave->conveyor_belts_active)
1420 // if they are not running, do not animate them.
1421 elemdrawing[O_CONVEYOR_LEFT] = ABS(elemdrawing[O_CONVEYOR_LEFT]);
1422 elemdrawing[O_CONVEYOR_RIGHT] = ABS(elemdrawing[O_CONVEYOR_RIGHT]);
1427 // also a hack, like biter_switch
1428 elemdrawing[O_PNEUMATIC_ACTIVE_LEFT] += 2;
1429 elemdrawing[O_PNEUMATIC_ACTIVE_RIGHT] += 2;
1430 elemdrawing[O_PLAYER_PNEUMATIC_LEFT] += 2;
1431 elemdrawing[O_PLAYER_PNEUMATIC_RIGHT] += 2;
1434 if ((cave->last_direction) == GD_MV_STILL)
1437 if (player_blinking && player_tapping)
1439 map = O_PLAYER_TAP_BLINK;
1440 draw = gd_elements[O_PLAYER_TAP_BLINK].image_game;
1442 else if (player_blinking)
1444 map = O_PLAYER_BLINK;
1445 draw = gd_elements[O_PLAYER_BLINK].image_game;
1447 else if (player_tapping)
1450 draw = gd_elements[O_PLAYER_TAP].image_game;
1455 draw = gd_elements[O_PLAYER].image_game;
1458 else if (cave->last_direction == GD_MV_UP && use_bd_up_down_graphics())
1461 draw = gd_elements[O_PLAYER_UP].image_game;
1463 else if (cave->last_direction == GD_MV_DOWN && use_bd_up_down_graphics())
1465 map = O_PLAYER_DOWN;
1466 draw = gd_elements[O_PLAYER_DOWN].image_game;
1468 else if (cave->last_horizontal_direction == GD_MV_LEFT)
1470 map = O_PLAYER_LEFT;
1471 draw = gd_elements[O_PLAYER_LEFT].image_game;
1475 // of course this is GD_MV_RIGHT.
1476 map = O_PLAYER_RIGHT;
1477 draw = gd_elements[O_PLAYER_RIGHT].image_game;
1480 elemmapping[O_PLAYER] = map;
1481 elemmapping[O_PLAYER_GLUED] = map;
1483 elemdrawing[O_PLAYER] = draw;
1484 elemdrawing[O_PLAYER_GLUED] = draw;
1486 // player with bomb/rocketlauncher does not blink or tap - no graphics drawn for that.
1487 // running is drawn using w/o bomb/rocketlauncher cells */
1488 if (cave->last_direction != GD_MV_STILL)
1490 elemmapping[O_PLAYER_BOMB] = map;
1491 elemdrawing[O_PLAYER_BOMB] = draw;
1493 elemmapping[O_PLAYER_ROCKET_LAUNCHER] = map;
1494 elemdrawing[O_PLAYER_ROCKET_LAUNCHER] = draw;
1497 elemmapping[O_INBOX] = (cave->inbox_flash_toggle ? O_INBOX_OPEN : O_INBOX_CLOSED);
1498 elemdrawing[O_INBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1500 elemmapping[O_OUTBOX] = (cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED);
1501 elemdrawing[O_OUTBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1503 // hack, not fit into gd_elements
1504 elemmapping[O_BITER_SWITCH] = O_BITER_SWITCH_1 + cave->biter_delay_frame;
1505 // hack, not fit into gd_elements
1506 elemdrawing[O_BITER_SWITCH] = gd_elements[O_BITER_SWITCH].image_game + cave->biter_delay_frame;
1509 elemmapping[O_DIRT] = cave->dirt_looks_like;
1510 elemmapping[O_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1511 elemmapping[O_V_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1512 elemmapping[O_H_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1513 elemmapping[O_AMOEBA_2] = cave->amoeba_2_looks_like;
1516 elemdrawing[O_DIRT] = elemdrawing[cave->dirt_looks_like];
1517 elemdrawing[O_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1518 elemdrawing[O_V_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1519 elemdrawing[O_H_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1520 elemdrawing[O_AMOEBA_2] = elemdrawing[cave->amoeba_2_looks_like];
1522 // change only graphically
1523 if (hate_invisible_outbox)
1525 elemmapping[O_PRE_INVIS_OUTBOX] = elemmapping[O_PRE_OUTBOX];
1526 elemmapping[O_INVIS_OUTBOX] = elemmapping[O_OUTBOX];
1529 if (hate_invisible_outbox)
1531 elemdrawing[O_PRE_INVIS_OUTBOX] = elemdrawing[O_PRE_OUTBOX];
1532 elemdrawing[O_INVIS_OUTBOX] = elemdrawing[O_OUTBOX];
1535 for (y = cave->y1; y <= cave->y2; y++)
1537 for (x = cave->x1; x <= cave->x2; x++)
1539 GdElement actual = cave->map[y][x];
1541 // if covered, real element is not important
1542 if (actual & COVERED)
1545 map = elemmapping[actual];
1547 // if covered, real element is not important
1548 if (actual & COVERED)
1549 draw = gd_elements[O_COVERED].image_game;
1551 draw = elemdrawing[actual];
1553 // draw special graphics if player is pushing something
1554 if (use_bd_pushing_graphics() &&
1555 (cave->last_direction == GD_MV_LEFT || cave->last_direction == GD_MV_RIGHT) &&
1556 is_player(cave, x, y) && can_be_pushed_dir(cave, x, y, cave->last_direction))
1558 // special check needed when smooth game element movements selected in setup menu:
1559 // last element must either be player (before pushing) or pushable element (while pushing)
1560 // (extra check needed to prevent pushing animation when moving towards pushable element)
1561 if (!use_bd_smooth_movements() || last_element_buffer[y][x] != O_SPACE)
1563 if (cave->last_direction == GD_MV_LEFT)
1564 map = O_PLAYER_PUSH_LEFT;
1566 map = O_PLAYER_PUSH_RIGHT;
1568 if (cave->last_direction == GD_MV_LEFT)
1569 draw = elemdrawing[O_PLAYER_PUSH_LEFT];
1571 draw = elemdrawing[O_PLAYER_PUSH_RIGHT];
1575 // if negative, animated.
1577 draw = -draw + animcycle;
1580 if (cave->gate_open_flash)
1581 draw += GD_NUM_OF_CELLS;
1583 // set to buffer, with caching
1584 if (element_buffer[y][x] != map)
1585 element_buffer[y][x] = map;
1587 if (gfx_buffer[y][x] != draw)
1588 gfx_buffer[y][x] = draw | GD_REDRAW;
1594 cave time is rounded _UP_ to seconds. so at the exact moment when it
1596 2sec remaining to 1sec remaining, the player has exactly one second.
1598 to zero, it is the exact moment of timeout.
1600 internal time is milliseconds (or 1200 milliseconds for pal timing).
1602 int gd_cave_time_show(const GdCave *cave, int internal_time)
1604 return (internal_time + cave->timing_factor - 1) / cave->timing_factor;
1607 GdReplay *gd_replay_new(void)
1611 rep = checked_calloc(sizeof(GdReplay));
1612 rep->movements = checked_calloc(sizeof(GdReplayMovements));
1617 GdReplay *gd_replay_new_from_replay(GdReplay *orig)
1621 rep = get_memcpy(orig, sizeof(GdReplay));
1623 // replicate dynamic data
1624 rep->comment = getStringCopy(orig->comment);
1625 rep->movements = get_memcpy(orig->movements, sizeof(GdReplayMovements));
1630 void gd_replay_free(GdReplay *replay)
1632 checked_free(replay->movements);
1633 checked_free(replay->comment);
1637 // store movement in a replay
1638 void gd_replay_store_movement(GdReplay *replay, GdDirection player_move,
1639 boolean player_fire, boolean suicide)
1643 data[0] = ((player_move) |
1644 (player_fire ? GD_REPLAY_FIRE_MASK : 0) |
1645 (suicide ? GD_REPLAY_SUICIDE_MASK : 0));
1647 if (replay->movements->len < MAX_REPLAY_LEN)
1649 replay->movements->data[replay->movements->len++] = data[0];
1651 if (replay->movements->len == MAX_REPLAY_LEN)
1652 Warn("BD replay truncated: size exceeds maximum replay size %d", MAX_REPLAY_LEN);
1656 // calculate adler checksum for a rendered cave; this can be used for more caves.
1657 void gd_cave_adler_checksum_more(GdCave *cave, unsigned int *a, unsigned int *b)
1661 for (y = 0; y < cave->h; y++)
1662 for (x = 0; x < cave->w; x++)
1664 *a += gd_elements[cave->map[y][x]].character;
1672 // calculate adler checksum for a single rendered cave.
1673 unsigned int gd_cave_adler_checksum(GdCave *cave)
1678 gd_cave_adler_checksum_more(cave, &a, &b);
1679 return (b << 16) + a;
1682 boolean gd_cave_has_levels(GdCave *cave)
1685 int *cave_level_value[] =
1691 c.level_magic_wall_time,
1692 c.level_amoeba_time,
1693 c.level_amoeba_threshold,
1694 c.level_amoeba_2_time,
1695 c.level_amoeba_2_threshold,
1696 c.level_slime_permeability,
1697 c.level_slime_permeability_c64,
1698 c.level_slime_seed_c64,
1699 c.level_hatching_delay_frame,
1700 c.level_hatching_delay_time,
1702 c.level_penalty_time,
1708 for (i = 0; cave_level_value[i] != NULL; i++)
1709 for (j = 1; j < 5; j++)
1710 if (cave_level_value[i][j] != cave_level_value[i][0])
1713 for (j = 1; j < 5; j++)
1714 if (cave->level_rand[j] != j &&
1715 cave->level_rand[j - 1] != j - 1 &&
1716 cave->level_rand[j] != cave->level_rand[0])
1719 for (j = 1; j < 5; j++)
1720 if (cave->level_timevalue[j] != j + 1 &&
1721 cave->level_timevalue[j - 1] != j &&
1722 cave->level_timevalue[j] != cave->level_timevalue[0])
1728 boolean gd_caveset_has_levels(void)
1732 for (iter = gd_caveset; iter != NULL; iter = iter->next)
1733 if (gd_cave_has_levels((GdCave *)iter->data))