* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <glib.h>
-#include <glib/gi18n.h>
-
#include "main_bd.h"
-/* arrays for movements */
-/* also no1 and bd2 cave data import helpers; line direction coordinates */
+// arrays for movements
+// also no1 and bd2 cave data import helpers; line direction coordinates
const int gd_dx[] =
{
0, 0, 1, 1, 1, 0, -1, -1, -1, 0, 2, 2, 2, 0, -2, -2, -2
0, -1, -1, 0, 1, 1, 1, 0, -1, -2, -2, 0, 2, 2, 2, 0, -2
};
-/* TRANSLATORS:
- None here means "no direction to move"; when there is no gravity while stirring the pot. */
+// TRANSLATORS:
+// None here means "no direction to move"; when there is no gravity while stirring the pot.
static const char* direction_name[] =
{
N_("None"),
static HashTable *name_to_element;
GdElement gd_char_to_element[256];
-/* color of flashing the screen, gate opening to exit */
+// color of flashing the screen, gate opening to exit
const GdColor gd_flash_color = 0xFFFFC0;
-/* selected object in editor */
+// selected object in editor
const GdColor gd_select_color = 0x8080FF;
-/* direction to string and vice versa */
+// direction to string and vice versa
const char *gd_direction_get_visible_name(GdDirection dir)
{
return direction_name[dir];
{
int i;
- for (i = 1; i<G_N_ELEMENTS(direction_filename); i++)
+ for (i = 1; i < ARRAY_SIZE(direction_filename); i++)
if (strcasecmp(str, direction_filename[i]) == 0)
return (GdDirection) i;
return GD_MV_DOWN;
}
-/* scheduling name to string and vice versa */
+// scheduling name to string and vice versa
const char *gd_scheduling_get_filename(GdScheduling sched)
{
return scheduling_filename[sched];
{
int i;
- for (i = 0; i < G_N_ELEMENTS(scheduling_filename); i++)
+ for (i = 0; i < ARRAY_SIZE(scheduling_filename); i++)
if (strcasecmp(str, scheduling_filename[i]) == 0)
return (GdScheduling) i;
"properties" describes the structure and its pointers,
"defaults" are the pieces of data which will be copied to str.
*/
-void gd_struct_set_defaults_from_array(gpointer str,
+void gd_struct_set_defaults_from_array(void *str,
const GdStructDescriptor *properties,
GdPropertyDefault *defaults)
{
for (i = 0; defaults[i].offset != -1; i++)
{
- gpointer pvalue = G_STRUCT_MEMBER_P(str, defaults[i].offset);
- /* these point to the same, but to avoid the awkward cast syntax */
+ void *pvalue = STRUCT_MEMBER_P(str, defaults[i].offset);
+ // these point to the same, but to avoid the awkward cast syntax
int *ivalue = pvalue;
GdElement *evalue = pvalue;
GdDirection *dvalue = pvalue;
GdColor *cvalue = pvalue;
int j, n;
- /* check which property we are talking about: find it in gd_cave_properties. */
+ // check which property we are talking about: find it in gd_cave_properties.
n = defaults[i].property_index;
if (n == 0)
{
properties[n].offset != defaults[i].offset)
n++;
- /* remember so we will be fast later*/
+ // remember so we will be fast later
defaults[i].property_index = n;
}
- /* some properties are arrays. this loop fills all with the same values */
+ // some properties are arrays. this loop fills all with the same values
for (j = 0; j < properties[n].count; j++)
{
switch (properties[n].type)
{
- /* these are for the gui; do nothing */
+ // these are for the gui; do nothing
case GD_TAB:
case GD_LABEL:
- /* no default value for strings */
+ // no default value for strings
case GD_TYPE_STRING:
case GD_TYPE_LONGSTRING:
break;
case GD_TYPE_RATIO:
- /* this is also an integer, difference is only when saving to bdcff */
+ // this is also an integer, difference is only when saving to bdcff
case GD_TYPE_INT:
if (defaults[i].defval < properties[n].min ||
defaults[i].defval > properties[n].max)
break;
case GD_TYPE_PROBABILITY:
- /* floats are stored as integer, /million; but are integers */
+ // floats are stored as integer, /million; but are integers
if (defaults[i].defval < 0 ||
defaults[i].defval > 1000000)
Warn("integer property %s out of range", properties[n].identifier);
}
}
-/* creates the character->element conversion table; using
- the fixed-in-the-bdcff characters. later, this table
- may be filled with more elements.
+/*
+ creates the character->element conversion table; using
+ the fixed-in-the-bdcff characters. later, this table
+ may be filled with more elements.
*/
void gd_create_char_to_element_table(void)
{
int i;
- /* fill all with unknown */
- for (i = 0; i < G_N_ELEMENTS(gd_char_to_element); i++)
+ // fill all with unknown
+ for (i = 0; i < ARRAY_SIZE(gd_char_to_element); i++)
gd_char_to_element[i] = O_UNKNOWN;
- /* then set fixed characters */
+ // then set fixed characters
for (i = 0; i < O_MAX; i++)
{
int c = gd_elements[i].character;
}
}
-/* search the element database for the specified character, and return the element. */
-GdElement gd_get_element_from_character (guint8 character)
+// search the element database for the specified character, and return the element.
+GdElement gd_get_element_from_character (byte character)
{
if (gd_char_to_element[character] != O_UNKNOWN)
return gd_char_to_element[character];
{
int i;
- /* put names to a hash table */
- /* this is a helper for file read operations */
- /* maps copied strings to elements (integers) */
+ // put names to a hash table
+ // this is a helper for file read operations
+ // maps copied strings to elements (integers)
name_to_element = create_hashtable(gd_str_case_hash, gd_str_case_equal, NULL, NULL);
for (i = 0; i < O_MAX; i++)
{
char *key;
- key = g_ascii_strup(gd_elements[i].filename, -1);
+ key = getStringToUpper(gd_elements[i].filename);
- if (hashtable_exists(name_to_element, key)) /* hash value may be 0 */
+ if (hashtable_exists(name_to_element, key)) // hash value may be 0
Warn("Name %s already used for element %x", key, i);
hashtable_insert(name_to_element, key, INT_TO_PTR(i));
- /* ^^^ do not free "key", as hash table needs it during the whole time! */
+ // ^^^ do not free "key", as hash table needs it during the whole time!
- key = getStringCat2("SCANNED_", key); /* new string */
+ key = getStringCat2("SCANNED_", key); // new string
hashtable_insert(name_to_element, key, INT_TO_PTR(i));
- /* once again, do not free "key" ^^^ */
+ // once again, do not free "key" ^^^
}
- /* for compatibility with tim stridmann's memorydump->bdcff converter... .... ... */
+ // for compatibility with tim stridmann's memorydump->bdcff converter... .... ...
hashtable_insert(name_to_element, "HEXPANDING_WALL", INT_TO_PTR(O_H_EXPANDING_WALL));
hashtable_insert(name_to_element, "FALLING_DIAMOND", INT_TO_PTR(O_DIAMOND_F));
hashtable_insert(name_to_element, "FALLING_BOULDER", INT_TO_PTR(O_STONE_F));
hashtable_insert(name_to_element, "EXPLOSION5D", INT_TO_PTR(O_PRE_DIA_5));
hashtable_insert(name_to_element, "WALL2", INT_TO_PTR(O_STEEL_EXPLODABLE));
- /* compatibility with old bd-faq (pre disassembly of bladder) */
+ // compatibility with old bd-faq (pre disassembly of bladder)
hashtable_insert(name_to_element, "BLADDERd9", INT_TO_PTR(O_BLADDER_8));
- /* create table to show errors at the start of the application */
+ // create table to show errors at the start of the application
gd_create_char_to_element_table();
}
-/* search the element database for the specified name, and return the element */
+// search the element database for the specified name, and return the element
GdElement gd_get_element_from_string (const char *string)
{
- char *upper = g_ascii_strup(string, -1);
+ char *upper = getStringToUpper(string);
void *value;
boolean found;
return O_UNKNOWN;
}
- found = hashtable_exists(name_to_element, upper); /* hash value may be 0 */
+ found = hashtable_exists(name_to_element, upper); // hash value may be 0
if (found)
value = hashtable_search(name_to_element, upper);
free(upper);
gd_cave_set_defaults_from_array(cave, gd_cave_defaults_gdash);
- /* these did not fit into the descriptor array */
+ // these did not fit into the descriptor array
for (i = 0; i < 5; i++)
{
cave->level_rand[i] = i;
}
}
-/* for quicksort. compares two highscores. */
-int gd_highscore_compare(gconstpointer a, gconstpointer b)
+// for quicksort. compares two highscores.
+int gd_highscore_compare(const void *a, const void *b)
{
const GdHighScore *ha = a;
const GdHighScore *hb = b;
return hs[0].score > 0;
}
-/* return true if score achieved is a highscore */
+// return true if score achieved is a highscore
boolean gd_is_highscore(GdHighScore *scores, int score)
{
- /* if score is above zero AND bigger than the last one */
+ // if score is above zero AND bigger than the last one
if (score > 0 && score > scores[GD_HIGHSCORE_NUM-1].score)
return TRUE;
if (!gd_is_highscore(highscores, score))
return -1;
- /* overwrite the last one */
+ // overwrite the last one
gd_strcpy(highscores[GD_HIGHSCORE_NUM-1].name, name);
highscores[GD_HIGHSCORE_NUM-1].score = score;
- /* and sort */
+ // and sort
qsort(highscores, GD_HIGHSCORE_NUM, sizeof(GdHighScore), gd_highscore_compare);
for (i = 0; i < GD_HIGHSCORE_NUM; i++)
- if (g_str_equal(highscores[i].name, name) && highscores[i].score == score)
+ if (strEqual(highscores[i].name, name) && highscores[i].score == score)
return i;
return -1;
}
-/* for the case-insensitive hash keys */
+// for the case-insensitive hash keys
int gd_str_case_equal(void *s1, void *s2)
{
return strcasecmp(s1, s2) == 0;
cave = checked_calloc(sizeof(GdCave));
- /* hash table which stores unknown tags as strings. */
+ // hash table which stores unknown tags as strings.
cave->tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
gd_cave_set_gdash_defaults(cave);
return cave;
}
-/* cave maps.
- cave maps are continuous areas in memory. the allocated memory
- is width * height * bytes_per_cell long.
- the cave map[0] stores the pointer given by g_malloc().
- the map itself is also an allocated array of pointers to the
- beginning of rows.
- therefore:
- rows = new (pointers to rows);
- rows[0] = new map
- rows[1..h-1] = rows[0] + width * bytes
-
- freeing this:
- free(rows[0])
- free(rows)
+/*
+ cave maps.
+ cave maps are continuous areas in memory. the allocated memory
+ is width * height * bytes_per_cell long.
+ the cave map[0] stores the pointer given by g_malloc().
+ the map itself is also an allocated array of pointers to the
+ beginning of rows.
+ therefore:
+ rows = new (pointers to rows);
+ rows[0] = new map
+ rows[1..h-1] = rows[0] + width * bytes
+
+ freeing this:
+ free(rows[0])
+ free(rows)
*/
/*
allocate a cave map-like array, and initialize to zero.
one cell is cell_size bytes long.
*/
-gpointer gd_cave_map_new_for_cave(const GdCave *cave, const int cell_size)
+void *gd_cave_map_new_for_cave(const GdCave *cave, const int cell_size)
{
- gpointer *rows; /* this is void**, pointer to array of ... */
+ void **rows; // this is void**, pointer to array of ...
int y;
- rows = checked_malloc((cave->h) * sizeof(gpointer));
+ rows = checked_malloc((cave->h) * sizeof(void *));
rows[0] = checked_calloc(cell_size * cave->w * cave->h);
for (y = 1; y < cave->h; y++)
- /* base pointer + num_of_bytes_per_element * width * number_of_row; as sizeof(char) = 1 */
+ // base pointer + num_of_bytes_per_element * width * number_of_row; as sizeof(char) = 1
rows[y] = (char *)rows[0] + cell_size * cave->w * y;
return rows;
if map is null, this also returns null.
*/
-gpointer gd_cave_map_dup_size(const GdCave *cave, const gpointer map, const int cell_size)
+void *gd_cave_map_dup_size(const GdCave *cave, const void *map, const int cell_size)
{
- gpointer *rows;
- gpointer *maplines = (gpointer *)map;
+ void **rows;
+ void **maplines = (void **)map;
int y;
if (!map)
return NULL;
- rows = checked_malloc((cave->h) * sizeof(gpointer));
- rows[0] = g_memdup (maplines[0], cell_size * cave->w * cave->h);
+ rows = checked_malloc((cave->h) * sizeof(void *));
+ rows[0] = get_memcpy (maplines[0], cell_size * cave->w * cave->h);
for (y = 1; y < cave->h; y++)
rows[y] = (char *)rows[0] + cell_size * cave->w * y;
return rows;
}
-void gd_cave_map_free(gpointer map)
+void gd_cave_map_free(void *map)
{
- gpointer *maplines = (gpointer *) map;
+ void **maplines = (void **) map;
if (!map)
return;
if (cave->tags)
hashtable_destroy(cave->tags);
- if (cave->random) /* random generator is a GRand * */
- g_rand_free(cave->random);
+ if (cave->random) // random generator is a GdRand *
+ gd_rand_free(cave->random);
- /* free strings */
+ // free strings
for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
if (gd_cave_properties[i].type == GD_TYPE_LONGSTRING)
- checked_free(G_STRUCT_MEMBER(char *, cave, gd_cave_properties[i].offset));
+ checked_free(STRUCT_MEMBER(char *, cave, gd_cave_properties[i].offset));
- /* map */
+ // map
gd_cave_map_free(cave->map);
- /* rendered data */
+ // rendered data
gd_cave_map_free(cave->objects_order);
- /* hammered walls to reappear data */
+ // hammered walls to reappear data
gd_cave_map_free(cave->hammered_reappear);
- /* free objects */
- g_list_foreach(cave->objects, (GFunc) free, NULL);
- g_list_free (cave->objects);
+ // free objects
+ list_foreach(cave->objects, (list_fn) free, NULL);
+ list_free(cave->objects);
- /* free replays */
- g_list_foreach(cave->replays, (GFunc) gd_replay_free, NULL);
- g_list_free(cave->replays);
+ // free replays
+ list_foreach(cave->replays, (list_fn) gd_replay_free, NULL);
+ list_free(cave->replays);
- /* freeing main pointer */
+ // freeing main pointer
free (cave);
}
hashtable_insert(dest, getStringCopy(key), getStringCopy(value));
}
-/* copy cave from src to destination, with duplicating dynamically allocated data */
+// copy cave from src to destination, with duplicating dynamically allocated data
void gd_cave_copy(GdCave *dest, const GdCave *src)
{
int i;
- /* copy entire data */
- g_memmove(dest, src, sizeof(GdCave));
+ // copy entire data
+ memmove(dest, src, sizeof(GdCave));
- /* but duplicate dynamic data */
+ // but duplicate dynamic data
dest->tags = create_hashtable(gd_str_case_hash, gd_str_case_equal, free, free);
if (src->tags)
dest->map = gd_cave_map_dup(src, map);
dest->hammered_reappear = gd_cave_map_dup(src, hammered_reappear);
- /* for longstrings */
+ // for longstrings
for (i = 0; gd_cave_properties[i].identifier != NULL; i++)
if (gd_cave_properties[i].type == GD_TYPE_LONGSTRING)
- G_STRUCT_MEMBER(char *, dest, gd_cave_properties[i].offset) =
- getStringCopy(G_STRUCT_MEMBER(char *, src, gd_cave_properties[i].offset));
+ STRUCT_MEMBER(char *, dest, gd_cave_properties[i].offset) =
+ getStringCopy(STRUCT_MEMBER(char *, src, gd_cave_properties[i].offset));
- /* no reason to copy this */
+ // no reason to copy this
dest->objects_order = NULL;
- /* copy objects list */
+ // copy objects list
if (src->objects)
{
- GList *iter;
+ List *iter;
- dest->objects = NULL; /* new empty list */
- for (iter = src->objects; iter != NULL; iter = iter->next) /* do a deep copy */
- dest->objects = g_list_append(dest->objects, g_memdup (iter->data, sizeof (GdObject)));
+ dest->objects = NULL; // new empty list
+ for (iter = src->objects; iter != NULL; iter = iter->next) // do a deep copy
+ dest->objects = list_append(dest->objects, get_memcpy (iter->data, sizeof (GdObject)));
}
- /* copy replays */
+ // copy replays
if (src->replays)
{
- GList *iter;
+ List *iter;
dest->replays = NULL;
- for (iter = src->replays; iter != NULL; iter = iter->next) /* do a deep copy */
- dest->replays = g_list_append(dest->replays, gd_replay_new_from_replay(iter->data));
+ for (iter = src->replays; iter != NULL; iter = iter->next) // do a deep copy
+ dest->replays = list_append(dest->replays, gd_replay_new_from_replay(iter->data));
}
- /* copy random number generator */
+ // copy random number generator
if (src->random)
- dest->random = g_rand_copy(src->random);
+ dest->random = gd_rand_copy(src->random);
}
-/* create new cave, which is a copy of the cave given. */
+// create new cave, which is a copy of the cave given.
GdCave *gd_cave_new_from_cave(const GdCave *orig)
{
GdCave *cave;
Performs range checking.
If wraparound objects are selected, wraps around x coordinates, with or without lineshift.
(The y coordinate is not wrapped, as it did not work like that on the c64)
- order is a pointer to the GdObject describing this object. Thus the editor can identify which cell was created by which object.
+ order is a pointer to the GdObject describing this object. Thus the editor can identify
+ which cell was created by which object.
*/
void gd_cave_store_rc(GdCave *cave, int x, int y, const GdElement element, const void *order)
{
- /* if we do not need to draw, exit now */
+ // if we do not need to draw, exit now
if (element == O_NONE)
return;
- /* check bounds */
+ // check bounds
if (cave->wraparound_objects)
{
if (cave->lineshift)
{
- /* fit x coordinate within range, with correcting y at the same time */
+ // fit x coordinate within range, with correcting y at the same time
while (x < 0)
{
- x += cave->w; /* out of bounds on the left... */
- y--; /* previous row */
+ x += cave->w; // out of bounds on the left...
+ y--; // previous row
}
while (x >= cave->w)
y++;
}
- /* lineshifting does not fix the y coordinates.
- if out of bounds, element will not be displayed. */
- /* if such an object appeared in the c64 game, well, it was a buffer overrun. */
+ // lineshifting does not fix the y coordinates.
+ // if out of bounds, element will not be displayed.
+ // if such an object appeared in the c64 game, well, it was a buffer overrun.
}
else
{
- /* non lineshifting: changing x does not change y coordinate. */
+ // non lineshifting: changing x does not change y coordinate.
while (x < 0)
x += cave->w;
while (x >= cave->w)
x -= cave->w;
- /* after that, fix y coordinate */
+ // after that, fix y coordinate
while (y < 0)
y += cave->h;
}
}
- /* if the above wraparound code fixed the coordinates, this will always be true. */
- /* but see the above comment for lineshifting y coordinate */
+ // if the above wraparound code fixed the coordinates, this will always be true.
+ // but see the above comment for lineshifting y coordinate
if (x >= 0 && x < cave->w && y >= 0 && y < cave->h)
{
cave->map[y][x] = element;
GdElement gd_cave_get_rc(const GdCave *cave, int x, int y)
{
- /* always fix coordinates as if cave was wraparound. */
+ // always fix coordinates as if cave was wraparound.
- /* fix x coordinate */
+ // fix x coordinate
if (cave->lineshift)
{
- /* fit x coordinate within range, with correcting y at the same time */
+ // fit x coordinate within range, with correcting y at the same time
while (x < 0)
{
- x += cave->w; /* out of bounds on the left... */
- y--; /* previous row */
+ x += cave->w; // out of bounds on the left...
+ y--; // previous row
}
while (x >= cave->w)
{
}
else
{
- /* non lineshifting: changing x does not change y coordinate. */
+ // non lineshifting: changing x does not change y coordinate.
while (x < 0)
x += cave->w;
x -= cave->w;
}
- /* after that, fix y coordinate */
+ // after that, fix y coordinate
while (y < 0)
y += cave->h;
static inline void swap(int *i1, int *i2)
{
int t = *i1;
+
+ *i1 = *i2;
+ *i2 = t;
+}
+
+void gd_cave_set_random_c64_colors(GdCave *cave)
+{
+ const int bright_colors[] = { 1, 3, 7 };
+ const int dark_colors[] = { 2, 6, 8, 9, 11 };
+
+ // always black
+ cave->colorb = gd_c64_color(0);
+ cave->color0 = gd_c64_color(0);
+
+ // choose some bright color for brick
+ cave->color3 = gd_c64_color(bright_colors[gd_random_int_range(0, ARRAY_SIZE(bright_colors))]);
+
+ // choose a dark color for dirt, but should not be == color of brick
+ do
+ {
+ cave->color1 = gd_c64_color(dark_colors[gd_random_int_range(0, ARRAY_SIZE(dark_colors))]);
+ }
+ while (cave->color1 == cave->color3); // so it is not the same as color 1
+
+ // choose any but black for steel wall, but should not be == brick or dirt
+ do
+ {
+ // between 1 and 15 - do not use black for this.
+ cave->color2 = gd_c64_color(gd_random_int_range(1, 16));
+ }
+ while (cave->color1 == cave->color2 || cave->color2 == cave->color3); // so colors are not the same
+
+ // copy amoeba and slime color
+ cave->color4 = cave->color3;
+ cave->color5 = cave->color1;
+}
+
+static void cave_set_random_indexed_colors(GdCave *cave, GdColor (*color_indexer_func) (int, int))
+{
+ int hue = gd_random_int_range(0, 15);
+ int hue_spread = gd_random_int_range(1, 6); // 1..5
+
+ // we only use 0..6, as saturation 15 is too bright (almost always white)
+ // also, saturation 0..1..2 is too dark. the color0=black is there for dark.
+ // so this is also 1..5. when hue spread is low, brightness spread is high
+ int bri_spread = 6 - hue_spread;
+ int bri1 = 8, bri2 = 8 - bri_spread, bri3 = 8 + bri_spread;
+
+ // there are 15 valid choices for hue, so we do a %15
+ int col1 = hue, col2 = (hue + hue_spread + 15) % 15, col3 = (hue - hue_spread + 15) % 15;
+
+ // this makes up a random color, and selects a color triad by hue+5 and hue+10.
+ // also creates a random saturation.
+ // color of brick is 8+sat, so it is always a bright color.
+ // another two are 8-sat and 8.
+ // order of colors is also changed randomly.
+ if (gd_random_boolean()) swap(&bri1, &bri2);
+
+ // we do not touch bri3 (8+sat), as it should be a bright color
+ if (gd_random_boolean()) swap(&col1, &col2);
+ if (gd_random_boolean()) swap(&col2, &col3);
+ if (gd_random_boolean()) swap(&col1, &col3);
+
+ cave->colorb = color_indexer_func(0, 0);
+ cave->color0 = color_indexer_func(0, 0);
+ cave->color1 = color_indexer_func(col1 + 1, bri1);
+ cave->color2 = color_indexer_func(col2 + 1, bri2);
+ cave->color3 = color_indexer_func(col3 + 1, bri3);
+ // amoeba and slime are different
+ // some green thing
+ cave->color4 = color_indexer_func(gd_random_int_range(11, 13), gd_random_int_range(6, 12));
+ // some blueish thing
+ cave->color5 = color_indexer_func(gd_random_int_range(7, 10), gd_random_int_range(0, 6));
+}
+
+static void gd_cave_set_random_atari_colors(GdCave *cave)
+{
+ cave_set_random_indexed_colors(cave, gd_atari_color_huesat);
+}
+
+static void gd_cave_set_random_c64dtv_colors(GdCave *cave)
+{
+ cave_set_random_indexed_colors(cave, gd_c64dtv_color_huesat);
+}
+
+static inline void swapd(double *i1, double *i2)
+{
+ double t = *i1;
+
*i1 = *i2;
*i2 = t;
}
+static void gd_cave_set_random_rgb_colors(GdCave *cave)
+{
+ const double hue_max = 10.0 / 30.0;
+ // any hue allowed
+ double hue = gd_random_double();
+ // hue 360 degress=1. hue spread is min. 24 degrees, max 120 degrees (1/3)
+ double hue_spread = gd_random_double_range(2.0 / 30.0, hue_max);
+ double h1 = hue, h2 = hue + hue_spread, h3 = hue + 2 * hue_spread;
+ double v1, v2, v3;
+ double s1, s2, s3;
+
+ if (gd_random_boolean())
+ {
+ // when hue spread is low, brightness(saturation) spread is high
+ // 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
+ double spread = 0.1 + 0.3 * (1 - hue_spread / hue_max);
+ v1 = 0.6; // brightness variation, too
+ v2 = 0.7;
+ v3 = 0.8;
+ s1 = 0.5; // saturation is different
+ s2 = 0.5 - spread;
+ s3 = 0.5 + spread;
+ }
+ else
+ {
+ // when hue spread is low, brightness(saturation) spread is high
+ // 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
+ double spread = 0.1 + 0.15 * (1 - hue_spread / hue_max);
+ v1 = 0.5; // brightness is different
+ v2 = 0.5 + spread;
+ v3 = 0.5 + 2 * spread;
+ s1 = 0.7; // saturation is same - a not fully saturated one
+ s2 = 0.8;
+ s3 = 0.9;
+ }
+
+ // randomly change values, but do not touch v3, as cave->color3 should be a bright color
+ if (gd_random_boolean()) swapd(&v1, &v2);
+
+ // randomly change hues and saturations
+ if (gd_random_boolean()) swapd(&h1, &h2);
+ if (gd_random_boolean()) swapd(&h2, &h3);
+ if (gd_random_boolean()) swapd(&h1, &h3);
+ if (gd_random_boolean()) swapd(&s1, &s2);
+ if (gd_random_boolean()) swapd(&s2, &s3);
+ if (gd_random_boolean()) swapd(&s1, &s3);
+
+ h1 = h1 * 360.0;
+ h2 = h2 * 360.0;
+ h3 = h3 * 360.0;
+
+ cave->colorb = gd_color_get_from_hsv(0, 0, 0);
+ cave->color0 = gd_color_get_from_hsv(0, 0, 0); // black for background
+ cave->color1 = gd_color_get_from_hsv(h1, s1, v1); // dirt
+ cave->color2 = gd_color_get_from_hsv(h2, s2, v2); // steel
+ cave->color3 = gd_color_get_from_hsv(h3, s3, v3); // brick
+ // green(120+-20) with the saturation and brightness of brick
+ cave->color4 = gd_color_get_from_hsv(gd_random_int_range(100, 140), s2, v2);
+ // blue(240+-20) with saturation and brightness of dirt
+ cave->color5 = gd_color_get_from_hsv(gd_random_int_range(220, 260), s1, v1);
+}
+
+void gd_cave_set_random_colors(GdCave *cave, GdColorType type)
+{
+ switch (type)
+ {
+ case GD_COLOR_TYPE_RGB:
+ gd_cave_set_random_rgb_colors(cave);
+ break;
+
+ case GD_COLOR_TYPE_C64:
+ gd_cave_set_random_c64_colors(cave);
+ break;
+
+ case GD_COLOR_TYPE_C64DTV:
+ gd_cave_set_random_c64dtv_colors(cave);
+ break;
+
+ case GD_COLOR_TYPE_ATARI:
+ gd_cave_set_random_atari_colors(cave);
+ break;
+
+ default:
+ break;
+ }
+}
+
/*
shrink cave
if last line or last row is just steel wall (or (invisible) outbox).
}
empty;
- /* set to maximum size, then try to shrink */
+ // set to maximum size, then try to shrink
cave->x1 = 0;
cave->y1 = 0;
cave->x2 = cave->w - 1;
cave->y2 = cave->h - 1;
- /* search for empty, steel-wall-only last rows. */
- /* clear all lines, which are only steel wall.
- * and clear only one line, which is steel wall, but also has a player or an outbox. */
+ // search for empty, steel-wall-only last rows.
+ // clear all lines, which are only steel wall.
+ // and clear only one line, which is steel wall, but also has a player or an outbox.
empty = STEEL_ONLY;
do
{
switch (gd_cave_get_rc (cave, x, y))
{
- /* if steels only, this is to be deleted. */
+ // if steels only, this is to be deleted.
case O_STEEL:
break;
if (empty == STEEL_OR_OTHER)
empty = NO_SHRINK;
- /* if this, delete only this one, and exit. */
+ // if this, delete only this one, and exit.
if (empty == STEEL_ONLY)
empty = STEEL_OR_OTHER;
break;
default:
- /* anything else, that should be left in the cave. */
+ // anything else, that should be left in the cave.
empty = NO_SHRINK;
break;
}
}
}
- /* shrink if full steel or steel and player/outbox. */
+ // shrink if full steel or steel and player/outbox.
if (empty != NO_SHRINK)
- cave->y2--; /* one row shorter */
+ cave->y2--; // one row shorter
}
- while (empty == STEEL_ONLY); /* if found just steels, repeat. */
+ while (empty == STEEL_ONLY); // if found just steels, repeat.
- /* search for empty, steel-wall-only first rows. */
+ // search for empty, steel-wall-only first rows.
empty = STEEL_ONLY;
do
case O_PRE_OUTBOX:
case O_PRE_INVIS_OUTBOX:
case O_INBOX:
- /* shrink only lines, which have only ONE player or outbox.
- this is for bd4 intermission 2, for example. */
+ // shrink only lines, which have only ONE player or outbox.
+ // this is for bd4 intermission 2, for example.
if (empty == STEEL_OR_OTHER)
empty = NO_SHRINK;
if (empty == STEEL_ONLY)
if (empty != NO_SHRINK)
cave->y1++;
}
- while (empty == STEEL_ONLY); /* if found one, repeat. */
+ while (empty == STEEL_ONLY); // if found one, repeat.
- /* empty last columns. */
+ // empty last columns.
empty = STEEL_ONLY;
do
}
}
- /* just remember that one column shorter.
- free will know the size of memchunk, no need to realloc! */
+ // just remember that one column shorter.
+ // free will know the size of memchunk, no need to realloc!
if (empty != NO_SHRINK)
cave->x2--;
}
- while (empty == STEEL_ONLY); /* if found one, repeat. */
+ while (empty == STEEL_ONLY); // if found one, repeat.
- /* empty first columns. */
+ // empty first columns.
empty = STEEL_ONLY;
do
if (empty != NO_SHRINK)
cave->x1++;
}
- while (empty == STEEL_ONLY); /* if found one, repeat. */
+ while (empty == STEEL_ONLY); // if found one, repeat.
}
-/* check if cave visible part coordinates
- are outside cave sizes, or not in the right order.
- correct them if needed.
+/*
+ check if cave visible part coordinates
+ are outside cave sizes, or not in the right order.
+ correct them if needed.
*/
void gd_cave_correct_visible_size(GdCave *cave)
{
- /* change visible coordinates if they do not point to upperleft and lowerright */
+ // change visible coordinates if they do not point to upperleft and lowerright
if (cave->x2 < cave->x1)
{
int t = cave->x2;
cave->ckdelay_extra_for_animation += 2600;
}
-/* do some init - setup some cave variables before the game. */
+// do some init - setup some cave variables before the game.
void gd_cave_setup_for_game(GdCave *cave)
{
int x, y;
cave_set_ckdelay_extra_for_animation(cave);
- /* find the player which will be the one to scroll to at the beginning of the game
- (before the player's birth) */
+ // find the player which will be the one to scroll to at the beginning of the game
+ // (before the player's birth)
if (cave->active_is_first_found)
{
- /* uppermost player is active */
+ // uppermost player is active
for (y = cave->h - 1; y >= 0; y--)
{
for (x = cave->w - 1; x >= 0; x--)
}
else
{
- /* lowermost player is active */
+ // lowermost player is active
for (y = 0; y < cave->h; y++)
{
for (x = 0; x < cave->w; x++)
}
}
- /* select number of milliseconds (for pal and ntsc) */
+ // select number of milliseconds (for pal and ntsc)
cave->timing_factor = cave->pal_timing ? 1200 : 1000;
cave->time *= cave->timing_factor;
cave->hammered_reappear = gd_cave_map_new(cave, int);
}
-/* cave diamonds needed can be set to n<=0. */
-/* if so, count the diamonds at the time of the hatching, and decrement that value from */
-/* the number of diamonds found. */
-/* of course, this function is to be called from the cave engine, at the exact time of hatching. */
+// cave diamonds needed can be set to n<=0.
+// if so, count the diamonds at the time of the hatching, and decrement that value from
+// the number of diamonds found.
+// of course, this function is to be called from the cave engine, at the exact time of hatching.
void gd_cave_count_diamonds(GdCave *cave)
{
int x, y;
- /* if automatically counting diamonds. if this was negative,
- * the sum will be this less than the number of all the diamonds in the cave */
+ // if automatically counting diamonds. if this was negative,
+ // the sum will be this less than the number of all the diamonds in the cave
if (cave->diamonds_needed <= 0)
{
for (y = 0; y < cave->h; y++)
if (cave->map[y][x] == O_DIAMOND)
cave->diamonds_needed++;
- /* if still below zero, let this be 0, so gate will be open immediately */
+ // if still below zero, let this be 0, so gate will be open immediately
if (cave->diamonds_needed < 0)
cave->diamonds_needed = 0;
}
}
-/* takes a cave and a gfx buffer, and fills the buffer with cell indexes.
- the indexes might change if bonus life flash is active (small lines in
- "SPACE" cells),
- for the paused state (which is used in gdash but not in sdash) - yellowish
- color.
- also one can select the animation frame (0..7) to draw the cave on. so the
- caller manages
- increasing that.
-
- if a cell is changed, it is flagged with GD_REDRAW; the flag can be cleared
- by the caller.
+/*
+ takes a cave and a gfx buffer, and fills the buffer with cell indexes.
+ the indexes might change if bonus life flash is active (small lines in
+ "SPACE" cells),
+ for the paused state (which is used in gdash but not in sdash) - yellowish
+ color.
+ also one can select the animation frame (0..7) to draw the cave on. so the
+ caller manages
+ increasing that.
+
+ if a cell is changed, it is flagged with GD_REDRAW; the flag can be cleared
+ by the caller.
*/
-void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer,
+void gd_drawcave_game(const GdCave *cave,
+ int **element_buffer, int **last_element_buffer, int **gfx_buffer,
boolean bonus_life_flash, int animcycle, boolean hate_invisible_outbox)
{
static int player_blinking = 0;
static int player_tapping = 0;
- int elemmapping[O_MAX];
- int elemdrawing[O_MAX];
+ int elemmapping[O_MAX_ALL];
+ int elemdrawing[O_MAX_ALL];
int x, y, map, draw;
if (cave->last_direction)
{
- /* he is moving, so stop blinking and tapping. */
+ // he is moving, so stop blinking and tapping.
player_blinking = 0;
player_tapping = 0;
}
else
{
- /* he is idle, so animations can be done. */
+ // he is idle, so animations can be done.
if (animcycle == 0)
{
- /* blinking and tapping is started at the beginning of animation sequences. */
- /* 1/4 chance of blinking, every sequence. */
- player_blinking = g_random_int_range(0, 4) == 0;
+ // blinking and tapping is started at the beginning of animation sequences.
+ // 1/4 chance of blinking, every sequence.
+ player_blinking = gd_random_int_range(0, 4) == 0;
- /* 1/16 chance of starting or stopping tapping. */
- if (g_random_int_range(0, 16) == 0)
+ // 1/16 chance of starting or stopping tapping.
+ if (gd_random_int_range(0, 16) == 0)
player_tapping = !player_tapping;
}
}
- for (x = 0; x < O_MAX; x++)
+ for (x = 0; x < O_MAX_ALL; x++)
{
elemmapping[x] = x;
elemdrawing[x] = gd_elements[x].image_game;
elemdrawing[O_REPLICATOR_SWITCH] = gd_elements[cave->replicators_active ? O_REPLICATOR_SWITCH_ON : O_REPLICATOR_SWITCH_OFF].image_game;
if (cave->replicators_active)
- /* if the replicators are active, animate them. */
+ // if the replicators are active, animate them.
elemmapping[O_REPLICATOR] = O_REPLICATOR_ACTIVE;
if (!cave->replicators_active)
- /* if the replicators are inactive, do not animate them. */
+ // if the replicators are inactive, do not animate them.
elemdrawing[O_REPLICATOR] = ABS(elemdrawing[O_REPLICATOR]);
elemmapping[O_CONVEYOR_SWITCH] = (cave->conveyor_belts_active ? O_CONVEYOR_SWITCH_ON : O_CONVEYOR_SWITCH_OFF);
if (cave->conveyor_belts_direction_changed)
{
- /* if direction is changed, animation is changed. */
+ // if direction is changed, animation is changed.
int temp;
elemmapping[O_CONVEYOR_LEFT] = O_CONVEYOR_RIGHT;
if (cave->conveyor_belts_active)
{
- /* keep potentially changed direction */
+ // keep potentially changed direction
int offset = (O_CONVEYOR_LEFT_ACTIVE - O_CONVEYOR_LEFT);
- /* if they are running, animate them. */
+ // if they are running, animate them.
elemmapping[O_CONVEYOR_LEFT] += offset;
elemmapping[O_CONVEYOR_RIGHT] += offset;
}
if (!cave->conveyor_belts_active)
{
- /* if they are not running, do not animate them. */
+ // if they are not running, do not animate them.
elemdrawing[O_CONVEYOR_LEFT] = ABS(elemdrawing[O_CONVEYOR_LEFT]);
elemdrawing[O_CONVEYOR_RIGHT] = ABS(elemdrawing[O_CONVEYOR_RIGHT]);
}
if (animcycle & 2)
{
- /* also a hack, like biter_switch */
+ // also a hack, like biter_switch
elemdrawing[O_PNEUMATIC_ACTIVE_LEFT] += 2;
elemdrawing[O_PNEUMATIC_ACTIVE_RIGHT] += 2;
elemdrawing[O_PLAYER_PNEUMATIC_LEFT] += 2;
if ((cave->last_direction) == GD_MV_STILL)
{
- /* player is idle. */
+ // player is idle.
if (player_blinking && player_tapping)
{
map = O_PLAYER_TAP_BLINK;
draw = gd_elements[O_PLAYER].image_game;
}
}
+ else if (cave->last_direction == GD_MV_UP && use_bd_up_down_graphics())
+ {
+ map = O_PLAYER_UP;
+ draw = gd_elements[O_PLAYER_UP].image_game;
+ }
+ else if (cave->last_direction == GD_MV_DOWN && use_bd_up_down_graphics())
+ {
+ map = O_PLAYER_DOWN;
+ draw = gd_elements[O_PLAYER_DOWN].image_game;
+ }
else if (cave->last_horizontal_direction == GD_MV_LEFT)
{
map = O_PLAYER_LEFT;
}
else
{
- /* of course this is GD_MV_RIGHT. */
+ // of course this is GD_MV_RIGHT.
map = O_PLAYER_RIGHT;
draw = gd_elements[O_PLAYER_RIGHT].image_game;
}
elemdrawing[O_PLAYER] = draw;
elemdrawing[O_PLAYER_GLUED] = draw;
- /* player with bomb does not blink or tap - no graphics drawn for that.
- running is drawn using w/o bomb cells */
+ // player with bomb/rocketlauncher does not blink or tap - no graphics drawn for that.
+ // running is drawn using w/o bomb/rocketlauncher cells */
if (cave->last_direction != GD_MV_STILL)
{
elemmapping[O_PLAYER_BOMB] = map;
elemdrawing[O_PLAYER_BOMB] = draw;
+
+ elemmapping[O_PLAYER_ROCKET_LAUNCHER] = map;
+ elemdrawing[O_PLAYER_ROCKET_LAUNCHER] = draw;
}
elemmapping[O_INBOX] = (cave->inbox_flash_toggle ? O_INBOX_OPEN : O_INBOX_CLOSED);
elemmapping[O_OUTBOX] = (cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED);
elemdrawing[O_OUTBOX] = gd_elements[cave->inbox_flash_toggle ? O_OUTBOX_OPEN : O_OUTBOX_CLOSED].image_game;
- /* hack, not fit into gd_elements */
+ // hack, not fit into gd_elements
elemmapping[O_BITER_SWITCH] = O_BITER_SWITCH_1 + cave->biter_delay_frame;
- /* hack, not fit into gd_elements */
+ // hack, not fit into gd_elements
elemdrawing[O_BITER_SWITCH] = gd_elements[O_BITER_SWITCH].image_game + cave->biter_delay_frame;
- /* visual effects */
+ // visual effects
elemmapping[O_DIRT] = cave->dirt_looks_like;
elemmapping[O_EXPANDING_WALL] = cave->expanding_wall_looks_like;
elemmapping[O_V_EXPANDING_WALL] = cave->expanding_wall_looks_like;
elemmapping[O_H_EXPANDING_WALL] = cave->expanding_wall_looks_like;
elemmapping[O_AMOEBA_2] = cave->amoeba_2_looks_like;
- /* visual effects */
+ // visual effects
elemdrawing[O_DIRT] = elemdrawing[cave->dirt_looks_like];
elemdrawing[O_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
elemdrawing[O_V_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
elemdrawing[O_H_EXPANDING_WALL] = elemdrawing[cave->expanding_wall_looks_like];
elemdrawing[O_AMOEBA_2] = elemdrawing[cave->amoeba_2_looks_like];
- /* change only graphically */
+ // change only graphically
if (hate_invisible_outbox)
{
- elemmapping[O_PRE_INVIS_OUTBOX] = O_PRE_OUTBOX;
- elemmapping[O_INVIS_OUTBOX] = O_OUTBOX;
+ elemmapping[O_PRE_INVIS_OUTBOX] = elemmapping[O_PRE_OUTBOX];
+ elemmapping[O_INVIS_OUTBOX] = elemmapping[O_OUTBOX];
}
if (hate_invisible_outbox)
{
GdElement actual = cave->map[y][x];
- /* if covered, real element is not important */
+ // if covered, real element is not important
if (actual & COVERED)
map = O_COVERED;
else
map = elemmapping[actual];
- /* if covered, real element is not important */
+ // if covered, real element is not important
if (actual & COVERED)
draw = gd_elements[O_COVERED].image_game;
else
draw = elemdrawing[actual];
- /* if negative, animated. */
+ // draw special graphics if player is pushing something
+ if (use_bd_pushing_graphics() &&
+ (cave->last_direction == GD_MV_LEFT || cave->last_direction == GD_MV_RIGHT) &&
+ is_player(cave, x, y) && can_be_pushed_dir(cave, x, y, cave->last_direction))
+ {
+ // special check needed when smooth game element movements selected in setup menu:
+ // last element must either be player (before pushing) or pushable element (while pushing)
+ // (extra check needed to prevent pushing animation when moving towards pushable element)
+ if (!use_bd_smooth_movements() || last_element_buffer[y][x] != O_SPACE)
+ {
+ if (cave->last_direction == GD_MV_LEFT)
+ map = O_PLAYER_PUSH_LEFT;
+ else
+ map = O_PLAYER_PUSH_RIGHT;
+
+ if (cave->last_direction == GD_MV_LEFT)
+ draw = elemdrawing[O_PLAYER_PUSH_LEFT];
+ else
+ draw = elemdrawing[O_PLAYER_PUSH_RIGHT];
+ }
+ }
+
+ // if negative, animated.
if (draw < 0)
draw = -draw + animcycle;
- /* flash */
+ // flash
if (cave->gate_open_flash)
draw += GD_NUM_OF_CELLS;
- /* set to buffer, with caching */
+ // set to buffer, with caching
if (element_buffer[y][x] != map)
element_buffer[y][x] = map;
}
}
-/* cave time is rounded _UP_ to seconds. so at the exact moment when it
- changes from
- 2sec remaining to 1sec remaining, the player has exactly one second.
- when it changes
- to zero, it is the exact moment of timeout. */
-/* internal time is milliseconds (or 1200 milliseconds for pal timing). */
+/*
+ cave time is rounded _UP_ to seconds. so at the exact moment when it
+ changes from
+ 2sec remaining to 1sec remaining, the player has exactly one second.
+ when it changes
+ to zero, it is the exact moment of timeout.
+
+ internal time is milliseconds (or 1200 milliseconds for pal timing).
+*/
int gd_cave_time_show(const GdCave *cave, int internal_time)
{
return (internal_time + cave->timing_factor - 1) / cave->timing_factor;
GdReplay *rep;
rep = checked_calloc(sizeof(GdReplay));
-
- rep->movements = g_byte_array_new();
+ rep->movements = checked_calloc(sizeof(GdReplayMovements));
return rep;
}
{
GdReplay *rep;
- rep = g_memdup(orig, sizeof(GdReplay));
+ rep = get_memcpy(orig, sizeof(GdReplay));
- /* replicate dynamic data */
+ // replicate dynamic data
rep->comment = getStringCopy(orig->comment);
- rep->movements = g_byte_array_new();
- g_byte_array_append(rep->movements, orig->movements->data, orig->movements->len);
+ rep->movements = get_memcpy(orig->movements, sizeof(GdReplayMovements));
return rep;
}
void gd_replay_free(GdReplay *replay)
{
- g_byte_array_free(replay->movements, TRUE);
+ checked_free(replay->movements);
checked_free(replay->comment);
free(replay);
}
-/* store movement in a replay */
+// store movement in a replay
void gd_replay_store_movement(GdReplay *replay, GdDirection player_move,
boolean player_fire, boolean suicide)
{
- guint8 data[1];
+ byte data[1];
data[0] = ((player_move) |
(player_fire ? GD_REPLAY_FIRE_MASK : 0) |
(suicide ? GD_REPLAY_SUICIDE_MASK : 0));
- g_byte_array_append(replay->movements, data, 1);
+ if (replay->movements->len < MAX_REPLAY_LEN)
+ {
+ replay->movements->data[replay->movements->len++] = data[0];
+
+ if (replay->movements->len == MAX_REPLAY_LEN)
+ Warn("BD replay truncated: size exceeds maximum replay size %d", MAX_REPLAY_LEN);
+ }
}
-/* calculate adler checksum for a rendered cave; this can be used for more caves. */
-void gd_cave_adler_checksum_more(GdCave *cave, guint32 *a, guint32 *b)
+// calculate adler checksum for a rendered cave; this can be used for more caves.
+void gd_cave_adler_checksum_more(GdCave *cave, unsigned int *a, unsigned int *b)
{
int x, y;
}
}
-/* calculate adler checksum for a single rendered cave. */
-guint32
-gd_cave_adler_checksum(GdCave *cave)
+// calculate adler checksum for a single rendered cave.
+unsigned int gd_cave_adler_checksum(GdCave *cave)
{
- guint32 a = 1;
- guint32 b = 0;
+ unsigned int a = 1;
+ unsigned int b = 0;
gd_cave_adler_checksum_more(cave, &a, &b);
return (b << 16) + a;
}
-/* return c64 color with index. */
-GdColor gd_c64_color(int index)
+boolean gd_cave_has_levels(GdCave *cave)
{
- return (GD_COLOR_TYPE_C64 << 24) + index;
+ GdCave c = *cave;
+ int *cave_level_value[] =
+ {
+ c.level_diamonds,
+ c.level_speed,
+ c.level_ckdelay,
+ c.level_time,
+ c.level_magic_wall_time,
+ c.level_amoeba_time,
+ c.level_amoeba_threshold,
+ c.level_amoeba_2_time,
+ c.level_amoeba_2_threshold,
+ c.level_slime_permeability,
+ c.level_slime_permeability_c64,
+ c.level_slime_seed_c64,
+ c.level_hatching_delay_frame,
+ c.level_hatching_delay_time,
+ c.level_bonus_time,
+ c.level_penalty_time,
+
+ NULL
+ };
+ int i, j;
+
+ for (i = 0; cave_level_value[i] != NULL; i++)
+ for (j = 1; j < 5; j++)
+ if (cave_level_value[i][j] != cave_level_value[i][0])
+ return TRUE;
+
+ for (j = 1; j < 5; j++)
+ if (cave->level_rand[j] != j &&
+ cave->level_rand[j - 1] != j - 1 &&
+ cave->level_rand[j] != cave->level_rand[0])
+ return TRUE;
+
+ for (j = 1; j < 5; j++)
+ if (cave->level_timevalue[j] != j + 1 &&
+ cave->level_timevalue[j - 1] != j &&
+ cave->level_timevalue[j] != cave->level_timevalue[0])
+ return TRUE;
+
+ return FALSE;
+}
+
+boolean gd_caveset_has_levels(void)
+{
+ List *iter;
+
+ for (iter = gd_caveset; iter != NULL; iter = iter->next)
+ if (gd_cave_has_levels((GdCave *)iter->data))
+ return TRUE;
+
+ return FALSE;
}