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.
17 #include <glib/gi18n.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 */
51 gd_direction_get_visible_name(GdDirection dir
)
53 g_assert(dir
>=0 && dir
<G_N_ELEMENTS(direction_name
));
54 return direction_name
[dir
];
58 gd_direction_get_filename(GdDirection dir
)
60 g_assert(dir
>=0 && dir
<G_N_ELEMENTS(direction_filename
));
61 return direction_filename
[dir
];
65 gd_direction_from_string(const char *str
)
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
);
81 /* scheduling name to string and vice versa */
83 gd_scheduling_get_filename(GdScheduling sched
)
85 g_assert(sched
>=0 && sched
<G_N_ELEMENTS(scheduling_filename
));
86 return scheduling_filename
[sched
];
90 gd_scheduling_get_visible_name(GdScheduling sched
)
92 g_assert(sched
>=0 && sched
<G_N_ELEMENTS(scheduling_name
));
93 return scheduling_name
[sched
];
97 gd_scheduling_from_string(const char *str
)
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
;
123 fill a given struct with default properties.
124 "str" is the struct (data),
125 "properties" describes the structure and its pointers,
126 "defaults" are the pieces of data which will be copied to str.
129 gd_struct_set_defaults_from_array(gpointer str
, const GdStructDescriptor
*properties
, GdPropertyDefault
*defaults
)
133 for (i
=0; defaults
[i
].offset
!=-1; i
++) {
134 gpointer pvalue
=G_STRUCT_MEMBER_P(str
, defaults
[i
].offset
);
135 int *ivalue
=pvalue
; /* these point to the same, but to avoid the awkward cast syntax */
136 GdElement
*evalue
=pvalue
;
137 GdDirection
*dvalue
=pvalue
;
138 GdScheduling
*svalue
=pvalue
;
139 gboolean
*bvalue
=pvalue
;
140 GdColor
*cvalue
=pvalue
;
143 /* check which property we are talking about: find it in gd_cave_properties. */
144 n
=defaults
[i
].property_index
;
146 while(properties
[n
].identifier
!=NULL
&& properties
[n
].offset
!=defaults
[i
].offset
)
148 /* make sure we found it. */
149 g_assert(properties
[n
].identifier
!=NULL
);
151 /* remember so we will be fast later*/
152 defaults
[i
].property_index
=n
;
155 /* some properties are arrays. this loop fills all with the same values */
156 for (j
=0; j
<properties
[n
].count
; j
++)
157 switch (properties
[n
].type
) {
158 /* these are for the gui; do nothing */
161 /* no default value for strings */
163 case GD_TYPE_LONGSTRING
:
166 case GD_TYPE_RATIO
: /* this is also an integer, difference is only when saving to bdcff */
168 if (defaults
[i
].defval
<properties
[n
].min
|| defaults
[i
].defval
>properties
[n
].max
)
169 g_warning("integer property %s out of range", properties
[n
].identifier
);
170 ivalue
[j
]=defaults
[i
].defval
;
172 case GD_TYPE_PROBABILITY
: /* floats are stored as integer, /million; but are integers */
173 if (defaults
[i
].defval
<0 || defaults
[i
].defval
>1000000)
174 g_warning("integer property %s out of range", properties
[n
].identifier
);
175 ivalue
[j
]=defaults
[i
].defval
;
177 case GD_TYPE_BOOLEAN
:
178 bvalue
[j
]=defaults
[i
].defval
!=0;
180 case GD_TYPE_ELEMENT
:
182 evalue
[j
]=(GdElement
) defaults
[i
].defval
;
185 cvalue
[j
]=gd_c64_color(defaults
[i
].defval
);
187 case GD_TYPE_DIRECTION
:
188 dvalue
[j
]=(GdDirection
) defaults
[i
].defval
;
190 case GD_TYPE_SCHEDULING
:
191 svalue
[j
]=(GdScheduling
) defaults
[i
].defval
;
204 /* creates the character->element conversion table; using
205 the fixed-in-the-bdcff characters. later, this table
206 may be filled with more elements.
209 gd_create_char_to_element_table()
213 /* fill all with unknown */
214 for (i
=0; i
<G_N_ELEMENTS(gd_char_to_element
); i
++)
215 gd_char_to_element
[i
]=O_UNKNOWN
;
217 /* then set fixed characters */
218 for (i
=0; i
<O_MAX
; i
++) {
219 int c
=gd_elements
[i
].character
;
222 if (gd_char_to_element
[c
]!=O_UNKNOWN
)
223 g_warning("Character %c already used for element %x", c
, gd_char_to_element
[c
]);
224 gd_char_to_element
[c
]=i
;
230 /* search the element database for the specified character, and return the element. */
232 gd_get_element_from_character (guint8 character
)
234 if (gd_char_to_element
[character
]!=O_UNKNOWN
)
235 return gd_char_to_element
[character
];
237 g_warning ("Invalid character representing element: %c", character
);
245 do some init; this function is to be called at the start of the application
252 g_assert(MV_MAX
==G_N_ELEMENTS(gd_dx
));
253 g_assert(MV_MAX
==G_N_ELEMENTS(gd_dy
));
254 g_assert(GD_SCHEDULING_MAX
==G_N_ELEMENTS(scheduling_filename
));
255 g_assert(GD_SCHEDULING_MAX
==G_N_ELEMENTS(scheduling_name
));
257 /* put names to a hash table */
258 /* this is a helper for file read operations */
259 /* maps g_strdupped strings to elemenets (integers) */
260 name_to_element
=g_hash_table_new_full(gd_str_case_hash
, gd_str_case_equal
, g_free
, NULL
);
262 for (i
=0; i
<O_MAX
; i
++) {
264 g_assert(gd_elements
[i
].filename
!=NULL
&& !g_str_equal(gd_elements
[i
].filename
, ""));
266 key
=g_ascii_strup(gd_elements
[i
].filename
, -1);
267 if (g_hash_table_lookup_extended(name_to_element
, key
, NULL
, NULL
))
268 g_warning("Name %s already used for element %x", key
, i
);
269 g_hash_table_insert(name_to_element
, key
, GINT_TO_POINTER(i
));
270 /* ^^^ do not free "key", as hash table needs it during the whole time! */
272 key
=g_strdup_printf("SCANNED_%s", key
); /* new string */
273 g_hash_table_insert(name_to_element
, key
, GINT_TO_POINTER(i
));
274 /* once again, do not free "key" ^^^ */
276 /* for compatibility with tim stridmann's memorydump->bdcff converter... .... ... */
277 g_hash_table_insert(name_to_element
, "HEXPANDING_WALL", GINT_TO_POINTER(O_H_EXPANDING_WALL
));
278 g_hash_table_insert(name_to_element
, "FALLING_DIAMOND", GINT_TO_POINTER(O_DIAMOND_F
));
279 g_hash_table_insert(name_to_element
, "FALLING_BOULDER", GINT_TO_POINTER(O_STONE_F
));
280 g_hash_table_insert(name_to_element
, "EXPLOSION1S", GINT_TO_POINTER(O_EXPLODE_1
));
281 g_hash_table_insert(name_to_element
, "EXPLOSION2S", GINT_TO_POINTER(O_EXPLODE_2
));
282 g_hash_table_insert(name_to_element
, "EXPLOSION3S", GINT_TO_POINTER(O_EXPLODE_3
));
283 g_hash_table_insert(name_to_element
, "EXPLOSION4S", GINT_TO_POINTER(O_EXPLODE_4
));
284 g_hash_table_insert(name_to_element
, "EXPLOSION5S", GINT_TO_POINTER(O_EXPLODE_5
));
285 g_hash_table_insert(name_to_element
, "EXPLOSION1D", GINT_TO_POINTER(O_PRE_DIA_1
));
286 g_hash_table_insert(name_to_element
, "EXPLOSION2D", GINT_TO_POINTER(O_PRE_DIA_2
));
287 g_hash_table_insert(name_to_element
, "EXPLOSION3D", GINT_TO_POINTER(O_PRE_DIA_3
));
288 g_hash_table_insert(name_to_element
, "EXPLOSION4D", GINT_TO_POINTER(O_PRE_DIA_4
));
289 g_hash_table_insert(name_to_element
, "EXPLOSION5D", GINT_TO_POINTER(O_PRE_DIA_5
));
290 g_hash_table_insert(name_to_element
, "WALL2", GINT_TO_POINTER(O_STEEL_EXPLODABLE
));
291 /* compatibility with old bd-faq (pre disassembly of bladder) */
292 g_hash_table_insert(name_to_element
, "BLADDERd9", GINT_TO_POINTER(O_BLADDER_8
));
294 /* create table to show errors at the start of the application */
295 gd_create_char_to_element_table();
299 /* search the element database for the specified name, and return the element */
301 gd_get_element_from_string (const char *string
)
303 char *upper
=g_ascii_strup(string
, -1);
308 g_warning("Invalid string representing element: (null)");
312 found
=g_hash_table_lookup_extended(name_to_element
, upper
, NULL
, &value
);
315 return (GdElement
) (GPOINTER_TO_INT(value
));
317 g_warning("Invalid string representing element: %s", string
);
329 gd_cave_set_defaults_from_array(GdCave
* cave
, GdPropertyDefault
*defaults
)
331 gd_struct_set_defaults_from_array(cave
, gd_cave_properties
, defaults
);
336 load default values from description array
337 these are default for gdash and bdcff.
340 gd_cave_set_gdash_defaults(GdCave
* cave
)
344 gd_cave_set_defaults_from_array(cave
, gd_cave_defaults_gdash
);
346 /* these did not fit into the descriptor array */
347 for (i
=0; i
<5; i
++) {
348 cave
->level_rand
[i
]=i
;
349 cave
->level_timevalue
[i
]=i
+1;
358 /* for quicksort. compares two highscores. */
360 gd_highscore_compare(gconstpointer a
, gconstpointer b
)
362 const GdHighScore
*ha
=a
;
363 const GdHighScore
*hb
=b
;
364 return hb
->score
- ha
->score
;
368 gd_clear_highscore(GdHighScore
*hs
)
372 for (i
=0; i
<GD_HIGHSCORE_NUM
; i
++) {
373 strcpy(hs
[i
].name
, "");
379 gd_has_highscore(GdHighScore
*hs
)
381 return hs
[0].score
>0;
385 gd_cave_clear_highscore(GdCave
*cave
)
387 gd_clear_highscore(cave
->highscore
);
391 /* return true if score achieved is a highscore */
392 gboolean
gd_is_highscore(GdHighScore
*scores
, int score
)
394 /* if score is above zero AND bigger than the last one */
395 if (score
>0 && score
>scores
[GD_HIGHSCORE_NUM
-1].score
)
402 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
;
413 qsort(highscores
, GD_HIGHSCORE_NUM
, sizeof(GdHighScore
), gd_highscore_compare
);
415 for (i
=0; i
<GD_HIGHSCORE_NUM
; i
++)
416 if (g_str_equal(highscores
[i
].name
, name
) && highscores
[i
].score
==score
)
419 g_assert_not_reached();
427 /* for the case-insensitive hash keys */
429 gd_str_case_equal(gconstpointer s1
, gconstpointer s2
)
431 return g_ascii_strcasecmp(s1
, s2
)==0;
435 gd_str_case_hash(gconstpointer v
)
440 upper
=g_ascii_strup(v
, -1);
459 create new cave with default values.
460 sets every value, also default size, diamond value etc.
468 cave
=g_new0(GdCave
, 1);
470 /* hash table which stores unknown tags as strings. */
471 cave
->tags
=g_hash_table_new_full(gd_str_case_hash
, gd_str_case_equal
, g_free
, g_free
);
473 for (i
=0; gd_cave_properties
[i
].identifier
!=NULL
; i
++)
474 if (gd_cave_properties
[i
].type
==GD_TYPE_LONGSTRING
)
475 G_STRUCT_MEMBER(GString
*, cave
, gd_cave_properties
[i
].offset
)=g_string_new(NULL
);
478 gd_cave_set_gdash_defaults (cave
);
484 cave maps are continuous areas in memory. the allocated memory
485 is width*height*bytes_per_cell long.
486 the cave map[0] stores the pointer given by g_malloc().
487 the map itself is also an allocated array of pointers to the
490 rows=new (pointers to rows);
492 rows[1..h-1]=rows[0]+width*bytes
500 allocate a cave map-like array, and initialize to zero.
501 one cell is cell_size bytes long.
504 gd_cave_map_new_for_cave(const GdCave
*cave
, const int cell_size
)
506 gpointer
*rows
; /* this is void**, pointer to array of ... */
509 rows
=g_new(gpointer
, cave
->h
);
510 rows
[0]=g_malloc0 (cell_size
*cave
->w
*cave
->h
);
511 for (y
=1; y
<cave
->h
; y
++)
512 /* base pointer+num_of_bytes_per_element*width*number_of_row; as sizeof(char)=1 */
513 rows
[y
]=(char *)rows
[0]+cell_size
*cave
->w
*y
;
520 if map is null, this also returns null.
523 gd_cave_map_dup_size(const GdCave
*cave
, const gpointer map
, const int cell_size
)
526 gpointer
*maplines
=(gpointer
*)map
;
532 rows
=g_new (gpointer
, cave
->h
);
533 rows
[0]=g_memdup (maplines
[0], cell_size
* cave
->w
* cave
->h
);
535 for (y
=1; y
< cave
->h
; y
++)
536 rows
[y
]=(char *)rows
[0]+cell_size
*cave
->w
*y
;
542 gd_cave_map_free(gpointer map
)
544 gpointer
*maplines
=(gpointer
*) map
;
560 frees memory associated to cave
563 gd_cave_free(GdCave
*cave
)
571 g_hash_table_destroy(cave
->tags
);
573 if (cave
->random
) /* random generator is a GRand * */
574 g_rand_free(cave
->random
);
577 for (i
=0; gd_cave_properties
[i
].identifier
!=NULL
; i
++)
578 if (gd_cave_properties
[i
].type
==GD_TYPE_LONGSTRING
)
579 g_string_free(G_STRUCT_MEMBER(GString
*, cave
, gd_cave_properties
[i
].offset
), TRUE
);
582 gd_cave_map_free(cave
->map
);
584 gd_cave_map_free(cave
->objects_order
);
585 /* hammered walls to reappear data */
586 gd_cave_map_free(cave
->hammered_reappear
);
588 g_list_foreach(cave
->objects
, (GFunc
) g_free
, NULL
);
589 g_list_free (cave
->objects
);
591 g_list_foreach(cave
->replays
, (GFunc
) gd_replay_free
, NULL
);
592 g_list_free(cave
->replays
);
594 /* freeing main pointer */
599 hash_copy_foreach(const char *key
, const char *value
, GHashTable
*dest
)
601 g_hash_table_insert(dest
, g_strdup(key
), g_strdup(value
));
604 /* copy cave from src to destination, with duplicating dynamically allocated data */
606 gd_cave_copy(GdCave
*dest
, const GdCave
*src
)
610 /* copy entire data */
611 g_memmove(dest
, src
, sizeof(GdCave
));
613 /* but duplicate dynamic data */
614 dest
->tags
=g_hash_table_new_full(gd_str_case_hash
, gd_str_case_equal
, g_free
, g_free
);
616 g_hash_table_foreach(src
->tags
, (GHFunc
) hash_copy_foreach
, dest
->tags
);
617 dest
->map
=gd_cave_map_dup(src
, map
);
618 dest
->hammered_reappear
=gd_cave_map_dup(src
, hammered_reappear
);
620 /* for longstrings */
621 for (i
=0; gd_cave_properties
[i
].identifier
!=NULL
; i
++)
622 if (gd_cave_properties
[i
].type
==GD_TYPE_LONGSTRING
)
623 G_STRUCT_MEMBER(GString
*, dest
, gd_cave_properties
[i
].offset
)=g_string_new(G_STRUCT_MEMBER(GString
*, src
, gd_cave_properties
[i
].offset
)->str
);
625 /* no reason to copy this */
626 dest
->objects_order
=NULL
;
628 /* copy objects list */
632 dest
->objects
=NULL
; /* new empty list */
633 for (iter
=src
->objects
; iter
!=NULL
; iter
=iter
->next
) /* do a deep copy */
634 dest
->objects
=g_list_append(dest
->objects
, g_memdup (iter
->data
, sizeof (GdObject
)));
642 for (iter
=src
->replays
; iter
!=NULL
; iter
=iter
->next
) /* do a deep copy */
643 dest
->replays
=g_list_append(dest
->replays
, gd_replay_new_from_replay(iter
->data
));
646 /* copy random number generator */
648 dest
->random
=g_rand_copy(src
->random
);
651 /* create new cave, which is a copy of the cave given. */
653 gd_cave_new_from_cave(const GdCave
*orig
)
658 gd_cave_copy(cave
, orig
);
665 Put an object to the specified position.
666 Performs range checking.
667 If wraparound objects are selected, wraps around x coordinates, with or without lineshift.
668 (The y coordinate is not wrapped, as it did not work like that on the c64)
669 order is a pointer to the GdObject describing this object. Thus the editor can identify which cell was created by which object.
672 gd_cave_store_rc(GdCave
*cave
, int x
, int y
, const GdElement element
, const void* order
)
674 /* if we do not need to draw, exit now */
679 if (cave
->wraparound_objects
) {
680 if (cave
->lineshift
) {
681 /* fit x coordinate within range, with correcting y at the same time */
683 x
+=cave
->w
; /* out of bounds on the left... */
684 y
--; /* previous row */
690 /* lineshifting does not fix the y coordinates. if out of bounds, element will not be displayed. */
691 /* if such an object appeared in the c64 game, well, it was a buffer overrun. */
693 /* non lineshifting: changing x does not change y coordinate. */
698 /* after that, fix y coordinate */
706 /* if the above wraparound code fixed the coordinates, this will always be true. */
707 /* but see the above comment for lineshifting y coordinate */
708 if (x
>=0 && x
<cave
->w
&& y
>=0 && y
<cave
->h
) {
709 cave
->map
[y
][x
]=element
;
710 cave
->objects_order
[y
][x
]=(void *)order
;
715 gd_cave_get_rc(const GdCave
*cave
, int x
, int y
)
717 /* always fix coordinates as if cave was wraparound. */
719 /* fix x coordinate */
720 if (cave
->lineshift
) {
721 /* fit x coordinate within range, with correcting y at the same time */
723 x
+=cave
->w
; /* out of bounds on the left... */
724 y
--; /* previous row */
731 /* non lineshifting: changing x does not change y coordinate. */
737 /* after that, fix y coordinate */
743 return cave
->map
[y
][x
];
754 gd_c64_random(GdC64RandomGenerator
*rand
)
756 unsigned int temp_rand_1
, temp_rand_2
, carry
, result
;
758 temp_rand_1
=(rand
->rand_seed_1
&0x0001) << 7;
759 temp_rand_2
=(rand
->rand_seed_2
>> 1)&0x007F;
760 result
=(rand
->rand_seed_2
)+((rand
->rand_seed_2
&0x0001) << 7);
762 result
=result
&0x00FF;
763 result
=result
+carry
+0x13;
765 rand
->rand_seed_2
=result
&0x00FF;
766 result
=rand
->rand_seed_1
+carry
+temp_rand_1
;
768 result
=result
&0x00FF;
769 result
=result
+carry
+temp_rand_2
;
770 rand
->rand_seed_1
=result
&0x00FF;
772 return rand
->rand_seed_1
;
777 C64 BD predictable random number generator.
778 Used to load the original caves imported from c64 files.
779 Also by the predictable slime.
782 gd_cave_c64_random(GdCave
*cave
)
784 return gd_c64_random(&cave
->c64_rand
);
788 gd_c64_random_set_seed(GdC64RandomGenerator
*rand
, int seed1
, int seed2
)
790 rand
->rand_seed_1
=seed1
;
791 rand
->rand_seed_2
=seed2
;
795 gd_cave_c64_random_set_seed(GdCave
*cave
, int seed1
, int seed2
)
797 gd_c64_random_set_seed(&cave
->c64_rand
, seed1
, seed2
);
809 select random colors for a given cave.
810 this function will select colors so that they should look somewhat nice; for example
811 brick walls won't be the darkest color, for example.
814 swap(int *i1
, int *i2
)
822 gd_cave_set_random_c64_colors(GdCave
*cave
)
824 const int bright_colors
[]={1, 3, 7};
825 const int dark_colors
[]={2, 6, 8, 9, 11};
828 cave
->colorb
=gd_c64_color(0);
829 cave
->color0
=gd_c64_color(0);
830 /* choose some bright color for brick */
831 cave
->color3
=gd_c64_color(bright_colors
[g_random_int_range(0, G_N_ELEMENTS(bright_colors
))]);
832 /* choose a dark color for dirt, but should not be == color of brick */
834 cave
->color1
=gd_c64_color(dark_colors
[g_random_int_range(0, G_N_ELEMENTS(dark_colors
))]);
835 } while (cave
->color1
==cave
->color3
); /* so it is not the same as color 1 */
836 /* choose any but black for steel wall, but should not be == brick or dirt */
838 /* between 1 and 15 - do not use black for this. */
839 cave
->color2
=gd_c64_color(g_random_int_range(1, 16));
840 } while (cave
->color1
==cave
->color2
|| cave
->color2
==cave
->color3
); /* so colors are not the same */
841 /* copy amoeba and slime color */
842 cave
->color4
=cave
->color3
;
843 cave
->color5
=cave
->color1
;
847 cave_set_random_indexed_colors(GdCave
*cave
, GdColor (*color_indexer_func
) (int, int))
849 int hue
=g_random_int_range(0, 15);
850 int hue_spread
=g_random_int_range(1, 6); /* 1..5 */
851 /* we only use 0..6, as saturation 15 is too bright (almost always white) */
852 /* also, saturation 0..1..2 is too dark. the color0=black is there for dark. */
853 int bri_spread
=6-hue_spread
; /* so this is also 1..5. when hue spread is low, brightness spread is high */
854 int bri1
=8, bri2
=8-bri_spread
, bri3
=8+bri_spread
;
855 /* there are 15 valid choices for hue, so we do a %15 */
856 int col1
=hue
, col2
=(hue
+hue_spread
+15)%15, col3
=(hue
-hue_spread
+15)%15;
858 /* this makes up a random color, and selects a color triad by hue+5 and hue+10. */
859 /* also creates a random saturation. */
860 /* color of brick is 8+sat, so it is always a bright color. */
861 /* another two are 8-sat and 8. */
862 /* order of colors is also changed randomly. */
864 if (g_random_boolean())
866 /* we do not touch bri3 (8+sat), as it should be a bright color */
867 if (g_random_boolean())
869 if (g_random_boolean())
871 if (g_random_boolean())
874 cave
->colorb
=color_indexer_func(0, 0);
875 cave
->color0
=color_indexer_func(0, 0);
876 cave
->color1
=color_indexer_func(col1
+1, bri1
);
877 cave
->color2
=color_indexer_func(col2
+1, bri2
);
878 cave
->color3
=color_indexer_func(col3
+1, bri3
);
879 /* amoeba and slime are different */
880 cave
->color4
=color_indexer_func(g_random_int_range(11, 13), g_random_int_range(6, 12)); /* some green thing */
881 cave
->color5
=color_indexer_func(g_random_int_range(7, 10), g_random_int_range(0, 6)); /* some blueish thing */
885 gd_cave_set_random_atari_colors(GdCave
*cave
)
887 cave_set_random_indexed_colors(cave
, gd_atari_color_huesat
);
891 gd_cave_set_random_c64dtv_colors(GdCave
*cave
)
893 cave_set_random_indexed_colors(cave
, gd_c64dtv_color_huesat
);
897 swapd(double *i1
, double *i2
)
905 gd_cave_set_random_rgb_colors(GdCave
*cave
)
907 const double hue_max
=10.0/30.0;
908 double hue
=g_random_double(); /* any hue allowed */
909 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) */
910 double h1
=hue
, h2
=hue
+hue_spread
, h3
=hue
+2*hue_spread
;
914 if (g_random_boolean()) {
915 /* when hue spread is low, brightness(saturation) spread is high */
916 /* 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 */
917 double spread
=0.1+0.3*(1-hue_spread
/hue_max
);
918 v1
=0.6; /* brightness variation, too */
921 s1
=0.5; /* saturation is different */
925 /* when hue spread is low, brightness(saturation) spread is high */
926 /* 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 */
927 double spread
=0.1+0.15*(1-hue_spread
/hue_max
);
928 v1
=0.5; /* brightness is different */
931 s1
=0.7; /* saturation is same - a not fully saturated one */
935 /* randomly change values, but do not touch v3, as cave->color3 should be a bright color */
936 if (g_random_boolean()) swapd(&v1
, &v2
);
937 /* randomly change hues and saturations */
938 if (g_random_boolean()) swapd(&h1
, &h2
);
939 if (g_random_boolean()) swapd(&h2
, &h3
);
940 if (g_random_boolean()) swapd(&h1
, &h3
);
941 if (g_random_boolean()) swapd(&s1
, &s2
);
942 if (g_random_boolean()) swapd(&s2
, &s3
);
943 if (g_random_boolean()) swapd(&s1
, &s3
);
949 cave
->colorb
=gd_color_get_from_hsv(0,0,0);
950 cave
->color0
=gd_color_get_from_hsv(0,0,0); /* black for background */
951 cave
->color1
=gd_color_get_from_hsv(h1
,s1
,v1
); /* dirt */
952 cave
->color2
=gd_color_get_from_hsv(h2
,s2
,v2
); /* steel */
953 cave
->color3
=gd_color_get_from_hsv(h3
,s3
,v3
); /* brick */
954 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 */
955 cave
->color5
=gd_color_get_from_hsv(g_random_int_range(220, 260),s1
,v1
); /* blue(240+-20) with saturation and brightness of dirt */
960 gd_cave_set_random_colors(GdCave
*cave
, GdColorType type
)
963 case GD_COLOR_TYPE_RGB
:
964 gd_cave_set_random_rgb_colors(cave
);
966 case GD_COLOR_TYPE_C64
:
967 gd_cave_set_random_c64_colors(cave
);
969 case GD_COLOR_TYPE_C64DTV
:
970 gd_cave_set_random_c64dtv_colors(cave
);
972 case GD_COLOR_TYPE_ATARI
:
973 gd_cave_set_random_atari_colors(cave
);
977 g_assert_not_reached();
985 if last line or last row is just steel wall (or (invisible) outbox).
986 used after loading a game for playing.
987 after this, ew and eh will contain the effective width and height.
990 gd_cave_auto_shrink(GdCave
*cave
)
1000 /* set to maximum size, then try to shrink */
1001 cave
->x1
=0; cave
->y1
=0;
1002 cave
->x2
=cave
->w
-1; cave
->y2
=cave
->h
-1;
1004 /* search for empty, steel-wall-only last rows. */
1005 /* clear all lines, which are only steel wall.
1006 * and clear only one line, which is steel wall, but also has a player or an outbox. */
1009 for (y
=cave
->y2
-1; y
<= cave
->y2
; y
++)
1010 for (x
=cave
->x1
; x
<= cave
->x2
; x
++)
1011 switch (gd_cave_get_rc (cave
, x
, y
)) {
1012 case O_STEEL
: /* if steels only, this is to be deleted. */
1015 case O_PRE_INVIS_OUTBOX
:
1017 if (empty
==STEEL_OR_OTHER
)
1019 if (empty
==STEEL_ONLY
) /* if this, delete only this one, and exit. */
1020 empty
=STEEL_OR_OTHER
;
1022 default: /* anything else, that should be left in the cave. */
1026 if (empty
!=NO_SHRINK
) /* shrink if full steel or steel and player/outbox. */
1027 cave
->y2
--; /* one row shorter */
1029 while (empty
==STEEL_ONLY
); /* if found just steels, repeat. */
1031 /* search for empty, steel-wall-only first rows. */
1034 for (y
=cave
->y1
; y
<= cave
->y1
+1; y
++)
1035 for (x
=cave
->x1
; x
<= cave
->x2
; x
++)
1036 switch (gd_cave_get_rc (cave
, x
, y
)) {
1040 case O_PRE_INVIS_OUTBOX
:
1042 /* shrink only lines, which have only ONE player or outbox. this is for bd4 intermission 2, for example. */
1043 if (empty
==STEEL_OR_OTHER
)
1045 if (empty
==STEEL_ONLY
)
1046 empty
=STEEL_OR_OTHER
;
1052 if (empty
!=NO_SHRINK
)
1055 while (empty
==STEEL_ONLY
); /* if found one, repeat. */
1057 /* empty last columns. */
1060 for (y
=cave
->y1
; y
<= cave
->y2
; y
++)
1061 for (x
=cave
->x2
-1; x
<= cave
->x2
; x
++)
1062 switch (gd_cave_get_rc (cave
, x
, y
)) {
1066 case O_PRE_INVIS_OUTBOX
:
1068 if (empty
==STEEL_OR_OTHER
)
1070 if (empty
==STEEL_ONLY
)
1071 empty
=STEEL_OR_OTHER
;
1077 if (empty
!=NO_SHRINK
)
1078 cave
->x2
--; /* just remember that one column shorter. g_free will know the size of memchunk, no need to realloc! */
1080 while (empty
==STEEL_ONLY
); /* if found one, repeat. */
1082 /* empty first columns. */
1085 for (y
=cave
->y1
; y
<= cave
->y2
; y
++)
1086 for (x
=cave
->x1
; x
<= cave
->x1
+1; x
++)
1087 switch (gd_cave_get_rc (cave
, x
, y
)) {
1091 case O_PRE_INVIS_OUTBOX
:
1093 if (empty
==STEEL_OR_OTHER
)
1095 if (empty
==STEEL_ONLY
)
1096 empty
=STEEL_OR_OTHER
;
1102 if (empty
!=NO_SHRINK
)
1105 while (empty
==STEEL_ONLY
); /* if found one, repeat. */
1108 /* check if cave visible part coordinates
1109 are outside cave sizes, or not in the right order.
1110 correct them if needed.
1113 gd_cave_correct_visible_size(GdCave
*cave
)
1115 g_assert(cave
!=NULL
);
1117 /* change visible coordinates if they do not point to upperleft and lowerright */
1118 if (cave
->x2
<cave
->x1
) {
1123 if (cave
->y2
<cave
->y1
) {
1132 if (cave
->x2
>cave
->w
-1)
1134 if (cave
->y2
>cave
->h
-1)
1150 bd1 and similar engines had animation bits in cave data, to set which elements to animate (firefly, butterfly, amoeba).
1151 animating an element also caused some delay each frame; according to my measurements, around 2.6 ms/element.
1154 cave_set_ckdelay_extra_for_animation(GdCave
*cave
)
1157 gboolean has_amoeba
=FALSE
, has_firefly
=FALSE
, has_butterfly
=FALSE
, has_slime
=FALSE
;
1158 g_assert(cave
->map
!=NULL
);
1160 for (y
=0; y
<cave
->h
; y
++)
1161 for (x
=0; x
<cave
->w
; x
++) {
1162 switch (cave
->map
[y
][x
]&~SCANNED
) {
1183 cave
->ckdelay_extra_for_animation
=0;
1185 cave
->ckdelay_extra_for_animation
+=2600;
1187 cave
->ckdelay_extra_for_animation
+=2600;
1189 cave
->ckdelay_extra_for_animation
+=2600;
1191 cave
->ckdelay_extra_for_animation
+=2600;
1195 /* do some init - setup some cave variables before the game. */
1197 gd_cave_setup_for_game(GdCave
*cave
)
1201 cave_set_ckdelay_extra_for_animation(cave
);
1203 /* find the player which will be the one to scroll to at the beginning of the game (before the player's birth) */
1204 if (cave
->active_is_first_found
) {
1205 /* uppermost player is active */
1206 for (y
=cave
->h
-1; y
>=0; y
--)
1207 for (x
=cave
->w
-1; x
>=0; x
--)
1208 if (cave
->map
[y
][x
]==O_INBOX
) {
1213 /* lowermost player is active */
1214 for (y
=0; y
<cave
->h
; y
++)
1215 for (x
=0; x
<cave
->w
; x
++)
1216 if (cave
->map
[y
][x
]==O_INBOX
) {
1222 /* select number of milliseconds (for pal and ntsc) */
1223 cave
->timing_factor
=cave
->pal_timing
?1200:1000;
1224 cave
->time
*=cave
->timing_factor
;
1225 cave
->magic_wall_time
*=cave
->timing_factor
;
1226 cave
->amoeba_time
*=cave
->timing_factor
;
1227 cave
->amoeba_2_time
*=cave
->timing_factor
;
1228 cave
->hatching_delay_time
*=cave
->timing_factor
;
1229 if (cave
->hammered_walls_reappear
)
1230 cave
->hammered_reappear
=gd_cave_map_new(cave
, int);
1233 /* cave diamonds needed can be set to n<=0. */
1234 /* if so, count the diamonds at the time of the hatching, and decrement that value from */
1235 /* the number of diamonds found. */
1236 /* of course, this function is to be called from the cave engine, at the exact time of hatching. */
1238 gd_cave_count_diamonds(GdCave
*cave
)
1242 /* if automatically counting diamonds. if this was negative,
1243 * the sum will be this less than the number of all the diamonds in the cave */
1244 if (cave
->diamonds_needed
<=0) {
1245 for (y
=0; y
<cave
->h
; y
++)
1246 for (x
=0; x
<cave
->w
; x
++)
1247 if (cave
->map
[y
][x
]==O_DIAMOND
)
1248 cave
->diamonds_needed
++;
1249 if (cave
->diamonds_needed
<0)
1250 /* if still below zero, let this be 0, so gate will be open immediately */
1251 cave
->diamonds_needed
=0;
1262 /* takes a cave and a gfx buffer, and fills the buffer with cell indexes.
1263 the indexes might change if bonus life flash is active (small lines in "SPACE" cells),
1264 for the paused state (which is used in gdash but not in sdash) - yellowish color.
1265 also one can select the animation frame (0..7) to draw the cave on. so the caller manages
1268 if a cell is changed, it is flagged with GD_REDRAW; the flag can be cleared by the caller.
1271 gd_drawcave_game(const GdCave
*cave
, int **gfx_buffer
, gboolean bonus_life_flash
, gboolean yellowish_color
, int animcycle
, gboolean hate_invisible_outbox
)
1273 static int player_blinking
=0;
1274 static int player_tapping
=0;
1275 int elemdrawing
[O_MAX
];
1278 g_assert(cave
!=NULL
);
1279 g_assert(cave
->map
!=NULL
);
1280 g_assert(gfx_buffer
!=NULL
);
1281 g_assert(animcycle
>=0 && animcycle
<=7);
1283 if (cave
->last_direction
) { /* he is moving, so stop blinking and tapping. */
1287 else { /* he is idle, so animations can be done. */
1288 if (animcycle
==0) { /* blinking and tapping is started at the beginning of animation sequences. */
1289 player_blinking
=g_random_int_range(0, 4)==0; /* 1/4 chance of blinking, every sequence. */
1290 if (g_random_int_range(0, 16)==0) /* 1/16 chance of starting or stopping tapping. */
1291 player_tapping
=!player_tapping
;
1295 for (x
=0; x
<O_MAX
; x
++)
1296 elemdrawing
[x
]=gd_elements
[x
].image_game
;
1297 if (bonus_life_flash
)
1298 elemdrawing
[O_SPACE
]=gd_elements
[O_FAKE_BONUS
].image_game
;
1299 elemdrawing
[O_MAGIC_WALL
]=gd_elements
[cave
->magic_wall_state
== GD_MW_ACTIVE
? O_MAGIC_WALL
: O_BRICK
].image_game
;
1300 elemdrawing
[O_CREATURE_SWITCH
]=gd_elements
[cave
->creatures_backwards
? O_CREATURE_SWITCH_ON
: O_CREATURE_SWITCH
].image_game
;
1301 elemdrawing
[O_EXPANDING_WALL_SWITCH
]=gd_elements
[cave
->expanding_wall_changed
? O_EXPANDING_WALL_SWITCH_VERT
: O_EXPANDING_WALL_SWITCH_HORIZ
].image_game
;
1302 elemdrawing
[O_GRAVITY_SWITCH
]=gd_elements
[cave
->gravity_switch_active
?O_GRAVITY_SWITCH_ACTIVE
:O_GRAVITY_SWITCH
].image_game
;
1303 elemdrawing
[O_REPLICATOR_SWITCH
]=gd_elements
[cave
->replicators_active
?O_REPLICATOR_SWITCH_ON
:O_REPLICATOR_SWITCH_OFF
].image_game
;
1304 if (!cave
->replicators_active
)
1305 /* if the replicators are inactive, do not animate them. */
1306 elemdrawing
[O_REPLICATOR
]=ABS(elemdrawing
[O_REPLICATOR
]);
1307 elemdrawing
[O_CONVEYOR_SWITCH
]=gd_elements
[cave
->conveyor_belts_active
?O_CONVEYOR_SWITCH_ON
:O_CONVEYOR_SWITCH_OFF
].image_game
;
1308 if (cave
->conveyor_belts_direction_changed
) {
1309 /* if direction is changed, animation is changed. */
1312 temp
=elemdrawing
[O_CONVEYOR_LEFT
];
1313 elemdrawing
[O_CONVEYOR_LEFT
]=elemdrawing
[O_CONVEYOR_RIGHT
];
1314 elemdrawing
[O_CONVEYOR_RIGHT
]=temp
;
1316 elemdrawing
[O_CONVEYOR_DIR_SWITCH
]=gd_elements
[O_CONVEYOR_DIR_CHANGED
].image_game
;
1319 elemdrawing
[O_CONVEYOR_DIR_SWITCH
]=gd_elements
[O_CONVEYOR_DIR_NORMAL
].image_game
;
1320 if (!cave
->conveyor_belts_active
) {
1321 /* if they are not running, do not animate them. */
1322 elemdrawing
[O_CONVEYOR_LEFT
]=ABS(elemdrawing
[O_CONVEYOR_LEFT
]);
1323 elemdrawing
[O_CONVEYOR_RIGHT
]=ABS(elemdrawing
[O_CONVEYOR_RIGHT
]);
1326 elemdrawing
[O_PNEUMATIC_ACTIVE_LEFT
]+=2; /* also a hack, like biter_switch */
1327 elemdrawing
[O_PNEUMATIC_ACTIVE_RIGHT
]+=2;
1328 elemdrawing
[O_PLAYER_PNEUMATIC_LEFT
]+=2;
1329 elemdrawing
[O_PLAYER_PNEUMATIC_RIGHT
]+=2;
1332 if ((cave
->last_direction
) == MV_STILL
) { /* player is idle. */
1333 if (player_blinking
&& player_tapping
)
1334 draw
=gd_elements
[O_PLAYER_TAP_BLINK
].image_game
;
1335 else if (player_blinking
)
1336 draw
=gd_elements
[O_PLAYER_BLINK
].image_game
;
1337 else if (player_tapping
)
1338 draw
=gd_elements
[O_PLAYER_TAP
].image_game
;
1340 draw
=gd_elements
[O_PLAYER
].image_game
;
1342 else if (cave
->last_horizontal_direction
== MV_LEFT
)
1343 draw
=gd_elements
[O_PLAYER_LEFT
].image_game
;
1345 /* of course this is MV_RIGHT. */
1346 draw
=gd_elements
[O_PLAYER_RIGHT
].image_game
;
1347 elemdrawing
[O_PLAYER
]=draw
;
1348 elemdrawing
[O_PLAYER_GLUED
]=draw
;
1349 /* player with bomb does not blink or tap - no graphics drawn for that. running is drawn using w/o bomb cells */
1350 if (cave
->last_direction
!=MV_STILL
)
1351 elemdrawing
[O_PLAYER_BOMB
]=draw
;
1352 elemdrawing
[O_INBOX
]=gd_elements
[cave
->inbox_flash_toggle
? O_OUTBOX_OPEN
: O_OUTBOX_CLOSED
].image_game
;
1353 elemdrawing
[O_OUTBOX
]=gd_elements
[cave
->inbox_flash_toggle
? O_OUTBOX_OPEN
: O_OUTBOX_CLOSED
].image_game
;
1354 elemdrawing
[O_BITER_SWITCH
]=gd_elements
[O_BITER_SWITCH
].image_game
+cave
->biter_delay_frame
; /* hack, not fit into gd_elements */
1355 /* visual effects */
1356 elemdrawing
[O_DIRT
]=elemdrawing
[cave
->dirt_looks_like
];
1357 elemdrawing
[O_EXPANDING_WALL
]=elemdrawing
[cave
->expanding_wall_looks_like
];
1358 elemdrawing
[O_V_EXPANDING_WALL
]=elemdrawing
[cave
->expanding_wall_looks_like
];
1359 elemdrawing
[O_H_EXPANDING_WALL
]=elemdrawing
[cave
->expanding_wall_looks_like
];
1360 elemdrawing
[O_AMOEBA_2
]=elemdrawing
[cave
->amoeba_2_looks_like
];
1362 /* change only graphically */
1363 if (hate_invisible_outbox
) {
1364 elemdrawing
[O_PRE_INVIS_OUTBOX
]=elemdrawing
[O_PRE_OUTBOX
];
1365 elemdrawing
[O_INVIS_OUTBOX
]=elemdrawing
[O_OUTBOX
];
1368 for (y
=cave
->y1
; y
<=cave
->y2
; y
++) {
1369 for (x
=cave
->x1
; x
<=cave
->x2
; x
++) {
1370 GdElement actual
=cave
->map
[y
][x
];
1372 /* if covered, real element is not important */
1373 if (actual
& COVERED
)
1374 draw
=gd_elements
[O_COVERED
].image_game
;
1376 draw
=elemdrawing
[actual
];
1378 /* if negative, animated. */
1380 draw
=-draw
+animcycle
;
1382 if (cave
->gate_open_flash
|| yellowish_color
)
1385 /* set to buffer, with caching */
1386 if (gfx_buffer
[y
][x
]!=draw
)
1387 gfx_buffer
[y
][x
]=draw
| GD_REDRAW
;
1396 /* cave time is rounded _UP_ to seconds. so at the exact moment when it changes from
1397 2sec remaining to 1sec remaining, the player has exactly one second. when it changes
1398 to zero, it is the exact moment of timeout. */
1399 /* internal time is milliseconds (or 1200 milliseconds for pal timing). */
1401 gd_cave_time_show(const GdCave
*cave
, int internal_time
)
1403 return (internal_time
+cave
->timing_factor
-1)/cave
->timing_factor
;
1420 rep
=g_new0(GdReplay
, 1);
1421 /* create dynamic objects */
1422 rep
->comment
=g_string_new(NULL
);
1423 rep
->movements
=g_byte_array_new();
1428 gd_replay_new_from_replay(GdReplay
*orig
)
1432 rep
=g_memdup(orig
, sizeof(GdReplay
));
1433 /* replicate dynamic data */
1434 rep
->comment
=g_string_new(orig
->comment
->str
);
1435 rep
->movements
=g_byte_array_new();
1436 g_byte_array_append(rep
->movements
, orig
->movements
->data
, orig
->movements
->len
);
1442 gd_replay_free(GdReplay
*replay
)
1444 g_byte_array_free(replay
->movements
, TRUE
);
1445 g_string_free(replay
->comment
, TRUE
);
1450 REPLAY_MOVE_MASK
=0x0f,
1451 REPLAY_FIRE_MASK
=0x10,
1452 REPLAY_SUICIDE_MASK
=0x20,
1455 /* store movement in a replay */
1457 gd_replay_store_movement(GdReplay
*replay
, GdDirection player_move
, gboolean player_fire
, gboolean suicide
)
1461 g_assert(player_move
==(player_move
& REPLAY_MOVE_MASK
));
1462 data
[0]=(player_move
)|(player_fire
?REPLAY_FIRE_MASK
:0)|(suicide
?REPLAY_SUICIDE_MASK
:0);
1464 g_byte_array_append(replay
->movements
, data
, 1);
1467 /* get next available movement from a replay; store variables to player_move, player_fire, suicide */
1468 /* return true if successful */
1470 gd_replay_get_next_movement(GdReplay
*replay
, GdDirection
*player_move
, gboolean
*player_fire
, gboolean
*suicide
)
1474 /* if no more available movements */
1475 if (replay
->current_playing_pos
>=replay
->movements
->len
)
1478 data
=replay
->movements
->data
[replay
->current_playing_pos
++];
1479 *suicide
=(data
&REPLAY_SUICIDE_MASK
)!=0;
1480 *player_fire
=(data
&REPLAY_FIRE_MASK
)!=0;
1481 *player_move
=(data
&REPLAY_MOVE_MASK
);
1486 void gd_replay_rewind(GdReplay
*replay
)
1488 replay
->current_playing_pos
=0;
1492 #define REPLAY_BDCFF_UP "u"
1493 #define REPLAY_BDCFF_UP_RIGHT "ur"
1494 #define REPLAY_BDCFF_RIGHT "r"
1495 #define REPLAY_BDCFF_DOWN_RIGHT "dr"
1496 #define REPLAY_BDCFF_DOWN "d"
1497 #define REPLAY_BDCFF_DOWN_LEFT "dl"
1498 #define REPLAY_BDCFF_LEFT "l"
1499 #define REPLAY_BDCFF_UP_LEFT "ul"
1500 /* when not moving */
1501 #define REPLAY_BDCFF_STILL "."
1502 /* when the fire is pressed */
1503 #define REPLAY_BDCFF_FIRE "F"
1504 #define REPLAY_BDCFF_SUICIDE "k"
1507 direction_to_bdcff(GdDirection mov
)
1511 case MV_STILL
: return REPLAY_BDCFF_STILL
;
1513 case MV_UP
: return REPLAY_BDCFF_UP
;
1514 case MV_UP_RIGHT
: return REPLAY_BDCFF_UP_RIGHT
;
1515 case MV_RIGHT
: return REPLAY_BDCFF_RIGHT
;
1516 case MV_DOWN_RIGHT
: return REPLAY_BDCFF_DOWN_RIGHT
;
1517 case MV_DOWN
: return REPLAY_BDCFF_DOWN
;
1518 case MV_DOWN_LEFT
: return REPLAY_BDCFF_DOWN_LEFT
;
1519 case MV_LEFT
: return REPLAY_BDCFF_LEFT
;
1520 case MV_UP_LEFT
: return REPLAY_BDCFF_UP_LEFT
;
1522 g_assert_not_reached(); /* programmer error */
1523 return REPLAY_BDCFF_STILL
;
1527 /* same as above; pressing fire will be a capital letter. */
1529 direction_fire_to_bdcff(GdDirection dir
, gboolean fire
)
1531 static char mov
[10];
1533 strcpy(mov
, direction_to_bdcff(dir
));
1537 for (i
=0; mov
[i
]!=0; i
++)
1538 mov
[i
]=g_ascii_toupper(mov
[i
]);
1545 gd_replay_movements_to_bdcff(GdReplay
*replay
)
1550 str
=g_string_new(NULL
);
1552 for (pos
=0; pos
<replay
->movements
->len
; pos
++) {
1556 /* if this is not the first movement, append a space. */
1558 g_string_append_c(str
, ' ');
1560 /* if same byte appears, count number of occurrences - something like an rle compression. */
1561 /* be sure not to cross the array boundaries */
1562 while (pos
<replay
->movements
->len
-1 && replay
->movements
->data
[pos
]==replay
->movements
->data
[pos
+1]) {
1566 data
=replay
->movements
->data
[pos
];
1567 if (data
& REPLAY_SUICIDE_MASK
)
1568 g_string_append(str
, REPLAY_BDCFF_SUICIDE
);
1569 g_string_append(str
, direction_fire_to_bdcff(data
& REPLAY_MOVE_MASK
, data
& REPLAY_FIRE_MASK
));
1571 g_string_append_printf(str
, "%d", num
);
1574 return g_string_free(str
, FALSE
);
1578 /* calculate adler checksum for a rendered cave; this can be used for more caves. */
1580 gd_cave_adler_checksum_more(GdCave
*cave
, guint32
*a
, guint32
*b
)
1584 g_assert(cave
->rendered
);
1586 for (y
=0; y
<cave
->h
; y
++)
1587 for (x
=0; x
<cave
->w
; x
++) {
1588 *a
+=gd_elements
[cave
->map
[y
][x
]].character
;
1596 /* calculate adler checksum for a single rendered cave. */
1598 gd_cave_adler_checksum(GdCave
*cave
)
1603 gd_cave_adler_checksum_more(cave
, &a
, &b
);