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);
779 if last line or last row is just steel wall (or (invisible) outbox).
780 used after loading a game for playing.
781 after this, ew and eh will contain the effective width and height.
783 void gd_cave_auto_shrink(GdCave *cave)
795 /* set to maximum size, then try to shrink */
798 cave->x2 = cave->w - 1;
799 cave->y2 = cave->h - 1;
801 /* search for empty, steel-wall-only last rows. */
802 /* clear all lines, which are only steel wall.
803 * and clear only one line, which is steel wall, but also has a player or an outbox. */
808 for (y = cave->y2 - 1; y <= cave->y2; y++)
810 for (x = cave->x1; x <= cave->x2; x++)
812 switch (gd_cave_get_rc (cave, x, y))
814 /* if steels only, this is to be deleted. */
819 case O_PRE_INVIS_OUTBOX:
821 if (empty == STEEL_OR_OTHER)
824 /* if this, delete only this one, and exit. */
825 if (empty == STEEL_ONLY)
826 empty = STEEL_OR_OTHER;
830 /* anything else, that should be left in the cave. */
837 /* shrink if full steel or steel and player/outbox. */
838 if (empty != NO_SHRINK)
839 cave->y2--; /* one row shorter */
841 while (empty == STEEL_ONLY); /* if found just steels, repeat. */
843 /* search for empty, steel-wall-only first rows. */
848 for (y = cave->y1; y <= cave->y1 + 1; y++)
850 for (x = cave->x1; x <= cave->x2; x++)
852 switch (gd_cave_get_rc (cave, x, y))
858 case O_PRE_INVIS_OUTBOX:
860 /* shrink only lines, which have only ONE player or outbox.
861 this is for bd4 intermission 2, for example. */
862 if (empty == STEEL_OR_OTHER)
864 if (empty == STEEL_ONLY)
865 empty = STEEL_OR_OTHER;
875 if (empty != NO_SHRINK)
878 while (empty == STEEL_ONLY); /* if found one, repeat. */
880 /* empty last columns. */
885 for (y = cave->y1; y <= cave->y2; y++)
887 for (x = cave->x2 - 1; x <= cave->x2; x++)
889 switch (gd_cave_get_rc (cave, x, y))
895 case O_PRE_INVIS_OUTBOX:
897 if (empty == STEEL_OR_OTHER)
899 if (empty == STEEL_ONLY)
900 empty = STEEL_OR_OTHER;
910 /* just remember that one column shorter.
911 free will know the size of memchunk, no need to realloc! */
912 if (empty != NO_SHRINK)
915 while (empty == STEEL_ONLY); /* if found one, repeat. */
917 /* empty first columns. */
922 for (y = cave->y1; y <= cave->y2; y++)
924 for (x = cave->x1; x <= cave->x1 + 1; x++)
926 switch (gd_cave_get_rc (cave, x, y))
932 case O_PRE_INVIS_OUTBOX:
934 if (empty == STEEL_OR_OTHER)
936 if (empty == STEEL_ONLY)
937 empty = STEEL_OR_OTHER;
947 if (empty != NO_SHRINK)
950 while (empty == STEEL_ONLY); /* if found one, repeat. */
953 /* check if cave visible part coordinates
954 are outside cave sizes, or not in the right order.
955 correct them if needed.
957 void gd_cave_correct_visible_size(GdCave *cave)
959 /* change visible coordinates if they do not point to upperleft and lowerright */
960 if (cave->x2 < cave->x1)
967 if (cave->y2 < cave->y1)
980 if (cave->x2 > cave->w - 1)
981 cave->x2 = cave->w - 1;
983 if (cave->y2 > cave->h - 1)
984 cave->y2 = cave->h - 1;
988 bd1 and similar engines had animation bits in cave data, to set which elements to animate
989 (firefly, butterfly, amoeba).
990 animating an element also caused some delay each frame; according to my measurements,
991 around 2.6 ms/element.
993 static void cave_set_ckdelay_extra_for_animation(GdCave *cave)
996 boolean has_amoeba = FALSE, has_firefly = FALSE, has_butterfly = FALSE;
998 for (y = 0; y < cave->h; y++)
1000 for (x = 0; x < cave->w; x++)
1002 switch (cave->map[y][x] & ~SCANNED)
1015 has_butterfly = TRUE;
1025 cave->ckdelay_extra_for_animation = 0;
1027 cave->ckdelay_extra_for_animation += 2600;
1029 cave->ckdelay_extra_for_animation += 2600;
1031 cave->ckdelay_extra_for_animation += 2600;
1033 cave->ckdelay_extra_for_animation += 2600;
1036 /* do some init - setup some cave variables before the game. */
1037 void gd_cave_setup_for_game(GdCave *cave)
1041 cave_set_ckdelay_extra_for_animation(cave);
1043 /* find the player which will be the one to scroll to at the beginning of the game
1044 (before the player's birth) */
1045 if (cave->active_is_first_found)
1047 /* uppermost player is active */
1048 for (y = cave->h - 1; y >= 0; y--)
1050 for (x = cave->w - 1; x >= 0; x--)
1052 if (cave->map[y][x] == O_INBOX)
1062 /* lowermost player is active */
1063 for (y = 0; y < cave->h; y++)
1065 for (x = 0; x < cave->w; x++)
1067 if (cave->map[y][x] == O_INBOX)
1076 /* select number of milliseconds (for pal and ntsc) */
1077 cave->timing_factor = cave->pal_timing ? 1200 : 1000;
1079 cave->time *= cave->timing_factor;
1080 cave->magic_wall_time *= cave->timing_factor;
1081 cave->amoeba_time *= cave->timing_factor;
1082 cave->amoeba_2_time *= cave->timing_factor;
1083 cave->hatching_delay_time *= cave->timing_factor;
1085 if (cave->hammered_walls_reappear)
1086 cave->hammered_reappear = gd_cave_map_new(cave, int);
1089 /* cave diamonds needed can be set to n<=0. */
1090 /* if so, count the diamonds at the time of the hatching, and decrement that value from */
1091 /* the number of diamonds found. */
1092 /* of course, this function is to be called from the cave engine, at the exact time of hatching. */
1093 void gd_cave_count_diamonds(GdCave *cave)
1097 /* if automatically counting diamonds. if this was negative,
1098 * the sum will be this less than the number of all the diamonds in the cave */
1099 if (cave->diamonds_needed <= 0)
1101 for (y = 0; y < cave->h; y++)
1102 for (x = 0; x < cave->w; x++)
1103 if (cave->map[y][x] == O_DIAMOND)
1104 cave->diamonds_needed++;
1106 /* if still below zero, let this be 0, so gate will be open immediately */
1107 if (cave->diamonds_needed < 0)
1108 cave->diamonds_needed = 0;
1112 /* takes a cave and a gfx buffer, and fills the buffer with cell indexes.
1113 the indexes might change if bonus life flash is active (small lines in
1115 for the paused state (which is used in gdash but not in sdash) - yellowish
1117 also one can select the animation frame (0..7) to draw the cave on. so the
1121 if a cell is changed, it is flagged with GD_REDRAW; the flag can be cleared
1124 void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer,
1125 boolean bonus_life_flash, int animcycle, boolean hate_invisible_outbox)
1127 static int player_blinking = 0;
1128 static int player_tapping = 0;
1129 int elemmapping[O_MAX];
1130 int elemdrawing[O_MAX];
1131 int x, y, map, draw;
1133 if (cave->last_direction)
1135 /* he is moving, so stop blinking and tapping. */
1136 player_blinking = 0;
1141 /* he is idle, so animations can be done. */
1144 /* blinking and tapping is started at the beginning of animation sequences. */
1145 /* 1/4 chance of blinking, every sequence. */
1146 player_blinking = gd_random_int_range(0, 4) == 0;
1148 /* 1/16 chance of starting or stopping tapping. */
1149 if (gd_random_int_range(0, 16) == 0)
1150 player_tapping = !player_tapping;
1154 for (x = 0; x < O_MAX; x++)
1157 elemdrawing[x] = gd_elements[x].image_game;
1160 if (bonus_life_flash)
1162 elemmapping[O_SPACE] = O_FAKE_BONUS;
1163 elemdrawing[O_SPACE] = gd_elements[O_FAKE_BONUS].image_game;
1166 elemmapping[O_MAGIC_WALL] = (cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK);
1167 elemdrawing[O_MAGIC_WALL] = gd_elements[cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK].image_game;
1169 elemmapping[O_CREATURE_SWITCH] = (cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH);
1170 elemdrawing[O_CREATURE_SWITCH] = gd_elements[cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH].image_game;
1172 elemmapping[O_EXPANDING_WALL_SWITCH] = (cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ);
1173 elemdrawing[O_EXPANDING_WALL_SWITCH] = gd_elements[cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ].image_game;
1175 elemmapping[O_GRAVITY_SWITCH] = (cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH);
1176 elemdrawing[O_GRAVITY_SWITCH] = gd_elements[cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH].image_game;
1178 elemmapping[O_REPLICATOR_SWITCH] = (cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF);
1179 elemdrawing[O_REPLICATOR_SWITCH] = gd_elements[cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF].image_game;
1181 if (cave->replicators_active)
1182 /* if the replicators are active, animate them. */
1183 elemmapping[O_REPLICATOR] = O_REPLICATOR_ACTIVE;
1185 if (!cave->replicators_active)
1186 /* if the replicators are inactive, do not animate them. */
1187 elemdrawing[O_REPLICATOR] = ABS(elemdrawing[O_REPLICATOR]);
1189 elemmapping[O_CONVEYOR_SWITCH] = (cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF);
1190 elemdrawing[O_CONVEYOR_SWITCH] = gd_elements[cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF].image_game;
1192 if (cave->conveyor_belts_direction_changed)
1194 /* if direction is changed, animation is changed. */
1197 elemmapping[O_CONVEYOR_LEFT] = O_CONVEYOR_RIGHT;
1198 elemmapping[O_CONVEYOR_RIGHT] = O_CONVEYOR_LEFT;
1200 temp = elemdrawing[O_CONVEYOR_LEFT];
1201 elemdrawing[O_CONVEYOR_LEFT] = elemdrawing[O_CONVEYOR_RIGHT];
1202 elemdrawing[O_CONVEYOR_RIGHT] = temp;
1204 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_CHANGED;
1205 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_CHANGED].image_game;
1209 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_NORMAL;
1210 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_NORMAL].image_game;
1213 if (cave->conveyor_belts_active)
1215 /* keep potentially changed direction */
1216 int offset = (O_CONVEYOR_LEFT_ACTIVE - O_CONVEYOR_LEFT);
1218 /* if they are running, animate them. */
1219 elemmapping[O_CONVEYOR_LEFT] += offset;
1220 elemmapping[O_CONVEYOR_RIGHT] += offset;
1222 if (!cave->conveyor_belts_active)
1224 /* if they are not running, do not animate them. */
1225 elemdrawing[O_CONVEYOR_LEFT] = ABS(elemdrawing[O_CONVEYOR_LEFT]);
1226 elemdrawing[O_CONVEYOR_RIGHT] = ABS(elemdrawing[O_CONVEYOR_RIGHT]);
1231 /* also a hack, like biter_switch */
1232 elemdrawing[O_PNEUMATIC_ACTIVE_LEFT] += 2;
1233 elemdrawing[O_PNEUMATIC_ACTIVE_RIGHT] += 2;
1234 elemdrawing[O_PLAYER_PNEUMATIC_LEFT] += 2;
1235 elemdrawing[O_PLAYER_PNEUMATIC_RIGHT] += 2;
1238 if ((cave->last_direction) == GD_MV_STILL)
1240 /* player is idle. */
1241 if (player_blinking && player_tapping)
1243 map = O_PLAYER_TAP_BLINK;
1244 draw = gd_elements[O_PLAYER_TAP_BLINK].image_game;
1246 else if (player_blinking)
1248 map = O_PLAYER_BLINK;
1249 draw = gd_elements[O_PLAYER_BLINK].image_game;
1251 else if (player_tapping)
1254 draw = gd_elements[O_PLAYER_TAP].image_game;
1259 draw = gd_elements[O_PLAYER].image_game;
1262 else if (cave->last_horizontal_direction == GD_MV_LEFT)
1264 map = O_PLAYER_LEFT;
1265 draw = gd_elements[O_PLAYER_LEFT].image_game;
1269 /* of course this is GD_MV_RIGHT. */
1270 map = O_PLAYER_RIGHT;
1271 draw = gd_elements[O_PLAYER_RIGHT].image_game;
1274 elemmapping[O_PLAYER] = map;
1275 elemmapping[O_PLAYER_GLUED] = map;
1277 elemdrawing[O_PLAYER] = draw;
1278 elemdrawing[O_PLAYER_GLUED] = draw;
1280 /* player with bomb does not blink or tap - no graphics drawn for that.
1281 running is drawn using w/o bomb cells */
1282 if (cave->last_direction != GD_MV_STILL)
1284 elemmapping[O_PLAYER_BOMB] = map;
1285 elemdrawing[O_PLAYER_BOMB] = draw;
1288 elemmapping[O_INBOX] = (cave->inbox_flash_toggle ? O_INBOX_OPEN : O_INBOX_CLOSED);
1289 elemdrawing[O_INBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1291 elemmapping[O_OUTBOX] = (cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED);
1292 elemdrawing[O_OUTBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1294 /* hack, not fit into gd_elements */
1295 elemmapping[O_BITER_SWITCH] = O_BITER_SWITCH_1 + cave->biter_delay_frame;
1296 /* hack, not fit into gd_elements */
1297 elemdrawing[O_BITER_SWITCH] = gd_elements[O_BITER_SWITCH].image_game + cave->biter_delay_frame;
1299 /* visual effects */
1300 elemmapping[O_DIRT] = cave->dirt_looks_like;
1301 elemmapping[O_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1302 elemmapping[O_V_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1303 elemmapping[O_H_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1304 elemmapping[O_AMOEBA_2] = cave->amoeba_2_looks_like;
1306 /* visual effects */
1307 elemdrawing[O_DIRT] = elemdrawing[cave->dirt_looks_like];
1308 elemdrawing[O_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1309 elemdrawing[O_V_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1310 elemdrawing[O_H_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1311 elemdrawing[O_AMOEBA_2] = elemdrawing[cave->amoeba_2_looks_like];
1313 /* change only graphically */
1314 if (hate_invisible_outbox)
1316 elemmapping[O_PRE_INVIS_OUTBOX] = O_PRE_OUTBOX;
1317 elemmapping[O_INVIS_OUTBOX] = O_OUTBOX;
1320 if (hate_invisible_outbox)
1322 elemdrawing[O_PRE_INVIS_OUTBOX] = elemdrawing[O_PRE_OUTBOX];
1323 elemdrawing[O_INVIS_OUTBOX] = elemdrawing[O_OUTBOX];
1326 for (y = cave->y1; y <= cave->y2; y++)
1328 for (x = cave->x1; x <= cave->x2; x++)
1330 GdElement actual = cave->map[y][x];
1332 /* if covered, real element is not important */
1333 if (actual & COVERED)
1336 map = elemmapping[actual];
1338 /* if covered, real element is not important */
1339 if (actual & COVERED)
1340 draw = gd_elements[O_COVERED].image_game;
1342 draw = elemdrawing[actual];
1344 /* if negative, animated. */
1346 draw = -draw + animcycle;
1349 if (cave->gate_open_flash)
1350 draw += GD_NUM_OF_CELLS;
1352 /* set to buffer, with caching */
1353 if (element_buffer[y][x] != map)
1354 element_buffer[y][x] = map;
1356 if (gfx_buffer[y][x] != draw)
1357 gfx_buffer[y][x] = draw | GD_REDRAW;
1362 /* cave time is rounded _UP_ to seconds. so at the exact moment when it
1364 2sec remaining to 1sec remaining, the player has exactly one second.
1366 to zero, it is the exact moment of timeout. */
1367 /* internal time is milliseconds (or 1200 milliseconds for pal timing). */
1368 int gd_cave_time_show(const GdCave *cave, int internal_time)
1370 return (internal_time + cave->timing_factor - 1) / cave->timing_factor;
1373 GdReplay *gd_replay_new(void)
1377 rep = checked_calloc(sizeof(GdReplay));
1378 rep->movements = checked_calloc(sizeof(GdReplayMovements));
1383 GdReplay *gd_replay_new_from_replay(GdReplay *orig)
1387 rep = get_memcpy(orig, sizeof(GdReplay));
1389 /* replicate dynamic data */
1390 rep->comment = getStringCopy(orig->comment);
1391 rep->movements = get_memcpy(orig->movements, sizeof(GdReplayMovements));
1396 void gd_replay_free(GdReplay *replay)
1398 checked_free(replay->movements);
1399 checked_free(replay->comment);
1403 /* store movement in a replay */
1404 void gd_replay_store_movement(GdReplay *replay, GdDirection player_move,
1405 boolean player_fire, boolean suicide)
1409 data[0] = ((player_move) |
1410 (player_fire ? GD_REPLAY_FIRE_MASK : 0) |
1411 (suicide ? GD_REPLAY_SUICIDE_MASK : 0));
1413 if (replay->movements->len < MAX_REPLAY_LEN)
1415 replay->movements->data[replay->movements->len++] = data[0];
1417 if (replay->movements->len == MAX_REPLAY_LEN)
1418 Warn("BD replay truncated: size exceeds maximum replay size %d", MAX_REPLAY_LEN);
1422 /* calculate adler checksum for a rendered cave; this can be used for more caves. */
1423 void gd_cave_adler_checksum_more(GdCave *cave, unsigned int *a, unsigned int *b)
1427 for (y = 0; y < cave->h; y++)
1428 for (x = 0; x < cave->w; x++)
1430 *a += gd_elements[cave->map[y][x]].character;
1438 /* calculate adler checksum for a single rendered cave. */
1439 unsigned int gd_cave_adler_checksum(GdCave *cave)
1444 gd_cave_adler_checksum_more(cave, &a, &b);
1445 return (b << 16) + a;