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.
18 #include <glib/gi18n.h>
23 /* arrays for movements */
24 /* also no1 and bd2 cave data import helpers; line direction coordinates */
27 0, 0, 1, 1, 1, 0, -1, -1, -1, 0, 2, 2, 2, 0, -2, -2, -2
31 0, -1, -1, 0, 1, 1, 1, 0, -1, -2, -2, 0, 2, 2, 2, 0, -2
35 None here means "no direction to move"; when there is no gravity while stirring the pot. */
36 static const char* direction_name[] =
49 static const char* direction_filename[] =
62 static const char* scheduling_name[] =
70 "Atari BD2/Construction Kit"
73 static const char* scheduling_filename[] =
84 static HashTable *name_to_element;
85 GdElement gd_char_to_element[256];
87 /* color of flashing the screen, gate opening to exit */
88 const GdColor gd_flash_color = 0xFFFFC0;
90 /* selected object in editor */
91 const GdColor gd_select_color = 0x8080FF;
93 /* direction to string and vice versa */
94 const char *gd_direction_get_visible_name(GdDirection dir)
96 return direction_name[dir];
99 const char *gd_direction_get_filename(GdDirection dir)
101 return direction_filename[dir];
104 GdDirection gd_direction_from_string(const char *str)
108 for (i = 1; i<G_N_ELEMENTS(direction_filename); i++)
109 if (strcasecmp(str, direction_filename[i]) == 0)
110 return (GdDirection) i;
112 Warn("invalid direction name '%s', defaulting to down", str);
116 /* scheduling name to string and vice versa */
117 const char *gd_scheduling_get_filename(GdScheduling sched)
119 return scheduling_filename[sched];
122 const char *gd_scheduling_get_visible_name(GdScheduling sched)
124 return scheduling_name[sched];
127 GdScheduling gd_scheduling_from_string(const char *str)
131 for (i = 0; i < G_N_ELEMENTS(scheduling_filename); i++)
132 if (strcasecmp(str, scheduling_filename[i]) == 0)
133 return (GdScheduling) i;
135 Warn("invalid scheduling name '%s', defaulting to plck", str);
137 return GD_SCHEDULING_PLCK;
141 fill a given struct with default properties.
142 "str" is the struct (data),
143 "properties" describes the structure and its pointers,
144 "defaults" are the pieces of data which will be copied to str.
146 void gd_struct_set_defaults_from_array(gpointer str,
147 const GdStructDescriptor *properties,
148 GdPropertyDefault *defaults)
152 for (i = 0; defaults[i].offset != -1; i++)
154 gpointer pvalue = G_STRUCT_MEMBER_P(str, defaults[i].offset);
155 /* these point to the same, but to avoid the awkward cast syntax */
156 int *ivalue = pvalue;
157 GdElement *evalue = pvalue;
158 GdDirection *dvalue = pvalue;
159 GdScheduling *svalue = pvalue;
160 boolean *bvalue = pvalue;
161 GdColor *cvalue = pvalue;
164 /* check which property we are talking about: find it in gd_cave_properties. */
165 n = defaults[i].property_index;
168 while (properties[n].identifier != NULL &&
169 properties[n].offset != defaults[i].offset)
172 /* remember so we will be fast later*/
173 defaults[i].property_index = n;
176 /* some properties are arrays. this loop fills all with the same values */
177 for (j = 0; j < properties[n].count; j++)
179 switch (properties[n].type)
181 /* these are for the gui; do nothing */
184 /* no default value for strings */
186 case GD_TYPE_LONGSTRING:
190 /* this is also an integer, difference is only when saving to bdcff */
192 if (defaults[i].defval < properties[n].min ||
193 defaults[i].defval > properties[n].max)
194 Warn("integer property %s out of range", properties[n].identifier);
195 ivalue[j] = defaults[i].defval;
198 case GD_TYPE_PROBABILITY:
199 /* floats are stored as integer, /million; but are integers */
200 if (defaults[i].defval < 0 ||
201 defaults[i].defval > 1000000)
202 Warn("integer property %s out of range", properties[n].identifier);
203 ivalue[j] = defaults[i].defval;
206 case GD_TYPE_BOOLEAN:
207 bvalue[j] = defaults[i].defval != 0;
210 case GD_TYPE_ELEMENT:
212 evalue[j] = (GdElement) defaults[i].defval;
216 cvalue[j] = gd_c64_color(defaults[i].defval);
219 case GD_TYPE_DIRECTION:
220 dvalue[j] = (GdDirection) defaults[i].defval;
223 case GD_TYPE_SCHEDULING:
224 svalue[j] = (GdScheduling) defaults[i].defval;
231 /* creates the character->element conversion table; using
232 the fixed-in-the-bdcff characters. later, this table
233 may be filled with more elements.
235 void gd_create_char_to_element_table(void)
239 /* fill all with unknown */
240 for (i = 0; i < G_N_ELEMENTS(gd_char_to_element); i++)
241 gd_char_to_element[i] = O_UNKNOWN;
243 /* then set fixed characters */
244 for (i = 0; i < O_MAX; i++)
246 int c = gd_elements[i].character;
250 if (gd_char_to_element[c] != O_UNKNOWN)
251 Warn("Character %c already used for element %x", c, gd_char_to_element[c]);
253 gd_char_to_element[c] = i;
258 /* search the element database for the specified character, and return the element. */
259 GdElement gd_get_element_from_character (guint8 character)
261 if (gd_char_to_element[character] != O_UNKNOWN)
262 return gd_char_to_element[character];
264 Warn ("Invalid character representing element: %c", character);
270 do some init; this function is to be called at the start of the application
272 void gd_cave_init(void)
276 /* put names to a hash table */
277 /* this is a helper for file read operations */
278 /* maps copied strings to elements (integers) */
279 name_to_element = create_hashtable(gd_str_case_hash, gd_str_case_equal, NULL, NULL);
281 for (i = 0; i < O_MAX; i++)
285 key = getStringToUpper(gd_elements[i].filename);
287 if (hashtable_exists(name_to_element, key)) /* hash value may be 0 */
288 Warn("Name %s already used for element %x", key, i);
290 hashtable_insert(name_to_element, key, INT_TO_PTR(i));
291 /* ^^^ do not free "key", as hash table needs it during the whole time! */
293 key = getStringCat2("SCANNED_", key); /* new string */
295 hashtable_insert(name_to_element, key, INT_TO_PTR(i));
296 /* once again, do not free "key" ^^^ */
299 /* for compatibility with tim stridmann's memorydump->bdcff converter... .... ... */
300 hashtable_insert(name_to_element, "HEXPANDING_WALL", INT_TO_PTR(O_H_EXPANDING_WALL));
301 hashtable_insert(name_to_element, "FALLING_DIAMOND", INT_TO_PTR(O_DIAMOND_F));
302 hashtable_insert(name_to_element, "FALLING_BOULDER", INT_TO_PTR(O_STONE_F));
303 hashtable_insert(name_to_element, "EXPLOSION1S", INT_TO_PTR(O_EXPLODE_1));
304 hashtable_insert(name_to_element, "EXPLOSION2S", INT_TO_PTR(O_EXPLODE_2));
305 hashtable_insert(name_to_element, "EXPLOSION3S", INT_TO_PTR(O_EXPLODE_3));
306 hashtable_insert(name_to_element, "EXPLOSION4S", INT_TO_PTR(O_EXPLODE_4));
307 hashtable_insert(name_to_element, "EXPLOSION5S", INT_TO_PTR(O_EXPLODE_5));
308 hashtable_insert(name_to_element, "EXPLOSION1D", INT_TO_PTR(O_PRE_DIA_1));
309 hashtable_insert(name_to_element, "EXPLOSION2D", INT_TO_PTR(O_PRE_DIA_2));
310 hashtable_insert(name_to_element, "EXPLOSION3D", INT_TO_PTR(O_PRE_DIA_3));
311 hashtable_insert(name_to_element, "EXPLOSION4D", INT_TO_PTR(O_PRE_DIA_4));
312 hashtable_insert(name_to_element, "EXPLOSION5D", INT_TO_PTR(O_PRE_DIA_5));
313 hashtable_insert(name_to_element, "WALL2", INT_TO_PTR(O_STEEL_EXPLODABLE));
315 /* compatibility with old bd-faq (pre disassembly of bladder) */
316 hashtable_insert(name_to_element, "BLADDERd9", INT_TO_PTR(O_BLADDER_8));
318 /* create table to show errors at the start of the application */
319 gd_create_char_to_element_table();
322 /* search the element database for the specified name, and return the element */
323 GdElement gd_get_element_from_string (const char *string)
325 char *upper = getStringToUpper(string);
331 Warn("Invalid string representing element: (null)");
335 found = hashtable_exists(name_to_element, upper); /* hash value may be 0 */
337 value = hashtable_search(name_to_element, upper);
340 return (GdElement) (PTR_TO_INT(value));
342 Warn("Invalid string representing element: '%s'", string);
347 void gd_cave_set_defaults_from_array(GdCave* cave, GdPropertyDefault *defaults)
349 gd_struct_set_defaults_from_array(cave, gd_cave_properties, defaults);
353 load default values from description array
354 these are default for gdash and bdcff.
356 void gd_cave_set_gdash_defaults(GdCave* cave)
360 gd_cave_set_defaults_from_array(cave, gd_cave_defaults_gdash);
362 /* these did not fit into the descriptor array */
363 for (i = 0; i < 5; i++)
365 cave->level_rand[i] = i;
366 cave->level_timevalue[i] = i + 1;
370 /* for quicksort. compares two highscores. */
371 int gd_highscore_compare(gconstpointer a, gconstpointer b)
373 const GdHighScore *ha = a;
374 const GdHighScore *hb = b;
375 return hb->score - ha->score;
378 void gd_clear_highscore(GdHighScore *hs)
382 for (i = 0; i < GD_HIGHSCORE_NUM; i++)
384 strcpy(hs[i].name, "");
389 boolean gd_has_highscore(GdHighScore *hs)
391 return hs[0].score > 0;
394 /* return true if score achieved is a highscore */
395 boolean gd_is_highscore(GdHighScore *scores, int score)
397 /* if score is above zero AND bigger than the last one */
398 if (score > 0 && score > scores[GD_HIGHSCORE_NUM-1].score)
404 int gd_add_highscore(GdHighScore *highscores, const char *name, int score)
408 if (!gd_is_highscore(highscores, score))
411 /* overwrite the last one */
412 gd_strcpy(highscores[GD_HIGHSCORE_NUM-1].name, name);
413 highscores[GD_HIGHSCORE_NUM-1].score = score;
416 qsort(highscores, GD_HIGHSCORE_NUM, sizeof(GdHighScore), gd_highscore_compare);
418 for (i = 0; i < GD_HIGHSCORE_NUM; i++)
419 if (strEqual(highscores[i].name, name) && highscores[i].score == score)
425 /* for the case-insensitive hash keys */
426 int gd_str_case_equal(void *s1, void *s2)
428 return strcasecmp(s1, s2) == 0;
431 unsigned int gd_str_case_hash(void *v)
433 char *upper = getStringToUpper(v);
434 unsigned int hash = get_hash_from_string(upper);
442 create new cave with default values.
443 sets every value, also default size, diamond value etc.
445 GdCave *gd_cave_new(void)
449 cave = checked_calloc(sizeof(GdCave));
451 /* hash table which stores unknown tags as strings. */
452 cave->tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
454 gd_cave_set_gdash_defaults(cave);
460 cave maps are continuous areas in memory. the allocated memory
461 is width * height * bytes_per_cell long.
462 the cave map[0] stores the pointer given by g_malloc().
463 the map itself is also an allocated array of pointers to the
466 rows = new (pointers to rows);
468 rows[1..h-1] = rows[0] + width * bytes
476 allocate a cave map-like array, and initialize to zero.
477 one cell is cell_size bytes long.
479 gpointer gd_cave_map_new_for_cave(const GdCave *cave, const int cell_size)
481 gpointer *rows; /* this is void**, pointer to array of ... */
484 rows = checked_malloc((cave->h) * sizeof(gpointer));
485 rows[0] = checked_calloc(cell_size * cave->w * cave->h);
487 for (y = 1; y < cave->h; y++)
488 /* base pointer + num_of_bytes_per_element * width * number_of_row; as sizeof(char) = 1 */
489 rows[y] = (char *)rows[0] + cell_size * cave->w * y;
497 if map is null, this also returns null.
499 gpointer gd_cave_map_dup_size(const GdCave *cave, const gpointer map, const int cell_size)
502 gpointer *maplines = (gpointer *)map;
508 rows = checked_malloc((cave->h) * sizeof(gpointer));
509 rows[0] = get_memcpy (maplines[0], cell_size * cave->w * cave->h);
511 for (y = 1; y < cave->h; y++)
512 rows[y] = (char *)rows[0] + cell_size * cave->w * y;
517 void gd_cave_map_free(gpointer map)
519 gpointer *maplines = (gpointer *) map;
529 frees memory associated to cave
531 void gd_cave_free(GdCave *cave)
539 hashtable_destroy(cave->tags);
541 if (cave->random) /* random generator is a GdRand * */
542 gd_rand_free(cave->random);
545 for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
546 if (gd_cave_properties[i].type == GD_TYPE_LONGSTRING)
547 checked_free(G_STRUCT_MEMBER(char *, cave, gd_cave_properties[i].offset));
550 gd_cave_map_free(cave->map);
553 gd_cave_map_free(cave->objects_order);
555 /* hammered walls to reappear data */
556 gd_cave_map_free(cave->hammered_reappear);
559 list_foreach(cave->objects, (list_fn) free, NULL);
560 list_free(cave->objects);
563 list_foreach(cave->replays, (list_fn) gd_replay_free, NULL);
564 list_free(cave->replays);
566 /* freeing main pointer */
570 static void hash_copy_foreach(const char *key, const char *value, HashTable *dest)
572 hashtable_insert(dest, getStringCopy(key), getStringCopy(value));
575 /* copy cave from src to destination, with duplicating dynamically allocated data */
576 void gd_cave_copy(GdCave *dest, const GdCave *src)
580 /* copy entire data */
581 memmove(dest, src, sizeof(GdCave));
583 /* but duplicate dynamic data */
584 dest->tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
587 hashtable_foreach(src->tags, (hashtable_fn)hash_copy_foreach, dest->tags);
589 dest->map = gd_cave_map_dup(src, map);
590 dest->hammered_reappear = gd_cave_map_dup(src, hammered_reappear);
592 /* for longstrings */
593 for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
594 if (gd_cave_properties[i].type == GD_TYPE_LONGSTRING)
595 G_STRUCT_MEMBER(char *, dest, gd_cave_properties[i].offset) =
596 getStringCopy(G_STRUCT_MEMBER(char *, src, gd_cave_properties[i].offset));
598 /* no reason to copy this */
599 dest->objects_order = NULL;
601 /* copy objects list */
606 dest->objects = NULL; /* new empty list */
607 for (iter = src->objects; iter != NULL; iter = iter->next) /* do a deep copy */
608 dest->objects = list_append(dest->objects, get_memcpy (iter->data, sizeof (GdObject)));
616 dest->replays = NULL;
617 for (iter = src->replays; iter != NULL; iter = iter->next) /* do a deep copy */
618 dest->replays = list_append(dest->replays, gd_replay_new_from_replay(iter->data));
621 /* copy random number generator */
623 dest->random = gd_rand_copy(src->random);
626 /* create new cave, which is a copy of the cave given. */
627 GdCave *gd_cave_new_from_cave(const GdCave *orig)
631 cave = gd_cave_new();
632 gd_cave_copy(cave, orig);
638 Put an object to the specified position.
639 Performs range checking.
640 If wraparound objects are selected, wraps around x coordinates, with or without lineshift.
641 (The y coordinate is not wrapped, as it did not work like that on the c64)
642 order is a pointer to the GdObject describing this object. Thus the editor can identify 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... */
659 y--; /* previous row */
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. */
703 /* fix x coordinate */
706 /* fit x coordinate within range, with correcting y at the same time */
709 x += cave->w; /* out of bounds on the left... */
710 y--; /* previous row */
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);
782 if last line or last row is just steel wall (or (invisible) outbox).
783 used after loading a game for playing.
784 after this, ew and eh will contain the effective width and height.
786 void gd_cave_auto_shrink(GdCave *cave)
798 /* set to maximum size, then try to shrink */
801 cave->x2 = cave->w - 1;
802 cave->y2 = cave->h - 1;
804 /* search for empty, steel-wall-only last rows. */
805 /* clear all lines, which are only steel wall.
806 * and clear only one line, which is steel wall, but also has a player or an outbox. */
811 for (y = cave->y2 - 1; y <= cave->y2; y++)
813 for (x = cave->x1; x <= cave->x2; x++)
815 switch (gd_cave_get_rc (cave, x, y))
817 /* if steels only, this is to be deleted. */
822 case O_PRE_INVIS_OUTBOX:
824 if (empty == STEEL_OR_OTHER)
827 /* if this, delete only this one, and exit. */
828 if (empty == STEEL_ONLY)
829 empty = STEEL_OR_OTHER;
833 /* anything else, that should be left in the cave. */
840 /* shrink if full steel or steel and player/outbox. */
841 if (empty != NO_SHRINK)
842 cave->y2--; /* one row shorter */
844 while (empty == STEEL_ONLY); /* if found just steels, repeat. */
846 /* search for empty, steel-wall-only first rows. */
851 for (y = cave->y1; y <= cave->y1 + 1; y++)
853 for (x = cave->x1; x <= cave->x2; x++)
855 switch (gd_cave_get_rc (cave, x, y))
861 case O_PRE_INVIS_OUTBOX:
863 /* shrink only lines, which have only ONE player or outbox.
864 this is for bd4 intermission 2, for example. */
865 if (empty == STEEL_OR_OTHER)
867 if (empty == STEEL_ONLY)
868 empty = STEEL_OR_OTHER;
878 if (empty != NO_SHRINK)
881 while (empty == STEEL_ONLY); /* if found one, repeat. */
883 /* empty last columns. */
888 for (y = cave->y1; y <= cave->y2; y++)
890 for (x = cave->x2 - 1; x <= cave->x2; x++)
892 switch (gd_cave_get_rc (cave, x, y))
898 case O_PRE_INVIS_OUTBOX:
900 if (empty == STEEL_OR_OTHER)
902 if (empty == STEEL_ONLY)
903 empty = STEEL_OR_OTHER;
913 /* just remember that one column shorter.
914 free will know the size of memchunk, no need to realloc! */
915 if (empty != NO_SHRINK)
918 while (empty == STEEL_ONLY); /* if found one, repeat. */
920 /* empty first columns. */
925 for (y = cave->y1; y <= cave->y2; y++)
927 for (x = cave->x1; x <= cave->x1 + 1; x++)
929 switch (gd_cave_get_rc (cave, x, y))
935 case O_PRE_INVIS_OUTBOX:
937 if (empty == STEEL_OR_OTHER)
939 if (empty == STEEL_ONLY)
940 empty = STEEL_OR_OTHER;
950 if (empty != NO_SHRINK)
953 while (empty == STEEL_ONLY); /* if found one, repeat. */
956 /* check if cave visible part coordinates
957 are outside cave sizes, or not in the right order.
958 correct them if needed.
960 void gd_cave_correct_visible_size(GdCave *cave)
962 /* change visible coordinates if they do not point to upperleft and lowerright */
963 if (cave->x2 < cave->x1)
970 if (cave->y2 < cave->y1)
983 if (cave->x2 > cave->w - 1)
984 cave->x2 = cave->w - 1;
986 if (cave->y2 > cave->h - 1)
987 cave->y2 = cave->h - 1;
991 bd1 and similar engines had animation bits in cave data, to set which elements to animate
992 (firefly, butterfly, amoeba).
993 animating an element also caused some delay each frame; according to my measurements,
994 around 2.6 ms/element.
996 static void cave_set_ckdelay_extra_for_animation(GdCave *cave)
999 boolean has_amoeba = FALSE, has_firefly = FALSE, has_butterfly = FALSE;
1001 for (y = 0; y < cave->h; y++)
1003 for (x = 0; x < cave->w; x++)
1005 switch (cave->map[y][x] & ~SCANNED)
1018 has_butterfly = TRUE;
1028 cave->ckdelay_extra_for_animation = 0;
1030 cave->ckdelay_extra_for_animation += 2600;
1032 cave->ckdelay_extra_for_animation += 2600;
1034 cave->ckdelay_extra_for_animation += 2600;
1036 cave->ckdelay_extra_for_animation += 2600;
1039 /* do some init - setup some cave variables before the game. */
1040 void gd_cave_setup_for_game(GdCave *cave)
1044 cave_set_ckdelay_extra_for_animation(cave);
1046 /* find the player which will be the one to scroll to at the beginning of the game
1047 (before the player's birth) */
1048 if (cave->active_is_first_found)
1050 /* uppermost player is active */
1051 for (y = cave->h - 1; y >= 0; y--)
1053 for (x = cave->w - 1; x >= 0; x--)
1055 if (cave->map[y][x] == O_INBOX)
1065 /* lowermost player is active */
1066 for (y = 0; y < cave->h; y++)
1068 for (x = 0; x < cave->w; x++)
1070 if (cave->map[y][x] == O_INBOX)
1079 /* select number of milliseconds (for pal and ntsc) */
1080 cave->timing_factor = cave->pal_timing ? 1200 : 1000;
1082 cave->time *= cave->timing_factor;
1083 cave->magic_wall_time *= cave->timing_factor;
1084 cave->amoeba_time *= cave->timing_factor;
1085 cave->amoeba_2_time *= cave->timing_factor;
1086 cave->hatching_delay_time *= cave->timing_factor;
1088 if (cave->hammered_walls_reappear)
1089 cave->hammered_reappear = gd_cave_map_new(cave, int);
1092 /* cave diamonds needed can be set to n<=0. */
1093 /* if so, count the diamonds at the time of the hatching, and decrement that value from */
1094 /* the number of diamonds found. */
1095 /* of course, this function is to be called from the cave engine, at the exact time of hatching. */
1096 void gd_cave_count_diamonds(GdCave *cave)
1100 /* if automatically counting diamonds. if this was negative,
1101 * the sum will be this less than the number of all the diamonds in the cave */
1102 if (cave->diamonds_needed <= 0)
1104 for (y = 0; y < cave->h; y++)
1105 for (x = 0; x < cave->w; x++)
1106 if (cave->map[y][x] == O_DIAMOND)
1107 cave->diamonds_needed++;
1109 /* if still below zero, let this be 0, so gate will be open immediately */
1110 if (cave->diamonds_needed < 0)
1111 cave->diamonds_needed = 0;
1115 /* takes a cave and a gfx buffer, and fills the buffer with cell indexes.
1116 the indexes might change if bonus life flash is active (small lines in
1118 for the paused state (which is used in gdash but not in sdash) - yellowish
1120 also one can select the animation frame (0..7) to draw the cave on. so the
1124 if a cell is changed, it is flagged with GD_REDRAW; the flag can be cleared
1127 void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer,
1128 boolean bonus_life_flash, int animcycle, boolean hate_invisible_outbox)
1130 static int player_blinking = 0;
1131 static int player_tapping = 0;
1132 int elemmapping[O_MAX];
1133 int elemdrawing[O_MAX];
1134 int x, y, map, draw;
1136 if (cave->last_direction)
1138 /* he is moving, so stop blinking and tapping. */
1139 player_blinking = 0;
1144 /* he is idle, so animations can be done. */
1147 /* blinking and tapping is started at the beginning of animation sequences. */
1148 /* 1/4 chance of blinking, every sequence. */
1149 player_blinking = gd_random_int_range(0, 4) == 0;
1151 /* 1/16 chance of starting or stopping tapping. */
1152 if (gd_random_int_range(0, 16) == 0)
1153 player_tapping = !player_tapping;
1157 for (x = 0; x < O_MAX; x++)
1160 elemdrawing[x] = gd_elements[x].image_game;
1163 if (bonus_life_flash)
1165 elemmapping[O_SPACE] = O_FAKE_BONUS;
1166 elemdrawing[O_SPACE] = gd_elements[O_FAKE_BONUS].image_game;
1169 elemmapping[O_MAGIC_WALL] = (cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK);
1170 elemdrawing[O_MAGIC_WALL] = gd_elements[cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK].image_game;
1172 elemmapping[O_CREATURE_SWITCH] = (cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH);
1173 elemdrawing[O_CREATURE_SWITCH] = gd_elements[cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH].image_game;
1175 elemmapping[O_EXPANDING_WALL_SWITCH] = (cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ);
1176 elemdrawing[O_EXPANDING_WALL_SWITCH] = gd_elements[cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ].image_game;
1178 elemmapping[O_GRAVITY_SWITCH] = (cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH);
1179 elemdrawing[O_GRAVITY_SWITCH] = gd_elements[cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH].image_game;
1181 elemmapping[O_REPLICATOR_SWITCH] = (cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF);
1182 elemdrawing[O_REPLICATOR_SWITCH] = gd_elements[cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF].image_game;
1184 if (cave->replicators_active)
1185 /* if the replicators are active, animate them. */
1186 elemmapping[O_REPLICATOR] = O_REPLICATOR_ACTIVE;
1188 if (!cave->replicators_active)
1189 /* if the replicators are inactive, do not animate them. */
1190 elemdrawing[O_REPLICATOR] = ABS(elemdrawing[O_REPLICATOR]);
1192 elemmapping[O_CONVEYOR_SWITCH] = (cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF);
1193 elemdrawing[O_CONVEYOR_SWITCH] = gd_elements[cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF].image_game;
1195 if (cave->conveyor_belts_direction_changed)
1197 /* if direction is changed, animation is changed. */
1200 elemmapping[O_CONVEYOR_LEFT] = O_CONVEYOR_RIGHT;
1201 elemmapping[O_CONVEYOR_RIGHT] = O_CONVEYOR_LEFT;
1203 temp = elemdrawing[O_CONVEYOR_LEFT];
1204 elemdrawing[O_CONVEYOR_LEFT] = elemdrawing[O_CONVEYOR_RIGHT];
1205 elemdrawing[O_CONVEYOR_RIGHT] = temp;
1207 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_CHANGED;
1208 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_CHANGED].image_game;
1212 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_NORMAL;
1213 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_NORMAL].image_game;
1216 if (cave->conveyor_belts_active)
1218 /* keep potentially changed direction */
1219 int offset = (O_CONVEYOR_LEFT_ACTIVE - O_CONVEYOR_LEFT);
1221 /* if they are running, animate them. */
1222 elemmapping[O_CONVEYOR_LEFT] += offset;
1223 elemmapping[O_CONVEYOR_RIGHT] += offset;
1225 if (!cave->conveyor_belts_active)
1227 /* if they are not running, do not animate them. */
1228 elemdrawing[O_CONVEYOR_LEFT] = ABS(elemdrawing[O_CONVEYOR_LEFT]);
1229 elemdrawing[O_CONVEYOR_RIGHT] = ABS(elemdrawing[O_CONVEYOR_RIGHT]);
1234 /* also a hack, like biter_switch */
1235 elemdrawing[O_PNEUMATIC_ACTIVE_LEFT] += 2;
1236 elemdrawing[O_PNEUMATIC_ACTIVE_RIGHT] += 2;
1237 elemdrawing[O_PLAYER_PNEUMATIC_LEFT] += 2;
1238 elemdrawing[O_PLAYER_PNEUMATIC_RIGHT] += 2;
1241 if ((cave->last_direction) == GD_MV_STILL)
1243 /* player is idle. */
1244 if (player_blinking && player_tapping)
1246 map = O_PLAYER_TAP_BLINK;
1247 draw = gd_elements[O_PLAYER_TAP_BLINK].image_game;
1249 else if (player_blinking)
1251 map = O_PLAYER_BLINK;
1252 draw = gd_elements[O_PLAYER_BLINK].image_game;
1254 else if (player_tapping)
1257 draw = gd_elements[O_PLAYER_TAP].image_game;
1262 draw = gd_elements[O_PLAYER].image_game;
1265 else if (cave->last_horizontal_direction == GD_MV_LEFT)
1267 map = O_PLAYER_LEFT;
1268 draw = gd_elements[O_PLAYER_LEFT].image_game;
1272 /* of course this is GD_MV_RIGHT. */
1273 map = O_PLAYER_RIGHT;
1274 draw = gd_elements[O_PLAYER_RIGHT].image_game;
1277 elemmapping[O_PLAYER] = map;
1278 elemmapping[O_PLAYER_GLUED] = map;
1280 elemdrawing[O_PLAYER] = draw;
1281 elemdrawing[O_PLAYER_GLUED] = draw;
1283 /* player with bomb does not blink or tap - no graphics drawn for that.
1284 running is drawn using w/o bomb cells */
1285 if (cave->last_direction != GD_MV_STILL)
1287 elemmapping[O_PLAYER_BOMB] = map;
1288 elemdrawing[O_PLAYER_BOMB] = draw;
1291 elemmapping[O_INBOX] = (cave->inbox_flash_toggle ? O_INBOX_OPEN : O_INBOX_CLOSED);
1292 elemdrawing[O_INBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1294 elemmapping[O_OUTBOX] = (cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED);
1295 elemdrawing[O_OUTBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1297 /* hack, not fit into gd_elements */
1298 elemmapping[O_BITER_SWITCH] = O_BITER_SWITCH_1 + cave->biter_delay_frame;
1299 /* hack, not fit into gd_elements */
1300 elemdrawing[O_BITER_SWITCH] = gd_elements[O_BITER_SWITCH].image_game + cave->biter_delay_frame;
1302 /* visual effects */
1303 elemmapping[O_DIRT] = cave->dirt_looks_like;
1304 elemmapping[O_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1305 elemmapping[O_V_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1306 elemmapping[O_H_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1307 elemmapping[O_AMOEBA_2] = cave->amoeba_2_looks_like;
1309 /* visual effects */
1310 elemdrawing[O_DIRT] = elemdrawing[cave->dirt_looks_like];
1311 elemdrawing[O_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1312 elemdrawing[O_V_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1313 elemdrawing[O_H_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1314 elemdrawing[O_AMOEBA_2] = elemdrawing[cave->amoeba_2_looks_like];
1316 /* change only graphically */
1317 if (hate_invisible_outbox)
1319 elemmapping[O_PRE_INVIS_OUTBOX] = O_PRE_OUTBOX;
1320 elemmapping[O_INVIS_OUTBOX] = O_OUTBOX;
1323 if (hate_invisible_outbox)
1325 elemdrawing[O_PRE_INVIS_OUTBOX] = elemdrawing[O_PRE_OUTBOX];
1326 elemdrawing[O_INVIS_OUTBOX] = elemdrawing[O_OUTBOX];
1329 for (y = cave->y1; y <= cave->y2; y++)
1331 for (x = cave->x1; x <= cave->x2; x++)
1333 GdElement actual = cave->map[y][x];
1335 /* if covered, real element is not important */
1336 if (actual & COVERED)
1339 map = elemmapping[actual];
1341 /* if covered, real element is not important */
1342 if (actual & COVERED)
1343 draw = gd_elements[O_COVERED].image_game;
1345 draw = elemdrawing[actual];
1347 /* if negative, animated. */
1349 draw = -draw + animcycle;
1352 if (cave->gate_open_flash)
1353 draw += GD_NUM_OF_CELLS;
1355 /* set to buffer, with caching */
1356 if (element_buffer[y][x] != map)
1357 element_buffer[y][x] = map;
1359 if (gfx_buffer[y][x] != draw)
1360 gfx_buffer[y][x] = draw | GD_REDRAW;
1365 /* cave time is rounded _UP_ to seconds. so at the exact moment when it
1367 2sec remaining to 1sec remaining, the player has exactly one second.
1369 to zero, it is the exact moment of timeout. */
1370 /* internal time is milliseconds (or 1200 milliseconds for pal timing). */
1371 int gd_cave_time_show(const GdCave *cave, int internal_time)
1373 return (internal_time + cave->timing_factor - 1) / cave->timing_factor;
1376 GdReplay *gd_replay_new(void)
1380 rep = checked_calloc(sizeof(GdReplay));
1381 rep->movements = checked_calloc(sizeof(GdReplayMovements));
1386 GdReplay *gd_replay_new_from_replay(GdReplay *orig)
1390 rep = get_memcpy(orig, sizeof(GdReplay));
1392 /* replicate dynamic data */
1393 rep->comment = getStringCopy(orig->comment);
1394 rep->movements = get_memcpy(orig->movements, sizeof(GdReplayMovements));
1399 void gd_replay_free(GdReplay *replay)
1401 checked_free(replay->movements);
1402 checked_free(replay->comment);
1406 /* store movement in a replay */
1407 void gd_replay_store_movement(GdReplay *replay, GdDirection player_move,
1408 boolean player_fire, boolean suicide)
1412 data[0] = ((player_move) |
1413 (player_fire ? GD_REPLAY_FIRE_MASK : 0) |
1414 (suicide ? GD_REPLAY_SUICIDE_MASK : 0));
1416 if (replay->movements->len < MAX_REPLAY_LEN)
1418 replay->movements->data[replay->movements->len++] = data[0];
1420 if (replay->movements->len == MAX_REPLAY_LEN)
1421 Warn("BD replay truncated: size exceeds maximum replay size %d", MAX_REPLAY_LEN);
1425 /* calculate adler checksum for a rendered cave; this can be used for more caves. */
1426 void gd_cave_adler_checksum_more(GdCave *cave, guint32 *a, guint32 *b)
1430 for (y = 0; y < cave->h; y++)
1431 for (x = 0; x < cave->w; x++)
1433 *a += gd_elements[cave->map[y][x]].character;
1441 /* calculate adler checksum for a single rendered cave. */
1443 gd_cave_adler_checksum(GdCave *cave)
1448 gd_cave_adler_checksum_more(cave, &a, &b);
1449 return (b << 16) + a;
1452 /* return c64 color with index. */
1453 GdColor gd_c64_color(int index)
1455 return (GD_COLOR_TYPE_C64 << 24) + index;