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;
228 /* creates the character->element conversion table; using
229 the fixed-in-the-bdcff characters. later, this table
230 may be filled with more elements.
232 void gd_create_char_to_element_table(void)
236 /* fill all with unknown */
237 for (i = 0; i < ARRAY_SIZE(gd_char_to_element); i++)
238 gd_char_to_element[i] = O_UNKNOWN;
240 /* then set fixed characters */
241 for (i = 0; i < O_MAX; i++)
243 int c = gd_elements[i].character;
247 if (gd_char_to_element[c] != O_UNKNOWN)
248 Warn("Character %c already used for element %x", c, gd_char_to_element[c]);
250 gd_char_to_element[c] = i;
255 /* search the element database for the specified character, and return the element. */
256 GdElement gd_get_element_from_character (byte character)
258 if (gd_char_to_element[character] != O_UNKNOWN)
259 return gd_char_to_element[character];
261 Warn ("Invalid character representing element: %c", character);
267 do some init; this function is to be called at the start of the application
269 void gd_cave_init(void)
273 /* put names to a hash table */
274 /* this is a helper for file read operations */
275 /* maps copied strings to elements (integers) */
276 name_to_element = create_hashtable(gd_str_case_hash, gd_str_case_equal, NULL, NULL);
278 for (i = 0; i < O_MAX; i++)
282 key = getStringToUpper(gd_elements[i].filename);
284 if (hashtable_exists(name_to_element, key)) /* hash value may be 0 */
285 Warn("Name %s already used for element %x", key, i);
287 hashtable_insert(name_to_element, key, INT_TO_PTR(i));
288 /* ^^^ do not free "key", as hash table needs it during the whole time! */
290 key = getStringCat2("SCANNED_", key); /* new string */
292 hashtable_insert(name_to_element, key, INT_TO_PTR(i));
293 /* once again, do not free "key" ^^^ */
296 /* for compatibility with tim stridmann's memorydump->bdcff converter... .... ... */
297 hashtable_insert(name_to_element, "HEXPANDING_WALL", INT_TO_PTR(O_H_EXPANDING_WALL));
298 hashtable_insert(name_to_element, "FALLING_DIAMOND", INT_TO_PTR(O_DIAMOND_F));
299 hashtable_insert(name_to_element, "FALLING_BOULDER", INT_TO_PTR(O_STONE_F));
300 hashtable_insert(name_to_element, "EXPLOSION1S", INT_TO_PTR(O_EXPLODE_1));
301 hashtable_insert(name_to_element, "EXPLOSION2S", INT_TO_PTR(O_EXPLODE_2));
302 hashtable_insert(name_to_element, "EXPLOSION3S", INT_TO_PTR(O_EXPLODE_3));
303 hashtable_insert(name_to_element, "EXPLOSION4S", INT_TO_PTR(O_EXPLODE_4));
304 hashtable_insert(name_to_element, "EXPLOSION5S", INT_TO_PTR(O_EXPLODE_5));
305 hashtable_insert(name_to_element, "EXPLOSION1D", INT_TO_PTR(O_PRE_DIA_1));
306 hashtable_insert(name_to_element, "EXPLOSION2D", INT_TO_PTR(O_PRE_DIA_2));
307 hashtable_insert(name_to_element, "EXPLOSION3D", INT_TO_PTR(O_PRE_DIA_3));
308 hashtable_insert(name_to_element, "EXPLOSION4D", INT_TO_PTR(O_PRE_DIA_4));
309 hashtable_insert(name_to_element, "EXPLOSION5D", INT_TO_PTR(O_PRE_DIA_5));
310 hashtable_insert(name_to_element, "WALL2", INT_TO_PTR(O_STEEL_EXPLODABLE));
312 /* compatibility with old bd-faq (pre disassembly of bladder) */
313 hashtable_insert(name_to_element, "BLADDERd9", INT_TO_PTR(O_BLADDER_8));
315 /* create table to show errors at the start of the application */
316 gd_create_char_to_element_table();
319 /* search the element database for the specified name, and return the element */
320 GdElement gd_get_element_from_string (const char *string)
322 char *upper = getStringToUpper(string);
328 Warn("Invalid string representing element: (null)");
332 found = hashtable_exists(name_to_element, upper); /* hash value may be 0 */
334 value = hashtable_search(name_to_element, upper);
337 return (GdElement) (PTR_TO_INT(value));
339 Warn("Invalid string representing element: '%s'", string);
344 void gd_cave_set_defaults_from_array(GdCave* cave, GdPropertyDefault *defaults)
346 gd_struct_set_defaults_from_array(cave, gd_cave_properties, defaults);
350 load default values from description array
351 these are default for gdash and bdcff.
353 void gd_cave_set_gdash_defaults(GdCave* cave)
357 gd_cave_set_defaults_from_array(cave, gd_cave_defaults_gdash);
359 /* these did not fit into the descriptor array */
360 for (i = 0; i < 5; i++)
362 cave->level_rand[i] = i;
363 cave->level_timevalue[i] = i + 1;
367 /* for quicksort. compares two highscores. */
368 int gd_highscore_compare(const void *a, const void *b)
370 const GdHighScore *ha = a;
371 const GdHighScore *hb = b;
372 return hb->score - ha->score;
375 void gd_clear_highscore(GdHighScore *hs)
379 for (i = 0; i < GD_HIGHSCORE_NUM; i++)
381 strcpy(hs[i].name, "");
386 boolean gd_has_highscore(GdHighScore *hs)
388 return hs[0].score > 0;
391 /* return true if score achieved is a highscore */
392 boolean gd_is_highscore(GdHighScore *scores, int score)
394 /* if score is above zero AND bigger than the last one */
395 if (score > 0 && score > scores[GD_HIGHSCORE_NUM-1].score)
401 int gd_add_highscore(GdHighScore *highscores, const char *name, int score)
405 if (!gd_is_highscore(highscores, score))
408 /* overwrite the last one */
409 gd_strcpy(highscores[GD_HIGHSCORE_NUM-1].name, name);
410 highscores[GD_HIGHSCORE_NUM-1].score = score;
413 qsort(highscores, GD_HIGHSCORE_NUM, sizeof(GdHighScore), gd_highscore_compare);
415 for (i = 0; i < GD_HIGHSCORE_NUM; i++)
416 if (strEqual(highscores[i].name, name) && highscores[i].score == score)
422 /* for the case-insensitive hash keys */
423 int gd_str_case_equal(void *s1, void *s2)
425 return strcasecmp(s1, s2) == 0;
428 unsigned int gd_str_case_hash(void *v)
430 char *upper = getStringToUpper(v);
431 unsigned int hash = get_hash_from_string(upper);
439 create new cave with default values.
440 sets every value, also default size, diamond value etc.
442 GdCave *gd_cave_new(void)
446 cave = checked_calloc(sizeof(GdCave));
448 /* hash table which stores unknown tags as strings. */
449 cave->tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
451 gd_cave_set_gdash_defaults(cave);
457 cave maps are continuous areas in memory. the allocated memory
458 is width * height * bytes_per_cell long.
459 the cave map[0] stores the pointer given by g_malloc().
460 the map itself is also an allocated array of pointers to the
463 rows = new (pointers to rows);
465 rows[1..h-1] = rows[0] + width * bytes
473 allocate a cave map-like array, and initialize to zero.
474 one cell is cell_size bytes long.
476 void *gd_cave_map_new_for_cave(const GdCave *cave, const int cell_size)
478 void **rows; /* this is void**, pointer to array of ... */
481 rows = checked_malloc((cave->h) * sizeof(void *));
482 rows[0] = checked_calloc(cell_size * cave->w * cave->h);
484 for (y = 1; y < cave->h; y++)
485 /* base pointer + num_of_bytes_per_element * width * number_of_row; as sizeof(char) = 1 */
486 rows[y] = (char *)rows[0] + cell_size * cave->w * y;
494 if map is null, this also returns null.
496 void *gd_cave_map_dup_size(const GdCave *cave, const void *map, const int cell_size)
499 void **maplines = (void **)map;
505 rows = checked_malloc((cave->h) * sizeof(void *));
506 rows[0] = get_memcpy (maplines[0], cell_size * cave->w * cave->h);
508 for (y = 1; y < cave->h; y++)
509 rows[y] = (char *)rows[0] + cell_size * cave->w * y;
514 void gd_cave_map_free(void *map)
516 void **maplines = (void **) map;
526 frees memory associated to cave
528 void gd_cave_free(GdCave *cave)
536 hashtable_destroy(cave->tags);
538 if (cave->random) /* random generator is a GdRand * */
539 gd_rand_free(cave->random);
542 for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
543 if (gd_cave_properties[i].type == GD_TYPE_LONGSTRING)
544 checked_free(STRUCT_MEMBER(char *, cave, gd_cave_properties[i].offset));
547 gd_cave_map_free(cave->map);
550 gd_cave_map_free(cave->objects_order);
552 /* hammered walls to reappear data */
553 gd_cave_map_free(cave->hammered_reappear);
556 list_foreach(cave->objects, (list_fn) free, NULL);
557 list_free(cave->objects);
560 list_foreach(cave->replays, (list_fn) gd_replay_free, NULL);
561 list_free(cave->replays);
563 /* freeing main pointer */
567 static void hash_copy_foreach(const char *key, const char *value, HashTable *dest)
569 hashtable_insert(dest, getStringCopy(key), getStringCopy(value));
572 /* copy cave from src to destination, with duplicating dynamically allocated data */
573 void gd_cave_copy(GdCave *dest, const GdCave *src)
577 /* copy entire data */
578 memmove(dest, src, sizeof(GdCave));
580 /* but duplicate dynamic data */
581 dest->tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
584 hashtable_foreach(src->tags, (hashtable_fn)hash_copy_foreach, dest->tags);
586 dest->map = gd_cave_map_dup(src, map);
587 dest->hammered_reappear = gd_cave_map_dup(src, hammered_reappear);
589 /* for longstrings */
590 for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
591 if (gd_cave_properties[i].type == GD_TYPE_LONGSTRING)
592 STRUCT_MEMBER(char *, dest, gd_cave_properties[i].offset) =
593 getStringCopy(STRUCT_MEMBER(char *, src, gd_cave_properties[i].offset));
595 /* no reason to copy this */
596 dest->objects_order = NULL;
598 /* copy objects list */
603 dest->objects = NULL; /* new empty list */
604 for (iter = src->objects; iter != NULL; iter = iter->next) /* do a deep copy */
605 dest->objects = list_append(dest->objects, get_memcpy (iter->data, sizeof (GdObject)));
613 dest->replays = NULL;
614 for (iter = src->replays; iter != NULL; iter = iter->next) /* do a deep copy */
615 dest->replays = list_append(dest->replays, gd_replay_new_from_replay(iter->data));
618 /* copy random number generator */
620 dest->random = gd_rand_copy(src->random);
623 /* create new cave, which is a copy of the cave given. */
624 GdCave *gd_cave_new_from_cave(const GdCave *orig)
628 cave = gd_cave_new();
629 gd_cave_copy(cave, orig);
635 Put an object to the specified position.
636 Performs range checking.
637 If wraparound objects are selected, wraps around x coordinates, with or without lineshift.
638 (The y coordinate is not wrapped, as it did not work like that on the c64)
639 order is a pointer to the GdObject describing this object. Thus the editor can identify which cell was created by which object.
641 void gd_cave_store_rc(GdCave *cave, int x, int y, const GdElement element, const void *order)
643 /* if we do not need to draw, exit now */
644 if (element == O_NONE)
648 if (cave->wraparound_objects)
652 /* fit x coordinate within range, with correcting y at the same time */
655 x += cave->w; /* out of bounds on the left... */
656 y--; /* previous row */
665 /* lineshifting does not fix the y coordinates.
666 if out of bounds, element will not be displayed. */
667 /* if such an object appeared in the c64 game, well, it was a buffer overrun. */
671 /* non lineshifting: changing x does not change y coordinate. */
678 /* after that, fix y coordinate */
687 /* if the above wraparound code fixed the coordinates, this will always be true. */
688 /* but see the above comment for lineshifting y coordinate */
689 if (x >= 0 && x < cave->w && y >= 0 && y < cave->h)
691 cave->map[y][x] = element;
692 cave->objects_order[y][x] = (void *)order;
696 GdElement gd_cave_get_rc(const GdCave *cave, int x, int y)
698 /* always fix coordinates as if cave was wraparound. */
700 /* fix x coordinate */
703 /* fit x coordinate within range, with correcting y at the same time */
706 x += cave->w; /* out of bounds on the left... */
707 y--; /* previous row */
717 /* non lineshifting: changing x does not change y coordinate. */
725 /* after that, fix y coordinate */
732 return cave->map[y][x];
735 unsigned int gd_c64_random(GdC64RandomGenerator *rand)
737 unsigned int temp_rand_1, temp_rand_2, carry, result;
739 temp_rand_1 = (rand->rand_seed_1 & 0x0001) << 7;
740 temp_rand_2 = (rand->rand_seed_2 >> 1) & 0x007F;
741 result = (rand->rand_seed_2) + ((rand->rand_seed_2 & 0x0001) << 7);
742 carry = (result >> 8);
743 result = result & 0x00FF;
744 result = result + carry + 0x13;
745 carry = (result >> 8);
746 rand->rand_seed_2 = result & 0x00FF;
747 result = rand->rand_seed_1 + carry + temp_rand_1;
748 carry = (result >> 8);
749 result = result & 0x00FF;
750 result = result + carry + temp_rand_2;
751 rand->rand_seed_1 = result & 0x00FF;
753 return rand->rand_seed_1;
757 C64 BD predictable random number generator.
758 Used to load the original caves imported from c64 files.
759 Also by the predictable slime.
761 unsigned int gd_cave_c64_random(GdCave *cave)
763 return gd_c64_random(&cave->c64_rand);
766 void gd_c64_random_set_seed(GdC64RandomGenerator *rand, int seed1, int seed2)
768 rand->rand_seed_1 = seed1;
769 rand->rand_seed_2 = seed2;
772 void gd_cave_c64_random_set_seed(GdCave *cave, int seed1, int seed2)
774 gd_c64_random_set_seed(&cave->c64_rand, seed1, seed2);
777 void gd_cave_set_random_c64_colors(GdCave *cave)
779 const int bright_colors[] = { 1, 3, 7 };
780 const int dark_colors[] = { 2, 6, 8, 9, 11 };
783 cave->colorb = gd_c64_color(0);
784 cave->color0 = gd_c64_color(0);
786 /* choose some bright color for brick */
787 cave->color3 = gd_c64_color(bright_colors[gd_random_int_range(0, ARRAY_SIZE(bright_colors))]);
789 /* choose a dark color for dirt, but should not be == color of brick */
792 cave->color1 = gd_c64_color(dark_colors[gd_random_int_range(0, ARRAY_SIZE(dark_colors))]);
794 while (cave->color1 == cave->color3); /* so it is not the same as color 1 */
796 /* choose any but black for steel wall, but should not be == brick or dirt */
799 /* between 1 and 15 - do not use black for this. */
800 cave->color2 = gd_c64_color(gd_random_int_range(1, 16));
802 while (cave->color1 == cave->color2 || cave->color2 == cave->color3); /* so colors are not the same */
804 /* copy amoeba and slime color */
805 cave->color4 = cave->color3;
806 cave->color5 = cave->color1;
811 if last line or last row is just steel wall (or (invisible) outbox).
812 used after loading a game for playing.
813 after this, ew and eh will contain the effective width and height.
815 void gd_cave_auto_shrink(GdCave *cave)
827 /* set to maximum size, then try to shrink */
830 cave->x2 = cave->w - 1;
831 cave->y2 = cave->h - 1;
833 /* search for empty, steel-wall-only last rows. */
834 /* clear all lines, which are only steel wall.
835 * and clear only one line, which is steel wall, but also has a player or an outbox. */
840 for (y = cave->y2 - 1; y <= cave->y2; y++)
842 for (x = cave->x1; x <= cave->x2; x++)
844 switch (gd_cave_get_rc (cave, x, y))
846 /* if steels only, this is to be deleted. */
851 case O_PRE_INVIS_OUTBOX:
853 if (empty == STEEL_OR_OTHER)
856 /* if this, delete only this one, and exit. */
857 if (empty == STEEL_ONLY)
858 empty = STEEL_OR_OTHER;
862 /* anything else, that should be left in the cave. */
869 /* shrink if full steel or steel and player/outbox. */
870 if (empty != NO_SHRINK)
871 cave->y2--; /* one row shorter */
873 while (empty == STEEL_ONLY); /* if found just steels, repeat. */
875 /* search for empty, steel-wall-only first rows. */
880 for (y = cave->y1; y <= cave->y1 + 1; y++)
882 for (x = cave->x1; x <= cave->x2; x++)
884 switch (gd_cave_get_rc (cave, x, y))
890 case O_PRE_INVIS_OUTBOX:
892 /* shrink only lines, which have only ONE player or outbox.
893 this is for bd4 intermission 2, for example. */
894 if (empty == STEEL_OR_OTHER)
896 if (empty == STEEL_ONLY)
897 empty = STEEL_OR_OTHER;
907 if (empty != NO_SHRINK)
910 while (empty == STEEL_ONLY); /* if found one, repeat. */
912 /* empty last columns. */
917 for (y = cave->y1; y <= cave->y2; y++)
919 for (x = cave->x2 - 1; x <= cave->x2; x++)
921 switch (gd_cave_get_rc (cave, x, y))
927 case O_PRE_INVIS_OUTBOX:
929 if (empty == STEEL_OR_OTHER)
931 if (empty == STEEL_ONLY)
932 empty = STEEL_OR_OTHER;
942 /* just remember that one column shorter.
943 free will know the size of memchunk, no need to realloc! */
944 if (empty != NO_SHRINK)
947 while (empty == STEEL_ONLY); /* if found one, repeat. */
949 /* empty first columns. */
954 for (y = cave->y1; y <= cave->y2; y++)
956 for (x = cave->x1; x <= cave->x1 + 1; x++)
958 switch (gd_cave_get_rc (cave, x, y))
964 case O_PRE_INVIS_OUTBOX:
966 if (empty == STEEL_OR_OTHER)
968 if (empty == STEEL_ONLY)
969 empty = STEEL_OR_OTHER;
979 if (empty != NO_SHRINK)
982 while (empty == STEEL_ONLY); /* if found one, repeat. */
985 /* check if cave visible part coordinates
986 are outside cave sizes, or not in the right order.
987 correct them if needed.
989 void gd_cave_correct_visible_size(GdCave *cave)
991 /* change visible coordinates if they do not point to upperleft and lowerright */
992 if (cave->x2 < cave->x1)
999 if (cave->y2 < cave->y1)
1002 cave->y2 = cave->y1;
1012 if (cave->x2 > cave->w - 1)
1013 cave->x2 = cave->w - 1;
1015 if (cave->y2 > cave->h - 1)
1016 cave->y2 = cave->h - 1;
1020 bd1 and similar engines had animation bits in cave data, to set which elements to animate
1021 (firefly, butterfly, amoeba).
1022 animating an element also caused some delay each frame; according to my measurements,
1023 around 2.6 ms/element.
1025 static void cave_set_ckdelay_extra_for_animation(GdCave *cave)
1028 boolean has_amoeba = FALSE, has_firefly = FALSE, has_butterfly = FALSE;
1030 for (y = 0; y < cave->h; y++)
1032 for (x = 0; x < cave->w; x++)
1034 switch (cave->map[y][x] & ~SCANNED)
1047 has_butterfly = TRUE;
1057 cave->ckdelay_extra_for_animation = 0;
1059 cave->ckdelay_extra_for_animation += 2600;
1061 cave->ckdelay_extra_for_animation += 2600;
1063 cave->ckdelay_extra_for_animation += 2600;
1065 cave->ckdelay_extra_for_animation += 2600;
1068 /* do some init - setup some cave variables before the game. */
1069 void gd_cave_setup_for_game(GdCave *cave)
1073 cave_set_ckdelay_extra_for_animation(cave);
1075 /* find the player which will be the one to scroll to at the beginning of the game
1076 (before the player's birth) */
1077 if (cave->active_is_first_found)
1079 /* uppermost player is active */
1080 for (y = cave->h - 1; y >= 0; y--)
1082 for (x = cave->w - 1; x >= 0; x--)
1084 if (cave->map[y][x] == O_INBOX)
1094 /* lowermost player is active */
1095 for (y = 0; y < cave->h; y++)
1097 for (x = 0; x < cave->w; x++)
1099 if (cave->map[y][x] == O_INBOX)
1108 /* select number of milliseconds (for pal and ntsc) */
1109 cave->timing_factor = cave->pal_timing ? 1200 : 1000;
1111 cave->time *= cave->timing_factor;
1112 cave->magic_wall_time *= cave->timing_factor;
1113 cave->amoeba_time *= cave->timing_factor;
1114 cave->amoeba_2_time *= cave->timing_factor;
1115 cave->hatching_delay_time *= cave->timing_factor;
1117 if (cave->hammered_walls_reappear)
1118 cave->hammered_reappear = gd_cave_map_new(cave, int);
1121 /* cave diamonds needed can be set to n<=0. */
1122 /* if so, count the diamonds at the time of the hatching, and decrement that value from */
1123 /* the number of diamonds found. */
1124 /* of course, this function is to be called from the cave engine, at the exact time of hatching. */
1125 void gd_cave_count_diamonds(GdCave *cave)
1129 /* if automatically counting diamonds. if this was negative,
1130 * the sum will be this less than the number of all the diamonds in the cave */
1131 if (cave->diamonds_needed <= 0)
1133 for (y = 0; y < cave->h; y++)
1134 for (x = 0; x < cave->w; x++)
1135 if (cave->map[y][x] == O_DIAMOND)
1136 cave->diamonds_needed++;
1138 /* if still below zero, let this be 0, so gate will be open immediately */
1139 if (cave->diamonds_needed < 0)
1140 cave->diamonds_needed = 0;
1144 /* takes a cave and a gfx buffer, and fills the buffer with cell indexes.
1145 the indexes might change if bonus life flash is active (small lines in
1147 for the paused state (which is used in gdash but not in sdash) - yellowish
1149 also one can select the animation frame (0..7) to draw the cave on. so the
1153 if a cell is changed, it is flagged with GD_REDRAW; the flag can be cleared
1156 void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer,
1157 boolean bonus_life_flash, int animcycle, boolean hate_invisible_outbox)
1159 static int player_blinking = 0;
1160 static int player_tapping = 0;
1161 int elemmapping[O_MAX];
1162 int elemdrawing[O_MAX];
1163 int x, y, map, draw;
1165 if (cave->last_direction)
1167 /* he is moving, so stop blinking and tapping. */
1168 player_blinking = 0;
1173 /* he is idle, so animations can be done. */
1176 /* blinking and tapping is started at the beginning of animation sequences. */
1177 /* 1/4 chance of blinking, every sequence. */
1178 player_blinking = gd_random_int_range(0, 4) == 0;
1180 /* 1/16 chance of starting or stopping tapping. */
1181 if (gd_random_int_range(0, 16) == 0)
1182 player_tapping = !player_tapping;
1186 for (x = 0; x < O_MAX; x++)
1189 elemdrawing[x] = gd_elements[x].image_game;
1192 if (bonus_life_flash)
1194 elemmapping[O_SPACE] = O_FAKE_BONUS;
1195 elemdrawing[O_SPACE] = gd_elements[O_FAKE_BONUS].image_game;
1198 elemmapping[O_MAGIC_WALL] = (cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK);
1199 elemdrawing[O_MAGIC_WALL] = gd_elements[cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK].image_game;
1201 elemmapping[O_CREATURE_SWITCH] = (cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH);
1202 elemdrawing[O_CREATURE_SWITCH] = gd_elements[cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH].image_game;
1204 elemmapping[O_EXPANDING_WALL_SWITCH] = (cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ);
1205 elemdrawing[O_EXPANDING_WALL_SWITCH] = gd_elements[cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ].image_game;
1207 elemmapping[O_GRAVITY_SWITCH] = (cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH);
1208 elemdrawing[O_GRAVITY_SWITCH] = gd_elements[cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH].image_game;
1210 elemmapping[O_REPLICATOR_SWITCH] = (cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF);
1211 elemdrawing[O_REPLICATOR_SWITCH] = gd_elements[cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF].image_game;
1213 if (cave->replicators_active)
1214 /* if the replicators are active, animate them. */
1215 elemmapping[O_REPLICATOR] = O_REPLICATOR_ACTIVE;
1217 if (!cave->replicators_active)
1218 /* if the replicators are inactive, do not animate them. */
1219 elemdrawing[O_REPLICATOR] = ABS(elemdrawing[O_REPLICATOR]);
1221 elemmapping[O_CONVEYOR_SWITCH] = (cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF);
1222 elemdrawing[O_CONVEYOR_SWITCH] = gd_elements[cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF].image_game;
1224 if (cave->conveyor_belts_direction_changed)
1226 /* if direction is changed, animation is changed. */
1229 elemmapping[O_CONVEYOR_LEFT] = O_CONVEYOR_RIGHT;
1230 elemmapping[O_CONVEYOR_RIGHT] = O_CONVEYOR_LEFT;
1232 temp = elemdrawing[O_CONVEYOR_LEFT];
1233 elemdrawing[O_CONVEYOR_LEFT] = elemdrawing[O_CONVEYOR_RIGHT];
1234 elemdrawing[O_CONVEYOR_RIGHT] = temp;
1236 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_CHANGED;
1237 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_CHANGED].image_game;
1241 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_NORMAL;
1242 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_NORMAL].image_game;
1245 if (cave->conveyor_belts_active)
1247 /* keep potentially changed direction */
1248 int offset = (O_CONVEYOR_LEFT_ACTIVE - O_CONVEYOR_LEFT);
1250 /* if they are running, animate them. */
1251 elemmapping[O_CONVEYOR_LEFT] += offset;
1252 elemmapping[O_CONVEYOR_RIGHT] += offset;
1254 if (!cave->conveyor_belts_active)
1256 /* if they are not running, do not animate them. */
1257 elemdrawing[O_CONVEYOR_LEFT] = ABS(elemdrawing[O_CONVEYOR_LEFT]);
1258 elemdrawing[O_CONVEYOR_RIGHT] = ABS(elemdrawing[O_CONVEYOR_RIGHT]);
1263 /* also a hack, like biter_switch */
1264 elemdrawing[O_PNEUMATIC_ACTIVE_LEFT] += 2;
1265 elemdrawing[O_PNEUMATIC_ACTIVE_RIGHT] += 2;
1266 elemdrawing[O_PLAYER_PNEUMATIC_LEFT] += 2;
1267 elemdrawing[O_PLAYER_PNEUMATIC_RIGHT] += 2;
1270 if ((cave->last_direction) == GD_MV_STILL)
1272 /* player is idle. */
1273 if (player_blinking && player_tapping)
1275 map = O_PLAYER_TAP_BLINK;
1276 draw = gd_elements[O_PLAYER_TAP_BLINK].image_game;
1278 else if (player_blinking)
1280 map = O_PLAYER_BLINK;
1281 draw = gd_elements[O_PLAYER_BLINK].image_game;
1283 else if (player_tapping)
1286 draw = gd_elements[O_PLAYER_TAP].image_game;
1291 draw = gd_elements[O_PLAYER].image_game;
1294 else if (cave->last_horizontal_direction == GD_MV_LEFT)
1296 map = O_PLAYER_LEFT;
1297 draw = gd_elements[O_PLAYER_LEFT].image_game;
1301 /* of course this is GD_MV_RIGHT. */
1302 map = O_PLAYER_RIGHT;
1303 draw = gd_elements[O_PLAYER_RIGHT].image_game;
1306 elemmapping[O_PLAYER] = map;
1307 elemmapping[O_PLAYER_GLUED] = map;
1309 elemdrawing[O_PLAYER] = draw;
1310 elemdrawing[O_PLAYER_GLUED] = draw;
1312 /* player with bomb does not blink or tap - no graphics drawn for that.
1313 running is drawn using w/o bomb cells */
1314 if (cave->last_direction != GD_MV_STILL)
1316 elemmapping[O_PLAYER_BOMB] = map;
1317 elemdrawing[O_PLAYER_BOMB] = draw;
1320 elemmapping[O_INBOX] = (cave->inbox_flash_toggle ? O_INBOX_OPEN : O_INBOX_CLOSED);
1321 elemdrawing[O_INBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1323 elemmapping[O_OUTBOX] = (cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED);
1324 elemdrawing[O_OUTBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1326 /* hack, not fit into gd_elements */
1327 elemmapping[O_BITER_SWITCH] = O_BITER_SWITCH_1 + cave->biter_delay_frame;
1328 /* hack, not fit into gd_elements */
1329 elemdrawing[O_BITER_SWITCH] = gd_elements[O_BITER_SWITCH].image_game + cave->biter_delay_frame;
1331 /* visual effects */
1332 elemmapping[O_DIRT] = cave->dirt_looks_like;
1333 elemmapping[O_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1334 elemmapping[O_V_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1335 elemmapping[O_H_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1336 elemmapping[O_AMOEBA_2] = cave->amoeba_2_looks_like;
1338 /* visual effects */
1339 elemdrawing[O_DIRT] = elemdrawing[cave->dirt_looks_like];
1340 elemdrawing[O_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1341 elemdrawing[O_V_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1342 elemdrawing[O_H_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1343 elemdrawing[O_AMOEBA_2] = elemdrawing[cave->amoeba_2_looks_like];
1345 /* change only graphically */
1346 if (hate_invisible_outbox)
1348 elemmapping[O_PRE_INVIS_OUTBOX] = O_PRE_OUTBOX;
1349 elemmapping[O_INVIS_OUTBOX] = O_OUTBOX;
1352 if (hate_invisible_outbox)
1354 elemdrawing[O_PRE_INVIS_OUTBOX] = elemdrawing[O_PRE_OUTBOX];
1355 elemdrawing[O_INVIS_OUTBOX] = elemdrawing[O_OUTBOX];
1358 for (y = cave->y1; y <= cave->y2; y++)
1360 for (x = cave->x1; x <= cave->x2; x++)
1362 GdElement actual = cave->map[y][x];
1364 /* if covered, real element is not important */
1365 if (actual & COVERED)
1368 map = elemmapping[actual];
1370 /* if covered, real element is not important */
1371 if (actual & COVERED)
1372 draw = gd_elements[O_COVERED].image_game;
1374 draw = elemdrawing[actual];
1376 /* if negative, animated. */
1378 draw = -draw + animcycle;
1381 if (cave->gate_open_flash)
1382 draw += GD_NUM_OF_CELLS;
1384 /* set to buffer, with caching */
1385 if (element_buffer[y][x] != map)
1386 element_buffer[y][x] = map;
1388 if (gfx_buffer[y][x] != draw)
1389 gfx_buffer[y][x] = draw | GD_REDRAW;
1394 /* cave time is rounded _UP_ to seconds. so at the exact moment when it
1396 2sec remaining to 1sec remaining, the player has exactly one second.
1398 to zero, it is the exact moment of timeout. */
1399 /* internal time is milliseconds (or 1200 milliseconds for pal timing). */
1400 int gd_cave_time_show(const GdCave *cave, int internal_time)
1402 return (internal_time + cave->timing_factor - 1) / cave->timing_factor;
1405 GdReplay *gd_replay_new(void)
1409 rep = checked_calloc(sizeof(GdReplay));
1410 rep->movements = checked_calloc(sizeof(GdReplayMovements));
1415 GdReplay *gd_replay_new_from_replay(GdReplay *orig)
1419 rep = get_memcpy(orig, sizeof(GdReplay));
1421 /* replicate dynamic data */
1422 rep->comment = getStringCopy(orig->comment);
1423 rep->movements = get_memcpy(orig->movements, sizeof(GdReplayMovements));
1428 void gd_replay_free(GdReplay *replay)
1430 checked_free(replay->movements);
1431 checked_free(replay->comment);
1435 /* store movement in a replay */
1436 void gd_replay_store_movement(GdReplay *replay, GdDirection player_move,
1437 boolean player_fire, boolean suicide)
1441 data[0] = ((player_move) |
1442 (player_fire ? GD_REPLAY_FIRE_MASK : 0) |
1443 (suicide ? GD_REPLAY_SUICIDE_MASK : 0));
1445 if (replay->movements->len < MAX_REPLAY_LEN)
1447 replay->movements->data[replay->movements->len++] = data[0];
1449 if (replay->movements->len == MAX_REPLAY_LEN)
1450 Warn("BD replay truncated: size exceeds maximum replay size %d", MAX_REPLAY_LEN);
1454 /* calculate adler checksum for a rendered cave; this can be used for more caves. */
1455 void gd_cave_adler_checksum_more(GdCave *cave, unsigned int *a, unsigned int *b)
1459 for (y = 0; y < cave->h; y++)
1460 for (x = 0; x < cave->w; x++)
1462 *a += gd_elements[cave->map[y][x]].character;
1470 /* calculate adler checksum for a single rendered cave. */
1471 unsigned int gd_cave_adler_checksum(GdCave *cave)
1476 gd_cave_adler_checksum_more(cave, &a, &b);
1477 return (b << 16) + a;