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 GRand * */
542 g_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 = g_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);
781 select random colors for a given cave.
782 this function will select colors so that they should look somewhat nice; for example
783 brick walls won't be the darkest color, for example.
785 static inline void swap(int *i1, int *i2)
794 if last line or last row is just steel wall (or (invisible) outbox).
795 used after loading a game for playing.
796 after this, ew and eh will contain the effective width and height.
798 void gd_cave_auto_shrink(GdCave *cave)
810 /* set to maximum size, then try to shrink */
813 cave->x2 = cave->w - 1;
814 cave->y2 = cave->h - 1;
816 /* search for empty, steel-wall-only last rows. */
817 /* clear all lines, which are only steel wall.
818 * and clear only one line, which is steel wall, but also has a player or an outbox. */
823 for (y = cave->y2 - 1; y <= cave->y2; y++)
825 for (x = cave->x1; x <= cave->x2; x++)
827 switch (gd_cave_get_rc (cave, x, y))
829 /* if steels only, this is to be deleted. */
834 case O_PRE_INVIS_OUTBOX:
836 if (empty == STEEL_OR_OTHER)
839 /* if this, delete only this one, and exit. */
840 if (empty == STEEL_ONLY)
841 empty = STEEL_OR_OTHER;
845 /* anything else, that should be left in the cave. */
852 /* shrink if full steel or steel and player/outbox. */
853 if (empty != NO_SHRINK)
854 cave->y2--; /* one row shorter */
856 while (empty == STEEL_ONLY); /* if found just steels, repeat. */
858 /* search for empty, steel-wall-only first rows. */
863 for (y = cave->y1; y <= cave->y1 + 1; y++)
865 for (x = cave->x1; x <= cave->x2; x++)
867 switch (gd_cave_get_rc (cave, x, y))
873 case O_PRE_INVIS_OUTBOX:
875 /* shrink only lines, which have only ONE player or outbox.
876 this is for bd4 intermission 2, for example. */
877 if (empty == STEEL_OR_OTHER)
879 if (empty == STEEL_ONLY)
880 empty = STEEL_OR_OTHER;
890 if (empty != NO_SHRINK)
893 while (empty == STEEL_ONLY); /* if found one, repeat. */
895 /* empty last columns. */
900 for (y = cave->y1; y <= cave->y2; y++)
902 for (x = cave->x2 - 1; x <= cave->x2; x++)
904 switch (gd_cave_get_rc (cave, x, y))
910 case O_PRE_INVIS_OUTBOX:
912 if (empty == STEEL_OR_OTHER)
914 if (empty == STEEL_ONLY)
915 empty = STEEL_OR_OTHER;
925 /* just remember that one column shorter.
926 free will know the size of memchunk, no need to realloc! */
927 if (empty != NO_SHRINK)
930 while (empty == STEEL_ONLY); /* if found one, repeat. */
932 /* empty first columns. */
937 for (y = cave->y1; y <= cave->y2; y++)
939 for (x = cave->x1; x <= cave->x1 + 1; x++)
941 switch (gd_cave_get_rc (cave, x, y))
947 case O_PRE_INVIS_OUTBOX:
949 if (empty == STEEL_OR_OTHER)
951 if (empty == STEEL_ONLY)
952 empty = STEEL_OR_OTHER;
962 if (empty != NO_SHRINK)
965 while (empty == STEEL_ONLY); /* if found one, repeat. */
968 /* check if cave visible part coordinates
969 are outside cave sizes, or not in the right order.
970 correct them if needed.
972 void gd_cave_correct_visible_size(GdCave *cave)
974 /* change visible coordinates if they do not point to upperleft and lowerright */
975 if (cave->x2 < cave->x1)
982 if (cave->y2 < cave->y1)
995 if (cave->x2 > cave->w - 1)
996 cave->x2 = cave->w - 1;
998 if (cave->y2 > cave->h - 1)
999 cave->y2 = cave->h - 1;
1003 bd1 and similar engines had animation bits in cave data, to set which elements to animate
1004 (firefly, butterfly, amoeba).
1005 animating an element also caused some delay each frame; according to my measurements,
1006 around 2.6 ms/element.
1008 static void cave_set_ckdelay_extra_for_animation(GdCave *cave)
1011 boolean has_amoeba = FALSE, has_firefly = FALSE, has_butterfly = FALSE;
1013 for (y = 0; y < cave->h; y++)
1015 for (x = 0; x < cave->w; x++)
1017 switch (cave->map[y][x] & ~SCANNED)
1030 has_butterfly = TRUE;
1040 cave->ckdelay_extra_for_animation = 0;
1042 cave->ckdelay_extra_for_animation += 2600;
1044 cave->ckdelay_extra_for_animation += 2600;
1046 cave->ckdelay_extra_for_animation += 2600;
1048 cave->ckdelay_extra_for_animation += 2600;
1051 /* do some init - setup some cave variables before the game. */
1052 void gd_cave_setup_for_game(GdCave *cave)
1056 cave_set_ckdelay_extra_for_animation(cave);
1058 /* find the player which will be the one to scroll to at the beginning of the game
1059 (before the player's birth) */
1060 if (cave->active_is_first_found)
1062 /* uppermost player is active */
1063 for (y = cave->h - 1; y >= 0; y--)
1065 for (x = cave->w - 1; x >= 0; x--)
1067 if (cave->map[y][x] == O_INBOX)
1077 /* lowermost player is active */
1078 for (y = 0; y < cave->h; y++)
1080 for (x = 0; x < cave->w; x++)
1082 if (cave->map[y][x] == O_INBOX)
1091 /* select number of milliseconds (for pal and ntsc) */
1092 cave->timing_factor = cave->pal_timing ? 1200 : 1000;
1094 cave->time *= cave->timing_factor;
1095 cave->magic_wall_time *= cave->timing_factor;
1096 cave->amoeba_time *= cave->timing_factor;
1097 cave->amoeba_2_time *= cave->timing_factor;
1098 cave->hatching_delay_time *= cave->timing_factor;
1100 if (cave->hammered_walls_reappear)
1101 cave->hammered_reappear = gd_cave_map_new(cave, int);
1104 /* cave diamonds needed can be set to n<=0. */
1105 /* if so, count the diamonds at the time of the hatching, and decrement that value from */
1106 /* the number of diamonds found. */
1107 /* of course, this function is to be called from the cave engine, at the exact time of hatching. */
1108 void gd_cave_count_diamonds(GdCave *cave)
1112 /* if automatically counting diamonds. if this was negative,
1113 * the sum will be this less than the number of all the diamonds in the cave */
1114 if (cave->diamonds_needed <= 0)
1116 for (y = 0; y < cave->h; y++)
1117 for (x = 0; x < cave->w; x++)
1118 if (cave->map[y][x] == O_DIAMOND)
1119 cave->diamonds_needed++;
1121 /* if still below zero, let this be 0, so gate will be open immediately */
1122 if (cave->diamonds_needed < 0)
1123 cave->diamonds_needed = 0;
1127 /* takes a cave and a gfx buffer, and fills the buffer with cell indexes.
1128 the indexes might change if bonus life flash is active (small lines in
1130 for the paused state (which is used in gdash but not in sdash) - yellowish
1132 also one can select the animation frame (0..7) to draw the cave on. so the
1136 if a cell is changed, it is flagged with GD_REDRAW; the flag can be cleared
1139 void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer,
1140 boolean bonus_life_flash, int animcycle, boolean hate_invisible_outbox)
1142 static int player_blinking = 0;
1143 static int player_tapping = 0;
1144 int elemmapping[O_MAX];
1145 int elemdrawing[O_MAX];
1146 int x, y, map, draw;
1148 if (cave->last_direction)
1150 /* he is moving, so stop blinking and tapping. */
1151 player_blinking = 0;
1156 /* he is idle, so animations can be done. */
1159 /* blinking and tapping is started at the beginning of animation sequences. */
1160 /* 1/4 chance of blinking, every sequence. */
1161 player_blinking = g_random_int_range(0, 4) == 0;
1163 /* 1/16 chance of starting or stopping tapping. */
1164 if (g_random_int_range(0, 16) == 0)
1165 player_tapping = !player_tapping;
1169 for (x = 0; x < O_MAX; x++)
1172 elemdrawing[x] = gd_elements[x].image_game;
1175 if (bonus_life_flash)
1177 elemmapping[O_SPACE] = O_FAKE_BONUS;
1178 elemdrawing[O_SPACE] = gd_elements[O_FAKE_BONUS].image_game;
1181 elemmapping[O_MAGIC_WALL] = (cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK);
1182 elemdrawing[O_MAGIC_WALL] = gd_elements[cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK].image_game;
1184 elemmapping[O_CREATURE_SWITCH] = (cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH);
1185 elemdrawing[O_CREATURE_SWITCH] = gd_elements[cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH].image_game;
1187 elemmapping[O_EXPANDING_WALL_SWITCH] = (cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ);
1188 elemdrawing[O_EXPANDING_WALL_SWITCH] = gd_elements[cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ].image_game;
1190 elemmapping[O_GRAVITY_SWITCH] = (cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH);
1191 elemdrawing[O_GRAVITY_SWITCH] = gd_elements[cave->gravity_switch_active ? O_GRAVITY_SWITCH_ACTIVE : O_GRAVITY_SWITCH].image_game;
1193 elemmapping[O_REPLICATOR_SWITCH] = (cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF);
1194 elemdrawing[O_REPLICATOR_SWITCH] = gd_elements[cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF].image_game;
1196 if (cave->replicators_active)
1197 /* if the replicators are active, animate them. */
1198 elemmapping[O_REPLICATOR] = O_REPLICATOR_ACTIVE;
1200 if (!cave->replicators_active)
1201 /* if the replicators are inactive, do not animate them. */
1202 elemdrawing[O_REPLICATOR] = ABS(elemdrawing[O_REPLICATOR]);
1204 elemmapping[O_CONVEYOR_SWITCH] = (cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF);
1205 elemdrawing[O_CONVEYOR_SWITCH] = gd_elements[cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF].image_game;
1207 if (cave->conveyor_belts_direction_changed)
1209 /* if direction is changed, animation is changed. */
1212 elemmapping[O_CONVEYOR_LEFT] = O_CONVEYOR_RIGHT;
1213 elemmapping[O_CONVEYOR_RIGHT] = O_CONVEYOR_LEFT;
1215 temp = elemdrawing[O_CONVEYOR_LEFT];
1216 elemdrawing[O_CONVEYOR_LEFT] = elemdrawing[O_CONVEYOR_RIGHT];
1217 elemdrawing[O_CONVEYOR_RIGHT] = temp;
1219 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_CHANGED;
1220 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_CHANGED].image_game;
1224 elemmapping[O_CONVEYOR_DIR_SWITCH] = O_CONVEYOR_DIR_NORMAL;
1225 elemdrawing[O_CONVEYOR_DIR_SWITCH] = gd_elements[O_CONVEYOR_DIR_NORMAL].image_game;
1228 if (cave->conveyor_belts_active)
1230 /* keep potentially changed direction */
1231 int offset = (O_CONVEYOR_LEFT_ACTIVE - O_CONVEYOR_LEFT);
1233 /* if they are running, animate them. */
1234 elemmapping[O_CONVEYOR_LEFT] += offset;
1235 elemmapping[O_CONVEYOR_RIGHT] += offset;
1237 if (!cave->conveyor_belts_active)
1239 /* if they are not running, do not animate them. */
1240 elemdrawing[O_CONVEYOR_LEFT] = ABS(elemdrawing[O_CONVEYOR_LEFT]);
1241 elemdrawing[O_CONVEYOR_RIGHT] = ABS(elemdrawing[O_CONVEYOR_RIGHT]);
1246 /* also a hack, like biter_switch */
1247 elemdrawing[O_PNEUMATIC_ACTIVE_LEFT] += 2;
1248 elemdrawing[O_PNEUMATIC_ACTIVE_RIGHT] += 2;
1249 elemdrawing[O_PLAYER_PNEUMATIC_LEFT] += 2;
1250 elemdrawing[O_PLAYER_PNEUMATIC_RIGHT] += 2;
1253 if ((cave->last_direction) == GD_MV_STILL)
1255 /* player is idle. */
1256 if (player_blinking && player_tapping)
1258 map = O_PLAYER_TAP_BLINK;
1259 draw = gd_elements[O_PLAYER_TAP_BLINK].image_game;
1261 else if (player_blinking)
1263 map = O_PLAYER_BLINK;
1264 draw = gd_elements[O_PLAYER_BLINK].image_game;
1266 else if (player_tapping)
1269 draw = gd_elements[O_PLAYER_TAP].image_game;
1274 draw = gd_elements[O_PLAYER].image_game;
1277 else if (cave->last_horizontal_direction == GD_MV_LEFT)
1279 map = O_PLAYER_LEFT;
1280 draw = gd_elements[O_PLAYER_LEFT].image_game;
1284 /* of course this is GD_MV_RIGHT. */
1285 map = O_PLAYER_RIGHT;
1286 draw = gd_elements[O_PLAYER_RIGHT].image_game;
1289 elemmapping[O_PLAYER] = map;
1290 elemmapping[O_PLAYER_GLUED] = map;
1292 elemdrawing[O_PLAYER] = draw;
1293 elemdrawing[O_PLAYER_GLUED] = draw;
1295 /* player with bomb does not blink or tap - no graphics drawn for that.
1296 running is drawn using w/o bomb cells */
1297 if (cave->last_direction != GD_MV_STILL)
1299 elemmapping[O_PLAYER_BOMB] = map;
1300 elemdrawing[O_PLAYER_BOMB] = draw;
1303 elemmapping[O_INBOX] = (cave->inbox_flash_toggle ? O_INBOX_OPEN : O_INBOX_CLOSED);
1304 elemdrawing[O_INBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1306 elemmapping[O_OUTBOX] = (cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED);
1307 elemdrawing[O_OUTBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1309 /* hack, not fit into gd_elements */
1310 elemmapping[O_BITER_SWITCH] = O_BITER_SWITCH_1 + cave->biter_delay_frame;
1311 /* hack, not fit into gd_elements */
1312 elemdrawing[O_BITER_SWITCH] = gd_elements[O_BITER_SWITCH].image_game + cave->biter_delay_frame;
1314 /* visual effects */
1315 elemmapping[O_DIRT] = cave->dirt_looks_like;
1316 elemmapping[O_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1317 elemmapping[O_V_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1318 elemmapping[O_H_EXPANDING_WALL] = cave->expanding_wall_looks_like;
1319 elemmapping[O_AMOEBA_2] = cave->amoeba_2_looks_like;
1321 /* visual effects */
1322 elemdrawing[O_DIRT] = elemdrawing[cave->dirt_looks_like];
1323 elemdrawing[O_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1324 elemdrawing[O_V_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1325 elemdrawing[O_H_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
1326 elemdrawing[O_AMOEBA_2] = elemdrawing[cave->amoeba_2_looks_like];
1328 /* change only graphically */
1329 if (hate_invisible_outbox)
1331 elemmapping[O_PRE_INVIS_OUTBOX] = O_PRE_OUTBOX;
1332 elemmapping[O_INVIS_OUTBOX] = O_OUTBOX;
1335 if (hate_invisible_outbox)
1337 elemdrawing[O_PRE_INVIS_OUTBOX] = elemdrawing[O_PRE_OUTBOX];
1338 elemdrawing[O_INVIS_OUTBOX] = elemdrawing[O_OUTBOX];
1341 for (y = cave->y1; y <= cave->y2; y++)
1343 for (x = cave->x1; x <= cave->x2; x++)
1345 GdElement actual = cave->map[y][x];
1347 /* if covered, real element is not important */
1348 if (actual & COVERED)
1351 map = elemmapping[actual];
1353 /* if covered, real element is not important */
1354 if (actual & COVERED)
1355 draw = gd_elements[O_COVERED].image_game;
1357 draw = elemdrawing[actual];
1359 /* if negative, animated. */
1361 draw = -draw + animcycle;
1364 if (cave->gate_open_flash)
1365 draw += GD_NUM_OF_CELLS;
1367 /* set to buffer, with caching */
1368 if (element_buffer[y][x] != map)
1369 element_buffer[y][x] = map;
1371 if (gfx_buffer[y][x] != draw)
1372 gfx_buffer[y][x] = draw | GD_REDRAW;
1377 /* cave time is rounded _UP_ to seconds. so at the exact moment when it
1379 2sec remaining to 1sec remaining, the player has exactly one second.
1381 to zero, it is the exact moment of timeout. */
1382 /* internal time is milliseconds (or 1200 milliseconds for pal timing). */
1383 int gd_cave_time_show(const GdCave *cave, int internal_time)
1385 return (internal_time + cave->timing_factor - 1) / cave->timing_factor;
1388 GdReplay *gd_replay_new(void)
1392 rep = checked_calloc(sizeof(GdReplay));
1394 rep->movements = g_byte_array_new();
1399 GdReplay *gd_replay_new_from_replay(GdReplay *orig)
1403 rep = get_memcpy(orig, sizeof(GdReplay));
1405 /* replicate dynamic data */
1406 rep->comment = getStringCopy(orig->comment);
1407 rep->movements = g_byte_array_new();
1408 g_byte_array_append(rep->movements, orig->movements->data, orig->movements->len);
1413 void gd_replay_free(GdReplay *replay)
1415 g_byte_array_free(replay->movements, TRUE);
1416 checked_free(replay->comment);
1420 /* store movement in a replay */
1421 void gd_replay_store_movement(GdReplay *replay, GdDirection player_move,
1422 boolean player_fire, boolean suicide)
1426 data[0] = ((player_move) |
1427 (player_fire ? GD_REPLAY_FIRE_MASK : 0) |
1428 (suicide ? GD_REPLAY_SUICIDE_MASK : 0));
1430 g_byte_array_append(replay->movements, data, 1);
1433 /* calculate adler checksum for a rendered cave; this can be used for more caves. */
1434 void gd_cave_adler_checksum_more(GdCave *cave, guint32 *a, guint32 *b)
1438 for (y = 0; y < cave->h; y++)
1439 for (x = 0; x < cave->w; x++)
1441 *a += gd_elements[cave->map[y][x]].character;
1449 /* calculate adler checksum for a single rendered cave. */
1451 gd_cave_adler_checksum(GdCave *cave)
1456 gd_cave_adler_checksum_more(cave, &a, &b);
1457 return (b << 16) + a;
1460 /* return c64 color with index. */
1461 GdColor gd_c64_color(int index)
1463 return (GD_COLOR_TYPE_C64 << 24) + index;