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);
780 void gd_cave_set_random_c64_colors(GdCave *cave)
782 const int bright_colors[] = { 1, 3, 7 };
783 const int dark_colors[] = { 2, 6, 8, 9, 11 };
786 cave->colorb = gd_c64_color(0);
787 cave->color0 = gd_c64_color(0);
789 // choose some bright color for brick
790 cave->color3 = gd_c64_color(bright_colors[gd_random_int_range(0, ARRAY_SIZE(bright_colors))]);
792 // choose a dark color for dirt, but should not be == color of brick
795 cave->color1 = gd_c64_color(dark_colors[gd_random_int_range(0, ARRAY_SIZE(dark_colors))]);
797 while (cave->color1 == cave->color3); // so it is not the same as color 1
799 // choose any but black for steel wall, but should not be == brick or dirt
802 // between 1 and 15 - do not use black for this.
803 cave->color2 = gd_c64_color(gd_random_int_range(1, 16));
805 while (cave->color1 == cave->color2 || cave->color2 == cave->color3); // so colors are not the same
807 // copy amoeba and slime color
808 cave->color4 = cave->color3;
809 cave->color5 = cave->color1;
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.
818 void gd_cave_auto_shrink(GdCave *cave)
830 // set to maximum size, then try to shrink
833 cave->x2 = cave->w - 1;
834 cave->y2 = cave->h - 1;
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.
843 for (y = cave->y2 - 1; y <= cave->y2; y++)
845 for (x = cave->x1; x <= cave->x2; x++)
847 switch (gd_cave_get_rc (cave, x, y))
849 // if steels only, this is to be deleted.
854 case O_PRE_INVIS_OUTBOX:
856 if (empty == STEEL_OR_OTHER)
859 // if this, delete only this one, and exit.
860 if (empty == STEEL_ONLY)
861 empty = STEEL_OR_OTHER;
865 // anything else, that should be left in the cave.
872 // shrink if full steel or steel and player/outbox.
873 if (empty != NO_SHRINK)
874 cave->y2--; // one row shorter
876 while (empty == STEEL_ONLY); // if found just steels, repeat.
878 // search for empty, steel-wall-only first rows.
883 for (y = cave->y1; y <= cave->y1 + 1; y++)
885 for (x = cave->x1; x <= cave->x2; x++)
887 switch (gd_cave_get_rc (cave, x, y))
893 case O_PRE_INVIS_OUTBOX:
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)
899 if (empty == STEEL_ONLY)
900 empty = STEEL_OR_OTHER;
910 if (empty != NO_SHRINK)
913 while (empty == STEEL_ONLY); // if found one, repeat.
915 // empty last columns.
920 for (y = cave->y1; y <= cave->y2; y++)
922 for (x = cave->x2 - 1; x <= cave->x2; x++)
924 switch (gd_cave_get_rc (cave, x, y))
930 case O_PRE_INVIS_OUTBOX:
932 if (empty == STEEL_OR_OTHER)
934 if (empty == STEEL_ONLY)
935 empty = STEEL_OR_OTHER;
945 // just remember that one column shorter.
946 // free will know the size of memchunk, no need to realloc!
947 if (empty != NO_SHRINK)
950 while (empty == STEEL_ONLY); // if found one, repeat.
952 // empty first columns.
957 for (y = cave->y1; y <= cave->y2; y++)
959 for (x = cave->x1; x <= cave->x1 + 1; x++)
961 switch (gd_cave_get_rc (cave, x, y))
967 case O_PRE_INVIS_OUTBOX:
969 if (empty == STEEL_OR_OTHER)
971 if (empty == STEEL_ONLY)
972 empty = STEEL_OR_OTHER;
982 if (empty != NO_SHRINK)
985 while (empty == STEEL_ONLY); // if found one, repeat.
989 check if cave visible part coordinates
990 are outside cave sizes, or not in the right order.
991 correct them if needed.
993 void gd_cave_correct_visible_size(GdCave *cave)
995 // change visible coordinates if they do not point to upperleft and lowerright
996 if (cave->x2 < cave->x1)
1003 if (cave->y2 < cave->y1)
1006 cave->y2 = cave->y1;
1016 if (cave->x2 > cave->w - 1)
1017 cave->x2 = cave->w - 1;
1019 if (cave->y2 > cave->h - 1)
1020 cave->y2 = cave->h - 1;
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.
1029 static void cave_set_ckdelay_extra_for_animation(GdCave *cave)
1032 boolean has_amoeba = FALSE, has_firefly = FALSE, has_butterfly = FALSE;
1034 for (y = 0; y < cave->h; y++)
1036 for (x = 0; x < cave->w; x++)
1038 switch (cave->map[y][x] & ~SCANNED)
1051 has_butterfly = TRUE;
1061 cave->ckdelay_extra_for_animation = 0;
1063 cave->ckdelay_extra_for_animation += 2600;
1065 cave->ckdelay_extra_for_animation += 2600;
1067 cave->ckdelay_extra_for_animation += 2600;
1069 cave->ckdelay_extra_for_animation += 2600;
1072 // do some init - setup some cave variables before the game.
1073 void gd_cave_setup_for_game(GdCave *cave)
1077 cave_set_ckdelay_extra_for_animation(cave);
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)
1083 // uppermost player is active
1084 for (y = cave->h - 1; y >= 0; y--)
1086 for (x = cave->w - 1; x >= 0; x--)
1088 if (cave->map[y][x] == O_INBOX)
1098 // lowermost player is active
1099 for (y = 0; y < cave->h; y++)
1101 for (x = 0; x < cave->w; x++)
1103 if (cave->map[y][x] == O_INBOX)
1112 // select number of milliseconds (for pal and ntsc)
1113 cave->timing_factor = cave->pal_timing ? 1200 : 1000;
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;
1121 if (cave->hammered_walls_reappear)
1122 cave->hammered_reappear = gd_cave_map_new(cave, int);
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)
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)
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++;
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;
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
1152 for the paused state (which is used in gdash but not in sdash) - yellowish
1154 also one can select the animation frame (0..7) to draw the cave on. so the
1158 if a cell is changed, it is flagged with GD_REDRAW; the flag can be cleared
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)
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;
1170 if (cave->last_direction)
1172 // he is moving, so stop blinking and tapping.
1173 player_blinking = 0;
1178 // he is idle, so animations can be done.
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;
1185 // 1/16 chance of starting or stopping tapping.
1186 if (gd_random_int_range(0, 16) == 0)
1187 player_tapping = !player_tapping;
1191 for (x = 0; x < O_MAX; x++)
1194 elemdrawing[x] = gd_elements[x].image_game;
1197 if (bonus_life_flash)
1199 elemmapping[O_SPACE] = O_FAKE_BONUS;
1200 elemdrawing[O_SPACE] = gd_elements[O_FAKE_BONUS].image_game;
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;
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;
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;
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;
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;
1218 if (cave->replicators_active)
1219 // if the replicators are active, animate them.
1220 elemmapping[O_REPLICATOR] = O_REPLICATOR_ACTIVE;
1222 if (!cave->replicators_active)
1223 // if the replicators are inactive, do not animate them.
1224 elemdrawing[O_REPLICATOR] = ABS(elemdrawing[O_REPLICATOR]);
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;
1229 if (cave->conveyor_belts_direction_changed)
1231 // if direction is changed, animation is changed.
1234 elemmapping[O_CONVEYOR_LEFT] = O_CONVEYOR_RIGHT;
1235 elemmapping[O_CONVEYOR_RIGHT] = O_CONVEYOR_LEFT;
1237 temp = elemdrawing[O_CONVEYOR_LEFT];
1238 elemdrawing[O_CONVEYOR_LEFT] = elemdrawing[O_CONVEYOR_RIGHT];
1239 elemdrawing[O_CONVEYOR_RIGHT] = temp;
1241 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_CHANGED;
1242 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_CHANGED].image_game;
1246 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_NORMAL;
1247 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_NORMAL].image_game;
1250 if (cave->conveyor_belts_active)
1252 // keep potentially changed direction
1253 int offset = (O_CONVEYOR_LEFT_ACTIVE - O_CONVEYOR_LEFT);
1255 // if they are running, animate them.
1256 elemmapping[O_CONVEYOR_LEFT] += offset;
1257 elemmapping[O_CONVEYOR_RIGHT] += offset;
1259 if (!cave->conveyor_belts_active)
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]);
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;
1275 if ((cave->last_direction) == GD_MV_STILL)
1278 if (player_blinking && player_tapping)
1280 map = O_PLAYER_TAP_BLINK;
1281 draw = gd_elements[O_PLAYER_TAP_BLINK].image_game;
1283 else if (player_blinking)
1285 map = O_PLAYER_BLINK;
1286 draw = gd_elements[O_PLAYER_BLINK].image_game;
1288 else if (player_tapping)
1291 draw = gd_elements[O_PLAYER_TAP].image_game;
1296 draw = gd_elements[O_PLAYER].image_game;
1299 else if (cave->last_horizontal_direction == GD_MV_LEFT)
1301 map = O_PLAYER_LEFT;
1302 draw = gd_elements[O_PLAYER_LEFT].image_game;
1306 // of course this is GD_MV_RIGHT.
1307 map = O_PLAYER_RIGHT;
1308 draw = gd_elements[O_PLAYER_RIGHT].image_game;
1311 elemmapping[O_PLAYER] = map;
1312 elemmapping[O_PLAYER_GLUED] = map;
1314 elemdrawing[O_PLAYER] = draw;
1315 elemdrawing[O_PLAYER_GLUED] = draw;
1317 // player with bomb/rocketlauncher does not blink or tap - no graphics drawn for that.
1318 // running is drawn using w/o bomb/rocketlauncher cells */
1319 if (cave->last_direction != GD_MV_STILL)
1321 elemmapping[O_PLAYER_BOMB] = map;
1322 elemdrawing[O_PLAYER_BOMB] = draw;
1324 elemmapping[O_PLAYER_ROCKET_LAUNCHER] = map;
1325 elemdrawing[O_PLAYER_ROCKET_LAUNCHER] = draw;
1328 elemmapping[O_INBOX] = (cave->inbox_flash_toggle ? O_INBOX_OPEN : O_INBOX_CLOSED);
1329 elemdrawing[O_INBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1331 elemmapping[O_OUTBOX] = (cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED);
1332 elemdrawing[O_OUTBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1334 // hack, not fit into gd_elements
1335 elemmapping[O_BITER_SWITCH] = O_BITER_SWITCH_1 + cave->biter_delay_frame;
1336 // hack, not fit into gd_elements
1337 elemdrawing[O_BITER_SWITCH] = gd_elements[O_BITER_SWITCH].image_game + cave->biter_delay_frame;
1340 elemmapping[O_DIRT] = cave->dirt_looks_like;
1341 elemmapping[O_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1342 elemmapping[O_V_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1343 elemmapping[O_H_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1344 elemmapping[O_AMOEBA_2] = cave->amoeba_2_looks_like;
1347 elemdrawing[O_DIRT] = elemdrawing[cave->dirt_looks_like];
1348 elemdrawing[O_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1349 elemdrawing[O_V_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1350 elemdrawing[O_H_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1351 elemdrawing[O_AMOEBA_2] = elemdrawing[cave->amoeba_2_looks_like];
1353 // change only graphically
1354 if (hate_invisible_outbox)
1356 elemmapping[O_PRE_INVIS_OUTBOX] = O_PRE_OUTBOX;
1357 elemmapping[O_INVIS_OUTBOX] = O_OUTBOX;
1360 if (hate_invisible_outbox)
1362 elemdrawing[O_PRE_INVIS_OUTBOX] = elemdrawing[O_PRE_OUTBOX];
1363 elemdrawing[O_INVIS_OUTBOX] = elemdrawing[O_OUTBOX];
1366 for (y = cave->y1; y <= cave->y2; y++)
1368 for (x = cave->x1; x <= cave->x2; x++)
1370 GdElement actual = cave->map[y][x];
1372 // if covered, real element is not important
1373 if (actual & COVERED)
1376 map = elemmapping[actual];
1378 // if covered, real element is not important
1379 if (actual & COVERED)
1380 draw = gd_elements[O_COVERED].image_game;
1382 draw = elemdrawing[actual];
1384 // if negative, animated.
1386 draw = -draw + animcycle;
1389 if (cave->gate_open_flash)
1390 draw += GD_NUM_OF_CELLS;
1392 // set to buffer, with caching
1393 if (element_buffer[y][x] != map)
1394 element_buffer[y][x] = map;
1396 if (gfx_buffer[y][x] != draw)
1397 gfx_buffer[y][x] = draw | GD_REDRAW;
1403 cave time is rounded _UP_ to seconds. so at the exact moment when it
1405 2sec remaining to 1sec remaining, the player has exactly one second.
1407 to zero, it is the exact moment of timeout.
1409 internal time is milliseconds (or 1200 milliseconds for pal timing).
1411 int gd_cave_time_show(const GdCave *cave, int internal_time)
1413 return (internal_time + cave->timing_factor - 1) / cave->timing_factor;
1416 GdReplay *gd_replay_new(void)
1420 rep = checked_calloc(sizeof(GdReplay));
1421 rep->movements = checked_calloc(sizeof(GdReplayMovements));
1426 GdReplay *gd_replay_new_from_replay(GdReplay *orig)
1430 rep = get_memcpy(orig, sizeof(GdReplay));
1432 // replicate dynamic data
1433 rep->comment = getStringCopy(orig->comment);
1434 rep->movements = get_memcpy(orig->movements, sizeof(GdReplayMovements));
1439 void gd_replay_free(GdReplay *replay)
1441 checked_free(replay->movements);
1442 checked_free(replay->comment);
1446 // store movement in a replay
1447 void gd_replay_store_movement(GdReplay *replay, GdDirection player_move,
1448 boolean player_fire, boolean suicide)
1452 data[0] = ((player_move) |
1453 (player_fire ? GD_REPLAY_FIRE_MASK : 0) |
1454 (suicide ? GD_REPLAY_SUICIDE_MASK : 0));
1456 if (replay->movements->len < MAX_REPLAY_LEN)
1458 replay->movements->data[replay->movements->len++] = data[0];
1460 if (replay->movements->len == MAX_REPLAY_LEN)
1461 Warn("BD replay truncated: size exceeds maximum replay size %d", MAX_REPLAY_LEN);
1465 // calculate adler checksum for a rendered cave; this can be used for more caves.
1466 void gd_cave_adler_checksum_more(GdCave *cave, unsigned int *a, unsigned int *b)
1470 for (y = 0; y < cave->h; y++)
1471 for (x = 0; x < cave->w; x++)
1473 *a += gd_elements[cave->map[y][x]].character;
1481 // calculate adler checksum for a single rendered cave.
1482 unsigned int gd_cave_adler_checksum(GdCave *cave)
1487 gd_cave_adler_checksum_more(cave, &a, &b);
1488 return (b << 16) + a;