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
;
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.
118 gd_create_char_to_element_table()
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
;
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. */
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
);
154 do some init; this function is to be called at the start of the application
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
++) {
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 */
210 gd_get_element_from_string (const char *string
)
212 char *upper
=g_ascii_strup(string
, -1);
217 g_warning("Invalid string representing element: (null)");
221 found
=g_hash_table_lookup_extended(name_to_element
, upper
, NULL
, &value
);
224 return (GdElement
) (GPOINTER_TO_INT(value
));
226 g_warning("Invalid string representing element: %s", string
);
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.
239 gd_struct_set_defaults_from_array(gpointer str
, const GdStructDescriptor
*properties
, GdPropertyDefault
*defaults
)
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
;
254 /* check which property we are talking about: find it in gd_cave_properties. */
255 n
=defaults
[i
].property_index
;
257 while(properties
[n
].identifier
!=NULL
&& properties
[n
].offset
!=defaults
[i
].offset
)
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 */
272 /* no default value for strings */
274 case GD_TYPE_LONGSTRING
:
277 case GD_TYPE_RATIO
: /* this is also an integer, difference is only when saving to bdcff */
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
;
283 case GD_TYPE_PROBABILITY
: /* floats are stored as integer, /million */
284 fvalue
[j
]=defaults
[i
].defval
/1000000.0;
286 case GD_TYPE_BOOLEAN
:
287 bvalue
[j
]=defaults
[i
].defval
!=0;
289 case GD_TYPE_ELEMENT
:
291 evalue
[j
]=(GdElement
) defaults
[i
].defval
;
294 cvalue
[j
]=gd_c64_color(defaults
[i
].defval
);
296 case GD_TYPE_DIRECTION
:
297 dvalue
[j
]=(GdDirection
) defaults
[i
].defval
;
299 case GD_TYPE_SCHEDULING
:
300 svalue
[j
]=(GdScheduling
) defaults
[i
].defval
;
308 gd_struct_explain_defaults_in_string(const GdStructDescriptor
*properties
, GdPropertyDefault
*defaults
)
313 defs
=g_string_new(NULL
);
315 for (i
=0; defaults
[i
].offset
!=-1; i
++) {
318 /* check which property we are talking about: find it in gd_cave_properties. */
319 n
=defaults
[i
].property_index
;
321 while(properties
[n
].identifier
!=NULL
&& properties
[n
].offset
!=defaults
[i
].offset
)
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 */
338 /* no default value for strings */
340 g_assert_not_reached();
344 case GD_TYPE_RATIO
: /* this is also an integer */
345 g_string_append_printf(defs
, "%d", defaults
[i
].defval
);
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);
350 case GD_TYPE_BOOLEAN
:
351 g_string_append_printf(defs
, "%s", defaults
[i
].defval
?_("Yes"):_("No"));
353 case GD_TYPE_ELEMENT
:
355 g_string_append_printf(defs
, "%s", _(gd_elements
[defaults
[i
].defval
].name
));
358 g_string_append_printf(defs
, "%s", gd_get_color_name(gd_c64_color(defaults
[i
].defval
)));
360 case GD_TYPE_DIRECTION
:
361 g_string_append_printf(defs
, "%s", direction_name
[defaults
[i
].defval
]);
363 case GD_TYPE_SCHEDULING
:
364 g_string_append_printf(defs
, "%s", scheduling_name
[(GdScheduling
) defaults
[i
].defval
]);
368 g_string_append_printf(defs
, "\n");
371 /* return char * data */
372 return g_string_free(defs
, FALSE
);
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.
394 gd_cave_set_gdash_defaults(GdCave
* cave
)
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
;
422 gd_clear_highscore(GdHighScore
*hs
)
426 for (i
=0; i
<GD_HIGHSCORE_NUM
; i
++) {
427 strcpy(hs
[i
].name
, "");
433 gd_has_highscore(GdHighScore
*hs
)
435 return hs
[0].score
>0;
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
)
456 gd_add_highscore(GdHighScore
*highscores
, const char *name
, int score
)
460 if (!gd_is_highscore(highscores
, score
))
463 /* overwrite the last one */
464 gd_strcpy(highscores
[GD_HIGHSCORE_NUM
-1].name
, name
);
465 highscores
[GD_HIGHSCORE_NUM
-1].score
=score
;
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
)
473 g_assert_not_reached();
481 /* for the case-insensitive hash keys */
483 gd_str_case_equal(gconstpointer s1
, gconstpointer s2
)
485 return g_ascii_strcasecmp(s1
, s2
)==0;
489 gd_str_case_hash(gconstpointer v
)
494 upper
=g_ascii_strup(v
, -1);
513 create new cave with default values.
514 sets every value, also default size, diamond value etc.
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
);
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
);
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
544 rows=new (pointers to rows);
546 rows[1..h-1]=rows[0]+width*bytes
554 allocate a cave map-like array, and initialize to zero.
555 one cell is cell_size bytes long.
558 gd_cave_map_new_for_cave(const GdCave
*cave
, const int cell_size
)
560 gpointer
*rows
; /* this is void**, pointer to array of ... */
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
;
574 if map is null, this also returns null.
577 gd_cave_map_dup_size(const GdCave
*cave
, const gpointer map
, const int cell_size
)
580 gpointer
*maplines
=(gpointer
*)map
;
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
;
596 gd_cave_map_free(gpointer map
)
598 gpointer
*maplines
=(gpointer
*) map
;
614 frees memory associated to cave
617 gd_cave_free(GdCave
*cave
)
625 g_hash_table_destroy(cave
->tags
);
627 if (cave
->random
) /* random generator is a GRand * */
628 g_rand_free(cave
->random
);
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
);
636 gd_cave_map_free(cave
->map
);
638 gd_cave_map_free(cave
->objects_order
);
639 /* hammered walls to reappear data */
640 gd_cave_map_free(cave
->hammered_reappear
);
642 g_list_foreach(cave
->objects
, (GFunc
) g_free
, NULL
);
643 g_list_free (cave
->objects
);
645 g_list_foreach(cave
->replays
, (GFunc
) gd_replay_free
, NULL
);
646 g_list_free(cave
->replays
);
648 /* freeing main pointer */
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 */
660 gd_cave_copy(GdCave
*dest
, const GdCave
*src
)
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
);
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 */
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
)));
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 */
702 dest
->random
=g_rand_copy(src
->random
);
705 /* create new cave, which is a copy of the cave given. */
707 gd_cave_new_from_cave(const GdCave
*orig
)
712 gd_cave_copy(cave
, orig
);
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.
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 */
733 if (cave
->wraparound_objects
) {
734 if (cave
->lineshift
) {
735 /* fit x coordinate within range, with correcting y at the same time */
737 x
+=cave
->w
; /* out of bounds on the left... */
738 y
--; /* previous row */
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. */
747 /* non lineshifting: changing x does not change y coordinate. */
752 /* after that, fix y coordinate */
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
;
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 */
777 x
+=cave
->w
; /* out of bounds on the left... */
778 y
--; /* previous row */
785 /* non lineshifting: changing x does not change y coordinate. */
791 /* after that, fix y coordinate */
797 return cave
->map
[y
][x
];
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);
816 result
=result
&0x00FF;
817 result
=result
+carry
+0x13;
819 rand
->rand_seed_2
=result
&0x00FF;
820 result
=rand
->rand_seed_1
+carry
+temp_rand_1
;
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.
836 gd_cave_c64_random(GdCave
*cave
)
838 return gd_c64_random(&cave
->c64_rand
);
842 gd_c64_random_set_seed(GdC64RandomGenerator
*rand
, int seed1
, int seed2
)
844 rand
->rand_seed_1
=seed1
;
845 rand
->rand_seed_2
=seed2
;
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.
868 swap(int *i1
, int *i2
)
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};
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 */
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 */
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
;
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())
920 /* we do not touch bri3 (8+sat), as it should be a bright color */
921 if (g_random_boolean())
923 if (g_random_boolean())
925 if (g_random_boolean())
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 */
939 gd_cave_set_random_atari_colors(GdCave
*cave
)
941 cave_set_random_indexed_colors(cave
, gd_atari_color_huesat
);
945 gd_cave_set_random_c64dtv_colors(GdCave
*cave
)
947 cave_set_random_indexed_colors(cave
, gd_c64dtv_color_huesat
);
951 swapd(double *i1
, double *i2
)
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
;
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 */
975 s1
=0.5; /* saturation is different */
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 */
985 s1
=0.7; /* saturation is same - a not fully saturated one */
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
);
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 */
1014 gd_cave_set_random_colors(GdCave
*cave
, GdColorType type
)
1017 case GD_COLOR_TYPE_RGB
:
1018 gd_cave_set_random_rgb_colors(cave
);
1020 case GD_COLOR_TYPE_C64
:
1021 gd_cave_set_random_c64_colors(cave
);
1023 case GD_COLOR_TYPE_C64DTV
:
1024 gd_cave_set_random_c64dtv_colors(cave
);
1026 case GD_COLOR_TYPE_ATARI
:
1027 gd_cave_set_random_atari_colors(cave
);
1031 g_assert_not_reached();
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.
1044 gd_cave_auto_shrink(GdCave
*cave
)
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. */
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. */
1069 case O_PRE_INVIS_OUTBOX
:
1071 if (empty
==STEEL_OR_OTHER
)
1073 if (empty
==STEEL_ONLY
) /* if this, delete only this one, and exit. */
1074 empty
=STEEL_OR_OTHER
;
1076 default: /* anything else, that should be left in the cave. */
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. */
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
)) {
1094 case O_PRE_INVIS_OUTBOX
:
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
)
1099 if (empty
==STEEL_ONLY
)
1100 empty
=STEEL_OR_OTHER
;
1106 if (empty
!=NO_SHRINK
)
1109 while (empty
==STEEL_ONLY
); /* if found one, repeat. */
1111 /* empty last columns. */
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
)) {
1120 case O_PRE_INVIS_OUTBOX
:
1122 if (empty
==STEEL_OR_OTHER
)
1124 if (empty
==STEEL_ONLY
)
1125 empty
=STEEL_OR_OTHER
;
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. */
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
)) {
1145 case O_PRE_INVIS_OUTBOX
:
1147 if (empty
==STEEL_OR_OTHER
)
1149 if (empty
==STEEL_ONLY
)
1150 empty
=STEEL_OR_OTHER
;
1156 if (empty
!=NO_SHRINK
)
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.
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
) {
1177 if (cave
->y2
<cave
->y1
) {
1186 if (cave
->x2
>cave
->w
-1)
1188 if (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.
1208 cave_set_ckdelay_extra_for_animation(GdCave
*cave
)
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
) {
1237 cave
->ckdelay_extra_for_animation
=0;
1239 cave
->ckdelay_extra_for_animation
+=2600;
1241 cave
->ckdelay_extra_for_animation
+=2600;
1243 cave
->ckdelay_extra_for_animation
+=2600;
1245 cave
->ckdelay_extra_for_animation
+=2600;
1249 /* do some init - setup some cave variables before the game. */
1251 gd_cave_setup_for_game(GdCave
*cave
)
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
) {
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
) {
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. */
1292 gd_cave_count_diamonds(GdCave
*cave
)
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
1322 if a cell is changed, it is flagged with GD_REDRAW; the flag can be cleared by the caller.
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
];
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. */
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. */
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
;
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
]);
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
;
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
;
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
;
1430 draw
=elemdrawing
[actual
];
1432 /* if negative, animated. */
1434 draw
=-draw
+animcycle
;
1436 if (cave
->gate_open_flash
|| yellowish_color
)
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
;
1474 rep
=g_new0(GdReplay
, 1);
1475 rep
->movements
=g_byte_array_new();
1480 gd_replay_new_from_replay(GdReplay
*orig
)
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
);
1492 gd_replay_free(GdReplay
*replay
)
1494 g_byte_array_free(replay
->movements
, TRUE
);
1499 REPLAY_MOVE_MASK
=0x0f,
1500 REPLAY_FIRE_MASK
=0x10,
1501 REPLAY_SUICIDE_MASK
=0x20,
1504 /* store movement in a replay */
1506 gd_replay_store_movement(GdReplay
*replay
, GdDirection player_move
, gboolean player_fire
, gboolean suicide
)
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 */
1519 gd_replay_get_next_movement(GdReplay
*replay
, GdDirection
*player_move
, gboolean
*player_fire
, gboolean
*suicide
)
1523 /* if no more available movements */
1524 if (replay
->current_playing_pos
>=replay
->movements
->len
)
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
);
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"
1556 direction_to_bdcff(GdDirection mov
)
1560 case MV_STILL
: return REPLAY_BDCFF_STILL
;
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
;
1571 g_assert_not_reached(); /* programmer error */
1572 return REPLAY_BDCFF_STILL
;
1576 /* same as above; pressing fire will be a capital letter. */
1578 direction_fire_to_bdcff(GdDirection dir
, gboolean fire
)
1580 static char mov
[10];
1582 strcpy(mov
, direction_to_bdcff(dir
));
1586 for (i
=0; mov
[i
]!=0; i
++)
1587 mov
[i
]=g_ascii_toupper(mov
[i
]);
1594 gd_replay_movements_to_bdcff(GdReplay
*replay
)
1599 str
=g_string_new(NULL
);
1601 for (pos
=0; pos
<replay
->movements
->len
; pos
++) {
1605 /* if this is not the first movement, append a space. */
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]) {
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
));
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. */
1629 gd_cave_adler_checksum_more(GdCave
*cave
, guint32
*a
, guint32
*b
)
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
;
1645 /* calculate adler checksum for a single rendered cave. */
1647 gd_cave_adler_checksum(GdCave
*cave
)
1652 gd_cave_adler_checksum_more(cave
, &a
, &b
);