2009050
[gdash.git] / src / cave.c
blobdaa195d6d8687f426bd68d46a800cca9b56f770a
1 /*
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.
16 #include <glib.h>
17 #include <glib/gi18n.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <stdlib.h>
22 #include "config.h"
24 #include "cave.h"
25 #include "cavedb.h"
26 #include "caveset.h"
27 #include "caveobject.h"
29 /* arrays for movements */
30 /* also no1 and bd2 cave data import helpers; line direction coordinates */
31 const int gd_dx[]={ 0, 0, 1, 1, 1, 0, -1, -1, -1, 0, 2, 2, 2, 0, -2, -2, -2 };
32 const int gd_dy[]={ 0, -1, -1, 0, 1, 1, 1, 0, -1, -2, -2, 0, 2, 2, 2, 0, -2 };
33 /* TRANSLATORS: None here means "no direction to move"; when there is no gravity while stirring the pot. */
34 static const char* direction_name[]={ N_("None"), N_("Up"), N_("Up+right"), N_("Right"), N_("Down+right"), N_("Down"), N_("Down+left"), N_("Left"), N_("Up+left") };
35 static const char* direction_filename[]={ "none", "up", "upright", "right", "downright", "down", "downleft", "left", "upleft" };
36 static const char* scheduling_name[]={ N_("Milliseconds"), "BD1", "BD2", "Construction Kit", "Crazy Dream 7", "Atari BD1", "Atari BD2/Construction Kit" };
37 static const char* scheduling_filename[]={ "ms", "bd1", "bd2", "plck", "crdr7", "bd1atari", "bd2ckatari" };
39 static GHashTable *name_to_element;
40 GdElement gd_char_to_element[256];
42 /* color of flashing the screen, gate opening to exit */
43 const GdColor gd_flash_color=0xFFFFC0;
44 /* selected object in editor */
45 const GdColor gd_select_color=0x8080FF;
49 /* direction to string and vice versa */
50 const char *
51 gd_direction_get_visible_name(GdDirection dir)
53 g_assert(dir>=0 && dir<G_N_ELEMENTS(direction_name));
54 return direction_name[dir];
57 const char *
58 gd_direction_get_filename(GdDirection dir)
60 g_assert(dir>=0 && dir<G_N_ELEMENTS(direction_filename));
61 return direction_filename[dir];
64 GdDirection
65 gd_direction_from_string(const char *str)
67 int i;
69 g_assert(str!=NULL);
70 for (i=1; i<G_N_ELEMENTS(direction_filename); i++)
71 if (g_ascii_strcasecmp(str, direction_filename[i])==0)
72 return (GdDirection) i;
74 g_warning("invalid direction name '%s', defaulting to down", str);
75 return MV_DOWN;
81 /* scheduling name to string and vice versa */
82 const char *
83 gd_scheduling_get_filename(GdScheduling sched)
85 g_assert(sched>=0 && sched<G_N_ELEMENTS(scheduling_filename));
86 return scheduling_filename[sched];
89 const char *
90 gd_scheduling_get_visible_name(GdScheduling sched)
92 g_assert(sched>=0 && sched<G_N_ELEMENTS(scheduling_name));
93 return scheduling_name[sched];
96 GdScheduling
97 gd_scheduling_from_string(const char *str)
99 int i;
101 g_assert(str!=NULL);
102 for (i=0; i<G_N_ELEMENTS(scheduling_filename); i++)
103 if (g_ascii_strcasecmp(str, scheduling_filename[i])==0)
104 return (GdScheduling) i;
106 g_warning("invalid scheduling name '%s', defaulting to plck", str);
107 return GD_SCHEDULING_PLCK;
113 /* creates the character->element conversion table; using
114 the fixed-in-the-bdcff characters. later, this table
115 may be filled with more elements.
117 void
118 gd_create_char_to_element_table()
120 int i;
122 /* fill all with unknown */
123 for (i=0; i<G_N_ELEMENTS(gd_char_to_element); i++)
124 gd_char_to_element[i]=O_UNKNOWN;
126 /* then set fixed characters */
127 for (i=0; i<O_MAX; i++) {
128 int c=gd_elements[i].character;
130 if (c) {
131 if (gd_char_to_element[c]!=O_UNKNOWN)
132 g_warning("Character %c already used for element %x", c, gd_char_to_element[c]);
133 gd_char_to_element[c]=i;
139 /* search the element database for the specified character, and return the element. */
140 GdElement
141 gd_get_element_from_character (guint8 character)
143 if (gd_char_to_element[character]!=O_UNKNOWN)
144 return gd_char_to_element[character];
146 g_warning ("Invalid character representing element: %c", character);
147 return O_UNKNOWN;
154 do some init; this function is to be called at the start of the application
156 void
157 gd_cave_init()
159 int i;
161 g_assert(MV_MAX==G_N_ELEMENTS(gd_dx));
162 g_assert(MV_MAX==G_N_ELEMENTS(gd_dy));
163 g_assert(GD_SCHEDULING_MAX==G_N_ELEMENTS(scheduling_filename));
164 g_assert(GD_SCHEDULING_MAX==G_N_ELEMENTS(scheduling_name));
166 /* put names to a hash table */
167 /* this is a helper for file read operations */
168 /* maps g_strdupped strings to elemenets (integers) */
169 name_to_element=g_hash_table_new_full(gd_str_case_hash, gd_str_case_equal, g_free, NULL);
171 for (i=0; i<O_MAX; i++) {
172 char *key;
173 g_assert(gd_elements[i].filename!=NULL && !g_str_equal(gd_elements[i].filename, ""));
175 key=g_ascii_strup(gd_elements[i].filename, -1);
176 if (g_hash_table_lookup_extended(name_to_element, key, NULL, NULL))
177 g_warning("Name %s already used for element %x", key, i);
178 g_hash_table_insert(name_to_element, key, GINT_TO_POINTER(i));
179 /* ^^^ do not free "key", as hash table needs it during the whole time! */
181 key=g_strdup_printf("SCANNED_%s", key); /* new string */
182 g_hash_table_insert(name_to_element, key, GINT_TO_POINTER(i));
183 /* once again, do not free "key" ^^^ */
185 /* for compatibility with tim stridmann's memorydump->bdcff converter... .... ... */
186 g_hash_table_insert(name_to_element, "HEXPANDING_WALL", GINT_TO_POINTER(O_H_EXPANDING_WALL));
187 g_hash_table_insert(name_to_element, "FALLING_DIAMOND", GINT_TO_POINTER(O_DIAMOND_F));
188 g_hash_table_insert(name_to_element, "FALLING_BOULDER", GINT_TO_POINTER(O_STONE_F));
189 g_hash_table_insert(name_to_element, "EXPLOSION1S", GINT_TO_POINTER(O_EXPLODE_1));
190 g_hash_table_insert(name_to_element, "EXPLOSION2S", GINT_TO_POINTER(O_EXPLODE_2));
191 g_hash_table_insert(name_to_element, "EXPLOSION3S", GINT_TO_POINTER(O_EXPLODE_3));
192 g_hash_table_insert(name_to_element, "EXPLOSION4S", GINT_TO_POINTER(O_EXPLODE_4));
193 g_hash_table_insert(name_to_element, "EXPLOSION5S", GINT_TO_POINTER(O_EXPLODE_5));
194 g_hash_table_insert(name_to_element, "EXPLOSION1D", GINT_TO_POINTER(O_PRE_DIA_1));
195 g_hash_table_insert(name_to_element, "EXPLOSION2D", GINT_TO_POINTER(O_PRE_DIA_2));
196 g_hash_table_insert(name_to_element, "EXPLOSION3D", GINT_TO_POINTER(O_PRE_DIA_3));
197 g_hash_table_insert(name_to_element, "EXPLOSION4D", GINT_TO_POINTER(O_PRE_DIA_4));
198 g_hash_table_insert(name_to_element, "EXPLOSION5D", GINT_TO_POINTER(O_PRE_DIA_5));
199 g_hash_table_insert(name_to_element, "WALL2", GINT_TO_POINTER(O_STEEL_EXPLODABLE));
200 /* compatibility with old bd-faq (pre disassembly of bladder) */
201 g_hash_table_insert(name_to_element, "BLADDERd9", GINT_TO_POINTER(O_BLADDER_8));
203 /* create table to show errors at the start of the application */
204 gd_create_char_to_element_table();
208 /* search the element database for the specified name, and return the element */
209 GdElement
210 gd_get_element_from_string (const char *string)
212 char *upper=g_ascii_strup(string, -1);
213 gpointer value;
214 gboolean found;
216 if (!string) {
217 g_warning("Invalid string representing element: (null)");
218 return O_UNKNOWN;
221 found=g_hash_table_lookup_extended(name_to_element, upper, NULL, &value);
222 g_free(upper);
223 if (found)
224 return (GdElement) (GPOINTER_TO_INT(value));
226 g_warning("Invalid string representing element: %s", string);
227 return O_UNKNOWN;
233 fill a given struct with default properties.
234 "str" is the struct (data),
235 "properties" describes the structure and its pointers,
236 "defaults" are the pieces of data which will be copied to str.
238 void
239 gd_struct_set_defaults_from_array(gpointer str, const GdStructDescriptor *properties, GdPropertyDefault *defaults)
241 int i;
243 for (i=0; defaults[i].offset!=-1; i++) {
244 gpointer pvalue=G_STRUCT_MEMBER_P(str, defaults[i].offset);
245 int *ivalue=pvalue; /* these point to the same, but to avoid the awkward cast syntax */
246 GdElement *evalue=pvalue;
247 GdDirection *dvalue=pvalue;
248 GdScheduling *svalue=pvalue;
249 gboolean *bvalue=pvalue;
250 GdColor *cvalue=pvalue;
251 double *fvalue=pvalue;
252 int j, n;
254 /* check which property we are talking about: find it in gd_cave_properties. */
255 n=defaults[i].property_index;
256 if (n==0) {
257 while(properties[n].identifier!=NULL && properties[n].offset!=defaults[i].offset)
258 n++;
259 /* make sure we found it. */
260 g_assert(properties[n].identifier!=NULL);
262 /* remember so we will be fast later*/
263 defaults[i].property_index=n;
266 /* some properties are arrays. this loop fills all with the same values */
267 for (j=0; j<properties[n].count; j++)
268 switch (properties[n].type) {
269 /* these are for the gui; do nothing */
270 case GD_TAB:
271 case GD_LABEL:
272 /* no default value for strings */
273 case GD_TYPE_STRING:
274 case GD_TYPE_LONGSTRING:
275 break;
277 case GD_TYPE_RATIO: /* this is also an integer, difference is only when saving to bdcff */
278 case GD_TYPE_INT:
279 if (defaults[i].defval<properties[n].min || defaults[i].defval>properties[n].max)
280 g_warning("integer property %s out of range", properties[n].identifier);
281 ivalue[j]=defaults[i].defval;
282 break;
283 case GD_TYPE_PROBABILITY: /* floats are stored as integer, /million */
284 fvalue[j]=defaults[i].defval/1000000.0;
285 break;
286 case GD_TYPE_BOOLEAN:
287 bvalue[j]=defaults[i].defval!=0;
288 break;
289 case GD_TYPE_ELEMENT:
290 case GD_TYPE_EFFECT:
291 evalue[j]=(GdElement) defaults[i].defval;
292 break;
293 case GD_TYPE_COLOR:
294 cvalue[j]=gd_c64_color(defaults[i].defval);
295 break;
296 case GD_TYPE_DIRECTION:
297 dvalue[j]=(GdDirection) defaults[i].defval;
298 break;
299 case GD_TYPE_SCHEDULING:
300 svalue[j]=(GdScheduling) defaults[i].defval;
301 break;
306 #if 0
307 char *
308 gd_struct_explain_defaults_in_string(const GdStructDescriptor *properties, GdPropertyDefault *defaults)
310 GString *defs;
311 int i;
313 defs=g_string_new(NULL);
315 for (i=0; defaults[i].offset!=-1; i++) {
316 int j, n;
318 /* check which property we are talking about: find it in gd_cave_properties. */
319 n=defaults[i].property_index;
320 if (n==0) {
321 while(properties[n].identifier!=NULL && properties[n].offset!=defaults[i].offset)
322 n++;
323 /* make sure we found it. */
324 g_assert(properties[n].identifier!=NULL);
326 /* remember so we will be fast later*/
327 defaults[i].property_index=n;
330 g_string_append_printf(defs, "%s: ", _(properties[n].name));
332 for (j=0; j<properties[n].count; j++)
333 switch (properties[n].type) {
334 /* these are for the gui; should not be in the defaults array */
335 case GD_TAB:
336 case GD_LABEL:
337 case GD_LEVEL_LABEL:
338 /* no default value for strings */
339 case GD_TYPE_STRING:
340 g_assert_not_reached();
341 break;
343 case GD_TYPE_INT:
344 case GD_TYPE_RATIO: /* this is also an integer */
345 g_string_append_printf(defs, "%d", defaults[i].defval);
346 break;
347 case GD_TYPE_PROBABILITY: /* floats are stored as integer, /million*100 */
348 g_string_append_printf(defs, "%6.4f%%", defaults[i].defval/10000.0);
349 break;
350 case GD_TYPE_BOOLEAN:
351 g_string_append_printf(defs, "%s", defaults[i].defval?_("Yes"):_("No"));
352 break;
353 case GD_TYPE_ELEMENT:
354 case GD_TYPE_EFFECT:
355 g_string_append_printf(defs, "%s", _(gd_elements[defaults[i].defval].name));
356 break;
357 case GD_TYPE_COLOR:
358 g_string_append_printf(defs, "%s", gd_get_color_name(gd_c64_color(defaults[i].defval)));
359 break;
360 case GD_TYPE_DIRECTION:
361 g_string_append_printf(defs, "%s", direction_name[defaults[i].defval]);
362 break;
363 case GD_TYPE_SCHEDULING:
364 g_string_append_printf(defs, "%s", scheduling_name[(GdScheduling) defaults[i].defval]);
365 break;
368 g_string_append_printf(defs, "\n");
371 /* return char * data */
372 return g_string_free(defs, FALSE);
374 #endif
382 void
383 gd_cave_set_defaults_from_array(GdCave* cave, GdPropertyDefault *defaults)
385 gd_struct_set_defaults_from_array(cave, gd_cave_properties, defaults);
390 load default values from description array
391 these are default for gdash and bdcff.
393 void
394 gd_cave_set_gdash_defaults(GdCave* cave)
396 int i;
398 gd_cave_set_defaults_from_array(cave, gd_cave_defaults_gdash);
400 /* these did not fit into the descriptor array */
401 for (i=0; i<5; i++) {
402 cave->level_rand[i]=i;
403 cave->level_timevalue[i]=i+1;
412 /* for quicksort. compares two highscores. */
414 gd_highscore_compare(gconstpointer a, gconstpointer b)
416 const GdHighScore *ha=a;
417 const GdHighScore *hb=b;
418 return hb->score - ha->score;
421 void
422 gd_clear_highscore(GdHighScore *hs)
424 int i;
426 for (i=0; i<GD_HIGHSCORE_NUM; i++) {
427 strcpy(hs[i].name, "");
428 hs[i].score=0;
432 gboolean
433 gd_has_highscore(GdHighScore *hs)
435 return hs[0].score>0;
438 void
439 gd_cave_clear_highscore(GdCave *cave)
441 gd_clear_highscore(cave->highscore);
445 /* return true if score achieved is a highscore */
446 gboolean gd_is_highscore(GdHighScore *scores, int score)
448 /* if score is above zero AND bigger than the last one */
449 if (score>0 && score>scores[GD_HIGHSCORE_NUM-1].score)
450 return TRUE;
452 return FALSE;
456 gd_add_highscore(GdHighScore *highscores, const char *name, int score)
458 int i;
460 if (!gd_is_highscore(highscores, score))
461 return -1;
463 /* overwrite the last one */
464 gd_strcpy(highscores[GD_HIGHSCORE_NUM-1].name, name);
465 highscores[GD_HIGHSCORE_NUM-1].score=score;
466 /* and sort */
467 qsort(highscores, GD_HIGHSCORE_NUM, sizeof(GdHighScore), gd_highscore_compare);
469 for (i=0; i<GD_HIGHSCORE_NUM; i++)
470 if (g_str_equal(highscores[i].name, name) && highscores[i].score==score)
471 return i;
473 g_assert_not_reached();
474 return -1;
481 /* for the case-insensitive hash keys */
482 gboolean
483 gd_str_case_equal(gconstpointer s1, gconstpointer s2)
485 return g_ascii_strcasecmp(s1, s2)==0;
488 guint
489 gd_str_case_hash(gconstpointer v)
491 char *upper;
492 guint hash;
494 upper=g_ascii_strup(v, -1);
495 hash=g_str_hash(v);
496 g_free(upper);
497 return hash;
513 create new cave with default values.
514 sets every value, also default size, diamond value etc.
516 GdCave *
517 gd_cave_new(void)
519 int i;
520 GdCave *cave;
522 cave=g_new0(GdCave, 1);
524 /* hash table which stores unknown tags as strings. */
525 cave->tags=g_hash_table_new_full(gd_str_case_hash, gd_str_case_equal, g_free, g_free);
526 /* for strings */
527 for (i=0; gd_cave_properties[i].identifier!=NULL; i++)
528 if (gd_cave_properties[i].type==GD_TYPE_LONGSTRING)
529 G_STRUCT_MEMBER(GString *, cave, gd_cave_properties[i].offset)=g_string_new(NULL);
532 gd_cave_set_gdash_defaults (cave);
534 return cave;
537 /* cave maps.
538 cave maps are continuous areas in memory. the allocated memory
539 is width*height*bytes_per_cell long.
540 the cave map[0] stores the pointer given by g_malloc().
541 the map itself is also an allocated array of pointers to the
542 beginning of rows.
543 therefore:
544 rows=new (pointers to rows);
545 rows[0]=new map
546 rows[1..h-1]=rows[0]+width*bytes
548 freeing this:
549 free(rows[0])
550 free(rows)
554 allocate a cave map-like array, and initialize to zero.
555 one cell is cell_size bytes long.
557 gpointer
558 gd_cave_map_new_for_cave(const GdCave *cave, const int cell_size)
560 gpointer *rows; /* this is void**, pointer to array of ... */
561 int y;
563 rows=g_new(gpointer, cave->h);
564 rows[0]=g_malloc0 (cell_size*cave->w*cave->h);
565 for (y=1; y<cave->h; y++)
566 /* base pointer+num_of_bytes_per_element*width*number_of_row; as sizeof(char)=1 */
567 rows[y]=(char *)rows[0]+cell_size*cave->w*y;
568 return rows;
572 duplicate map
574 if map is null, this also returns null.
576 gpointer
577 gd_cave_map_dup_size(const GdCave *cave, const gpointer map, const int cell_size)
579 gpointer *rows;
580 gpointer *maplines=(gpointer *)map;
581 int y;
583 if (!map)
584 return NULL;
586 rows=g_new (gpointer, cave->h);
587 rows[0]=g_memdup (maplines[0], cell_size * cave->w * cave->h);
589 for (y=1; y < cave->h; y++)
590 rows[y]=(char *)rows[0]+cell_size*cave->w*y;
592 return rows;
595 void
596 gd_cave_map_free(gpointer map)
598 gpointer *maplines=(gpointer *) map;
600 if (!map)
601 return;
603 g_free(maplines[0]);
604 g_free(map);
614 frees memory associated to cave
616 void
617 gd_cave_free(GdCave *cave)
619 int i;
621 if (!cave)
622 return;
624 if (cave->tags)
625 g_hash_table_destroy(cave->tags);
627 if (cave->random) /* random generator is a GRand * */
628 g_rand_free(cave->random);
630 /* free GStrings */
631 for (i=0; gd_cave_properties[i].identifier!=NULL; i++)
632 if (gd_cave_properties[i].type==GD_TYPE_LONGSTRING)
633 g_string_free(G_STRUCT_MEMBER(GString *, cave, gd_cave_properties[i].offset), TRUE);
635 /* map */
636 gd_cave_map_free(cave->map);
637 /* rendered data */
638 gd_cave_map_free(cave->objects_order);
639 /* hammered walls to reappear data */
640 gd_cave_map_free(cave->hammered_reappear);
641 /* free objects */
642 g_list_foreach(cave->objects, (GFunc) g_free, NULL);
643 g_list_free (cave->objects);
644 /* free replays */
645 g_list_foreach(cave->replays, (GFunc) gd_replay_free, NULL);
646 g_list_free(cave->replays);
648 /* freeing main pointer */
649 g_free (cave);
652 static void
653 hash_copy_foreach(const char *key, const char *value, GHashTable *dest)
655 g_hash_table_insert(dest, g_strdup(key), g_strdup(value));
658 /* copy cave from src to destination, with duplicating dynamically allocated data */
659 void
660 gd_cave_copy(GdCave *dest, const GdCave *src)
662 int i;
664 /* copy entire data */
665 g_memmove(dest, src, sizeof(GdCave));
667 /* but duplicate dynamic data */
668 dest->tags=g_hash_table_new_full(gd_str_case_hash, gd_str_case_equal, g_free, g_free);
669 if (src->tags)
670 g_hash_table_foreach(src->tags, (GHFunc) hash_copy_foreach, dest->tags);
671 dest->map=gd_cave_map_dup (src, map);
672 dest->hammered_reappear=gd_cave_map_dup(src, hammered_reappear);
674 /* for longstrings */
675 for (i=0; gd_cave_properties[i].identifier!=NULL; i++)
676 if (gd_cave_properties[i].type==GD_TYPE_LONGSTRING)
677 G_STRUCT_MEMBER(GString *, dest, gd_cave_properties[i].offset)=g_string_new(G_STRUCT_MEMBER(GString *, src, gd_cave_properties[i].offset)->str);
679 /* no reason to copy this */
680 dest->objects_order=NULL;
682 /* copy objects list */
683 if (src->objects) {
684 GList *iter;
686 dest->objects=NULL; /* new empty list */
687 for (iter=src->objects; iter!=NULL; iter=iter->next) /* do a deep copy */
688 dest->objects=g_list_append(dest->objects, g_memdup (iter->data, sizeof (GdObject)));
691 /* copy replays */
692 if (src->replays) {
693 GList *iter;
695 dest->replays=NULL;
696 for (iter=src->replays; iter!=NULL; iter=iter->next) /* do a deep copy */
697 dest->replays=g_list_append(dest->replays, gd_replay_new_from_replay(iter->data));
700 /* copy random number generator */
701 if (src->random)
702 dest->random=g_rand_copy(src->random);
705 /* create new cave, which is a copy of the cave given. */
706 GdCave *
707 gd_cave_new_from_cave(const GdCave *orig)
709 GdCave *cave;
711 cave=gd_cave_new();
712 gd_cave_copy(cave, orig);
714 return cave;
719 Put an object to the specified position.
720 Performs range checking.
721 If wraparound objects are selected, wraps around x coordinates, with or without lineshift.
722 (The y coordinate is not wrapped, as it did not work like that on the c64)
723 order is a pointer to the GdObject describing this object. Thus the editor can identify which cell was created by which object.
725 void
726 gd_cave_store_rc(GdCave *cave, int x, int y, const GdElement element, const void* order)
728 /* if we do not need to draw, exit now */
729 if (element==O_NONE)
730 return;
732 /* check bounds */
733 if (cave->wraparound_objects) {
734 if (cave->lineshift) {
735 /* fit x coordinate within range, with correcting y at the same time */
736 while (x<0) {
737 x+=cave->w; /* out of bounds on the left... */
738 y--; /* previous row */
740 while (x>=cave->w) {
741 x-=cave->w;
742 y++;
744 /* lineshifting does not fix the y coordinates. if out of bounds, element will not be displayed. */
745 /* if such an object appeared in the c64 game, well, it was a buffer overrun. */
746 } else {
747 /* non lineshifting: changing x does not change y coordinate. */
748 while (x<0)
749 x+=cave->w;
750 while (x>=cave->w)
751 x-=cave->w;
752 /* after that, fix y coordinate */
753 while (y<0)
754 y+=cave->h;
755 while (y>=cave->h)
756 y-=cave->h;
760 /* if the above wraparound code fixed the coordinates, this will always be true. */
761 /* but see the above comment for lineshifting y coordinate */
762 if (x>=0 && x<cave->w && y>=0 && y<cave->h) {
763 cave->map[y][x]=element;
764 cave->objects_order[y][x]=(void *)order;
768 GdElement
769 gd_cave_get_rc(const GdCave *cave, int x, int y)
771 /* always fix coordinates as if cave was wraparound. */
773 /* fix x coordinate */
774 if (cave->lineshift) {
775 /* fit x coordinate within range, with correcting y at the same time */
776 while (x<0) {
777 x+=cave->w; /* out of bounds on the left... */
778 y--; /* previous row */
780 while (x>=cave->w) {
781 x-=cave->w;
782 y++;
784 } else {
785 /* non lineshifting: changing x does not change y coordinate. */
786 while (x<0)
787 x+=cave->w;
788 while (x>=cave->w)
789 x-=cave->w;
791 /* after that, fix y coordinate */
792 while (y<0)
793 y+=cave->h;
794 while (y>=cave->h)
795 y-=cave->h;
797 return cave->map[y][x];
807 unsigned int
808 gd_c64_random(GdC64RandomGenerator *rand)
810 unsigned int temp_rand_1, temp_rand_2, carry, result;
812 temp_rand_1=(rand->rand_seed_1&0x0001) << 7;
813 temp_rand_2=(rand->rand_seed_2 >> 1)&0x007F;
814 result=(rand->rand_seed_2)+((rand->rand_seed_2&0x0001) << 7);
815 carry=(result >> 8);
816 result=result&0x00FF;
817 result=result+carry+0x13;
818 carry=(result >> 8);
819 rand->rand_seed_2=result&0x00FF;
820 result=rand->rand_seed_1+carry+temp_rand_1;
821 carry=(result >> 8);
822 result=result&0x00FF;
823 result=result+carry+temp_rand_2;
824 rand->rand_seed_1=result&0x00FF;
826 return rand->rand_seed_1;
831 C64 BD predictable random number generator.
832 Used to load the original caves imported from c64 files.
833 Also by the predictable slime.
835 unsigned int
836 gd_cave_c64_random(GdCave *cave)
838 return gd_c64_random(&cave->c64_rand);
841 void
842 gd_c64_random_set_seed(GdC64RandomGenerator *rand, int seed1, int seed2)
844 rand->rand_seed_1=seed1;
845 rand->rand_seed_2=seed2;
848 void
849 gd_cave_c64_random_set_seed(GdCave *cave, int seed1, int seed2)
851 gd_c64_random_set_seed(&cave->c64_rand, seed1, seed2);
863 select random colors for a given cave.
864 this function will select colors so that they should look somewhat nice; for example
865 brick walls won't be the darkest color, for example.
867 static inline void
868 swap(int *i1, int *i2)
870 int t=*i1;
871 *i1=*i2;
872 *i2=t;
875 void
876 gd_cave_set_random_c64_colors(GdCave *cave)
878 const int bright_colors[]={1, 3, 7};
879 const int dark_colors[]={2, 6, 8, 9, 11};
881 /* always black */
882 cave->colorb=gd_c64_color(0);
883 cave->color0=gd_c64_color(0);
884 /* choose some bright color for brick */
885 cave->color3=gd_c64_color(bright_colors[g_random_int_range(0, G_N_ELEMENTS(bright_colors))]);
886 /* choose a dark color for dirt, but should not be == color of brick */
887 do {
888 cave->color1=gd_c64_color(dark_colors[g_random_int_range(0, G_N_ELEMENTS(dark_colors))]);
889 } while (cave->color1==cave->color3); /* so it is not the same as color 1 */
890 /* choose any but black for steel wall, but should not be == brick or dirt */
891 do {
892 /* between 1 and 15 - do not use black for this. */
893 cave->color2=gd_c64_color(g_random_int_range(1, 16));
894 } while (cave->color1==cave->color2 || cave->color2==cave->color3); /* so colors are not the same */
895 /* copy amoeba and slime color */
896 cave->color4=cave->color3;
897 cave->color5=cave->color1;
900 static void
901 cave_set_random_indexed_colors(GdCave *cave, GdColor (*color_indexer_func) (int, int))
903 int hue=g_random_int_range(0, 15);
904 int hue_spread=g_random_int_range(1, 6); /* 1..5 */
905 /* we only use 0..6, as saturation 15 is too bright (almost always white) */
906 /* also, saturation 0..1..2 is too dark. the color0=black is there for dark. */
907 int bri_spread=6-hue_spread; /* so this is also 1..5. when hue spread is low, brightness spread is high */
908 int bri1=8, bri2=8-bri_spread, bri3=8+bri_spread;
909 /* there are 15 valid choices for hue, so we do a %15 */
910 int col1=hue, col2=(hue+hue_spread+15)%15, col3=(hue-hue_spread+15)%15;
912 /* this makes up a random color, and selects a color triad by hue+5 and hue+10. */
913 /* also creates a random saturation. */
914 /* color of brick is 8+sat, so it is always a bright color. */
915 /* another two are 8-sat and 8. */
916 /* order of colors is also changed randomly. */
918 if (g_random_boolean())
919 swap(&bri1, &bri2);
920 /* we do not touch bri3 (8+sat), as it should be a bright color */
921 if (g_random_boolean())
922 swap(&col1, &col2);
923 if (g_random_boolean())
924 swap(&col2, &col3);
925 if (g_random_boolean())
926 swap(&col1, &col3);
928 cave->colorb=color_indexer_func(0, 0);
929 cave->color0=color_indexer_func(0, 0);
930 cave->color1=color_indexer_func(col1+1, bri1);
931 cave->color2=color_indexer_func(col2+1, bri2);
932 cave->color3=color_indexer_func(col3+1, bri3);
933 /* amoeba and slime are different */
934 cave->color4=color_indexer_func(g_random_int_range(11, 13), g_random_int_range(6, 12)); /* some green thing */
935 cave->color5=color_indexer_func(g_random_int_range(7, 10), g_random_int_range(0, 6)); /* some blueish thing */
938 void
939 gd_cave_set_random_atari_colors(GdCave *cave)
941 cave_set_random_indexed_colors(cave, gd_atari_color_huesat);
944 void
945 gd_cave_set_random_c64dtv_colors(GdCave *cave)
947 cave_set_random_indexed_colors(cave, gd_c64dtv_color_huesat);
950 static inline void
951 swapd(double *i1, double *i2)
953 double t=*i1;
954 *i1=*i2;
955 *i2=t;
958 void
959 gd_cave_set_random_rgb_colors(GdCave *cave)
961 const double hue_max=10.0/30.0;
962 double hue=g_random_double(); /* any hue allowed */
963 double hue_spread=g_random_double_range(2.0/30.0, hue_max); /* hue 360 degress=1. hue spread is min. 24 degrees, max 120 degrees (1/3) */
964 double h1=hue, h2=hue+hue_spread, h3=hue+2*hue_spread;
965 double v1, v2, v3;
966 double s1, s2, s3;
968 if (g_random_boolean()) {
969 /* when hue spread is low, brightness(saturation) spread is high */
970 /* this formula gives a number (x) between 0.1 and 0.4, which will be 0.5-x and 0.5+x, so the range is 0.1->0.9 */
971 double spread=0.1+0.3*(1-hue_spread/hue_max);
972 v1=0.6; /* brightness variation, too */
973 v2=0.7;
974 v3=0.8;
975 s1=0.5; /* saturation is different */
976 s2=0.5-spread;
977 s3=0.5+spread;
978 } else {
979 /* when hue spread is low, brightness(saturation) spread is high */
980 /* this formula gives a number (x) between 0.1 and 0.25, which will be 0.5+x and 0.5+2x, so the range is 0.5->0.9 */
981 double spread=0.1+0.15*(1-hue_spread/hue_max);
982 v1=0.5; /* brightness is different */
983 v2=0.5+spread;
984 v3=0.5+2*spread;
985 s1=0.7; /* saturation is same - a not fully saturated one */
986 s2=0.8;
987 s3=0.9;
989 /* randomly change values, but do not touch v3, as cave->color3 should be a bright color */
990 if (g_random_boolean()) swapd(&v1, &v2);
991 /* randomly change hues and saturations */
992 if (g_random_boolean()) swapd(&h1, &h2);
993 if (g_random_boolean()) swapd(&h2, &h3);
994 if (g_random_boolean()) swapd(&h1, &h3);
995 if (g_random_boolean()) swapd(&s1, &s2);
996 if (g_random_boolean()) swapd(&s2, &s3);
997 if (g_random_boolean()) swapd(&s1, &s3);
999 h1=h1*360.0;
1000 h2=h2*360.0;
1001 h3=h3*360.0;
1003 cave->colorb=gd_color_get_from_hsv(0,0,0);
1004 cave->color0=gd_color_get_from_hsv(0,0,0); /* black for background */
1005 cave->color1=gd_color_get_from_hsv(h1,s1,v1); /* dirt */
1006 cave->color2=gd_color_get_from_hsv(h2,s2,v2); /* steel */
1007 cave->color3=gd_color_get_from_hsv(h3,s3,v3); /* brick */
1008 cave->color4=gd_color_get_from_hsv(g_random_int_range(100, 140),s2,v2); /* green(120+-20) with the saturation and brightness of brick */
1009 cave->color5=gd_color_get_from_hsv(g_random_int_range(220, 260),s1,v1); /* blue(240+-20) with saturation and brightness of dirt */
1013 void
1014 gd_cave_set_random_colors(GdCave *cave, GdColorType type)
1016 switch (type) {
1017 case GD_COLOR_TYPE_RGB:
1018 gd_cave_set_random_rgb_colors(cave);
1019 break;
1020 case GD_COLOR_TYPE_C64:
1021 gd_cave_set_random_c64_colors(cave);
1022 break;
1023 case GD_COLOR_TYPE_C64DTV:
1024 gd_cave_set_random_c64dtv_colors(cave);
1025 break;
1026 case GD_COLOR_TYPE_ATARI:
1027 gd_cave_set_random_atari_colors(cave);
1028 break;
1030 default:
1031 g_assert_not_reached();
1038 shrink cave
1039 if last line or last row is just steel wall (or (invisible) outbox).
1040 used after loading a game for playing.
1041 after this, ew and eh will contain the effective width and height.
1043 void
1044 gd_cave_auto_shrink(GdCave *cave)
1047 int x, y;
1048 enum {
1049 STEEL_ONLY,
1050 STEEL_OR_OTHER,
1051 NO_SHRINK
1052 } empty;
1054 /* set to maximum size, then try to shrink */
1055 cave->x1=0; cave->y1=0;
1056 cave->x2=cave->w-1; cave->y2=cave->h-1;
1058 /* search for empty, steel-wall-only last rows. */
1059 /* clear all lines, which are only steel wall.
1060 * and clear only one line, which is steel wall, but also has a player or an outbox. */
1061 empty=STEEL_ONLY;
1062 do {
1063 for (y=cave->y2-1; y <= cave->y2; y++)
1064 for (x=cave->x1; x <= cave->x2; x++)
1065 switch (gd_cave_get_rc (cave, x, y)) {
1066 case O_STEEL: /* if steels only, this is to be deleted. */
1067 break;
1068 case O_PRE_OUTBOX:
1069 case O_PRE_INVIS_OUTBOX:
1070 case O_INBOX:
1071 if (empty==STEEL_OR_OTHER)
1072 empty=NO_SHRINK;
1073 if (empty==STEEL_ONLY) /* if this, delete only this one, and exit. */
1074 empty=STEEL_OR_OTHER;
1075 break;
1076 default: /* anything else, that should be left in the cave. */
1077 empty=NO_SHRINK;
1078 break;
1080 if (empty!=NO_SHRINK) /* shrink if full steel or steel and player/outbox. */
1081 cave->y2--; /* one row shorter */
1083 while (empty==STEEL_ONLY); /* if found just steels, repeat. */
1085 /* search for empty, steel-wall-only first rows. */
1086 empty=STEEL_ONLY;
1087 do {
1088 for (y=cave->y1; y <= cave->y1+1; y++)
1089 for (x=cave->x1; x <= cave->x2; x++)
1090 switch (gd_cave_get_rc (cave, x, y)) {
1091 case O_STEEL:
1092 break;
1093 case O_PRE_OUTBOX:
1094 case O_PRE_INVIS_OUTBOX:
1095 case O_INBOX:
1096 /* shrink only lines, which have only ONE player or outbox. this is for bd4 intermission 2, for example. */
1097 if (empty==STEEL_OR_OTHER)
1098 empty=NO_SHRINK;
1099 if (empty==STEEL_ONLY)
1100 empty=STEEL_OR_OTHER;
1101 break;
1102 default:
1103 empty=NO_SHRINK;
1104 break;
1106 if (empty!=NO_SHRINK)
1107 cave->y1++;
1109 while (empty==STEEL_ONLY); /* if found one, repeat. */
1111 /* empty last columns. */
1112 empty=STEEL_ONLY;
1113 do {
1114 for (y=cave->y1; y <= cave->y2; y++)
1115 for (x=cave->x2-1; x <= cave->x2; x++)
1116 switch (gd_cave_get_rc (cave, x, y)) {
1117 case O_STEEL:
1118 break;
1119 case O_PRE_OUTBOX:
1120 case O_PRE_INVIS_OUTBOX:
1121 case O_INBOX:
1122 if (empty==STEEL_OR_OTHER)
1123 empty=NO_SHRINK;
1124 if (empty==STEEL_ONLY)
1125 empty=STEEL_OR_OTHER;
1126 break;
1127 default:
1128 empty=NO_SHRINK;
1129 break;
1131 if (empty!=NO_SHRINK)
1132 cave->x2--; /* just remember that one column shorter. g_free will know the size of memchunk, no need to realloc! */
1134 while (empty==STEEL_ONLY); /* if found one, repeat. */
1136 /* empty first columns. */
1137 empty=STEEL_ONLY;
1138 do {
1139 for (y=cave->y1; y <= cave->y2; y++)
1140 for (x=cave->x1; x <= cave->x1+1; x++)
1141 switch (gd_cave_get_rc (cave, x, y)) {
1142 case O_STEEL:
1143 break;
1144 case O_PRE_OUTBOX:
1145 case O_PRE_INVIS_OUTBOX:
1146 case O_INBOX:
1147 if (empty==STEEL_OR_OTHER)
1148 empty=NO_SHRINK;
1149 if (empty==STEEL_ONLY)
1150 empty=STEEL_OR_OTHER;
1151 break;
1152 default:
1153 empty=NO_SHRINK;
1154 break;
1156 if (empty!=NO_SHRINK)
1157 cave->x1++;
1159 while (empty==STEEL_ONLY); /* if found one, repeat. */
1162 /* check if cave visible part coordinates
1163 are outside cave sizes, or not in the right order.
1164 correct them if needed.
1166 void
1167 gd_cave_correct_visible_size(GdCave *cave)
1169 g_assert(cave!=NULL);
1171 /* change visible coordinates if they do not point to upperleft and lowerright */
1172 if (cave->x2<cave->x1) {
1173 int t=cave->x2;
1174 cave->x2=cave->x1;
1175 cave->x1=t;
1177 if (cave->y2<cave->y1) {
1178 int t=cave->y2;
1179 cave->y2=cave->y1;
1180 cave->y1=t;
1182 if (cave->x1<0)
1183 cave->x1=0;
1184 if (cave->y1<0)
1185 cave->y1=0;
1186 if (cave->x2>cave->w-1)
1187 cave->x2=cave->w-1;
1188 if (cave->y2>cave->h-1)
1189 cave->y2=cave->h-1;
1204 bd1 and similar engines had animation bits in cave data, to set which elements to animate (firefly, butterfly, amoeba).
1205 animating an element also caused some delay each frame; according to my measurements, around 2.6 ms/element.
1207 static void
1208 cave_set_ckdelay_extra_for_animation(GdCave *cave)
1210 int x, y;
1211 gboolean has_amoeba=FALSE, has_firefly=FALSE, has_butterfly=FALSE, has_slime=FALSE;
1212 g_assert(cave->map!=NULL);
1214 for (y=0; y<cave->h; y++)
1215 for (x=0; x<cave->w; x++) {
1216 switch (cave->map[y][x]&~SCANNED) {
1217 case O_FIREFLY_1:
1218 case O_FIREFLY_2:
1219 case O_FIREFLY_3:
1220 case O_FIREFLY_4:
1221 has_firefly=TRUE;
1222 break;
1223 case O_BUTTER_1:
1224 case O_BUTTER_2:
1225 case O_BUTTER_3:
1226 case O_BUTTER_4:
1227 has_butterfly=TRUE;
1228 break;
1229 case O_AMOEBA:
1230 has_amoeba=TRUE;
1231 break;
1232 case O_SLIME:
1233 has_slime=TRUE;
1234 break;
1237 cave->ckdelay_extra_for_animation=0;
1238 if (has_amoeba)
1239 cave->ckdelay_extra_for_animation+=2600;
1240 if (has_firefly)
1241 cave->ckdelay_extra_for_animation+=2600;
1242 if (has_butterfly)
1243 cave->ckdelay_extra_for_animation+=2600;
1244 if (has_amoeba)
1245 cave->ckdelay_extra_for_animation+=2600;
1249 /* do some init - setup some cave variables before the game. */
1250 void
1251 gd_cave_setup_for_game(GdCave *cave)
1253 int x, y;
1255 cave_set_ckdelay_extra_for_animation(cave);
1257 /* find the player which will be the one to scroll to at the beginning of the game (before the player's birth) */
1258 if (cave->active_is_first_found) {
1259 /* uppermost player is active */
1260 for (y=cave->h-1; y>=0; y--)
1261 for (x=cave->w-1; x>=0; x--)
1262 if (cave->map[y][x]==O_INBOX) {
1263 cave->player_x=x;
1264 cave->player_y=y;
1266 } else {
1267 /* lowermost player is active */
1268 for (y=0; y<cave->h; y++)
1269 for (x=0; x<cave->w; x++)
1270 if (cave->map[y][x]==O_INBOX) {
1271 cave->player_x=x;
1272 cave->player_y=y;
1276 /* select number of milliseconds (for pal and ntsc) */
1277 cave->timing_factor=cave->pal_timing?1200:1000;
1278 cave->time*=cave->timing_factor;
1279 cave->magic_wall_time*=cave->timing_factor;
1280 cave->amoeba_time*=cave->timing_factor;
1281 cave->amoeba_2_time*=cave->timing_factor;
1282 cave->hatching_delay_time*=cave->timing_factor;
1283 if (cave->hammered_walls_reappear)
1284 cave->hammered_reappear=gd_cave_map_new(cave, int);
1287 /* cave diamonds needed can be set to n<=0. */
1288 /* if so, count the diamonds at the time of the hatching, and decrement that value from */
1289 /* the number of diamonds found. */
1290 /* of course, this function is to be called from the cave engine, at the exact time of hatching. */
1291 void
1292 gd_cave_count_diamonds(GdCave *cave)
1294 int x, y;
1296 /* if automatically counting diamonds. if this was negative,
1297 * the sum will be this less than the number of all the diamonds in the cave */
1298 if (cave->diamonds_needed<=0) {
1299 for (y=0; y<cave->h; y++)
1300 for (x=0; x<cave->w; x++)
1301 if (cave->map[y][x]==O_DIAMOND)
1302 cave->diamonds_needed++;
1303 if (cave->diamonds_needed<0)
1304 /* if still below zero, let this be 0, so gate will be open immediately */
1305 cave->diamonds_needed=0;
1316 /* takes a cave and a gfx buffer, and fills the buffer with cell indexes.
1317 the indexes might change if bonus life flash is active (small lines in "SPACE" cells),
1318 for the paused state (which is used in gdash but not in sdash) - yellowish color.
1319 also one can select the animation frame (0..7) to draw the cave on. so the caller manages
1320 increasing that.
1322 if a cell is changed, it is flagged with GD_REDRAW; the flag can be cleared by the caller.
1324 void
1325 gd_drawcave_game(const GdCave *cave, int **gfx_buffer, gboolean bonus_life_flash, gboolean yellowish_color, int animcycle, gboolean hate_invisible_outbox)
1327 static int player_blinking=0;
1328 static int player_tapping=0;
1329 int elemdrawing[O_MAX];
1330 int x, y, draw;
1332 g_assert(cave!=NULL);
1333 g_assert(cave->map!=NULL);
1334 g_assert(gfx_buffer!=NULL);
1335 g_assert(animcycle>=0 && animcycle<=7);
1337 if (cave->last_direction) { /* he is moving, so stop blinking and tapping. */
1338 player_blinking=0;
1339 player_tapping=0;
1341 else { /* he is idle, so animations can be done. */
1342 if (animcycle==0) { /* blinking and tapping is started at the beginning of animation sequences. */
1343 player_blinking=g_random_int_range(0, 4)==0; /* 1/4 chance of blinking, every sequence. */
1344 if (g_random_int_range(0, 16)==0) /* 1/16 chance of starting or stopping tapping. */
1345 player_tapping=!player_tapping;
1349 for (x=0; x<O_MAX; x++)
1350 elemdrawing[x]=gd_elements[x].image_game;
1351 if (bonus_life_flash)
1352 elemdrawing[O_SPACE]=gd_elements[O_FAKE_BONUS].image_game;
1353 elemdrawing[O_MAGIC_WALL]=gd_elements[cave->magic_wall_state == GD_MW_ACTIVE ? O_MAGIC_WALL : O_BRICK].image_game;
1354 elemdrawing[O_CREATURE_SWITCH]=gd_elements[cave->creatures_backwards ? O_CREATURE_SWITCH_ON : O_CREATURE_SWITCH].image_game;
1355 elemdrawing[O_EXPANDING_WALL_SWITCH]=gd_elements[cave->expanding_wall_changed ? O_EXPANDING_WALL_SWITCH_VERT : O_EXPANDING_WALL_SWITCH_HORIZ].image_game;
1356 elemdrawing[O_GRAVITY_SWITCH]=gd_elements[cave->gravity_switch_active?O_GRAVITY_SWITCH_ACTIVE:O_GRAVITY_SWITCH].image_game;
1357 elemdrawing[O_REPLICATOR_SWITCH]=gd_elements[cave->replicators_active?O_REPLICATOR_SWITCH_ON:O_REPLICATOR_SWITCH_OFF].image_game;
1358 if (!cave->replicators_active)
1359 /* if the replicators are inactive, do not animate them. */
1360 elemdrawing[O_REPLICATOR]=ABS(elemdrawing[O_REPLICATOR]);
1361 elemdrawing[O_CONVEYOR_SWITCH]=gd_elements[cave->conveyor_belts_active?O_CONVEYOR_SWITCH_ON:O_CONVEYOR_SWITCH_OFF].image_game;
1362 if (cave->conveyor_belts_direction_changed) {
1363 /* if direction is changed, animation is changed. */
1364 int temp;
1366 temp=elemdrawing[O_CONVEYOR_LEFT];
1367 elemdrawing[O_CONVEYOR_LEFT]=elemdrawing[O_CONVEYOR_RIGHT];
1368 elemdrawing[O_CONVEYOR_RIGHT]=temp;
1370 elemdrawing[O_CONVEYOR_DIR_SWITCH]=gd_elements[O_CONVEYOR_DIR_CHANGED].image_game;
1372 else
1373 elemdrawing[O_CONVEYOR_DIR_SWITCH]=gd_elements[O_CONVEYOR_DIR_NORMAL].image_game;
1374 if (!cave->conveyor_belts_active) {
1375 /* if they are not running, do not animate them. */
1376 elemdrawing[O_CONVEYOR_LEFT]=ABS(elemdrawing[O_CONVEYOR_LEFT]);
1377 elemdrawing[O_CONVEYOR_RIGHT]=ABS(elemdrawing[O_CONVEYOR_RIGHT]);
1379 if (animcycle&2) {
1380 elemdrawing[O_PNEUMATIC_ACTIVE_LEFT]+=2; /* also a hack, like biter_switch */
1381 elemdrawing[O_PNEUMATIC_ACTIVE_RIGHT]+=2;
1382 elemdrawing[O_PLAYER_PNEUMATIC_LEFT]+=2;
1383 elemdrawing[O_PLAYER_PNEUMATIC_RIGHT]+=2;
1386 if ((cave->last_direction) == MV_STILL) { /* player is idle. */
1387 if (player_blinking && player_tapping)
1388 draw=gd_elements[O_PLAYER_TAP_BLINK].image_game;
1389 else if (player_blinking)
1390 draw=gd_elements[O_PLAYER_BLINK].image_game;
1391 else if (player_tapping)
1392 draw=gd_elements[O_PLAYER_TAP].image_game;
1393 else
1394 draw=gd_elements[O_PLAYER].image_game;
1396 else if (cave->last_horizontal_direction == MV_LEFT)
1397 draw=gd_elements[O_PLAYER_LEFT].image_game;
1398 else
1399 /* of course this is MV_RIGHT. */
1400 draw=gd_elements[O_PLAYER_RIGHT].image_game;
1401 elemdrawing[O_PLAYER]=draw;
1402 elemdrawing[O_PLAYER_GLUED]=draw;
1403 /* player with bomb does not blink or tap - no graphics drawn for that. running is drawn using w/o bomb cells */
1404 if (cave->last_direction!=MV_STILL)
1405 elemdrawing[O_PLAYER_BOMB]=draw;
1406 elemdrawing[O_INBOX]=gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1407 elemdrawing[O_OUTBOX]=gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
1408 elemdrawing[O_BITER_SWITCH]=gd_elements[O_BITER_SWITCH].image_game+cave->biter_delay_frame; /* hack, not fit into gd_elements */
1409 /* visual effects */
1410 elemdrawing[O_DIRT]=elemdrawing[cave->dirt_looks_like];
1411 elemdrawing[O_EXPANDING_WALL]=elemdrawing[cave->expanding_wall_looks_like];
1412 elemdrawing[O_V_EXPANDING_WALL]=elemdrawing[cave->expanding_wall_looks_like];
1413 elemdrawing[O_H_EXPANDING_WALL]=elemdrawing[cave->expanding_wall_looks_like];
1414 elemdrawing[O_AMOEBA_2]=elemdrawing[cave->amoeba_2_looks_like];
1416 /* change only graphically */
1417 if (hate_invisible_outbox) {
1418 elemdrawing[O_PRE_INVIS_OUTBOX]=elemdrawing[O_PRE_OUTBOX];
1419 elemdrawing[O_INVIS_OUTBOX]=elemdrawing[O_OUTBOX];
1422 for (y=cave->y1; y<=cave->y2; y++) {
1423 for (x=cave->x1; x<=cave->x2; x++) {
1424 GdElement actual=cave->map[y][x];
1426 /* if covered, real element is not important */
1427 if (actual & COVERED)
1428 draw=gd_elements[O_COVERED].image_game;
1429 else
1430 draw=elemdrawing[actual];
1432 /* if negative, animated. */
1433 if (draw<0)
1434 draw=-draw+animcycle;
1435 /* flash */
1436 if (cave->gate_open_flash || yellowish_color)
1437 draw+=NUM_OF_CELLS;
1439 /* set to buffer, with caching */
1440 if (gfx_buffer[y][x]!=draw)
1441 gfx_buffer[y][x]=draw | GD_REDRAW;
1450 /* cave time is rounded _UP_ to seconds. so at the exact moment when it changes from
1451 2sec remaining to 1sec remaining, the player has exactly one second. when it changes
1452 to zero, it is the exact moment of timeout. */
1453 /* internal time is milliseconds (or 1200 milliseconds for pal timing). */
1455 gd_cave_time_show(const GdCave *cave, int internal_time)
1457 return (internal_time+cave->timing_factor-1)/cave->timing_factor;
1469 GdReplay *
1470 gd_replay_new()
1472 GdReplay *rep;
1474 rep=g_new0(GdReplay, 1);
1475 rep->movements=g_byte_array_new();
1476 return rep;
1479 GdReplay *
1480 gd_replay_new_from_replay(GdReplay *orig)
1482 GdReplay *rep;
1484 rep=g_memdup(orig, sizeof(GdReplay));
1485 rep->movements=g_byte_array_new();
1486 g_byte_array_append(rep->movements, orig->movements->data, orig->movements->len);
1488 return rep;
1491 void
1492 gd_replay_free(GdReplay *replay)
1494 g_byte_array_free(replay->movements, TRUE);
1495 g_free(replay);
1498 enum {
1499 REPLAY_MOVE_MASK=0x0f,
1500 REPLAY_FIRE_MASK=0x10,
1501 REPLAY_SUICIDE_MASK=0x20,
1504 /* store movement in a replay */
1505 void
1506 gd_replay_store_movement(GdReplay *replay, GdDirection player_move, gboolean player_fire, gboolean suicide)
1508 guint8 data[1];
1510 g_assert(player_move==(player_move & REPLAY_MOVE_MASK));
1511 data[0]=(player_move)|(player_fire?REPLAY_FIRE_MASK:0)|(suicide?REPLAY_SUICIDE_MASK:0);
1513 g_byte_array_append(replay->movements, data, 1);
1516 /* get next available movement from a replay; store variables to player_move, player_fire, suicide */
1517 /* return true if successful */
1518 gboolean
1519 gd_replay_get_next_movement(GdReplay *replay, GdDirection *player_move, gboolean *player_fire, gboolean *suicide)
1521 guint8 data;
1523 /* if no more available movements */
1524 if (replay->current_playing_pos>=replay->movements->len)
1525 return FALSE;
1527 data=replay->movements->data[replay->current_playing_pos++];
1528 *suicide=(data&REPLAY_SUICIDE_MASK)!=0;
1529 *player_fire=(data&REPLAY_FIRE_MASK)!=0;
1530 *player_move=(data&REPLAY_MOVE_MASK);
1532 return TRUE;
1535 void gd_replay_rewind(GdReplay *replay)
1537 replay->current_playing_pos=0;
1541 #define REPLAY_BDCFF_UP "u"
1542 #define REPLAY_BDCFF_UP_RIGHT "ur"
1543 #define REPLAY_BDCFF_RIGHT "r"
1544 #define REPLAY_BDCFF_DOWN_RIGHT "dr"
1545 #define REPLAY_BDCFF_DOWN "d"
1546 #define REPLAY_BDCFF_DOWN_LEFT "dl"
1547 #define REPLAY_BDCFF_LEFT "l"
1548 #define REPLAY_BDCFF_UP_LEFT "ul"
1549 /* when not moving */
1550 #define REPLAY_BDCFF_STILL "."
1551 /* when the fire is pressed */
1552 #define REPLAY_BDCFF_FIRE "F"
1553 #define REPLAY_BDCFF_SUICIDE "k"
1555 static char *
1556 direction_to_bdcff(GdDirection mov)
1558 switch (mov) {
1559 /* not moving */
1560 case MV_STILL: return REPLAY_BDCFF_STILL;
1561 /* directions */
1562 case MV_UP: return REPLAY_BDCFF_UP;
1563 case MV_UP_RIGHT: return REPLAY_BDCFF_UP_RIGHT;
1564 case MV_RIGHT: return REPLAY_BDCFF_RIGHT;
1565 case MV_DOWN_RIGHT: return REPLAY_BDCFF_DOWN_RIGHT;
1566 case MV_DOWN: return REPLAY_BDCFF_DOWN;
1567 case MV_DOWN_LEFT: return REPLAY_BDCFF_DOWN_LEFT;
1568 case MV_LEFT: return REPLAY_BDCFF_LEFT;
1569 case MV_UP_LEFT: return REPLAY_BDCFF_UP_LEFT;
1570 default:
1571 g_assert_not_reached(); /* programmer error */
1572 return REPLAY_BDCFF_STILL;
1576 /* same as above; pressing fire will be a capital letter. */
1577 static char *
1578 direction_fire_to_bdcff(GdDirection dir, gboolean fire)
1580 static char mov[10];
1582 strcpy(mov, direction_to_bdcff(dir));
1583 if (fire) {
1584 int i;
1586 for (i=0; mov[i]!=0; i++)
1587 mov[i]=g_ascii_toupper(mov[i]);
1590 return mov;
1593 char *
1594 gd_replay_movements_to_bdcff(GdReplay *replay)
1596 int pos;
1597 GString *str;
1599 str=g_string_new(NULL);
1601 for (pos=0; pos<replay->movements->len; pos++) {
1602 int num=1;
1603 guint8 data;
1605 /* if this is not the first movement, append a space. */
1606 if (str->len!=0)
1607 g_string_append_c(str, ' ');
1609 /* if same byte appears, count number of occurrences - something like an rle compression. */
1610 /* be sure not to cross the array boundaries */
1611 while (pos<replay->movements->len-1 && replay->movements->data[pos]==replay->movements->data[pos+1]) {
1612 pos++;
1613 num++;
1615 data=replay->movements->data[pos];
1616 if (data & REPLAY_SUICIDE_MASK)
1617 g_string_append(str, REPLAY_BDCFF_SUICIDE);
1618 g_string_append(str, direction_fire_to_bdcff(data & REPLAY_MOVE_MASK, data & REPLAY_FIRE_MASK));
1619 if (num!=1)
1620 g_string_append_printf(str, "%d", num);
1623 return g_string_free(str, FALSE);
1627 /* calculate adler checksum for a rendered cave; this can be used for more caves. */
1628 void
1629 gd_cave_adler_checksum_more(GdCave *cave, guint32 *a, guint32 *b)
1631 int x, y;
1633 g_assert(cave->rendered);
1635 for (y=0; y<cave->h; y++)
1636 for (x=0; x<cave->w; x++) {
1637 *a+=gd_elements[cave->map[y][x]].character;
1638 *b+=*a;
1640 *a %= 65521;
1641 *b %= 65521;
1645 /* calculate adler checksum for a single rendered cave. */
1646 guint32
1647 gd_cave_adler_checksum(GdCave *cave)
1649 guint32 a=1;
1650 guint32 b=0;
1652 gd_cave_adler_checksum_more(cave, &a, &b);
1653 return (b<<16)+a;