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 struct 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 g_strdupped strings to elemenets (integers) */
279 name_to_element = create_hashtable(str_case_hash, str_case_equal, NULL, NULL);
281 for (i = 0; i < O_MAX; i++)
285 key = g_ascii_strup(gd_elements[i].filename, -1);
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 = g_strdup_printf("SCANNED_%s", 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 = g_ascii_strup(string, -1);
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 (g_str_equal(highscores[i].name, name) && highscores[i].score == score)
425 /* for the case-insensitive hash keys */
426 int str_case_equal(void *s1, void *s2)
428 return strcasecmp(s1, s2) == 0;
431 unsigned int str_case_hash(void *v)
433 char *upper = getStringToUpper(v);
434 unsigned int hash = get_hash_from_string(upper);
441 /* for the case-insensitive hash keys */
442 boolean gd_str_case_equal(gconstpointer s1, gconstpointer s2)
444 return strcasecmp(s1, s2) == 0;
447 guint gd_str_case_hash(gconstpointer v)
452 upper = g_ascii_strup(v, -1);
453 hash = g_str_hash(v);
459 create new cave with default values.
460 sets every value, also default size, diamond value etc.
462 GdCave *gd_cave_new(void)
466 cave = checked_calloc(sizeof(GdCave));
468 /* hash table which stores unknown tags as strings. */
469 cave->tags = g_hash_table_new_full(gd_str_case_hash, gd_str_case_equal, free, free);
471 gd_cave_set_gdash_defaults(cave);
477 cave maps are continuous areas in memory. the allocated memory
478 is width * height * bytes_per_cell long.
479 the cave map[0] stores the pointer given by g_malloc().
480 the map itself is also an allocated array of pointers to the
483 rows = new (pointers to rows);
485 rows[1..h-1] = rows[0] + width * bytes
493 allocate a cave map-like array, and initialize to zero.
494 one cell is cell_size bytes long.
496 gpointer gd_cave_map_new_for_cave(const GdCave *cave, const int cell_size)
498 gpointer *rows; /* this is void**, pointer to array of ... */
501 rows = checked_malloc((cave->h) * sizeof(gpointer));
502 rows[0] = checked_calloc(cell_size * cave->w * cave->h);
504 for (y = 1; y < cave->h; y++)
505 /* base pointer + num_of_bytes_per_element * width * number_of_row; as sizeof(char) = 1 */
506 rows[y] = (char *)rows[0] + cell_size * cave->w * y;
514 if map is null, this also returns null.
516 gpointer gd_cave_map_dup_size(const GdCave *cave, const gpointer map, const int cell_size)
519 gpointer *maplines = (gpointer *)map;
525 rows = checked_malloc((cave->h) * sizeof(gpointer));
526 rows[0] = g_memdup (maplines[0], cell_size * cave->w * cave->h);
528 for (y = 1; y < cave->h; y++)
529 rows[y] = (char *)rows[0] + cell_size * cave->w * y;
534 void gd_cave_map_free(gpointer map)
536 gpointer *maplines = (gpointer *) map;
546 frees memory associated to cave
548 void gd_cave_free(GdCave *cave)
556 g_hash_table_destroy(cave->tags);
558 if (cave->random) /* random generator is a GRand * */
559 g_rand_free(cave->random);
562 for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
563 if (gd_cave_properties[i].type == GD_TYPE_LONGSTRING)
564 checked_free(G_STRUCT_MEMBER(char *, cave, gd_cave_properties[i].offset));
567 gd_cave_map_free(cave->map);
570 gd_cave_map_free(cave->objects_order);
572 /* hammered walls to reappear data */
573 gd_cave_map_free(cave->hammered_reappear);
576 g_list_foreach(cave->objects, (GFunc) free, NULL);
577 g_list_free (cave->objects);
580 g_list_foreach(cave->replays, (GFunc) gd_replay_free, NULL);
581 g_list_free(cave->replays);
583 /* freeing main pointer */
587 static void hash_copy_foreach(const char *key, const char *value, GHashTable *dest)
589 g_hash_table_insert(dest, g_strdup(key), g_strdup(value));
592 /* copy cave from src to destination, with duplicating dynamically allocated data */
593 void gd_cave_copy(GdCave *dest, const GdCave *src)
597 /* copy entire data */
598 g_memmove(dest, src, sizeof(GdCave));
600 /* but duplicate dynamic data */
601 dest->tags = g_hash_table_new_full(gd_str_case_hash, gd_str_case_equal,
604 g_hash_table_foreach(src->tags, (GHFunc) hash_copy_foreach, dest->tags);
606 dest->map = gd_cave_map_dup(src, map);
607 dest->hammered_reappear = gd_cave_map_dup(src, hammered_reappear);
609 /* for longstrings */
610 for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
611 if (gd_cave_properties[i].type == GD_TYPE_LONGSTRING)
612 G_STRUCT_MEMBER(char *, dest, gd_cave_properties[i].offset) =
613 getStringCopy(G_STRUCT_MEMBER(char *, src, gd_cave_properties[i].offset));
615 /* no reason to copy this */
616 dest->objects_order = NULL;
618 /* copy objects list */
623 dest->objects = NULL; /* new empty list */
624 for (iter = src->objects; iter != NULL; iter = iter->next) /* do a deep copy */
625 dest->objects = g_list_append(dest->objects, g_memdup (iter->data, sizeof (GdObject)));
633 dest->replays = NULL;
634 for (iter = src->replays; iter != NULL; iter = iter->next) /* do a deep copy */
635 dest->replays = g_list_append(dest->replays, gd_replay_new_from_replay(iter->data));
638 /* copy random number generator */
640 dest->random = g_rand_copy(src->random);
643 /* create new cave, which is a copy of the cave given. */
644 GdCave *gd_cave_new_from_cave(const GdCave *orig)
648 cave = gd_cave_new();
649 gd_cave_copy(cave, orig);
655 Put an object to the specified position.
656 Performs range checking.
657 If wraparound objects are selected, wraps around x coordinates, with or without lineshift.
658 (The y coordinate is not wrapped, as it did not work like that on the c64)
659 order is a pointer to the GdObject describing this object. Thus the editor can identify which cell was created by which object.
661 void gd_cave_store_rc(GdCave *cave, int x, int y, const GdElement element, const void *order)
663 /* if we do not need to draw, exit now */
664 if (element == O_NONE)
668 if (cave->wraparound_objects)
672 /* fit x coordinate within range, with correcting y at the same time */
675 x += cave->w; /* out of bounds on the left... */
676 y--; /* previous row */
685 /* lineshifting does not fix the y coordinates.
686 if out of bounds, element will not be displayed. */
687 /* if such an object appeared in the c64 game, well, it was a buffer overrun. */
691 /* non lineshifting: changing x does not change y coordinate. */
698 /* after that, fix y coordinate */
707 /* if the above wraparound code fixed the coordinates, this will always be true. */
708 /* but see the above comment for lineshifting y coordinate */
709 if (x >= 0 && x < cave->w && y >= 0 && y < cave->h)
711 cave->map[y][x] = element;
712 cave->objects_order[y][x] = (void *)order;
716 GdElement gd_cave_get_rc(const GdCave *cave, int x, int y)
718 /* always fix coordinates as if cave was wraparound. */
720 /* fix x coordinate */
723 /* fit x coordinate within range, with correcting y at the same time */
726 x += cave->w; /* out of bounds on the left... */
727 y--; /* previous row */
737 /* non lineshifting: changing x does not change y coordinate. */
745 /* after that, fix y coordinate */
752 return cave->map[y][x];
755 unsigned int gd_c64_random(GdC64RandomGenerator *rand)
757 unsigned int temp_rand_1, temp_rand_2, carry, result;
759 temp_rand_1 = (rand->rand_seed_1 & 0x0001) << 7;
760 temp_rand_2 = (rand->rand_seed_2 >> 1) & 0x007F;
761 result = (rand->rand_seed_2) + ((rand->rand_seed_2 & 0x0001) << 7);
762 carry = (result >> 8);
763 result = result & 0x00FF;
764 result = result + carry + 0x13;
765 carry = (result >> 8);
766 rand->rand_seed_2 = result & 0x00FF;
767 result = rand->rand_seed_1 + carry + temp_rand_1;
768 carry = (result >> 8);
769 result = result & 0x00FF;
770 result = result + carry + temp_rand_2;
771 rand->rand_seed_1 = result & 0x00FF;
773 return rand->rand_seed_1;
777 C64 BD predictable random number generator.
778 Used to load the original caves imported from c64 files.
779 Also by the predictable slime.
781 unsigned int gd_cave_c64_random(GdCave *cave)
783 return gd_c64_random(&cave->c64_rand);
786 void gd_c64_random_set_seed(GdC64RandomGenerator *rand, int seed1, int seed2)
788 rand->rand_seed_1 = seed1;
789 rand->rand_seed_2 = seed2;
792 void gd_cave_c64_random_set_seed(GdCave *cave, int seed1, int seed2)
794 gd_c64_random_set_seed(&cave->c64_rand, seed1, seed2);
798 select random colors for a given cave.
799 this function will select colors so that they should look somewhat nice; for example
800 brick walls won't be the darkest color, for example.
802 static inline void swap(int *i1, int *i2)
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 = g_random_int_range(0, 4) == 0;
1180 /* 1/16 chance of starting or stopping tapping. */
1181 if (g_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));
1411 rep->movements = g_byte_array_new();
1416 GdReplay *gd_replay_new_from_replay(GdReplay *orig)
1420 rep = g_memdup(orig, sizeof(GdReplay));
1422 /* replicate dynamic data */
1423 rep->comment = getStringCopy(orig->comment);
1424 rep->movements = g_byte_array_new();
1425 g_byte_array_append(rep->movements, orig->movements->data, orig->movements->len);
1430 void gd_replay_free(GdReplay *replay)
1432 g_byte_array_free(replay->movements, TRUE);
1433 checked_free(replay->comment);
1437 /* store movement in a replay */
1438 void gd_replay_store_movement(GdReplay *replay, GdDirection player_move,
1439 boolean player_fire, boolean suicide)
1443 data[0] = ((player_move) |
1444 (player_fire ? GD_REPLAY_FIRE_MASK : 0) |
1445 (suicide ? GD_REPLAY_SUICIDE_MASK : 0));
1447 g_byte_array_append(replay->movements, data, 1);
1450 /* calculate adler checksum for a rendered cave; this can be used for more caves. */
1451 void gd_cave_adler_checksum_more(GdCave *cave, guint32 *a, guint32 *b)
1455 for (y = 0; y < cave->h; y++)
1456 for (x = 0; x < cave->w; x++)
1458 *a += gd_elements[cave->map[y][x]].character;
1466 /* calculate adler checksum for a single rendered cave. */
1468 gd_cave_adler_checksum(GdCave *cave)
1473 gd_cave_adler_checksum_more(cave, &a, &b);
1474 return (b << 16) + a;
1477 /* return c64 color with index. */
1478 GdColor gd_c64_color(int index)
1480 return (GD_COLOR_TYPE_C64 << 24) + index;