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 GHashTable *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 = g_hash_table_new_full(gd_str_case_hash, gd_str_case_equal,
282 for (i = 0; i < O_MAX; i++)
286 key = g_ascii_strup(gd_elements[i].filename, -1);
288 if (g_hash_table_lookup_extended(name_to_element, key, NULL, NULL))
289 Warn("Name %s already used for element %x", key, i);
291 g_hash_table_insert(name_to_element, key, GINT_TO_POINTER(i));
292 /* ^^^ do not free "key", as hash table needs it during the whole time! */
294 key = g_strdup_printf("SCANNED_%s", key); /* new string */
296 g_hash_table_insert(name_to_element, key, GINT_TO_POINTER(i));
297 /* once again, do not free "key" ^^^ */
300 /* for compatibility with tim stridmann's memorydump->bdcff converter... .... ... */
301 g_hash_table_insert(name_to_element, "HEXPANDING_WALL", GINT_TO_POINTER(O_H_EXPANDING_WALL));
302 g_hash_table_insert(name_to_element, "FALLING_DIAMOND", GINT_TO_POINTER(O_DIAMOND_F));
303 g_hash_table_insert(name_to_element, "FALLING_BOULDER", GINT_TO_POINTER(O_STONE_F));
304 g_hash_table_insert(name_to_element, "EXPLOSION1S", GINT_TO_POINTER(O_EXPLODE_1));
305 g_hash_table_insert(name_to_element, "EXPLOSION2S", GINT_TO_POINTER(O_EXPLODE_2));
306 g_hash_table_insert(name_to_element, "EXPLOSION3S", GINT_TO_POINTER(O_EXPLODE_3));
307 g_hash_table_insert(name_to_element, "EXPLOSION4S", GINT_TO_POINTER(O_EXPLODE_4));
308 g_hash_table_insert(name_to_element, "EXPLOSION5S", GINT_TO_POINTER(O_EXPLODE_5));
309 g_hash_table_insert(name_to_element, "EXPLOSION1D", GINT_TO_POINTER(O_PRE_DIA_1));
310 g_hash_table_insert(name_to_element, "EXPLOSION2D", GINT_TO_POINTER(O_PRE_DIA_2));
311 g_hash_table_insert(name_to_element, "EXPLOSION3D", GINT_TO_POINTER(O_PRE_DIA_3));
312 g_hash_table_insert(name_to_element, "EXPLOSION4D", GINT_TO_POINTER(O_PRE_DIA_4));
313 g_hash_table_insert(name_to_element, "EXPLOSION5D", GINT_TO_POINTER(O_PRE_DIA_5));
314 g_hash_table_insert(name_to_element, "WALL2", GINT_TO_POINTER(O_STEEL_EXPLODABLE));
316 /* compatibility with old bd-faq (pre disassembly of bladder) */
317 g_hash_table_insert(name_to_element, "BLADDERd9", GINT_TO_POINTER(O_BLADDER_8));
319 /* create table to show errors at the start of the application */
320 gd_create_char_to_element_table();
323 /* search the element database for the specified name, and return the element */
324 GdElement gd_get_element_from_string (const char *string)
326 char *upper = g_ascii_strup(string, -1);
332 Warn("Invalid string representing element: (null)");
336 found = g_hash_table_lookup_extended(name_to_element, upper, NULL, &value);
339 return (GdElement) (GPOINTER_TO_INT(value));
341 Warn("Invalid string representing element: %s", string);
345 void gd_cave_set_defaults_from_array(GdCave* cave, GdPropertyDefault *defaults)
347 gd_struct_set_defaults_from_array(cave, gd_cave_properties, defaults);
351 load default values from description array
352 these are default for gdash and bdcff.
354 void gd_cave_set_gdash_defaults(GdCave* cave)
358 gd_cave_set_defaults_from_array(cave, gd_cave_defaults_gdash);
360 /* these did not fit into the descriptor array */
361 for (i = 0; i < 5; i++)
363 cave->level_rand[i] = i;
364 cave->level_timevalue[i] = i + 1;
368 /* for quicksort. compares two highscores. */
369 int gd_highscore_compare(gconstpointer a, gconstpointer b)
371 const GdHighScore *ha = a;
372 const GdHighScore *hb = b;
373 return hb->score - ha->score;
376 void gd_clear_highscore(GdHighScore *hs)
380 for (i = 0; i < GD_HIGHSCORE_NUM; i++)
382 strcpy(hs[i].name, "");
387 boolean gd_has_highscore(GdHighScore *hs)
389 return hs[0].score > 0;
392 /* return true if score achieved is a highscore */
393 boolean gd_is_highscore(GdHighScore *scores, int score)
395 /* if score is above zero AND bigger than the last one */
396 if (score > 0 && score > scores[GD_HIGHSCORE_NUM-1].score)
402 int gd_add_highscore(GdHighScore *highscores, const char *name, int score)
406 if (!gd_is_highscore(highscores, score))
409 /* overwrite the last one */
410 gd_strcpy(highscores[GD_HIGHSCORE_NUM-1].name, name);
411 highscores[GD_HIGHSCORE_NUM-1].score = score;
414 qsort(highscores, GD_HIGHSCORE_NUM, sizeof(GdHighScore), gd_highscore_compare);
416 for (i = 0; i < GD_HIGHSCORE_NUM; i++)
417 if (g_str_equal(highscores[i].name, name) && highscores[i].score == score)
423 /* for the case-insensitive hash keys */
424 boolean gd_str_case_equal(gconstpointer s1, gconstpointer s2)
426 return strcasecmp(s1, s2) == 0;
429 guint gd_str_case_hash(gconstpointer v)
434 upper = g_ascii_strup(v, -1);
435 hash = g_str_hash(v);
441 create new cave with default values.
442 sets every value, also default size, diamond value etc.
444 GdCave *gd_cave_new(void)
448 cave = checked_calloc(sizeof(GdCave));
450 /* hash table which stores unknown tags as strings. */
451 cave->tags = g_hash_table_new_full(gd_str_case_hash, gd_str_case_equal, free, free);
453 gd_cave_set_gdash_defaults(cave);
459 cave maps are continuous areas in memory. the allocated memory
460 is width * height * bytes_per_cell long.
461 the cave map[0] stores the pointer given by g_malloc().
462 the map itself is also an allocated array of pointers to the
465 rows = new (pointers to rows);
467 rows[1..h-1] = rows[0] + width * bytes
475 allocate a cave map-like array, and initialize to zero.
476 one cell is cell_size bytes long.
478 gpointer gd_cave_map_new_for_cave(const GdCave *cave, const int cell_size)
480 gpointer *rows; /* this is void**, pointer to array of ... */
483 rows = checked_malloc((cave->h) * sizeof(gpointer));
484 rows[0] = checked_calloc(cell_size * cave->w * cave->h);
486 for (y = 1; y < cave->h; y++)
487 /* base pointer + num_of_bytes_per_element * width * number_of_row; as sizeof(char) = 1 */
488 rows[y] = (char *)rows[0] + cell_size * cave->w * y;
496 if map is null, this also returns null.
498 gpointer gd_cave_map_dup_size(const GdCave *cave, const gpointer map, const int cell_size)
501 gpointer *maplines = (gpointer *)map;
507 rows = checked_malloc((cave->h) * sizeof(gpointer));
508 rows[0] = g_memdup (maplines[0], cell_size * cave->w * cave->h);
510 for (y = 1; y < cave->h; y++)
511 rows[y] = (char *)rows[0] + cell_size * cave->w * y;
516 void gd_cave_map_free(gpointer map)
518 gpointer *maplines = (gpointer *) map;
528 frees memory associated to cave
530 void gd_cave_free(GdCave *cave)
538 g_hash_table_destroy(cave->tags);
540 if (cave->random) /* random generator is a GRand * */
541 g_rand_free(cave->random);
544 for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
545 if (gd_cave_properties[i].type == GD_TYPE_LONGSTRING)
546 checked_free(G_STRUCT_MEMBER(char *, cave, gd_cave_properties[i].offset));
549 gd_cave_map_free(cave->map);
552 gd_cave_map_free(cave->objects_order);
554 /* hammered walls to reappear data */
555 gd_cave_map_free(cave->hammered_reappear);
558 g_list_foreach(cave->objects, (GFunc) free, NULL);
559 g_list_free (cave->objects);
562 g_list_foreach(cave->replays, (GFunc) gd_replay_free, NULL);
563 g_list_free(cave->replays);
565 /* freeing main pointer */
569 static void hash_copy_foreach(const char *key, const char *value, GHashTable *dest)
571 g_hash_table_insert(dest, g_strdup(key), g_strdup(value));
574 /* copy cave from src to destination, with duplicating dynamically allocated data */
575 void gd_cave_copy(GdCave *dest, const GdCave *src)
579 /* copy entire data */
580 g_memmove(dest, src, sizeof(GdCave));
582 /* but duplicate dynamic data */
583 dest->tags = g_hash_table_new_full(gd_str_case_hash, gd_str_case_equal,
586 g_hash_table_foreach(src->tags, (GHFunc) hash_copy_foreach, dest->tags);
588 dest->map = gd_cave_map_dup(src, map);
589 dest->hammered_reappear = gd_cave_map_dup(src, hammered_reappear);
591 /* for longstrings */
592 for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
593 if (gd_cave_properties[i].type == GD_TYPE_LONGSTRING)
594 G_STRUCT_MEMBER(char *, dest, gd_cave_properties[i].offset) =
595 getStringCopy(G_STRUCT_MEMBER(char *, src, gd_cave_properties[i].offset));
597 /* no reason to copy this */
598 dest->objects_order = NULL;
600 /* copy objects list */
605 dest->objects = NULL; /* new empty list */
606 for (iter = src->objects; iter != NULL; iter = iter->next) /* do a deep copy */
607 dest->objects = g_list_append(dest->objects, g_memdup (iter->data, sizeof (GdObject)));
615 dest->replays = NULL;
616 for (iter = src->replays; iter != NULL; iter = iter->next) /* do a deep copy */
617 dest->replays = g_list_append(dest->replays, gd_replay_new_from_replay(iter->data));
620 /* copy random number generator */
622 dest->random = g_rand_copy(src->random);
625 /* create new cave, which is a copy of the cave given. */
626 GdCave *gd_cave_new_from_cave(const GdCave *orig)
630 cave = gd_cave_new();
631 gd_cave_copy(cave, orig);
637 Put an object to the specified position.
638 Performs range checking.
639 If wraparound objects are selected, wraps around x coordinates, with or without lineshift.
640 (The y coordinate is not wrapped, as it did not work like that on the c64)
641 order is a pointer to the GdObject describing this object. Thus the editor can identify which cell was created by which object.
643 void gd_cave_store_rc(GdCave *cave, int x, int y, const GdElement element, const void *order)
645 /* if we do not need to draw, exit now */
646 if (element == O_NONE)
650 if (cave->wraparound_objects)
654 /* fit x coordinate within range, with correcting y at the same time */
657 x += cave->w; /* out of bounds on the left... */
658 y--; /* previous row */
667 /* lineshifting does not fix the y coordinates.
668 if out of bounds, element will not be displayed. */
669 /* if such an object appeared in the c64 game, well, it was a buffer overrun. */
673 /* non lineshifting: changing x does not change y coordinate. */
680 /* after that, fix y coordinate */
689 /* if the above wraparound code fixed the coordinates, this will always be true. */
690 /* but see the above comment for lineshifting y coordinate */
691 if (x >= 0 && x < cave->w && y >= 0 && y < cave->h)
693 cave->map[y][x] = element;
694 cave->objects_order[y][x] = (void *)order;
698 GdElement gd_cave_get_rc(const GdCave *cave, int x, int y)
700 /* always fix coordinates as if cave was wraparound. */
702 /* fix x coordinate */
705 /* fit x coordinate within range, with correcting y at the same time */
708 x += cave->w; /* out of bounds on the left... */
709 y--; /* previous row */
719 /* non lineshifting: changing x does not change y coordinate. */
727 /* after that, fix y coordinate */
734 return cave->map[y][x];
737 unsigned int gd_c64_random(GdC64RandomGenerator *rand)
739 unsigned int temp_rand_1, temp_rand_2, carry, result;
741 temp_rand_1 = (rand->rand_seed_1 & 0x0001) << 7;
742 temp_rand_2 = (rand->rand_seed_2 >> 1) & 0x007F;
743 result = (rand->rand_seed_2) + ((rand->rand_seed_2 & 0x0001) << 7);
744 carry = (result >> 8);
745 result = result & 0x00FF;
746 result = result + carry + 0x13;
747 carry = (result >> 8);
748 rand->rand_seed_2 = result & 0x00FF;
749 result = rand->rand_seed_1 + carry + temp_rand_1;
750 carry = (result >> 8);
751 result = result & 0x00FF;
752 result = result + carry + temp_rand_2;
753 rand->rand_seed_1 = result & 0x00FF;
755 return rand->rand_seed_1;
759 C64 BD predictable random number generator.
760 Used to load the original caves imported from c64 files.
761 Also by the predictable slime.
763 unsigned int gd_cave_c64_random(GdCave *cave)
765 return gd_c64_random(&cave->c64_rand);
768 void gd_c64_random_set_seed(GdC64RandomGenerator *rand, int seed1, int seed2)
770 rand->rand_seed_1 = seed1;
771 rand->rand_seed_2 = seed2;
774 void gd_cave_c64_random_set_seed(GdCave *cave, int seed1, int seed2)
776 gd_c64_random_set_seed(&cave->c64_rand, seed1, seed2);
780 select random colors for a given cave.
781 this function will select colors so that they should look somewhat nice; for example
782 brick walls won't be the darkest color, for example.
784 static inline void swap(int *i1, int *i2)
793 if last line or last row is just steel wall (or (invisible) outbox).
794 used after loading a game for playing.
795 after this, ew and eh will contain the effective width and height.
797 void gd_cave_auto_shrink(GdCave *cave)
809 /* set to maximum size, then try to shrink */
812 cave->x2 = cave->w - 1;
813 cave->y2 = cave->h - 1;
815 /* search for empty, steel-wall-only last rows. */
816 /* clear all lines, which are only steel wall.
817 * and clear only one line, which is steel wall, but also has a player or an outbox. */
822 for (y = cave->y2 - 1; y <= cave->y2; y++)
824 for (x = cave->x1; x <= cave->x2; x++)
826 switch (gd_cave_get_rc (cave, x, y))
828 /* if steels only, this is to be deleted. */
833 case O_PRE_INVIS_OUTBOX:
835 if (empty == STEEL_OR_OTHER)
838 /* if this, delete only this one, and exit. */
839 if (empty == STEEL_ONLY)
840 empty = STEEL_OR_OTHER;
844 /* anything else, that should be left in the cave. */
851 /* shrink if full steel or steel and player/outbox. */
852 if (empty != NO_SHRINK)
853 cave->y2--; /* one row shorter */
855 while (empty == STEEL_ONLY); /* if found just steels, repeat. */
857 /* search for empty, steel-wall-only first rows. */
862 for (y = cave->y1; y <= cave->y1 + 1; y++)
864 for (x = cave->x1; x <= cave->x2; x++)
866 switch (gd_cave_get_rc (cave, x, y))
872 case O_PRE_INVIS_OUTBOX:
874 /* shrink only lines, which have only ONE player or outbox.
875 this is for bd4 intermission 2, for example. */
876 if (empty == STEEL_OR_OTHER)
878 if (empty == STEEL_ONLY)
879 empty = STEEL_OR_OTHER;
889 if (empty != NO_SHRINK)
892 while (empty == STEEL_ONLY); /* if found one, repeat. */
894 /* empty last columns. */
899 for (y = cave->y1; y <= cave->y2; y++)
901 for (x = cave->x2 - 1; x <= cave->x2; x++)
903 switch (gd_cave_get_rc (cave, x, y))
909 case O_PRE_INVIS_OUTBOX:
911 if (empty == STEEL_OR_OTHER)
913 if (empty == STEEL_ONLY)
914 empty = STEEL_OR_OTHER;
924 /* just remember that one column shorter.
925 free will know the size of memchunk, no need to realloc! */
926 if (empty != NO_SHRINK)
929 while (empty == STEEL_ONLY); /* if found one, repeat. */
931 /* empty first columns. */
936 for (y = cave->y1; y <= cave->y2; y++)
938 for (x = cave->x1; x <= cave->x1 + 1; x++)
940 switch (gd_cave_get_rc (cave, x, y))
946 case O_PRE_INVIS_OUTBOX:
948 if (empty == STEEL_OR_OTHER)
950 if (empty == STEEL_ONLY)
951 empty = STEEL_OR_OTHER;
961 if (empty != NO_SHRINK)
964 while (empty == STEEL_ONLY); /* if found one, repeat. */
967 /* check if cave visible part coordinates
968 are outside cave sizes, or not in the right order.
969 correct them if needed.
971 void gd_cave_correct_visible_size(GdCave *cave)
973 /* change visible coordinates if they do not point to upperleft and lowerright */
974 if (cave->x2 < cave->x1)
981 if (cave->y2 < cave->y1)
994 if (cave->x2 > cave->w - 1)
995 cave->x2 = cave->w - 1;
997 if (cave->y2 > cave->h - 1)
998 cave->y2 = cave->h - 1;
1002 bd1 and similar engines had animation bits in cave data, to set which elements to animate
1003 (firefly, butterfly, amoeba).
1004 animating an element also caused some delay each frame; according to my measurements,
1005 around 2.6 ms/element.
1007 static void cave_set_ckdelay_extra_for_animation(GdCave *cave)
1010 boolean has_amoeba = FALSE, has_firefly = FALSE, has_butterfly = FALSE;
1012 for (y = 0; y < cave->h; y++)
1014 for (x = 0; x < cave->w; x++)
1016 switch (cave->map[y][x] & ~SCANNED)
1029 has_butterfly = TRUE;
1039 cave->ckdelay_extra_for_animation = 0;
1041 cave->ckdelay_extra_for_animation += 2600;
1043 cave->ckdelay_extra_for_animation += 2600;
1045 cave->ckdelay_extra_for_animation += 2600;
1047 cave->ckdelay_extra_for_animation += 2600;
1050 /* do some init - setup some cave variables before the game. */
1051 void gd_cave_setup_for_game(GdCave *cave)
1055 cave_set_ckdelay_extra_for_animation(cave);
1057 /* find the player which will be the one to scroll to at the beginning of the game
1058 (before the player's birth) */
1059 if (cave->active_is_first_found)
1061 /* uppermost player is active */
1062 for (y = cave->h - 1; y >= 0; y--)
1064 for (x = cave->w - 1; x >= 0; x--)
1066 if (cave->map[y][x] == O_INBOX)
1076 /* lowermost player is active */
1077 for (y = 0; y < cave->h; y++)
1079 for (x = 0; x < cave->w; x++)
1081 if (cave->map[y][x] == O_INBOX)
1090 /* select number of milliseconds (for pal and ntsc) */
1091 cave->timing_factor = cave->pal_timing ? 1200 : 1000;
1093 cave->time *= cave->timing_factor;
1094 cave->magic_wall_time *= cave->timing_factor;
1095 cave->amoeba_time *= cave->timing_factor;
1096 cave->amoeba_2_time *= cave->timing_factor;
1097 cave->hatching_delay_time *= cave->timing_factor;
1099 if (cave->hammered_walls_reappear)
1100 cave->hammered_reappear = gd_cave_map_new(cave, int);
1103 /* cave diamonds needed can be set to n<=0. */
1104 /* if so, count the diamonds at the time of the hatching, and decrement that value from */
1105 /* the number of diamonds found. */
1106 /* of course, this function is to be called from the cave engine, at the exact time of hatching. */
1107 void gd_cave_count_diamonds(GdCave *cave)
1111 /* if automatically counting diamonds. if this was negative,
1112 * the sum will be this less than the number of all the diamonds in the cave */
1113 if (cave->diamonds_needed <= 0)
1115 for (y = 0; y < cave->h; y++)
1116 for (x = 0; x < cave->w; x++)
1117 if (cave->map[y][x] == O_DIAMOND)
1118 cave->diamonds_needed++;
1120 /* if still below zero, let this be 0, so gate will be open immediately */
1121 if (cave->diamonds_needed < 0)
1122 cave->diamonds_needed = 0;
1126 /* takes a cave and a gfx buffer, and fills the buffer with cell indexes.
1127 the indexes might change if bonus life flash is active (small lines in
1129 for the paused state (which is used in gdash but not in sdash) - yellowish
1131 also one can select the animation frame (0..7) to draw the cave on. so the
1135 if a cell is changed, it is flagged with GD_REDRAW; the flag can be cleared
1138 void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer,
1139 boolean bonus_life_flash, int animcycle, boolean hate_invisible_outbox)
1141 static int player_blinking = 0;
1142 static int player_tapping = 0;
1143 int elemmapping[O_MAX];
1144 int elemdrawing[O_MAX];
1145 int x, y, map, draw;
1147 if (cave->last_direction)
1149 /* he is moving, so stop blinking and tapping. */
1150 player_blinking = 0;
1155 /* he is idle, so animations can be done. */
1158 /* blinking and tapping is started at the beginning of animation sequences. */
1159 /* 1/4 chance of blinking, every sequence. */
1160 player_blinking = g_random_int_range(0, 4) == 0;
1162 /* 1/16 chance of starting or stopping tapping. */
1163 if (g_random_int_range(0, 16) == 0)
1164 player_tapping = !player_tapping;
1168 for (x = 0; x < O_MAX; x++)
1171 elemdrawing[x] = gd_elements[x].image_game;
1174 if (bonus_life_flash)
1176 elemmapping[O_SPACE] = O_FAKE_BONUS;
1177 elemdrawing[O_SPACE] = gd_elements[O_FAKE_BONUS].image_game;
1180 elemmapping[O_MAGIC_WALL] = (cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK);
1181 elemdrawing[O_MAGIC_WALL] = gd_elements[cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK].image_game;
1183 elemmapping[O_CREATURE_SWITCH] = (cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH);
1184 elemdrawing[O_CREATURE_SWITCH] = gd_elements[cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH].image_game;
1186 elemmapping[O_EXPANDING_WALL_SWITCH] = (cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ);
1187 elemdrawing[O_EXPANDING_WALL_SWITCH] = gd_elements[cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ].image_game;
1189 elemmapping[O_GRAVITY_SWITCH] = (cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH);
1190 elemdrawing[O_GRAVITY_SWITCH] = gd_elements[cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH].image_game;
1192 elemmapping[O_REPLICATOR_SWITCH] = (cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF);
1193 elemdrawing[O_REPLICATOR_SWITCH] = gd_elements[cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF].image_game;
1195 if (cave->replicators_active)
1196 /* if the replicators are active, animate them. */
1197 elemmapping[O_REPLICATOR] = O_REPLICATOR_ACTIVE;
1199 if (!cave->replicators_active)
1200 /* if the replicators are inactive, do not animate them. */
1201 elemdrawing[O_REPLICATOR] = ABS(elemdrawing[O_REPLICATOR]);
1203 elemmapping[O_CONVEYOR_SWITCH] = (cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF);
1204 elemdrawing[O_CONVEYOR_SWITCH] = gd_elements[cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF].image_game;
1206 if (cave->conveyor_belts_direction_changed)
1208 /* if direction is changed, animation is changed. */
1211 elemmapping[O_CONVEYOR_LEFT] = O_CONVEYOR_RIGHT;
1212 elemmapping[O_CONVEYOR_RIGHT] = O_CONVEYOR_LEFT;
1214 temp = elemdrawing[O_CONVEYOR_LEFT];
1215 elemdrawing[O_CONVEYOR_LEFT] = elemdrawing[O_CONVEYOR_RIGHT];
1216 elemdrawing[O_CONVEYOR_RIGHT] = temp;
1218 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_CHANGED;
1219 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_CHANGED].image_game;
1223 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_NORMAL;
1224 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_NORMAL].image_game;
1227 if (cave->conveyor_belts_active)
1229 /* keep potentially changed direction */
1230 int offset = (O_CONVEYOR_LEFT_ACTIVE - O_CONVEYOR_LEFT);
1232 /* if they are running, animate them. */
1233 elemmapping[O_CONVEYOR_LEFT] += offset;
1234 elemmapping[O_CONVEYOR_RIGHT] += offset;
1236 if (!cave->conveyor_belts_active)
1238 /* if they are not running, do not animate them. */
1239 elemdrawing[O_CONVEYOR_LEFT] = ABS(elemdrawing[O_CONVEYOR_LEFT]);
1240 elemdrawing[O_CONVEYOR_RIGHT] = ABS(elemdrawing[O_CONVEYOR_RIGHT]);
1245 /* also a hack, like biter_switch */
1246 elemdrawing[O_PNEUMATIC_ACTIVE_LEFT] += 2;
1247 elemdrawing[O_PNEUMATIC_ACTIVE_RIGHT] += 2;
1248 elemdrawing[O_PLAYER_PNEUMATIC_LEFT] += 2;
1249 elemdrawing[O_PLAYER_PNEUMATIC_RIGHT] += 2;
1252 if ((cave->last_direction) == GD_MV_STILL)
1254 /* player is idle. */
1255 if (player_blinking && player_tapping)
1257 map = O_PLAYER_TAP_BLINK;
1258 draw = gd_elements[O_PLAYER_TAP_BLINK].image_game;
1260 else if (player_blinking)
1262 map = O_PLAYER_BLINK;
1263 draw = gd_elements[O_PLAYER_BLINK].image_game;
1265 else if (player_tapping)
1268 draw = gd_elements[O_PLAYER_TAP].image_game;
1273 draw = gd_elements[O_PLAYER].image_game;
1276 else if (cave->last_horizontal_direction == GD_MV_LEFT)
1278 map = O_PLAYER_LEFT;
1279 draw = gd_elements[O_PLAYER_LEFT].image_game;
1283 /* of course this is GD_MV_RIGHT. */
1284 map = O_PLAYER_RIGHT;
1285 draw = gd_elements[O_PLAYER_RIGHT].image_game;
1288 elemmapping[O_PLAYER] = map;
1289 elemmapping[O_PLAYER_GLUED] = map;
1291 elemdrawing[O_PLAYER] = draw;
1292 elemdrawing[O_PLAYER_GLUED] = draw;
1294 /* player with bomb does not blink or tap - no graphics drawn for that.
1295 running is drawn using w/o bomb cells */
1296 if (cave->last_direction != GD_MV_STILL)
1298 elemmapping[O_PLAYER_BOMB] = map;
1299 elemdrawing[O_PLAYER_BOMB] = draw;
1302 elemmapping[O_INBOX] = (cave->inbox_flash_toggle ? O_INBOX_OPEN : O_INBOX_CLOSED);
1303 elemdrawing[O_INBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1305 elemmapping[O_OUTBOX] = (cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED);
1306 elemdrawing[O_OUTBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1308 /* hack, not fit into gd_elements */
1309 elemmapping[O_BITER_SWITCH] = O_BITER_SWITCH_1 + cave->biter_delay_frame;
1310 /* hack, not fit into gd_elements */
1311 elemdrawing[O_BITER_SWITCH] = gd_elements[O_BITER_SWITCH].image_game + cave->biter_delay_frame;
1313 /* visual effects */
1314 elemmapping[O_DIRT] = cave->dirt_looks_like;
1315 elemmapping[O_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1316 elemmapping[O_V_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1317 elemmapping[O_H_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1318 elemmapping[O_AMOEBA_2] = cave->amoeba_2_looks_like;
1320 /* visual effects */
1321 elemdrawing[O_DIRT] = elemdrawing[cave->dirt_looks_like];
1322 elemdrawing[O_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1323 elemdrawing[O_V_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1324 elemdrawing[O_H_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1325 elemdrawing[O_AMOEBA_2] = elemdrawing[cave->amoeba_2_looks_like];
1327 /* change only graphically */
1328 if (hate_invisible_outbox)
1330 elemmapping[O_PRE_INVIS_OUTBOX] = O_PRE_OUTBOX;
1331 elemmapping[O_INVIS_OUTBOX] = O_OUTBOX;
1334 if (hate_invisible_outbox)
1336 elemdrawing[O_PRE_INVIS_OUTBOX] = elemdrawing[O_PRE_OUTBOX];
1337 elemdrawing[O_INVIS_OUTBOX] = elemdrawing[O_OUTBOX];
1340 for (y = cave->y1; y <= cave->y2; y++)
1342 for (x = cave->x1; x <= cave->x2; x++)
1344 GdElement actual = cave->map[y][x];
1346 /* if covered, real element is not important */
1347 if (actual & COVERED)
1350 map = elemmapping[actual];
1352 /* if covered, real element is not important */
1353 if (actual & COVERED)
1354 draw = gd_elements[O_COVERED].image_game;
1356 draw = elemdrawing[actual];
1358 /* if negative, animated. */
1360 draw = -draw + animcycle;
1363 if (cave->gate_open_flash)
1364 draw += GD_NUM_OF_CELLS;
1366 /* set to buffer, with caching */
1367 if (element_buffer[y][x] != map)
1368 element_buffer[y][x] = map;
1370 if (gfx_buffer[y][x] != draw)
1371 gfx_buffer[y][x] = draw | GD_REDRAW;
1376 /* cave time is rounded _UP_ to seconds. so at the exact moment when it
1378 2sec remaining to 1sec remaining, the player has exactly one second.
1380 to zero, it is the exact moment of timeout. */
1381 /* internal time is milliseconds (or 1200 milliseconds for pal timing). */
1382 int gd_cave_time_show(const GdCave *cave, int internal_time)
1384 return (internal_time + cave->timing_factor - 1) / cave->timing_factor;
1387 GdReplay *gd_replay_new(void)
1391 rep = checked_calloc(sizeof(GdReplay));
1393 rep->movements = g_byte_array_new();
1398 GdReplay *gd_replay_new_from_replay(GdReplay *orig)
1402 rep = g_memdup(orig, sizeof(GdReplay));
1404 /* replicate dynamic data */
1405 rep->comment = getStringCopy(orig->comment);
1406 rep->movements = g_byte_array_new();
1407 g_byte_array_append(rep->movements, orig->movements->data, orig->movements->len);
1412 void gd_replay_free(GdReplay *replay)
1414 g_byte_array_free(replay->movements, TRUE);
1415 checked_free(replay->comment);
1419 /* store movement in a replay */
1420 void gd_replay_store_movement(GdReplay *replay, GdDirection player_move,
1421 boolean player_fire, boolean suicide)
1425 data[0] = ((player_move) |
1426 (player_fire ? GD_REPLAY_FIRE_MASK : 0) |
1427 (suicide ? GD_REPLAY_SUICIDE_MASK : 0));
1429 g_byte_array_append(replay->movements, data, 1);
1432 /* calculate adler checksum for a rendered cave; this can be used for more caves. */
1433 void gd_cave_adler_checksum_more(GdCave *cave, guint32 *a, guint32 *b)
1437 for (y = 0; y < cave->h; y++)
1438 for (x = 0; x < cave->w; x++)
1440 *a += gd_elements[cave->map[y][x]].character;
1448 /* calculate adler checksum for a single rendered cave. */
1450 gd_cave_adler_checksum(GdCave *cave)
1455 gd_cave_adler_checksum_more(cave, &a, &b);
1456 return (b << 16) + a;
1459 /* return c64 color with index. */
1460 GdColor gd_c64_color(int index)
1462 return (GD_COLOR_TYPE_C64 << 24) + index;