X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame_bd%2Fbd_cave.c;h=d946bac4c203a3decaa24f2304211802e40a217c;hb=dc51f089096a0c775d2a03d0b862a14d6ce88d51;hp=a1d528ec507602acc918d2120f66f6b782aa8cb5;hpb=507622f89d162a0f98adc1e482da85aa6ae1cf73;p=rocksndiamonds.git diff --git a/src/game_bd/bd_cave.c b/src/game_bd/bd_cave.c index a1d528ec..d946bac4 100644 --- a/src/game_bd/bd_cave.c +++ b/src/game_bd/bd_cave.c @@ -17,8 +17,8 @@ #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 @@ -28,8 +28,8 @@ const int gd_dy[] = 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"), @@ -81,13 +81,13 @@ static const char* scheduling_filename[] = 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]; @@ -110,7 +110,7 @@ GdDirection gd_direction_from_string(const char *str) 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]; @@ -149,7 +149,7 @@ void gd_struct_set_defaults_from_array(void *str, for (i = 0; defaults[i].offset != -1; i++) { void *pvalue = STRUCT_MEMBER_P(str, defaults[i].offset); - /* these point to the same, but to avoid the awkward cast syntax */ + // these point to the same, but to avoid the awkward cast syntax int *ivalue = pvalue; GdElement *evalue = pvalue; GdDirection *dvalue = pvalue; @@ -158,7 +158,7 @@ void gd_struct_set_defaults_from_array(void *str, 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) { @@ -166,25 +166,25 @@ void gd_struct_set_defaults_from_array(void *str, 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) @@ -193,7 +193,7 @@ void gd_struct_set_defaults_from_array(void *str, 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); @@ -225,19 +225,20 @@ void gd_struct_set_defaults_from_array(void *str, } } -/* 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 */ + // 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; @@ -252,7 +253,7 @@ void gd_create_char_to_element_table(void) } } -/* search the element database for the specified character, and return the element. */ +// 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) @@ -270,9 +271,9 @@ void gd_cave_init(void) { 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++) @@ -281,19 +282,19 @@ void gd_cave_init(void) 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)); @@ -309,14 +310,14 @@ void gd_cave_init(void) 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 = getStringToUpper(string); @@ -329,7 +330,7 @@ GdElement gd_get_element_from_string (const char *string) 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); @@ -356,7 +357,7 @@ void gd_cave_set_gdash_defaults(GdCave* cave) 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; @@ -364,7 +365,7 @@ void gd_cave_set_gdash_defaults(GdCave* cave) } } -/* for quicksort. compares two highscores. */ +// for quicksort. compares two highscores. int gd_highscore_compare(const void *a, const void *b) { const GdHighScore *ha = a; @@ -388,10 +389,10 @@ boolean gd_has_highscore(GdHighScore *hs) 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; @@ -405,11 +406,11 @@ int gd_add_highscore(GdHighScore *highscores, const char *name, int score) 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++) @@ -419,7 +420,7 @@ int gd_add_highscore(GdHighScore *highscores, const char *name, int score) 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; @@ -445,7 +446,7 @@ GdCave *gd_cave_new(void) 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); @@ -453,20 +454,21 @@ GdCave *gd_cave_new(void) 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) */ /* @@ -475,14 +477,14 @@ GdCave *gd_cave_new(void) */ void *gd_cave_map_new_for_cave(const GdCave *cave, const int cell_size) { - void **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(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; @@ -535,32 +537,32 @@ void gd_cave_free(GdCave *cave) if (cave->tags) hashtable_destroy(cave->tags); - if (cave->random) /* random generator is a GdRand * */ + 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(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 */ + // free objects list_foreach(cave->objects, (list_fn) free, NULL); list_free(cave->objects); - /* free 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); } @@ -569,15 +571,15 @@ static void hash_copy_foreach(const char *key, const char *value, HashTable *des 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 */ + // 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) @@ -586,41 +588,41 @@ void gd_cave_copy(GdCave *dest, const GdCave *src) 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) 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) { List *iter; - dest->objects = NULL; /* new empty list */ - for (iter = src->objects; iter != NULL; iter = iter->next) /* do a deep copy */ + 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) { List *iter; dest->replays = NULL; - for (iter = src->replays; iter != NULL; iter = iter->next) /* do a deep copy */ + 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 = 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; @@ -636,24 +638,25 @@ GdCave *gd_cave_new_from_cave(const GdCave *orig) 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) @@ -662,20 +665,20 @@ void gd_cave_store_rc(GdCave *cave, int x, int y, const GdElement element, const 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; @@ -684,8 +687,8 @@ void gd_cave_store_rc(GdCave *cave, int x, int y, const GdElement element, const } } - /* 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; @@ -695,16 +698,16 @@ void gd_cave_store_rc(GdCave *cave, int x, int y, const GdElement element, const 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) { @@ -714,7 +717,7 @@ GdElement gd_cave_get_rc(const GdCave *cave, int x, int y) } 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; @@ -722,7 +725,7 @@ GdElement gd_cave_get_rc(const GdCave *cave, int x, int y) x -= cave->w; } - /* after that, fix y coordinate */ + // after that, fix y coordinate while (y < 0) y += cave->h; @@ -774,6 +777,196 @@ void gd_cave_c64_random_set_seed(GdCave *cave, int seed1, int seed2) gd_c64_random_set_seed(&cave->c64_rand, seed1, seed2); } +/* + select random colors for a given cave. + this function will select colors so that they should look somewhat nice; for example + brick walls won't be the darkest color, for example. +*/ +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). @@ -792,15 +985,15 @@ void gd_cave_auto_shrink(GdCave *cave) } 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 @@ -811,7 +1004,7 @@ void gd_cave_auto_shrink(GdCave *cave) { 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; @@ -821,26 +1014,26 @@ void gd_cave_auto_shrink(GdCave *cave) 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 @@ -857,8 +1050,8 @@ void gd_cave_auto_shrink(GdCave *cave) 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) @@ -875,9 +1068,9 @@ void gd_cave_auto_shrink(GdCave *cave) 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 @@ -907,14 +1100,14 @@ void gd_cave_auto_shrink(GdCave *cave) } } - /* 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 @@ -947,16 +1140,17 @@ void gd_cave_auto_shrink(GdCave *cave) 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; @@ -1033,18 +1227,18 @@ static void cave_set_ckdelay_extra_for_animation(GdCave *cave) 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--) @@ -1059,7 +1253,7 @@ void gd_cave_setup_for_game(GdCave *cave) } else { - /* lowermost player is active */ + // lowermost player is active for (y = 0; y < cave->h; y++) { for (x = 0; x < cave->w; x++) @@ -1073,7 +1267,7 @@ void gd_cave_setup_for_game(GdCave *cave) } } - /* 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; @@ -1086,16 +1280,16 @@ void gd_cave_setup_for_game(GdCave *cave) 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++) @@ -1103,55 +1297,57 @@ void gd_cave_count_diamonds(GdCave *cave) 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. */ + // 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. */ + // 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; @@ -1179,11 +1375,11 @@ void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer 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); @@ -1191,7 +1387,7 @@ void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer 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; @@ -1212,23 +1408,23 @@ void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer 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; @@ -1237,7 +1433,7 @@ void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer if ((cave->last_direction) == GD_MV_STILL) { - /* player is idle. */ + // player is idle. if (player_blinking && player_tapping) { map = O_PLAYER_TAP_BLINK; @@ -1259,6 +1455,16 @@ void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer 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; @@ -1266,7 +1472,7 @@ void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer } 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; } @@ -1277,12 +1483,15 @@ void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer 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); @@ -1291,30 +1500,30 @@ void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer 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) @@ -1329,27 +1538,49 @@ void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer { 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; @@ -1359,12 +1590,15 @@ void gd_drawcave_game(const GdCave *cave, int **element_buffer, int **gfx_buffer } } -/* 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; @@ -1386,7 +1620,7 @@ GdReplay *gd_replay_new_from_replay(GdReplay *orig) rep = get_memcpy(orig, sizeof(GdReplay)); - /* replicate dynamic data */ + // replicate dynamic data rep->comment = getStringCopy(orig->comment); rep->movements = get_memcpy(orig->movements, sizeof(GdReplayMovements)); @@ -1400,7 +1634,7 @@ void gd_replay_free(GdReplay *replay) 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) { @@ -1419,7 +1653,7 @@ void gd_replay_store_movement(GdReplay *replay, GdDirection player_move, } } -/* calculate adler checksum for a rendered cave; this can be used for more caves. */ +// 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; @@ -1435,7 +1669,7 @@ void gd_cave_adler_checksum_more(GdCave *cave, unsigned int *a, unsigned int *b) } } -/* calculate adler checksum for a single rendered cave. */ +// calculate adler checksum for a single rendered cave. unsigned int gd_cave_adler_checksum(GdCave *cave) { unsigned int a = 1;